refactor(lanes): drop the workflow skills and Superpowers link; pipelines go dormant

The plugin stops shipping its workflow layer:
- skills/ship-feature and skills/ship-feature-lane, the 18 stage/ship slash
  commands (plugin.json keeps agenthub-doctor/update/open), and all four
  agents/ (qc-local, senior-gate-reviewer, backend-reviewer, frontend-reviewer).
- CLI `agenthub skills install`, `agenthub lanes agents install`,
  `agenthub ship-full|ship-quick`; routes POST /api/skills/install and
  POST /api/lanes/:id/agents/install; the Settings button, the lane Tools
  menu item and the Add-lane setup step that called them.
- The Superpowers auto-install: server/lib/skill-plugins.js, the bootstrap's
  autoInstallSkillPlugins / skillPluginsSettled gate and the doctor row.

Pipelines are kept but dormant: server/data/pipelines/ is deleted, so no
template ships. `default` resolves to EMPTY_PIPELINE (no nodes, progress 0),
createLane still accepts it, POST /api/lanes/:id/stage answers 400
ENOPIPELINE, and the Workspace hides the stage timer, pickers, PipelineMap
and strip stage chip for a lane with no nodes. A template dropped into
DASHBOARD_PIPELINES_DIR turns it all back on. The server suite loads its
templates from server/__tests__/fixtures/pipelines/.

