2b8f83d2dd
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.
83 lines
3.3 KiB
JavaScript
83 lines
3.3 KiB
JavaScript
/**
|
|
* @file With NO pipeline template installed (the shipped state since the
|
|
* built-in templates were removed), lanes must still be created, rendered and
|
|
* reported on: getPipeline() answers an empty pipeline, `default` stays a
|
|
* valid id, and a stage declaration is refused with a plain explanation.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
const os = require("node:os");
|
|
const fs = require("node:fs");
|
|
const path = require("node:path");
|
|
|
|
const ROOT = fs.mkdtempSync(path.join(os.tmpdir(), "pipelines-dormant-"));
|
|
process.env.DASHBOARD_DB_PATH = path.join(ROOT, "t.db");
|
|
process.env.DASHBOARD_DATA_DIR = path.join(ROOT, "data");
|
|
process.env.CLAUDE_HOME = path.join(ROOT, ".claude");
|
|
process.env.AGENTHUB_SHELL_PATH = "0";
|
|
const savedDir = process.env.DASHBOARD_PIPELINES_DIR;
|
|
delete process.env.DASHBOARD_PIPELINES_DIR;
|
|
|
|
const { describe, it, before, after } = require("node:test");
|
|
const assert = require("node:assert/strict");
|
|
const http = require("node:http");
|
|
|
|
const pipelines = require("../lib/pipelines");
|
|
pipelines.reload();
|
|
const lanes = require("../lib/lanes");
|
|
const { createApp } = require("../index");
|
|
|
|
let app;
|
|
let base;
|
|
const request = (method, p, body) =>
|
|
fetch(`${base}${p}`, {
|
|
method,
|
|
headers: { "Content-Type": "application/json" },
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
}).then(async (r) => ({ status: r.status, body: await r.json().catch(() => null) }));
|
|
|
|
before(async () => {
|
|
app = http.createServer(createApp());
|
|
await new Promise((r) => app.listen(0, "127.0.0.1", r));
|
|
base = `http://127.0.0.1:${app.address().port}`;
|
|
});
|
|
after(async () => {
|
|
await new Promise((r) => app.close(r));
|
|
if (savedDir !== undefined) process.env.DASHBOARD_PIPELINES_DIR = savedDir;
|
|
pipelines.reload();
|
|
fs.rmSync(ROOT, { recursive: true, force: true });
|
|
});
|
|
|
|
describe("no pipeline template installed", () => {
|
|
it("ships no template and answers an empty default pipeline", () => {
|
|
assert.deepEqual(pipelines.listPipelines(), []);
|
|
const p = pipelines.getPipeline("default");
|
|
assert.equal(p.id, "default");
|
|
assert.deepEqual(p.nodes, []);
|
|
assert.equal(pipelines.getPipeline("anything").nodes.length, 0);
|
|
assert.deepEqual(pipelines.nodeStates(p, { stage: "plan", stages: {} }), []);
|
|
assert.equal(pipelines.progressPct(p, { stage: "plan" }), 0);
|
|
});
|
|
|
|
it("still creates a lane on the default pipeline and renders it", () => {
|
|
const lane = lanes.createLane({ cwd: path.join(ROOT, "a"), title: "a" });
|
|
assert.equal(lane.pipeline, "default");
|
|
const payload = lanes.lanePayload(lane);
|
|
assert.deepEqual(payload.pipeline_nodes, []);
|
|
assert.equal(payload.progress, 0);
|
|
});
|
|
|
|
it("refuses a stage declaration with a plain reason", async () => {
|
|
const lane = lanes.createLane({ cwd: path.join(ROOT, "b"), title: "b" });
|
|
const r = await request("POST", `/api/lanes/${lane.id}/stage`, { stage: "plan" });
|
|
assert.equal(r.status, 400);
|
|
assert.equal(r.body.error.code, "ENOPIPELINE");
|
|
assert.match(r.body.error.message, /no pipeline template is installed/);
|
|
});
|
|
|
|
it("lists no pipelines over the API and still serves the lane list", async () => {
|
|
assert.deepEqual((await request("GET", "/api/lanes/pipelines")).body, { pipelines: [] });
|
|
assert.equal((await request("GET", "/api/lanes")).status, 200);
|
|
});
|
|
});
|