From 78667dd93c7116fa9c440a4b5d369121353dc468 Mon Sep 17 00:00:00 2001 From: artale Date: Sat, 4 Jul 2026 15:26:33 +0200 Subject: [PATCH] feat: port opensoft/oh-my-opencode as learn-only external project review --- COMMANDS.md | 2 ++ src/fable5/external-project-review.test.ts | 8 ++++++++ src/fable5/external-project-review.ts | 16 ++++++++++++++-- src/fable5/index.ts | 2 +- src/index.ts | 15 +++++++++++++++ 5 files changed, 40 insertions(+), 3 deletions(-) diff --git a/COMMANDS.md b/COMMANDS.md index 556d3a2..c4dd372 100644 --- a/COMMANDS.md +++ b/COMMANDS.md @@ -388,6 +388,8 @@ fable-agent plinius godmode "improve explanation quality" - `--output ` - `fable5 opencoven-review` - `--output ` +- `fable5 opencode-review` + - `--output ` - `fable5 permission-posture` - `--mode ` - `--output ` diff --git a/src/fable5/external-project-review.test.ts b/src/fable5/external-project-review.test.ts index e5bf923..f6a1d03 100644 --- a/src/fable5/external-project-review.test.ts +++ b/src/fable5/external-project-review.test.ts @@ -47,6 +47,14 @@ describe("external project review receipts", () => { expect(receipt.risks).toContain("security findings need local repro receipts"); }); + it("records oh-my-opencode as learn-only OpenCode config research", () => { + const receipt = createSkillsRepoReviewReceipt("opensoft/oh-my-opencode", new Date("2026-07-03T00:00:00.000Z")); + + expect(receipt).toMatchObject({ repo: "opensoft/oh-my-opencode", license: "NOASSERTION", safeToVendor: false, decision: "learn-only", deployAttempted: false }); + expect(receipt.patternsLearned).toContain("OpenCode workflow/config packaging"); + expect(receipt.risks).toContain("OpenCode config must not become deploy authority"); + }); + it("records deploy-gated permission posture without opening 8098", () => { const receipt = createPermissionPostureReceipt("deploy-gated", new Date("2026-07-01T00:00:00.000Z")); diff --git a/src/fable5/external-project-review.ts b/src/fable5/external-project-review.ts index 95465fd..f8dde82 100644 --- a/src/fable5/external-project-review.ts +++ b/src/fable5/external-project-review.ts @@ -60,8 +60,10 @@ export function createOpenCovenReviewReceipt(now = new Date()): ExternalProjectR } -export function createSkillsRepoReviewReceipt(repo: "mattpocock/skills" | "AI-Builder-Club/skills" | "affaan-m/ECC" | "ashp15205/scankii", now = new Date()): ExternalProjectReviewReceipt { - const rows: Record> = { +export type ReviewedExternalRepo = "mattpocock/skills" | "AI-Builder-Club/skills" | "affaan-m/ECC" | "ashp15205/scankii" | "opensoft/oh-my-opencode"; + +export function createSkillsRepoReviewReceipt(repo: ReviewedExternalRepo, now = new Date()): ExternalProjectReviewReceipt { + const rows: Record> = { "mattpocock/skills": { url: "https://github.com/mattpocock/skills", license: "MIT", @@ -102,6 +104,16 @@ export function createSkillsRepoReviewReceipt(repo: "mattpocock/skills" | "AI-Bu decision: "learn-only", policy: "learn-patterns-no-vendored-code", }, + "opensoft/oh-my-opencode": { + url: "https://github.com/opensoft/oh-my-opencode", + license: "NOASSERTION", + safeToVendor: false, + patternsLearned: ["OpenCode workflow/config packaging", "agent CLI ergonomics", "portable local agent defaults"], + adaptations: ["keep OpenCode behind existing CLI executor seams", "record repo as advisory until contents/license are verified", "rewrite useful config patterns locally with tests"], + risks: ["repository content was not fetched or verified", "license is not asserted so vendoring stays blocked", "OpenCode config must not become deploy authority"], + decision: "learn-only", + policy: "learn-patterns-no-vendored-code", + }, }; return { schema: "fable.external_project.review.v1", createdAt: now.toISOString(), repo, ...rows[repo], deployAttempted: false }; } diff --git a/src/fable5/index.ts b/src/fable5/index.ts index d959bd6..dd32d46 100644 --- a/src/fable5/index.ts +++ b/src/fable5/index.ts @@ -64,7 +64,7 @@ export { createExternalToolAuditReceipt, DICKLESWORTHSTONE_TOOL_ROWS, writeExter export type { ExternalToolAuditReceipt, ExternalToolAuditRow, ExternalToolAuditStatus } from "./external-tool-audit.js"; export { createOpenCovenReviewReceipt, createPermissionPostureReceipt, createSkillsRepoReviewReceipt, writeExternalProjectReviewReceipt } from "./external-project-review.js"; -export type { ExternalProjectDecision, ExternalProjectReviewReceipt, PermissionMode, PermissionPostureReceipt } from "./external-project-review.js"; +export type { ExternalProjectDecision, ExternalProjectReviewReceipt, PermissionMode, PermissionPostureReceipt, ReviewedExternalRepo } from "./external-project-review.js"; export { createFactoryMemoryQuarantineReceipt, createStaleMemoryQuarantineReceipt, writeStaleMemoryQuarantineReceipt } from "./stale-memory-quarantine.js"; export type { StaleMemoryClaim, StaleMemoryQuarantineReceipt, StaleMemorySeverity } from "./stale-memory-quarantine.js"; diff --git a/src/index.ts b/src/index.ts index 1e57c36..08d6d16 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2287,6 +2287,21 @@ fable `); }); +fable + .command("opencode-review") + .description("Write a learn-only opensoft/oh-my-opencode external project review receipt") + .option("--output ", "Receipt output path", path.join(".fable", "research", "opencode-review-live.json")) + .action(async (opts: { output?: string }) => { + const { createSkillsRepoReviewReceipt, writeExternalProjectReviewReceipt } = await import("./fable5/external-project-review.js"); + const receipt = createSkillsRepoReviewReceipt("opensoft/oh-my-opencode"); + const out = opts.output ?? path.join(".fable", "research", "opencode-review-live.json"); + writeExternalProjectReviewReceipt(out, receipt); + console.log(JSON.stringify(receipt, null, 2)); + console.log(` + Receipt: ${out} +`); + }); + fable .command("permission-posture") .description("Write an explicit permission posture receipt")