diff --git a/bin/ccam.js b/bin/ccam.js index d3d93e6..47c4403 100755 --- a/bin/ccam.js +++ b/bin/ccam.js @@ -1520,6 +1520,27 @@ async function cmdLanesAdd(args) { console.log(`${c.green("✔")} Created lane #${lane.id}: ${lane.title}`); } +/** + * `ccam skills install` — copy this checkout's `.claude/skills/ship-feature-lane/` + * into `~/.claude/skills/ship-feature-lane/`, so `/ship-feature-lane` is + * discoverable from a session running inside any lane's own working + * directory, not just inside this repo. Pure filesystem action; does not + * talk to the dashboard server at all. Reinstall, not merge — overwrites an + * existing global copy with the current one. + */ +function cmdSkillsInstall() { + const src = path.join(REPO_ROOT, ".claude", "skills", "ship-feature-lane"); + if (!fs.existsSync(src)) { + console.error(`✖ not found: ${src}`); + process.exitCode = 1; + return; + } + const dest = path.join(require("node:os").homedir(), ".claude", "skills", "ship-feature-lane"); + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.cpSync(src, dest, { recursive: true, force: true }); + console.log(`${c.green("✔")} installed ship-feature-lane skill -> ${dest}`); +} + /** * `ccam lanes profile init ` — detect a Node.js project and scaffold * `.ccam/profile/`. Pure filesystem action against the SOURCE repo; does not @@ -2412,6 +2433,11 @@ const COMMAND_GROUPS = [ " []", "Check whether a named integration (tracker, dev_qc, ci_wait) is on for this lane — exit 0/1", ], + [ + "skills install", + "", + "Install .claude/skills/ship-feature-lane/ into ~/.claude/skills/ so /ship-feature-lane works from any lane", + ], [ "lock status|acquire|release", "[] [--holder X] [--timeout N]", @@ -3240,6 +3266,12 @@ async function runCommand(argv) { return cmdLanesIntegration(rest.slice(1)); } return cmdLanes(); + case "skills": { + if (rest[0] === "install") return cmdSkillsInstall(); + console.error("usage: ccam skills install"); + process.exitCode = 1; + return; + } case "lock": { const sub = rest[0]; if (sub === "status") return cmdLockStatus(rest.slice(1));