@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.
Files changed (186) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +19 -6
  3. package/README.tr.md +5 -5
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/facts.json +7 -7
  7. package/install/catalog-history.json +1 -1
  8. package/install/claude.mjs +122 -1
  9. package/install/codex.mjs +2 -0
  10. package/install/copilot.mjs +13 -0
  11. package/install/templates/claude-hooks.json +30 -1
  12. package/manifest.json +189 -77
  13. package/package.json +2 -1
  14. package/pipeline/commands/multi-agent/analysis/SKILL.md +3 -1
  15. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +13 -15
  16. package/pipeline/commands/multi-agent/office-off/SKILL.md +27 -0
  17. package/pipeline/commands/multi-agent/office-on/SKILL.md +50 -0
  18. package/pipeline/commands/multi-agent/office-status/SKILL.md +29 -0
  19. package/pipeline/commands/multi-agent/resume/SKILL.md +2 -1
  20. package/pipeline/commands/multi-agent/serve/SKILL.md +4 -2
  21. package/pipeline/commands/multi-agent/stack/SKILL.md +15 -8
  22. package/pipeline/commands/multi-agent/steer/SKILL.md +10 -9
  23. package/pipeline/commands/multi-agent/sync/SKILL.md +3 -3
  24. package/pipeline/contract/CHANGELOG.md +62 -0
  25. package/pipeline/contract/README.md +52 -6
  26. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  27. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  28. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  29. package/pipeline/contract/manifest.json +367 -22
  30. package/pipeline/contract/types/index.d.ts +531 -4
  31. package/pipeline/lib/hook-registration.mjs +252 -0
  32. package/pipeline/lib/prefs-value.mjs +44 -0
  33. package/pipeline/lib/run-completion.mjs +148 -0
  34. package/pipeline/multi-agent-refs/analysis/render.md +4 -4
  35. package/pipeline/multi-agent-refs/cross-cli-contract.md +5 -4
  36. package/pipeline/multi-agent-refs/features/doctor.md +11 -0
  37. package/pipeline/multi-agent-refs/features/office-threat-model.md +91 -0
  38. package/pipeline/multi-agent-refs/help/en.md +3 -0
  39. package/pipeline/multi-agent-refs/help/tr.md +3 -0
  40. package/pipeline/multi-agent-refs/phases/operations.md +17 -0
  41. package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
  42. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +2 -2
  43. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +1 -1
  44. package/pipeline/multi-agent-refs/phases/phase-3-review.md +2 -2
  45. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +7 -6
  46. package/pipeline/multi-agent-refs/phases/phase-5-report.md +5 -4
  47. package/pipeline/office/NOTICE.md +42 -0
  48. package/pipeline/office/assets/BIN.png +0 -0
  49. package/pipeline/office/assets/BOOKSHELF.png +0 -0
  50. package/pipeline/office/assets/CACTUS.png +0 -0
  51. package/pipeline/office/assets/CLOCK.png +0 -0
  52. package/pipeline/office/assets/COFFEE.png +0 -0
  53. package/pipeline/office/assets/COFFEE_TABLE.png +0 -0
  54. package/pipeline/office/assets/CUSHIONED_BENCH.png +0 -0
  55. package/pipeline/office/assets/CUSHIONED_CHAIR_BACK.png +0 -0
  56. package/pipeline/office/assets/CUSHIONED_CHAIR_FRONT.png +0 -0
  57. package/pipeline/office/assets/CUSHIONED_CHAIR_SIDE.png +0 -0
  58. package/pipeline/office/assets/DESK_FRONT.png +0 -0
  59. package/pipeline/office/assets/DESK_SIDE.png +0 -0
  60. package/pipeline/office/assets/DOUBLE_BOOKSHELF.png +0 -0
  61. package/pipeline/office/assets/HANGING_PLANT.png +0 -0
  62. package/pipeline/office/assets/LARGE_PAINTING.png +0 -0
  63. package/pipeline/office/assets/LARGE_PLANT.png +0 -0
  64. package/pipeline/office/assets/PC_BACK.png +0 -0
  65. package/pipeline/office/assets/PC_FRONT_OFF.png +0 -0
  66. package/pipeline/office/assets/PC_FRONT_ON_1.png +0 -0
  67. package/pipeline/office/assets/PC_FRONT_ON_2.png +0 -0
  68. package/pipeline/office/assets/PC_FRONT_ON_3.png +0 -0
  69. package/pipeline/office/assets/PC_SIDE.png +0 -0
  70. package/pipeline/office/assets/PLANT.png +0 -0
  71. package/pipeline/office/assets/PLANT_2.png +0 -0
  72. package/pipeline/office/assets/POT.png +0 -0
  73. package/pipeline/office/assets/SMALL_PAINTING.png +0 -0
  74. package/pipeline/office/assets/SMALL_PAINTING_2.png +0 -0
  75. package/pipeline/office/assets/SMALL_TABLE_FRONT.png +0 -0
  76. package/pipeline/office/assets/SMALL_TABLE_SIDE.png +0 -0
  77. package/pipeline/office/assets/SOFA_BACK.png +0 -0
  78. package/pipeline/office/assets/SOFA_FRONT.png +0 -0
  79. package/pipeline/office/assets/SOFA_SIDE.png +0 -0
  80. package/pipeline/office/assets/TABLE_FRONT.png +0 -0
  81. package/pipeline/office/assets/WHITEBOARD.png +0 -0
  82. package/pipeline/office/assets/WOODEN_BENCH.png +0 -0
  83. package/pipeline/office/assets/WOODEN_CHAIR_BACK.png +0 -0
  84. package/pipeline/office/assets/WOODEN_CHAIR_FRONT.png +0 -0
  85. package/pipeline/office/assets/WOODEN_CHAIR_SIDE.png +0 -0
  86. package/pipeline/office/assets/char_0.png +0 -0
  87. package/pipeline/office/assets/char_1.png +0 -0
  88. package/pipeline/office/assets/char_2.png +0 -0
  89. package/pipeline/office/assets/char_3.png +0 -0
  90. package/pipeline/office/assets/char_4.png +0 -0
  91. package/pipeline/office/assets/char_5.png +0 -0
  92. package/pipeline/office/assets/floor_0.png +0 -0
  93. package/pipeline/office/assets/floor_1.png +0 -0
  94. package/pipeline/office/assets/floor_2.png +0 -0
  95. package/pipeline/office/assets/floor_3.png +0 -0
  96. package/pipeline/office/assets/floor_4.png +0 -0
  97. package/pipeline/office/assets/floor_5.png +0 -0
  98. package/pipeline/office/assets/floor_6.png +0 -0
  99. package/pipeline/office/assets/floor_7.png +0 -0
  100. package/pipeline/office/assets/floor_8.png +0 -0
  101. package/pipeline/office/assets/wall_0.png +0 -0
  102. package/pipeline/office/index.html +5319 -0
  103. package/pipeline/preferences-template.json +7 -0
  104. package/pipeline/rules/figma-pipeline.md +2 -0
  105. package/pipeline/schemas/activity-chat.schema.json +78 -0
  106. package/pipeline/schemas/activity-roles.json +48 -0
  107. package/pipeline/schemas/activity-roles.schema.json +49 -0
  108. package/pipeline/schemas/activity.schema.json +429 -0
  109. package/pipeline/schemas/agent-state.schema.json +20 -5
  110. package/pipeline/schemas/autopilot-on-request.schema.json +33 -0
  111. package/pipeline/schemas/autopilot-on.schema.json +111 -0
  112. package/pipeline/schemas/contract-error.schema.json +33 -3
  113. package/pipeline/schemas/issue-maturity.schema.json +68 -0
  114. package/pipeline/schemas/ledger.schema.json +202 -0
  115. package/pipeline/schemas/permissions.schema.json +47 -0
  116. package/pipeline/schemas/phases.json +3 -2
  117. package/pipeline/schemas/phases.schema.json +5 -1
  118. package/pipeline/schemas/prefs.schema.json +56 -0
  119. package/pipeline/schemas/prs.schema.json +73 -0
  120. package/pipeline/schemas/review-verdict-request.schema.json +20 -0
  121. package/pipeline/schemas/review-verdict.schema.json +62 -0
  122. package/pipeline/schemas/routine-delete.schema.json +59 -0
  123. package/pipeline/schemas/routines.schema.json +38 -0
  124. package/pipeline/schemas/run-evidence.schema.json +29 -0
  125. package/pipeline/schemas/run-results.schema.json +218 -0
  126. package/pipeline/schemas/runs-index.schema.json +1 -1
  127. package/pipeline/schemas/service-read.schema.json +58 -0
  128. package/pipeline/schemas/stack-change.schema.json +82 -0
  129. package/pipeline/schemas/stack-request.schema.json +33 -0
  130. package/pipeline/schemas/stack.schema.json +59 -0
  131. package/pipeline/schemas/steer-request.schema.json +25 -0
  132. package/pipeline/schemas/steer.schema.json +59 -0
  133. package/pipeline/schemas/tool-call-request.schema.json +19 -0
  134. package/pipeline/schemas/tool-call.schema.json +47 -0
  135. package/pipeline/schemas/tools.schema.json +60 -0
  136. package/pipeline/scripts/_active-run.mjs +158 -0
  137. package/pipeline/scripts/_autopilot-outcomes.mjs +1 -0
  138. package/pipeline/scripts/_cost.mjs +66 -1
  139. package/pipeline/scripts/_guard_rules.py +605 -0
  140. package/pipeline/scripts/_office-smoke-lib.sh +115 -0
  141. package/pipeline/scripts/_spawn-async.mjs +73 -0
  142. package/pipeline/scripts/_transcript-tail.mjs +274 -0
  143. package/pipeline/scripts/activity-snapshot.mjs +1027 -0
  144. package/pipeline/scripts/agent-guard.py +118 -7
  145. package/pipeline/scripts/agent-guard.sh +50 -3
  146. package/pipeline/scripts/argv-secret-scan.mjs +114 -0
  147. package/pipeline/scripts/autopilot-control.mjs +214 -9
  148. package/pipeline/scripts/autopilot-runner.mjs +8 -2
  149. package/pipeline/scripts/confirm-command.mjs +70 -0
  150. package/pipeline/scripts/contract-server.mjs +1178 -48
  151. package/pipeline/scripts/cost-analyze.mjs +2 -53
  152. package/pipeline/scripts/cost-table.json +1 -0
  153. package/pipeline/scripts/doctor.mjs +54 -0
  154. package/pipeline/scripts/evidence-gate.mjs +8 -4
  155. package/pipeline/scripts/gate-ledger.mjs +163 -7
  156. package/pipeline/scripts/office-build.mjs +89 -0
  157. package/pipeline/scripts/office-control.mjs +383 -0
  158. package/pipeline/scripts/office-ledger.mjs +409 -0
  159. package/pipeline/scripts/plan-coverage-gate.mjs +9 -8
  160. package/pipeline/scripts/pre-commit-check.sh +12 -0
  161. package/pipeline/scripts/routine-registry.mjs +42 -6
  162. package/pipeline/scripts/run-results.mjs +373 -0
  163. package/pipeline/scripts/run-scope-guard.mjs +188 -0
  164. package/pipeline/scripts/run-scope-guard.sh +23 -0
  165. package/pipeline/scripts/run-stop-guard.mjs +246 -0
  166. package/pipeline/scripts/run-stop-guard.sh +25 -0
  167. package/pipeline/scripts/service-read.mjs +244 -0
  168. package/pipeline/scripts/stack-toggle.mjs +285 -0
  169. package/pipeline/scripts/steer-run.mjs +97 -0
  170. package/pipeline/scripts/toolkit-tools.mjs +599 -0
  171. package/pipeline/scripts/uninstall.mjs +4 -0
  172. package/pipeline/scripts/validate-state.mjs +26 -0
  173. package/pipeline/scripts/work-items.mjs +304 -0
  174. package/pipeline/scripts/write-state.mjs +114 -0
  175. package/pipeline/skills/.skill-manifest.json +19 -7
  176. package/pipeline/skills/.skills-index.json +34 -1
  177. package/pipeline/skills/shared/README.md +6 -3
  178. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +13 -15
  179. package/pipeline/skills/shared/core/multi-agent-office-off/SKILL.md +16 -0
  180. package/pipeline/skills/shared/core/multi-agent-office-on/SKILL.md +22 -0
  181. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +17 -0
  182. package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +4 -2
  183. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +14 -6
  184. package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +10 -9
  185. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
  186. package/pipeline/skills/skills-index.md +4 -1
@@ -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"));
@@ -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
  }