Docs: CLAUDE.md (marker, retired rules), README(.vi), INSTALL, ARCHITECTURE,
docs/LANES|PLUGINS|CLI|API, agenthub-introduction.html.
This commit is contained in:
2026-09-28 17:44:27 +07:00
parent 3c238bc5c4
commit 2b8f83d2dd
72 changed files with 325 additions and 3305 deletions
+1 -156
View File
@@ -1654,27 +1654,6 @@ async function cmdLanesAdd(args) {
console.log(`${c.green("✔")} Created lane #${lane.id}: ${lane.title}`);
}
/**
* `agenthub skills install` — copy this checkout's `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 { installShipFeatureLaneSkill } = require(
path.join(REPO_ROOT, "server", "lib", "skills-install.js")
);
try {
const result = installShipFeatureLaneSkill({ repoRoot: REPO_ROOT });
console.log(`${c.green("✔")} installed ship-feature-lane skill -> ${result.path}`);
} catch (err) {
console.error(`✖ ${err.message}`);
process.exitCode = 1;
}
}
/**
* `agenthub lanes gc [--dry-run]` — reap orphaned Playwright MCP processes
* (owning session died) and cap oversized hook logs, across every lane on
@@ -2417,25 +2396,6 @@ async function cmdLanesProofLink(args) {
);
}
/** `agenthub lanes agents install [<id>] [--cwd path]` — write the ship-feature-lane
* agent templates (qc-local, senior-gate-reviewer) into <lane>/.claude/agents/,
* git-excluded. Never run automatically; a session calls it explicitly. */
async function cmdLanesAgentsInstall(args) {
const resolved = await resolveLaneArg(args);
if (!resolved) return;
const result = await post(
`/api/lanes/${resolved.laneId}/agents/install`,
{},
{ allowError: true }
);
if (result.status) {
console.error(`✖ agents install → ${result.data?.error?.message || result.status}`);
process.exitCode = 1;
return;
}
console.log(`${c.green("✔")} installed: ${result.installed.join(", ")}`);
}
/** `agenthub lanes mcp sync [<id>] [--cwd path]` — relocate the source repo's
* already-configured MCP servers into <lane>/.mcp.json and seed Chromium
* profiles. Never run automatically; a session calls it explicitly. */
@@ -2514,8 +2474,7 @@ async function cmdLanesCi(args) {
* was display-only: the dashboard rendered a copy button and the operator
* pasted it into a session by hand. This runs it, in the lane's own cwd:
*
* - a slash command (`/superpowers:brainstorming`) starts `claude` with it
* as the prompt, exactly like `agenthub ship-full` does with the whole skill;
* - a slash command starts `claude` with it as the prompt;
* - anything else (`npm test`, `agenthub lanes hook ci-gate`) is spawned directly.
*
* With no node id it lists the nodes and their commands, which is also the
@@ -2678,88 +2637,6 @@ async function cmdLanesShell(args) {
});
}
/**
* Start a `claude` session in this lane driven by a pipeline's own skill.
* Two commands, two depths of work:
*
* agenthub ship-full → the 16-node `ship-feature` template, `/agenthub:ship-feature-lane`
* — autonomous to a published PR, then watched until a
* human merges it.
* agenthub ship-quick → the 8-node `default` template, `/agenthub:ship-feature`
* — in-repo feature work that stops at review/gate.
*
* Each command puts the lane on its own template, so the choice is the command
* you type; the skill string itself lives in the template's `skill` field and
* is never duplicated here.
*
* The prompt is passed as ONE argv element to `claude`, never through a shell —
* a requirement containing quotes, `$`, or `;` is text, not code. `--print`
* shows the exact command, switches nothing, and runs nothing.
*/
const SHIP_TEMPLATES = {
"ship-full": "ship-feature",
"ship-quick": "default",
};
async function cmdShip(command, args) {
const templateId = SHIP_TEMPLATES[command];
const { flags, positional } = parseArgs(args);
const resolved = await resolveLaneArg(args);
if (!resolved) return;
let { lane } = await get(`/api/lanes/${resolved.laneId}`);
const { pipelines } = await get("/api/lanes/pipelines");
const skill = pipelines.find((p) => p.id === templateId)?.skill;
if (!skill) {
console.error(
`✖ pipeline template "${templateId}" declares no driving skill — ` +
`add a "skill" field to it (see docs/LANES.md § Custom template format)`
);
process.exitCode = 1;
return;
}
// `resolveLaneArg` only strips a leading bare lane id; the rest of the
// positionals are the requirement, joined back into one prompt line.
const requirement = positional
.filter((p) => p !== String(resolved.laneId))
.join(" ")
.trim();
const prompt = requirement ? `${skill} ${requirement}` : skill;
if (flags.print || !requirement) {
// `--print` shows what would run, so it switches nothing.
if (!requirement) console.error(`usage: agenthub ${command} "<requirement>" [<id>] [--print]`);
console.log(`cd ${lane.cwd} && claude ${JSON.stringify(prompt)}`);
if (!requirement) process.exitCode = 1;
return;
}
// The command picks the pipeline, not the lane's current setting: put the
// lane on the matching template first so the dashboard renders the right
// node count from the start rather than after the skill self-corrects.
if (lane.pipeline !== templateId) {
({ lane } = await patch(`/api/lanes/${lane.id}`, { pipeline: templateId }));
}
console.log(
`${c.dim(`lane #${lane.id} · pipeline ${lane.pipeline} (${lane.pipeline_nodes.length} nodes)`)}\n` +
`${c.cyan("→")} claude ${JSON.stringify(prompt)}`
);
const child = spawn("claude", [prompt], { stdio: "inherit", cwd: lane.cwd });
await new Promise((resolve) => {
child.on("close", (code) => {
if (code) process.exitCode = code;
resolve();
});
child.on("error", (err) => {
console.error(c.red(`✖ ${err?.message || err}`));
process.exitCode = 1;
resolve();
});
});
}
async function cmdFeatureShow(args) {
const slug = args.find((arg) => !arg.startsWith("--"));
if (!slug) {
@@ -2938,16 +2815,6 @@ const COMMAND_GROUPS = [
"[<node-id>] [extra…] [<id>] [--print]",
"Run ONE pipeline node's own command in the lane's cwd (no id: list every node and its command)",
],
[
"ship-full",
'"<requirement>" [<id>] [--print]',
"Run the 16-stage lane pipeline: implement → gates → e2e → review → QC → senior gate → PR, watched until a human merges (--print: show the command, run nothing)",
],
[
"ship-quick",
'"<requirement>" [<id>] [--print]',
"Run the 8-stage in-repo pipeline: plan → implement → tests → review → gate → ship. No PR, no browser QC",
],
[
"lanes shell",
"[<id>]",
@@ -2976,11 +2843,6 @@ const COMMAND_GROUPS = [
"[<id>]",
"Converge clone-root proof/ onto .playwright-mcp/proof (idempotent, never automatic)",
],
[
"lanes agents install",
"[<id>]",
"Write the ship-feature-lane agent templates (qc-local, senior-gate-reviewer) into <lane>/.claude/agents/, git-excluded",
],
[
"lanes mcp sync",
"[<id>]",
@@ -3001,11 +2863,6 @@ const COMMAND_GROUPS = [
"[--dry-run]",
"Reap orphaned Playwright MCP processes + cap oversized hook logs, across every lane",
],
[
"skills install",
"",
"Install skills/ship-feature-lane/ into ~/.claude/skills/ so /ship-feature-lane works from any lane",
],
[
"lock status|acquire|release",
"[<name>] [--holder X] [--timeout N]",
@@ -3832,9 +3689,6 @@ async function runCommand(argv) {
return cmdLanesRuntime(rest[0], rest.slice(1));
}
if (rest[0] === "proof-link") return cmdLanesProofLink(rest.slice(1));
if (rest[0] === "agents" && rest[1] === "install") {
return cmdLanesAgentsInstall(rest.slice(2));
}
if (rest[0] === "mcp" && rest[1] === "sync") {
return cmdLanesMcpSync(rest.slice(2));
}
@@ -3857,12 +3711,6 @@ async function runCommand(argv) {
return cmdLanesGc(rest.slice(1));
}
return cmdLanes();
case "skills": {
if (rest[0] === "install") return cmdSkillsInstall();
console.error("usage: agenthub skills install");
process.exitCode = 1;
return;
}
case "lock": {
const sub = rest[0];
if (sub === "status") return cmdLockStatus(rest.slice(1));
@@ -3876,9 +3724,6 @@ async function runCommand(argv) {
}
case "stage":
return cmdStage(rest);
case "ship-full":
case "ship-quick":
return cmdShip(cmd, rest);
case "feature": {
const sub = rest[0];
if (sub === "list") return cmdFeatureList(rest.slice(1));