From 696fb834b8873ac9f6df0c4d45377bdd7c3c5762 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 10 Oct 2026 14:20:22 +0800 Subject: [PATCH] fix(skills): expose authored skill to repository discovery --- .../reverse-engineer-anything/SKILL.md | 0 .../references/android-applications.md | 0 .../references/evidence-workflows.md | 0 .../references/javascript-applications.md | 0 .../references/native-and-artifacts.md | 0 .../references/runtime-observation.md | 0 .github/pull_request_template.md | 2 +- AGENTS.md | 2 +- CONTRIBUTING.md | 8 +- docs/installation.md | 2 +- scripts/generate-package-metadata.mjs | 2 +- scripts/generate-skill-metadata.mjs | 4 +- scripts/verify-package-pack.mjs | 2 +- scripts/verify-package.mjs | 4 + scripts/verify-repository-skill.mjs | 97 +++++++++++++++++++ .../filesystem/dependencyInstall.test.ts | 2 +- .../filesystem/generatedSkill.test.ts | 2 +- turbo.json | 6 +- 18 files changed, 119 insertions(+), 14 deletions(-) rename {skill-src => .agents/skills}/reverse-engineer-anything/SKILL.md (100%) rename {skill-src => .agents/skills}/reverse-engineer-anything/references/android-applications.md (100%) rename {skill-src => .agents/skills}/reverse-engineer-anything/references/evidence-workflows.md (100%) rename {skill-src => .agents/skills}/reverse-engineer-anything/references/javascript-applications.md (100%) rename {skill-src => .agents/skills}/reverse-engineer-anything/references/native-and-artifacts.md (100%) rename {skill-src => .agents/skills}/reverse-engineer-anything/references/runtime-observation.md (100%) create mode 100644 scripts/verify-repository-skill.mjs diff --git a/skill-src/reverse-engineer-anything/SKILL.md b/.agents/skills/reverse-engineer-anything/SKILL.md similarity index 100% rename from skill-src/reverse-engineer-anything/SKILL.md rename to .agents/skills/reverse-engineer-anything/SKILL.md diff --git a/skill-src/reverse-engineer-anything/references/android-applications.md b/.agents/skills/reverse-engineer-anything/references/android-applications.md similarity index 100% rename from skill-src/reverse-engineer-anything/references/android-applications.md rename to .agents/skills/reverse-engineer-anything/references/android-applications.md diff --git a/skill-src/reverse-engineer-anything/references/evidence-workflows.md b/.agents/skills/reverse-engineer-anything/references/evidence-workflows.md similarity index 100% rename from skill-src/reverse-engineer-anything/references/evidence-workflows.md rename to .agents/skills/reverse-engineer-anything/references/evidence-workflows.md diff --git a/skill-src/reverse-engineer-anything/references/javascript-applications.md b/.agents/skills/reverse-engineer-anything/references/javascript-applications.md similarity index 100% rename from skill-src/reverse-engineer-anything/references/javascript-applications.md rename to .agents/skills/reverse-engineer-anything/references/javascript-applications.md diff --git a/skill-src/reverse-engineer-anything/references/native-and-artifacts.md b/.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md similarity index 100% rename from skill-src/reverse-engineer-anything/references/native-and-artifacts.md rename to .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md diff --git a/skill-src/reverse-engineer-anything/references/runtime-observation.md b/.agents/skills/reverse-engineer-anything/references/runtime-observation.md similarity index 100% rename from skill-src/reverse-engineer-anything/references/runtime-observation.md rename to .agents/skills/reverse-engineer-anything/references/runtime-observation.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 8ffbeb73b..e33ce01f4 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -28,7 +28,7 @@ intentionally not included. --> - Provider, bridge, target-format, or platform compatibility: - Evidence, artifact, provenance, or reconstruction contract: - Process execution, authorization, cleanup, or containment impact: -- Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `skill-src/` and never commit build outputs): +- Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs): ## Evidence and regression coverage diff --git a/AGENTS.md b/AGENTS.md index 15d3984ff..6e5a2df44 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,7 +35,7 @@ Use [.nvmrc](.nvmrc) and `package.json#packageManager` for the pinned toolchain. Use relevant checks for the change. The complete local gate, `npm run check:pr`, is optional for broad changes or CI diagnosis. See [CONTRIBUTING.md](CONTRIBUTING.md) for development, generated-file ownership, and release conventions. -`docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored outputs. Edit source contracts and `skill-src/`, then regenerate as needed. `.cache/mcp-tool-catalog.json` is test metadata; runtime builds and source checking do not consume it. Never commit binaries, provider project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts. +`docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/` are ignored outputs. Edit source contracts and `.agents/skills/reverse-engineer-anything/`, then regenerate as needed. `.cache/mcp-tool-catalog.json` is test metadata; runtime builds and source checking do not consume it. Never commit binaries, provider project documents, credentials, `dist/`, `node_modules/`, or local planning artifacts. ## Tool and Test Changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 07c71c4c5..9f4e6969b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -95,8 +95,12 @@ Pre-commit formats and lints staged files; pre-push runs `check:fast`. `evidence:generate` prepares only the managed commitment and its runtime/skill dependencies. The ignored outputs are `docs/public/product-catalog.json`, `docs/verification/managed-conformance-*.json`, and `skills/`. Edit authored -skill instructions in `skill-src/`. CI validates generated outputs and retains -them as artifacts; do not commit them. Reviewed source metadata, including +skill instructions in `.agents/skills/reverse-engineer-anything/`, where Skills +can discover the public bundle directly from a clean checkout. `verify:package` +installs that bundle with Skills 1.7.2 using default repository-root discovery +and checks every installed file, alongside the packaged setup checks. +CI validates generated outputs and retains them as artifacts; do not commit +them. Reviewed source metadata, including `src/generatedPackageMetadata.ts` and `docs/error-contract.schema.json`, remains tracked and checked for freshness. diff --git a/docs/installation.md b/docs/installation.md index e54cc73ae..38ea09f5a 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -95,7 +95,7 @@ npx skills add morluto/rea --skill reverse-engineer-anything This installs agent instructions and bundled references, not REA MCP registration or analysis engines. Follow the skill's -[conditional connection guide](https://github.com/morluto/rea/blob/main/skill-src/reverse-engineer-anything/SKILL.md#connect-only-when-needed). +[conditional connection guide](https://github.com/morluto/rea/blob/main/.agents/skills/reverse-engineer-anything/SKILL.md#connect-only-when-needed). Working tools can be used immediately. If tools are missing, inspect the current client's registration with `doctor --client codex --json` (substitute its client ID), then plan repairs with `setup --client codex --dry-run --json`. Show and diff --git a/scripts/generate-package-metadata.mjs b/scripts/generate-package-metadata.mjs index 8247dac1c..0130401b1 100644 --- a/scripts/generate-package-metadata.mjs +++ b/scripts/generate-package-metadata.mjs @@ -17,7 +17,7 @@ const lock = JSON.parse( await readFile(join(root, "package-lock.json"), "utf8"), ); const skill = await readFile( - join(root, "skill-src/reverse-engineer-anything/SKILL.md"), + join(root, ".agents/skills/reverse-engineer-anything/SKILL.md"), "utf8", ); const skillVersion = /^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(skill)?.[1]; diff --git a/scripts/generate-skill-metadata.mjs b/scripts/generate-skill-metadata.mjs index 5bb2e3ccf..3ca0a467d 100644 --- a/scripts/generate-skill-metadata.mjs +++ b/scripts/generate-skill-metadata.mjs @@ -10,7 +10,7 @@ for (const argument of arguments_) throw new Error(`Unknown skill metadata option: ${argument}`); const root = join(dirname(fileURLToPath(import.meta.url)), ".."); -const sourceRoot = join(root, "skill-src/reverse-engineer-anything"); +const sourceRoot = join(root, ".agents/skills/reverse-engineer-anything"); const outputRoot = join(root, "skills/reverse-engineer-anything"); const check = arguments_.has("--check"); const current = ( @@ -25,7 +25,7 @@ const { PRODUCT_IDENTITY } = await import( ); if (/^\s{2}(?:tool_count|catalog_digest):/mu.test(current)) throw new Error( - "Catalog metadata belongs in the generated skill, not skill-src", + "Catalog metadata belongs in the generated skill, not the authored source", ); const versionLine = /^ {2}version: "[^"\r\n]+"$/mu; if (!versionLine.test(current)) diff --git a/scripts/verify-package-pack.mjs b/scripts/verify-package-pack.mjs index 96f05417b..fc9676745 100644 --- a/scripts/verify-package-pack.mjs +++ b/scripts/verify-package-pack.mjs @@ -38,7 +38,7 @@ export async function verifyPackagePack({ root, workspace }) { throw new Error("package did not expose both rea command entry points"); if (packedManifest.mcpName !== "io.github.morluto/rea") throw new Error("package did not retain its MCP Registry ownership marker"); - if (packedFiles.some((path) => path.startsWith("package/skill-src/"))) + if (packedFiles.some((path) => path.startsWith("package/.agents/"))) throw new Error( "package included authored skill sources instead of only the generated bundle", ); diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs index 24923f7f6..b70b06064 100644 --- a/scripts/verify-package.mjs +++ b/scripts/verify-package.mjs @@ -17,6 +17,7 @@ import { verifyPackageMcp } from "./verify-package-mcp.mjs"; import { verifyPackagePack } from "./verify-package-pack.mjs"; import { verifyPackagePlatform } from "./verify-package-platform.mjs"; import { verifyPackageSetup } from "./verify-package-setup.mjs"; +import { verifyRepositorySkill } from "./verify-repository-skill.mjs"; import { verifyManaged } from "./verify-package-managed.mjs"; import { verifyUnknownProvider } from "./verify-package-unknown-provider.mjs"; import { completeVerifierRun, createVerifierRun } from "./lib/verifier-run.mjs"; @@ -34,9 +35,11 @@ let tarball; let mcpStartup; let mcpModuleLoading; let update; +let repositorySkill; let report; try { + repositorySkill = await verifyRepositorySkill({ root, workspace }); ({ tarball } = await verifyPackagePack({ root, workspace })); const environmentData = await verifyPackageEnvironment({ root, @@ -156,6 +159,7 @@ try { configSymlinkLifecycle: supportedSetupHost, skill: supportedSetupHost, skillReferences: supportedSetupHost, + repositorySkill, mcpTools: TOOL_CONTRACTS.length, mcpPrompts: prompts.names.length, promptCompletion: true, diff --git a/scripts/verify-repository-skill.mjs b/scripts/verify-repository-skill.mjs new file mode 100644 index 000000000..8340bce7b --- /dev/null +++ b/scripts/verify-repository-skill.mjs @@ -0,0 +1,97 @@ +import { cp, mkdir, readFile, readdir } from "node:fs/promises"; +import { dirname, join, relative } from "node:path"; + +import { exec, pathExists } from "./lib/verify-package-core.mjs"; + +/** Exercise Skills' default root discovery without ignored build outputs. */ +export async function verifyRepositorySkill({ root, workspace }) { + const snapshot = join(workspace, "repository"); + const consumer = join(workspace, "skill-consumer"); + const home = join(workspace, "skill-consumer-home"); + const tracked = ( + await exec( + "git", + ["ls-files", "--cached", "--format=%(objectmode) %(path)", "-z"], + { cwd: root }, + ) + ).stdout + .split("\0") + .filter(Boolean); + for (const entry of tracked) { + const mode = entry.slice(0, 6); + const path = entry.slice(7); + const destination = join(snapshot, path); + // A GitHub clone without submodule initialization has only these directories. + if (mode === "160000") { + await mkdir(destination, { recursive: true }); + continue; + } + await mkdir(dirname(destination), { recursive: true }); + await cp(join(root, path), destination, { verbatimSymlinks: true }); + } + if (await pathExists(join(snapshot, "skills"))) + throw new Error("repository skill check must run without generated skills"); + await mkdir(consumer, { recursive: true }); + await mkdir(home, { recursive: true }); + const skillsVersion = "1.7.2"; + await exec( + "npm", + [ + "exec", + "--yes", + "--package", + `skills@${skillsVersion}`, + "--", + "skills", + "add", + snapshot, + "--skill", + "reverse-engineer-anything", + "--agent", + "codex", + "--yes", + ], + { + cwd: consumer, + env: { + ...process.env, + HOME: home, + USERPROFILE: home, + XDG_CONFIG_HOME: join(home, ".config"), + CODEX_HOME: join(home, ".codex"), + DO_NOT_TRACK: "1", + SKILLS_NO_TELEMETRY: "1", + CI: "1", + }, + timeout: 120_000, + }, + ); + const authoredRoot = join( + snapshot, + ".agents/skills/reverse-engineer-anything", + ); + const installedSkills = join(consumer, ".agents/skills"); + const installedRoot = join(installedSkills, "reverse-engineer-anything"); + const files = await filePaths(authoredRoot); + if ( + JSON.stringify(await readdir(installedSkills)) !== + JSON.stringify(["reverse-engineer-anything"]) || + JSON.stringify(await filePaths(installedRoot)) !== JSON.stringify(files) + ) + throw new Error( + "Skills did not install exactly the selected public bundle", + ); + for (const path of files) { + const authored = await readFile(join(authoredRoot, path)); + if (!authored.equals(await readFile(join(installedRoot, path)))) + throw new Error(`Skills changed authored skill content: ${path}`); + } + return { skillsVersion, files }; +} + +async function filePaths(root) { + return (await readdir(root, { recursive: true, withFileTypes: true })) + .filter((entry) => entry.isFile()) + .map((entry) => relative(root, join(entry.parentPath, entry.name))) + .sort(); +} diff --git a/tests/boundary/filesystem/dependencyInstall.test.ts b/tests/boundary/filesystem/dependencyInstall.test.ts index 66378360f..806d91b32 100644 --- a/tests/boundary/filesystem/dependencyInstall.test.ts +++ b/tests/boundary/filesystem/dependencyInstall.test.ts @@ -169,7 +169,7 @@ const createBuildCacheFixture = async (): Promise => { "scripts/clean-build-output.mjs", "scripts/generate-package-metadata.mjs", "scripts/lib/generated-file.mjs", - "skill-src/reverse-engineer-anything/SKILL.md", + ".agents/skills/reverse-engineer-anything/SKILL.md", "src/generatedPackageMetadata.ts", ]; await Promise.all( diff --git a/tests/boundary/filesystem/generatedSkill.test.ts b/tests/boundary/filesystem/generatedSkill.test.ts index 5df6e3146..2b7ebf754 100644 --- a/tests/boundary/filesystem/generatedSkill.test.ts +++ b/tests/boundary/filesystem/generatedSkill.test.ts @@ -21,7 +21,7 @@ async function fixture(eol = "\n") { await mkdir(dirname(join(root, path)), { recursive: true }); await copyFile(join(repositoryRoot, path), join(root, path)); } - const sourceRoot = join(root, "skill-src/reverse-engineer-anything"); + const sourceRoot = join(root, ".agents/skills/reverse-engineer-anything"); const outputRoot = join(root, "skills/reverse-engineer-anything"); await mkdir(join(sourceRoot, "references"), { recursive: true }); await mkdir(join(root, "dist")); diff --git a/turbo.json b/turbo.json index 392523708..5b93503d7 100644 --- a/turbo.json +++ b/turbo.json @@ -39,7 +39,7 @@ "package-lock.json", "scripts/generate-package-metadata.mjs", "scripts/lib/generated-file.mjs", - "skill-src/reverse-engineer-anything/SKILL.md", + ".agents/skills/reverse-engineer-anything/SKILL.md", "src/generatedPackageMetadata.ts" ] }, @@ -138,7 +138,7 @@ "docs/**", "!docs/public/product-catalog.json", "!docs/verification/managed-conformance-*.json", - "skill-src/**", + ".agents/skills/reverse-engineer-anything/**", "README.md", { "mode": "dependencyOutputs", @@ -170,7 +170,7 @@ "package-lock.json", "scripts/generate-skill-metadata.mjs", "scripts/lib/generated-file.mjs", - "skill-src/**" + ".agents/skills/reverse-engineer-anything/**" ], "outputs": ["skills/**"] },