Skip to content

feat(skills): plugin resource shipping — skills carry their own resources - #2540

Merged
namastex888 merged 4 commits into
devfrom
wish/plugin-resource-shipping
Jul 10, 2026
Merged

namastex888 merged 4 commits into
devfrom
wish/plugin-resource-shipping

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Summary

Fresh plugin installs break /wish outside the genie repo: the skill pointed at repo-root templates/wish-template.md and ran the repo-only wish linter (observed live 2026-07-09). This wish makes skills self-contained and the regression class mechanically impossible.

  • G1 — wish template moves INTO the skill (skills/wish/templates/), scaffold via ${CLAUDE_SKILL_DIR}; repo-only lint behind a same-line package.json probe; e2e/README/prose consumers repointed (paraphrase rule keeps prose lint-clean)
  • G2 — resource-shipping lint rule in scripts/skills-lint.ts (scans fences AND inline-code spans; same-line probe discriminator; genie-hacks allowlist) + 15 fixture tests
  • G3 — scripts/fresh-install-smoke.ts (${CLAUDE_SKILL_DIR} resolution + bare-repo scaffold with no genie on PATH) wired into CI's unit job + release-lag note in the plugin README

Execution

Wish: .genie/wishes/plugin-resource-shipping/WISH.md (plan review SHIP ×3 loops). Executed via orchestrated subagents under the routing matrix (engineers opus·high, reviewers opus·xhigh, final gate fable·high) — first wish run on the new routing economics. All groups 0 fix loops; per-group reviews SHIP; final gate proved all 5 success criteria, full check 773 pass / 1 skip / 0 fail. Branch rebuilt off origin/dev after a concurrent session switched the shared checkout (details in the wish's execution review).

Follow-ups (LOW, noted in wish)

  • smoke temp-dir cleanup bypassed by process.exit on one failure path
  • same-line guard discriminator is substring-based
  • live installed-plugin scaffold QA after next release

@coderabbitai

ghost commented Jul 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dddabe67-0a41-4e29-85b0-63ae1887be95

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wish/plugin-resource-shipping

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@namastex888
namastex888 merged commit ed710e9 into dev Jul 10, 2026

ghost left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a fresh-install smoke test and a skills linter to ensure that skills resolve their resources correctly using ${CLAUDE_SKILL_DIR} and do not invoke unguarded repo-only commands. It also updates various skill documents and end-to-end tests to align with these new linting rules, and documents the plugin update cadence. The review feedback highlights a resource leak in the smoke test where process.exit(1) bypasses temporary directory cleanup, and suggests a more robust regex in the linter to detect repo-root script invocations with path prefixes or alternative runners.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +29 to +32
function fail(message: string): never {
console.error(`fresh-install-smoke: FAIL — ${message}`);
process.exit(1);
}

ghost Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling process.exit(1) inside fail immediately terminates the process, which bypasses the finally block in runWishScaffoldSmoke and leaves temporary directories on disk. Changing fail to throw an error allows the finally block to execute and clean up the temporary directories before the process exits.

function fail(message: string): never {
  throw new Error(message);
}

Comment on lines +145 to +152
function main(): void {
const { skillsDir } = parseArgs(process.argv.slice(2));
if (!existsSync(skillsDir)) fail(`skills dir not found: ${skillsDir}`);
const refs = checkSkillDirReferences(skillsDir);
runWishScaffoldSmoke(skillsDir);
const summary = `${refs} \${CLAUDE_SKILL_DIR} reference(s) resolved, wish scaffold works with no genie on PATH`;
console.log(`fresh-install-smoke: OK (${summary})`);
}

ghost Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Wrap the main execution in a try-catch block to catch the error thrown by fail, print the failure message, and exit with code 1. This ensures that any errors thrown during the smoke test are gracefully handled and the process exits with a non-zero code after cleaning up temporary directories.

Suggested change
function main(): void {
const { skillsDir } = parseArgs(process.argv.slice(2));
if (!existsSync(skillsDir)) fail(`skills dir not found: ${skillsDir}`);
const refs = checkSkillDirReferences(skillsDir);
runWishScaffoldSmoke(skillsDir);
const summary = `${refs} \${CLAUDE_SKILL_DIR} reference(s) resolved, wish scaffold works with no genie on PATH`;
console.log(`fresh-install-smoke: OK (${summary})`);
}
function main(): void {
try {
const { skillsDir } = parseArgs(process.argv.slice(2));
if (!existsSync(skillsDir)) fail(`skills dir not found: ${skillsDir}`);
const refs = checkSkillDirReferences(skillsDir);
runWishScaffoldSmoke(skillsDir);
const summary = `${refs} \${CLAUDE_SKILL_DIR} reference(s) resolved, wish scaffold works with no genie on PATH`;
console.log(`fresh-install-smoke: OK (${summary})`);
} catch (err) {
console.error(`fresh-install-smoke: FAIL — ${(err as Error).message}`);
process.exit(1);
}
}

Comment thread scripts/skills-lint.ts
Comment on lines +183 to +185
if (/(?:\bbun run |\bbun |\bnode |\.\/|\bsh |\bbash )scripts\/[A-Za-z0-9_./-]+\.ts\b/.test(line)) {
violations.push({ rule: 'repo-script-invocation', snippet });
}

ghost Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The regex for detecting repo-root script invocations does not match when there is a path prefix (like ./) between the execution command and scripts/ (e.g., bash ./scripts/foo.ts or bun ./scripts/foo.ts). Additionally, it doesn't match other common runners like tsx or ts-node. Updating the regex to allow optional ./ prefixes and more runners makes the lint rule more robust.

Suggested change
if (/(?:\bbun run |\bbun |\bnode |\.\/|\bsh |\bbash )scripts\/[A-Za-z0-9_./-]+\.ts\b/.test(line)) {
violations.push({ rule: 'repo-script-invocation', snippet });
}
if (/(?:\bbun run |\bbun |\bnode |\.\/|\bsh |\bbash |\btsx |\bts-node )\s*(?:\.\/)?scripts\/[A-Za-z0-9_./-]+\.ts\b/.test(line)) {
violations.push({ rule: 'repo-script-invocation', snippet });
}

ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a3143b437

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +92 to +93
const stdout = execFileSync('bun', [SCRIPT], {
env: { ...process.env, SKILLS_LINT_DIR: dir },

ghost Jul 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make fixture lint tests independent of a built genie binary

In a clean checkout where dist/genie.js has not been built and no global genie is on PATH, these new fixture tests fail before exercising the resource rules because the spawned skills-lint.ts immediately probes genie --help (/bin/sh: 1: genie: not found). CI happens to run bun run build before bun test, but the documented bun test workflow now fails locally; the test should stub/provide the CLI surface or otherwise avoid requiring a prior build.

Useful? React with 👍 / 👎.

Comment thread skills/wish/SKILL.md
```
Tasks carry the `--wish`/`--group` linkage; the dependency DAG stays in the WISH.md document, not in task rows. If creation fails (no `.genie/genie.db` yet, CLI unavailable), warn and continue — WISH.md in git is the source of truth and must remain usable by `/work` without task rows.
9. **Handoff:** run `bun run wishes:lint`. If it reports any error, surface it and stop — never hand a structurally broken wish onward. Only after lint passes, auto-invoke `/review` (plan review) on the WISH.md. Never suggest `/work` directly — the review gate comes first.
9. **Handoff:** run the wish linter — inside the genie repo, `grep -q '"wishes:lint"' package.json 2>/dev/null && bun run wishes:lint`. If it reports any error, surface it and stop — never hand a structurally broken wish onward. Only after lint passes, auto-invoke `/review` (plan review) on the WISH.md. Never suggest `/work` directly — the review gate comes first.

ghost Jul 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the optional wish-lint guard return success when absent

For fresh plugin consumers that are not the genie repo, this guarded command still exits non-zero when package.json is missing (grep exits 2) or lacks the script (grep exits 1), so following /wish can still be treated as a failed handoff before /review even though the repo-only linter is supposed to be optional outside the genie repo. Use a guard form that returns success when the script is unavailable but preserves bun run wishes:lint failures when it is present, such as a one-line if grep ...; then bun run ...; fi.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant