@mmerterden/multi-agent-pipeline 20.28.0 → 20.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +19 -6
- package/README.tr.md +5 -5
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/facts.json +7 -7
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +122 -1
- package/install/codex.mjs +2 -0
- package/install/copilot.mjs +13 -0
- package/install/templates/claude-hooks.json +30 -1
- package/manifest.json +189 -77
- package/package.json +2 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +3 -1
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +13 -15
- package/pipeline/commands/multi-agent/office-off/SKILL.md +27 -0
- package/pipeline/commands/multi-agent/office-on/SKILL.md +50 -0
- package/pipeline/commands/multi-agent/office-status/SKILL.md +29 -0
- package/pipeline/commands/multi-agent/resume/SKILL.md +2 -1
- package/pipeline/commands/multi-agent/serve/SKILL.md +4 -2
- package/pipeline/commands/multi-agent/stack/SKILL.md +15 -8
- package/pipeline/commands/multi-agent/steer/SKILL.md +10 -9
- package/pipeline/commands/multi-agent/sync/SKILL.md +3 -3
- package/pipeline/contract/CHANGELOG.md +62 -0
- package/pipeline/contract/README.md +52 -6
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/manifest.json +367 -22
- package/pipeline/contract/types/index.d.ts +531 -4
- package/pipeline/lib/hook-registration.mjs +252 -0
- package/pipeline/lib/prefs-value.mjs +44 -0
- package/pipeline/lib/run-completion.mjs +148 -0
- package/pipeline/multi-agent-refs/analysis/render.md +4 -4
- package/pipeline/multi-agent-refs/cross-cli-contract.md +5 -4
- package/pipeline/multi-agent-refs/features/doctor.md +11 -0
- package/pipeline/multi-agent-refs/features/office-threat-model.md +91 -0
- package/pipeline/multi-agent-refs/help/en.md +3 -0
- package/pipeline/multi-agent-refs/help/tr.md +3 -0
- package/pipeline/multi-agent-refs/phases/operations.md +17 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +7 -6
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +5 -4
- package/pipeline/office/NOTICE.md +42 -0
- package/pipeline/office/assets/BIN.png +0 -0
- package/pipeline/office/assets/BOOKSHELF.png +0 -0
- package/pipeline/office/assets/CACTUS.png +0 -0
- package/pipeline/office/assets/CLOCK.png +0 -0
- package/pipeline/office/assets/COFFEE.png +0 -0
- package/pipeline/office/assets/COFFEE_TABLE.png +0 -0
- package/pipeline/office/assets/CUSHIONED_BENCH.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_BACK.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_FRONT.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_SIDE.png +0 -0
- package/pipeline/office/assets/DESK_FRONT.png +0 -0
- package/pipeline/office/assets/DESK_SIDE.png +0 -0
- package/pipeline/office/assets/DOUBLE_BOOKSHELF.png +0 -0
- package/pipeline/office/assets/HANGING_PLANT.png +0 -0
- package/pipeline/office/assets/LARGE_PAINTING.png +0 -0
- package/pipeline/office/assets/LARGE_PLANT.png +0 -0
- package/pipeline/office/assets/PC_BACK.png +0 -0
- package/pipeline/office/assets/PC_FRONT_OFF.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_1.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_2.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_3.png +0 -0
- package/pipeline/office/assets/PC_SIDE.png +0 -0
- package/pipeline/office/assets/PLANT.png +0 -0
- package/pipeline/office/assets/PLANT_2.png +0 -0
- package/pipeline/office/assets/POT.png +0 -0
- package/pipeline/office/assets/SMALL_PAINTING.png +0 -0
- package/pipeline/office/assets/SMALL_PAINTING_2.png +0 -0
- package/pipeline/office/assets/SMALL_TABLE_FRONT.png +0 -0
- package/pipeline/office/assets/SMALL_TABLE_SIDE.png +0 -0
- package/pipeline/office/assets/SOFA_BACK.png +0 -0
- package/pipeline/office/assets/SOFA_FRONT.png +0 -0
- package/pipeline/office/assets/SOFA_SIDE.png +0 -0
- package/pipeline/office/assets/TABLE_FRONT.png +0 -0
- package/pipeline/office/assets/WHITEBOARD.png +0 -0
- package/pipeline/office/assets/WOODEN_BENCH.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_BACK.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_FRONT.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_SIDE.png +0 -0
- package/pipeline/office/assets/char_0.png +0 -0
- package/pipeline/office/assets/char_1.png +0 -0
- package/pipeline/office/assets/char_2.png +0 -0
- package/pipeline/office/assets/char_3.png +0 -0
- package/pipeline/office/assets/char_4.png +0 -0
- package/pipeline/office/assets/char_5.png +0 -0
- package/pipeline/office/assets/floor_0.png +0 -0
- package/pipeline/office/assets/floor_1.png +0 -0
- package/pipeline/office/assets/floor_2.png +0 -0
- package/pipeline/office/assets/floor_3.png +0 -0
- package/pipeline/office/assets/floor_4.png +0 -0
- package/pipeline/office/assets/floor_5.png +0 -0
- package/pipeline/office/assets/floor_6.png +0 -0
- package/pipeline/office/assets/floor_7.png +0 -0
- package/pipeline/office/assets/floor_8.png +0 -0
- package/pipeline/office/assets/wall_0.png +0 -0
- package/pipeline/office/index.html +5319 -0
- package/pipeline/preferences-template.json +7 -0
- package/pipeline/rules/figma-pipeline.md +2 -0
- package/pipeline/schemas/activity-chat.schema.json +78 -0
- package/pipeline/schemas/activity-roles.json +48 -0
- package/pipeline/schemas/activity-roles.schema.json +49 -0
- package/pipeline/schemas/activity.schema.json +429 -0
- package/pipeline/schemas/agent-state.schema.json +20 -5
- package/pipeline/schemas/autopilot-on-request.schema.json +33 -0
- package/pipeline/schemas/autopilot-on.schema.json +111 -0
- package/pipeline/schemas/contract-error.schema.json +33 -3
- package/pipeline/schemas/issue-maturity.schema.json +68 -0
- package/pipeline/schemas/ledger.schema.json +202 -0
- package/pipeline/schemas/permissions.schema.json +47 -0
- package/pipeline/schemas/phases.json +3 -2
- package/pipeline/schemas/phases.schema.json +5 -1
- package/pipeline/schemas/prefs.schema.json +56 -0
- package/pipeline/schemas/prs.schema.json +73 -0
- package/pipeline/schemas/review-verdict-request.schema.json +20 -0
- package/pipeline/schemas/review-verdict.schema.json +62 -0
- package/pipeline/schemas/routine-delete.schema.json +59 -0
- package/pipeline/schemas/routines.schema.json +38 -0
- package/pipeline/schemas/run-evidence.schema.json +29 -0
- package/pipeline/schemas/run-results.schema.json +218 -0
- package/pipeline/schemas/runs-index.schema.json +1 -1
- package/pipeline/schemas/service-read.schema.json +58 -0
- package/pipeline/schemas/stack-change.schema.json +82 -0
- package/pipeline/schemas/stack-request.schema.json +33 -0
- package/pipeline/schemas/stack.schema.json +59 -0
- package/pipeline/schemas/steer-request.schema.json +25 -0
- package/pipeline/schemas/steer.schema.json +59 -0
- package/pipeline/schemas/tool-call-request.schema.json +19 -0
- package/pipeline/schemas/tool-call.schema.json +47 -0
- package/pipeline/schemas/tools.schema.json +60 -0
- package/pipeline/scripts/_active-run.mjs +158 -0
- package/pipeline/scripts/_autopilot-outcomes.mjs +1 -0
- package/pipeline/scripts/_cost.mjs +66 -1
- package/pipeline/scripts/_guard_rules.py +605 -0
- package/pipeline/scripts/_office-smoke-lib.sh +115 -0
- package/pipeline/scripts/_spawn-async.mjs +73 -0
- package/pipeline/scripts/_transcript-tail.mjs +274 -0
- package/pipeline/scripts/activity-snapshot.mjs +1027 -0
- package/pipeline/scripts/agent-guard.py +118 -7
- package/pipeline/scripts/agent-guard.sh +50 -3
- package/pipeline/scripts/argv-secret-scan.mjs +114 -0
- package/pipeline/scripts/autopilot-control.mjs +214 -9
- package/pipeline/scripts/autopilot-runner.mjs +8 -2
- package/pipeline/scripts/confirm-command.mjs +70 -0
- package/pipeline/scripts/contract-server.mjs +1178 -48
- package/pipeline/scripts/cost-analyze.mjs +2 -53
- package/pipeline/scripts/cost-table.json +1 -0
- package/pipeline/scripts/doctor.mjs +54 -0
- package/pipeline/scripts/evidence-gate.mjs +8 -4
- package/pipeline/scripts/gate-ledger.mjs +163 -7
- package/pipeline/scripts/office-build.mjs +89 -0
- package/pipeline/scripts/office-control.mjs +383 -0
- package/pipeline/scripts/office-ledger.mjs +409 -0
- package/pipeline/scripts/plan-coverage-gate.mjs +9 -8
- package/pipeline/scripts/pre-commit-check.sh +12 -0
- package/pipeline/scripts/routine-registry.mjs +42 -6
- package/pipeline/scripts/run-results.mjs +373 -0
- package/pipeline/scripts/run-scope-guard.mjs +188 -0
- package/pipeline/scripts/run-scope-guard.sh +23 -0
- package/pipeline/scripts/run-stop-guard.mjs +246 -0
- package/pipeline/scripts/run-stop-guard.sh +25 -0
- package/pipeline/scripts/service-read.mjs +244 -0
- package/pipeline/scripts/stack-toggle.mjs +285 -0
- package/pipeline/scripts/steer-run.mjs +97 -0
- package/pipeline/scripts/toolkit-tools.mjs +599 -0
- package/pipeline/scripts/uninstall.mjs +4 -0
- package/pipeline/scripts/validate-state.mjs +26 -0
- package/pipeline/scripts/work-items.mjs +304 -0
- package/pipeline/scripts/write-state.mjs +114 -0
- package/pipeline/skills/.skill-manifest.json +19 -7
- package/pipeline/skills/.skills-index.json +34 -1
- package/pipeline/skills/shared/README.md +6 -3
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +13 -15
- package/pipeline/skills/shared/core/multi-agent-office-off/SKILL.md +16 -0
- package/pipeline/skills/shared/core/multi-agent-office-on/SKILL.md +22 -0
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +17 -0
- package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +4 -2
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +14 -6
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +10 -9
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
- package/pipeline/skills/skills-index.md +4 -1
package/install/claude.mjs
CHANGED
|
@@ -196,7 +196,7 @@ function installOwnedTrees({ pipelineSrc, claudeDir, manifest, adopt }) {
|
|
|
196
196
|
};
|
|
197
197
|
|
|
198
198
|
console.log(
|
|
199
|
-
" [Claude Code] Installing pipeline commands, refs, scripts, schemas, lib, templates...",
|
|
199
|
+
" [Claude Code] Installing pipeline commands, refs, scripts, schemas, office, lib, templates...",
|
|
200
200
|
);
|
|
201
201
|
|
|
202
202
|
const nsSrc = join(pipelineSrc, "commands", "multi-agent");
|
|
@@ -242,6 +242,14 @@ function installOwnedTrees({ pipelineSrc, claudeDir, manifest, adopt }) {
|
|
|
242
242
|
logs.push(` -> ${countFiles(schemasSrc)} files copied to ${schemasDest}`);
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
+
// The office panel page, served from ../office beside the scripts directory.
|
|
246
|
+
const officeSrc = join(pipelineSrc, "office");
|
|
247
|
+
const officeDest = join(claudeDir, "office");
|
|
248
|
+
add(officeDest, "office", officeSrc, (staged) => {
|
|
249
|
+
copyDir(officeSrc, staged);
|
|
250
|
+
logs.push(` -> ${countFiles(officeSrc)} files copied to ${officeDest}`);
|
|
251
|
+
});
|
|
252
|
+
|
|
245
253
|
for (const [name, src] of [
|
|
246
254
|
["lib", join(pipelineSrc, "lib")],
|
|
247
255
|
["templates", join(dirname(pipelineSrc), "install", "templates")],
|
|
@@ -315,6 +323,7 @@ function linkOwnedTrees(pipelineSrc, claudeDir) {
|
|
|
315
323
|
["multi-agent-refs", join(pipelineSrc, "multi-agent-refs")],
|
|
316
324
|
["scripts", join(pipelineSrc, "scripts")],
|
|
317
325
|
["schemas", join(pipelineSrc, "schemas")],
|
|
326
|
+
["office", join(pipelineSrc, "office")],
|
|
318
327
|
["lib", join(pipelineSrc, "lib")],
|
|
319
328
|
["templates", join(dirname(pipelineSrc), "install", "templates")],
|
|
320
329
|
];
|
|
@@ -837,6 +846,93 @@ export function quotePipelineHookCommands(hooks) {
|
|
|
837
846
|
return changed;
|
|
838
847
|
}
|
|
839
848
|
|
|
849
|
+
/**
|
|
850
|
+
* The run-scoped hooks the installer registers beside the two gates above:
|
|
851
|
+
* run-stop-guard.sh (Stop) and run-scope-guard.sh (Figma MCP
|
|
852
|
+
* tools, file edits). Each is silent outside a pipeline run and fails open, so
|
|
853
|
+
* registering them by default changes nothing until a run is active. The same
|
|
854
|
+
* entries are in install/templates/claude-hooks.json.
|
|
855
|
+
*/
|
|
856
|
+
export const RUN_HOOKS = [
|
|
857
|
+
{
|
|
858
|
+
event: "Stop",
|
|
859
|
+
script: "run-stop-guard.sh",
|
|
860
|
+
timeout: 15,
|
|
861
|
+
statusMessage: "Checking the pipeline run is not left mid-phase...",
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
event: "PreToolUse",
|
|
865
|
+
matcher: "mcp__.*[Ff]igma.*",
|
|
866
|
+
script: "run-scope-guard.sh",
|
|
867
|
+
timeout: 10,
|
|
868
|
+
statusMessage: "Checking the Figma call against the run's phase...",
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
event: "PreToolUse",
|
|
872
|
+
matcher: AGENT_GUARD_FILE_MATCHER,
|
|
873
|
+
script: "run-scope-guard.sh",
|
|
874
|
+
timeout: 10,
|
|
875
|
+
statusMessage: "Checking the edit is inside the run's worktree...",
|
|
876
|
+
},
|
|
877
|
+
];
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* The record of which RUN_HOOKS an install has registered, under ~/.claude. A
|
|
881
|
+
* key in it whose hook is no longer in settings.json was removed by the user,
|
|
882
|
+
* and the next install leaves it removed. uninstall.mjs deletes the file.
|
|
883
|
+
*/
|
|
884
|
+
export const HOOK_MANIFEST = "multi-agent-hook-manifest.json";
|
|
885
|
+
|
|
886
|
+
/** event|matcher|script, the identity of one RUN_HOOKS entry. */
|
|
887
|
+
export const hookKey = (r) => `${r.event}|${r.matcher || ""}|${r.script}`;
|
|
888
|
+
|
|
889
|
+
function readHookManifest(path) {
|
|
890
|
+
try {
|
|
891
|
+
const doc = JSON.parse(readFileSync(path, "utf-8"));
|
|
892
|
+
return new Set(Array.isArray(doc?.registered) ? doc.registered : []);
|
|
893
|
+
} catch {
|
|
894
|
+
return new Set();
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/** True when an entry of the same event and matcher already runs the script. */
|
|
899
|
+
export function hasHook(hooks, r) {
|
|
900
|
+
const groups = Array.isArray(hooks?.[r.event]) ? hooks[r.event] : [];
|
|
901
|
+
return groups.some(
|
|
902
|
+
(g) =>
|
|
903
|
+
(g?.matcher || "") === (r.matcher || "") &&
|
|
904
|
+
Array.isArray(g.hooks) &&
|
|
905
|
+
g.hooks.some((h) => typeof h?.command === "string" && h.command.includes(r.script)),
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Add one pipeline hook unless an entry of the same event and matcher already
|
|
911
|
+
* runs its script. Returns true when it changed `hooks`.
|
|
912
|
+
*
|
|
913
|
+
* @param {object} hooks settings.hooks
|
|
914
|
+
* @param {{event: string, matcher?: string, script: string, timeout: number, statusMessage: string}} r
|
|
915
|
+
*/
|
|
916
|
+
export function ensureHook(hooks, r) {
|
|
917
|
+
if (!Array.isArray(hooks[r.event])) hooks[r.event] = [];
|
|
918
|
+
const groups = hooks[r.event];
|
|
919
|
+
if (hasHook(hooks, r)) return false;
|
|
920
|
+
const hook = {
|
|
921
|
+
type: "command",
|
|
922
|
+
command: pipelineHookCommand(r.script),
|
|
923
|
+
timeout: r.timeout,
|
|
924
|
+
statusMessage: r.statusMessage,
|
|
925
|
+
};
|
|
926
|
+
// Joined to a group of the same matcher when there is one, so a file edit
|
|
927
|
+
// shows one group with both guards rather than two groups.
|
|
928
|
+
const same = groups.find(
|
|
929
|
+
(g) => (g?.matcher || "") === (r.matcher || "") && Array.isArray(g.hooks),
|
|
930
|
+
);
|
|
931
|
+
if (same) same.hooks.push(hook);
|
|
932
|
+
else groups.push(r.matcher ? { matcher: r.matcher, hooks: [hook] } : { hooks: [hook] });
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
|
|
840
936
|
export function configureSettings(home) {
|
|
841
937
|
console.log(" [Claude Code] Configuring hooks + context management...");
|
|
842
938
|
const SETTINGS_PATH = join(home, ".claude", "settings.json");
|
|
@@ -942,6 +1038,26 @@ export function configureSettings(home) {
|
|
|
942
1038
|
settingsChanged = true;
|
|
943
1039
|
}
|
|
944
1040
|
|
|
1041
|
+
const manifestPath = join(home, ".claude", HOOK_MANIFEST);
|
|
1042
|
+
const manifest = readHookManifest(manifestPath);
|
|
1043
|
+
let manifestChanged = false;
|
|
1044
|
+
for (const r of RUN_HOOKS) {
|
|
1045
|
+
const key = hookKey(r);
|
|
1046
|
+
const had = manifest.has(key);
|
|
1047
|
+
if (hasHook(settings.hooks, r)) {
|
|
1048
|
+
if (!had) {
|
|
1049
|
+
manifest.add(key);
|
|
1050
|
+
manifestChanged = true;
|
|
1051
|
+
}
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
// Registered by an earlier install and gone now: the user removed it.
|
|
1055
|
+
if (had) continue;
|
|
1056
|
+
ensureHook(settings.hooks, r);
|
|
1057
|
+
manifest.add(key);
|
|
1058
|
+
settingsChanged = true;
|
|
1059
|
+
manifestChanged = true;
|
|
1060
|
+
}
|
|
945
1061
|
if (collapseDuplicateHooks(settings.hooks)) settingsChanged = true;
|
|
946
1062
|
|
|
947
1063
|
if (settingsChanged && isDryRun()) {
|
|
@@ -953,6 +1069,11 @@ export function configureSettings(home) {
|
|
|
953
1069
|
} else {
|
|
954
1070
|
console.log(" -> Hooks + env already configured");
|
|
955
1071
|
}
|
|
1072
|
+
// After settings.json, so a failed write never records hooks as
|
|
1073
|
+
// registered and the next install still adds them.
|
|
1074
|
+
if (manifestChanged && !isDryRun()) {
|
|
1075
|
+
writeFile(manifestPath, `${JSON.stringify({ registered: [...manifest].sort() }, null, 2)}\n`);
|
|
1076
|
+
}
|
|
956
1077
|
} catch (e) {
|
|
957
1078
|
console.log(` -> Warning: Could not configure settings.json: ${e.message}`);
|
|
958
1079
|
}
|
package/install/codex.mjs
CHANGED
|
@@ -209,6 +209,7 @@ export function installCodex(ctx) {
|
|
|
209
209
|
const CODEX_SCRIPTS = join(CODEX_DIR, "scripts");
|
|
210
210
|
const CODEX_LIB = join(CODEX_DIR, "lib");
|
|
211
211
|
const CODEX_SCHEMAS = join(CODEX_DIR, "schemas");
|
|
212
|
+
const CODEX_OFFICE = join(CODEX_DIR, "office");
|
|
212
213
|
const CODEX_TEMPLATES = join(CODEX_DIR, "templates");
|
|
213
214
|
const CODEX_RULES = join(CODEX_DIR, "rules");
|
|
214
215
|
const CODEX_SKILL_REFS = join(CODEX_MA_REFS, "skills");
|
|
@@ -224,6 +225,7 @@ export function installCodex(ctx) {
|
|
|
224
225
|
installScripts(pipelineSrc, CODEX_SCRIPTS, useSymlinks);
|
|
225
226
|
installTree("lib", pipelineSrc, CODEX_LIB, useSymlinks);
|
|
226
227
|
installTree("schemas", pipelineSrc, CODEX_SCHEMAS, useSymlinks);
|
|
228
|
+
installTree("office", pipelineSrc, CODEX_OFFICE, useSymlinks);
|
|
227
229
|
installTree("rules", pipelineSrc, CODEX_RULES, useSymlinks);
|
|
228
230
|
installTemplates(pipelineSrc, CODEX_TEMPLATES, useSymlinks);
|
|
229
231
|
writeAgentsMd(join(CODEX_DIR, "AGENTS.md"));
|
package/install/copilot.mjs
CHANGED
|
@@ -61,6 +61,7 @@ export function installCopilot(ctx) {
|
|
|
61
61
|
const COPILOT_AGENTS = join(COPILOT_DIR, "agents");
|
|
62
62
|
const COPILOT_SKILLS = join(COPILOT_DIR, "skills");
|
|
63
63
|
const COPILOT_SCHEMAS = join(COPILOT_DIR, "schemas");
|
|
64
|
+
const COPILOT_OFFICE = join(COPILOT_DIR, "office");
|
|
64
65
|
const COPILOT_LIB = join(COPILOT_DIR, "lib");
|
|
65
66
|
const COPILOT_RULES = join(COPILOT_DIR, "rules");
|
|
66
67
|
const COPILOT_TEMPLATES = join(COPILOT_DIR, "templates");
|
|
@@ -72,6 +73,7 @@ export function installCopilot(ctx) {
|
|
|
72
73
|
installScripts(pipelineSrc, COPILOT_SCRIPTS, useSymlinks);
|
|
73
74
|
installAgents(pipelineSrc, COPILOT_AGENTS, useSymlinks);
|
|
74
75
|
installSchemas(pipelineSrc, COPILOT_SCHEMAS, useSymlinks);
|
|
76
|
+
installOffice(pipelineSrc, COPILOT_OFFICE, useSymlinks);
|
|
75
77
|
installLib(pipelineSrc, COPILOT_LIB, useSymlinks);
|
|
76
78
|
installRules(pipelineSrc, COPILOT_RULES, useSymlinks);
|
|
77
79
|
installTemplates(pipelineSrc, COPILOT_TEMPLATES, useSymlinks);
|
|
@@ -263,6 +265,17 @@ function installSchemas(pipelineSrc, dest, useSymlinks) {
|
|
|
263
265
|
console.log(` -> ${countFiles(schemasSrc)} files copied to ${dest}`);
|
|
264
266
|
}
|
|
265
267
|
|
|
268
|
+
/** The office panel page, served from ../office beside the scripts directory. */
|
|
269
|
+
function installOffice(pipelineSrc, dest, useSymlinks) {
|
|
270
|
+
console.log(" [Copilot CLI] Installing the office panel page...");
|
|
271
|
+
const officeSrc = join(pipelineSrc, "office");
|
|
272
|
+
if (!existsSync(officeSrc)) return;
|
|
273
|
+
if (!useSymlinks && ensureRealDir(dest) === null) return;
|
|
274
|
+
wipeDir(dest);
|
|
275
|
+
copyDir(officeSrc, dest, { useSymlinks });
|
|
276
|
+
console.log(` -> ${countFiles(officeSrc)} files copied to ${dest}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
266
279
|
function installTemplates(pipelineSrc, dest, useSymlinks) {
|
|
267
280
|
// Only install/claude.mjs used to lay this tree down, so a Copilot-only or
|
|
268
281
|
// Codex-only machine had no ~/.claude/templates - and autopilot-on reads the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_readme": "Recommended Claude Code hooks for multi-agent-pipeline. `install` registers the pre-commit and agent-guard gates itself (install/claude.mjs configureSettings); /multi-agent:setup offers to merge the rest of this block. Merge the `hooks` object into your ~/.claude/settings.json to make these deterministic PreToolUse gates real (exit 2 blocks the tool call). A PreToolUse matcher is a regex over the TOOL NAME, so each gate registers on a tool name and reads the command from its stdin payload. Three PreToolUse gates ship here: (1) pre-commit-check.sh on Bash, which acts only on a `git commit` and blocks a secret in the staged diff; (2) agent-guard.sh on Bash, on Edit|Write|NotebookEdit, and on WebFetch plus every multi-agent-toolkit tool (WebFetch|mcp__multi-agent-toolkit__.*). Always, it blocks AI/assistant attribution in a commit message and a force-push, delete or mirror push to a protected branch (main/master/develop), including one inside a subshell or a `bash -c` / `eval` line. With MULTI_AGENT_UNATTENDED=1 in the hook's environment it also applies the unattended policy: no push, PR or tracker write, no Keychain read, no fetch outside the host allowlist (curl, WebFetch, the toolkit's web tools, agent_run_steps and open_url), no package install, and no write into a protected path (features/unattended-security.md); (3) check-read-size.sh on Read|Bash, which inspects Read plus the shell commands that read a file whole (cat/head/tail/sed), returns immediately for everything else, and routes an oversized read to a cheap worker instead of the caller's own rung; it is inert until `prefs.global.bulkRead.mode` is set to observe or enforce. Failure behaviour: pre-commit-check.sh and check-read-size.sh fail open on an internal error. agent-guard.sh fails open when attended, so a guard bug cannot break a legitimate tool call, and fails closed under MULTI_AGENT_UNATTENDED=1, where nobody is there to notice a gap. None of the three executes the inspected command, and none needs run-specific arguments. The other deterministic gates (evidence, consensus, intent, learnings) take run-specific arguments and are phase-enforced by the pipeline instead. Three capture hooks ship alongside them and block nothing: (4) SessionEnd runs capture-flush.sh --if-stale, which writes a run's triage findings and durable learnings into the per-repo stores when the run ended before Phase 5, where those writes otherwise happen; the same hook runs note-session.sh, which records the mechanical shape of a session outside the pipeline (tools used, commands that failed, calls the user refused). (5) PreCompact runs capture-flush.sh without --if-stale: a compaction summarizes the conversation mid-phase, so it is the moment unflushed findings are at risk, and both writes are idempotent, so a finished run costs two no-op writes. The staleness test exists only so a session exit does not re-flush a run that already finished. (6) SessionStart runs capture-resume.sh, which prints at most two lines: an unfinished run and how to resume it, and a stale pipeline-observation queue. No capture hook calls a model or reads a payload - note-session.sh keeps a command's first word and an exit code, never an argument or any output - and all exit 0 on every path, because a hook that fails a session over bookkeeping costs more than the bookkeeping. One PostToolUse hook ships as well and blocks nothing: (7) edit-syntax-gate.sh on Edit|Write|MultiEdit parses the file the edit just wrote (swiftc -parse, node --check, py_compile into a temp dir, JSON.parse, bash -n; Kotlin skipped) under a 2 s deadline and, when it no longer parses, hands the model the error as PostToolUse additionalContext; it exits 0 on every path, stays silent when a parser is missing or slow, and is inert until `prefs.global.editSyntaxGate.enabled` is true. These registrations are Claude Code's. On Codex only the syntax gate is offered, from codex-hooks.json through host-hooks.mjs; Copilot CLI gets none, since as of 1.0.45 it discards postToolUse output; the other gates are Claude Code only. multi-agent:setup offers to merge this block.",
|
|
2
|
+
"_readme": "Recommended Claude Code hooks for multi-agent-pipeline. `install` registers the pre-commit and agent-guard gates and the run-scoped run-stop-guard / run-scope-guard hooks itself (install/claude.mjs configureSettings, RUN_HOOKS); /multi-agent:setup offers to merge the rest of this block. Merge the `hooks` object into your ~/.claude/settings.json to make these deterministic PreToolUse gates real (exit 2 blocks the tool call). A PreToolUse matcher is a regex over the TOOL NAME, so each gate registers on a tool name and reads the command from its stdin payload. Three PreToolUse gates ship here: (1) pre-commit-check.sh on Bash, which acts only on a `git commit` and blocks a secret in the staged diff; (2) agent-guard.sh on Bash, on Edit|Write|NotebookEdit, and on WebFetch plus every multi-agent-toolkit tool (WebFetch|mcp__multi-agent-toolkit__.*). Always, it blocks AI/assistant attribution in a commit message and a force-push, delete or mirror push to a protected branch (main/master/develop), including one inside a subshell or a `bash -c` / `eval` line. With MULTI_AGENT_UNATTENDED=1 in the hook's environment it also applies the unattended policy: no push, PR or tracker write, no Keychain read, no fetch outside the host allowlist (curl, WebFetch, the toolkit's web tools, agent_run_steps and open_url), no package install, and no write into a protected path (features/unattended-security.md); (3) check-read-size.sh on Read|Bash, which inspects Read plus the shell commands that read a file whole (cat/head/tail/sed), returns immediately for everything else, and routes an oversized read to a cheap worker instead of the caller's own rung; it is inert until `prefs.global.bulkRead.mode` is set to observe or enforce. Failure behaviour: pre-commit-check.sh and check-read-size.sh fail open on an internal error. agent-guard.sh fails open when attended, so a guard bug cannot break a legitimate tool call, and fails closed under MULTI_AGENT_UNATTENDED=1, where nobody is there to notice a gap. None of the three executes the inspected command, and none needs run-specific arguments. The other deterministic gates (evidence, consensus, intent, learnings) take run-specific arguments and are phase-enforced by the pipeline instead. Three capture hooks ship alongside them and block nothing: (4) SessionEnd runs capture-flush.sh --if-stale, which writes a run's triage findings and durable learnings into the per-repo stores when the run ended before Phase 5, where those writes otherwise happen; the same hook runs note-session.sh, which records the mechanical shape of a session outside the pipeline (tools used, commands that failed, calls the user refused). (5) PreCompact runs capture-flush.sh without --if-stale: a compaction summarizes the conversation mid-phase, so it is the moment unflushed findings are at risk, and both writes are idempotent, so a finished run costs two no-op writes. The staleness test exists only so a session exit does not re-flush a run that already finished. (6) SessionStart runs capture-resume.sh, which prints at most two lines: an unfinished run and how to resume it, and a stale pipeline-observation queue. No capture hook calls a model or reads a payload - note-session.sh keeps a command's first word and an exit code, never an argument or any output - and all exit 0 on every path, because a hook that fails a session over bookkeeping costs more than the bookkeeping. A run-scoped PreToolUse gate ships too: (2b) run-scope-guard.sh on every Figma MCP tool (mcp__.*[Ff]igma.*) denies a Figma call above phases.json thresholds.mcpAllowedThroughPhase for the run bound to the session, unless the run is in analysis mode, and records each allowed call into telemetry.mcpCalls[]; on Edit|Write|NotebookEdit the same script warns, attended only, when the target is outside the bound run's worktree (additionalContext for the model, systemMessage for the user; the call proceeds). Outside a run it is silent, and it fails open. One PostToolUse hook ships as well and blocks nothing: (7) edit-syntax-gate.sh on Edit|Write|MultiEdit parses the file the edit just wrote (swiftc -parse, node --check, py_compile into a temp dir, JSON.parse, bash -n; Kotlin skipped) under a 2 s deadline and, when it no longer parses, hands the model the error as PostToolUse additionalContext; it exits 0 on every path, stays silent when a parser is missing or slow, and is inert until `prefs.global.editSyntaxGate.enabled` is true. One Stop hook ships as well: (8) run-stop-guard.sh answers {\"decision\":\"block\"} when the session driving an active pipeline run (bound through agent-state.json sessionId or hostSessionId, never by a worktree match) tries to stop while the run is in_progress below its mode's last phase with no haltReason or waitingFor; the reason says to finish the current phase, record the wait, or record a halt. A turn that ends on a question to the user, a session with background tasks or crons, and prefs.global.stopGuard.enabled false are never held, and SubagentStop is not registered: a subagent's stop is the dispatching session's business. After three consecutive blocks it records the halt itself (status paused, haltReason <phase>:stop-guard), which write-state clears when the run advances a phase. It never blocks outside a run and fails open, with a line in <log root>/hook-failures.log, on any error. These registrations are Claude Code's; README \"Hooks per host\" lists which have no equivalent elsewhere. On Codex only the syntax gate is offered, from codex-hooks.json through host-hooks.mjs; Copilot CLI gets none, since as of 1.0.45 it discards postToolUse output; the other gates are Claude Code only. multi-agent:setup offers to merge this block.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"PreToolUse": [
|
|
5
5
|
{
|
|
@@ -27,6 +27,12 @@
|
|
|
27
27
|
"command": "bash \"$HOME/.claude/scripts/agent-guard.sh\"",
|
|
28
28
|
"timeout": 10,
|
|
29
29
|
"statusMessage": "Checking the edit target..."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "bash \"$HOME/.claude/scripts/run-scope-guard.sh\"",
|
|
34
|
+
"timeout": 10,
|
|
35
|
+
"statusMessage": "Checking the edit is inside the run's worktree..."
|
|
30
36
|
}
|
|
31
37
|
]
|
|
32
38
|
},
|
|
@@ -51,6 +57,17 @@
|
|
|
51
57
|
"statusMessage": "Checking read size..."
|
|
52
58
|
}
|
|
53
59
|
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"matcher": "mcp__.*[Ff]igma.*",
|
|
63
|
+
"hooks": [
|
|
64
|
+
{
|
|
65
|
+
"type": "command",
|
|
66
|
+
"command": "bash \"$HOME/.claude/scripts/run-scope-guard.sh\"",
|
|
67
|
+
"timeout": 10,
|
|
68
|
+
"statusMessage": "Checking the Figma call against the run's phase..."
|
|
69
|
+
}
|
|
70
|
+
]
|
|
54
71
|
}
|
|
55
72
|
],
|
|
56
73
|
"PostToolUse": [
|
|
@@ -108,6 +125,18 @@
|
|
|
108
125
|
}
|
|
109
126
|
]
|
|
110
127
|
}
|
|
128
|
+
],
|
|
129
|
+
"Stop": [
|
|
130
|
+
{
|
|
131
|
+
"hooks": [
|
|
132
|
+
{
|
|
133
|
+
"type": "command",
|
|
134
|
+
"command": "bash \"$HOME/.claude/scripts/run-stop-guard.sh\"",
|
|
135
|
+
"timeout": 15,
|
|
136
|
+
"statusMessage": "Checking the pipeline run is not left mid-phase..."
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
111
140
|
]
|
|
112
141
|
}
|
|
113
142
|
}
|