agentopology 0.5.2 → 0.6.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 (115) hide show
  1. package/README.md +5 -5
  2. package/dist/__tests__/e2e/roundtrip.test.d.ts +16 -0
  3. package/dist/__tests__/e2e/roundtrip.test.d.ts.map +1 -0
  4. package/dist/__tests__/e2e/roundtrip.test.js +178 -0
  5. package/dist/__tests__/e2e/roundtrip.test.js.map +1 -0
  6. package/dist/__tests__/e2e/scaffold-owns.test.d.ts +10 -0
  7. package/dist/__tests__/e2e/scaffold-owns.test.d.ts.map +1 -0
  8. package/dist/__tests__/e2e/scaffold-owns.test.js +154 -0
  9. package/dist/__tests__/e2e/scaffold-owns.test.js.map +1 -0
  10. package/dist/__tests__/global-setup.d.ts +7 -0
  11. package/dist/__tests__/global-setup.d.ts.map +1 -0
  12. package/dist/__tests__/global-setup.js +16 -0
  13. package/dist/__tests__/global-setup.js.map +1 -0
  14. package/dist/__tests__/live/claude-code.live.test.d.ts +20 -0
  15. package/dist/__tests__/live/claude-code.live.test.d.ts.map +1 -0
  16. package/dist/__tests__/live/claude-code.live.test.js +136 -0
  17. package/dist/__tests__/live/claude-code.live.test.js.map +1 -0
  18. package/dist/__tests__/live/zz-debug.test.d.ts +2 -0
  19. package/dist/__tests__/live/zz-debug.test.d.ts.map +1 -0
  20. package/dist/__tests__/live/zz-debug.test.js +4 -0
  21. package/dist/__tests__/live/zz-debug.test.js.map +1 -0
  22. package/dist/__tests__/open-findings.test.d.ts +10 -0
  23. package/dist/__tests__/open-findings.test.d.ts.map +1 -0
  24. package/dist/__tests__/open-findings.test.js +37 -0
  25. package/dist/__tests__/open-findings.test.js.map +1 -0
  26. package/dist/__tests__/spec-testing-sync.test.d.ts +10 -0
  27. package/dist/__tests__/spec-testing-sync.test.d.ts.map +1 -0
  28. package/dist/__tests__/spec-testing-sync.test.js +57 -0
  29. package/dist/__tests__/spec-testing-sync.test.js.map +1 -0
  30. package/dist/bindings/__tests__/hook-command.test.d.ts +9 -0
  31. package/dist/bindings/__tests__/hook-command.test.d.ts.map +1 -0
  32. package/dist/bindings/__tests__/hook-command.test.js +88 -0
  33. package/dist/bindings/__tests__/hook-command.test.js.map +1 -0
  34. package/dist/bindings/__tests__/mod-plugin.test.d.ts +9 -0
  35. package/dist/bindings/__tests__/mod-plugin.test.d.ts.map +1 -0
  36. package/dist/bindings/__tests__/mod-plugin.test.js +164 -0
  37. package/dist/bindings/__tests__/mod-plugin.test.js.map +1 -0
  38. package/dist/bindings/claude-code.d.ts +52 -1
  39. package/dist/bindings/claude-code.d.ts.map +1 -1
  40. package/dist/bindings/claude-code.js +306 -68
  41. package/dist/bindings/claude-code.js.map +1 -1
  42. package/dist/bindings/index.d.ts +7 -0
  43. package/dist/bindings/index.d.ts.map +1 -1
  44. package/dist/bindings/index.js +26 -7
  45. package/dist/bindings/index.js.map +1 -1
  46. package/dist/bindings/types.d.ts +6 -0
  47. package/dist/bindings/types.d.ts.map +1 -1
  48. package/dist/bindings/types.js.map +1 -1
  49. package/dist/cli/__tests__/cli.test.d.ts +1 -1
  50. package/dist/cli/__tests__/cli.test.js +1 -1
  51. package/dist/cli/index.js +118 -78
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/docs/content.d.ts.map +1 -1
  54. package/dist/docs/content.js +39 -3
  55. package/dist/docs/content.js.map +1 -1
  56. package/dist/docs/teach.d.ts.map +1 -1
  57. package/dist/docs/teach.js +36 -0
  58. package/dist/docs/teach.js.map +1 -1
  59. package/dist/import/__tests__/import.test.js +21 -9
  60. package/dist/import/__tests__/import.test.js.map +1 -1
  61. package/dist/import/claude-code.d.ts.map +1 -1
  62. package/dist/import/claude-code.js +128 -44
  63. package/dist/import/claude-code.js.map +1 -1
  64. package/dist/import/serializer.d.ts.map +1 -1
  65. package/dist/import/serializer.js +10 -0
  66. package/dist/import/serializer.js.map +1 -1
  67. package/dist/parser/__tests__/mod-hooks.test.d.ts +10 -0
  68. package/dist/parser/__tests__/mod-hooks.test.d.ts.map +1 -0
  69. package/dist/parser/__tests__/mod-hooks.test.js +136 -0
  70. package/dist/parser/__tests__/mod-hooks.test.js.map +1 -0
  71. package/dist/parser/ast.d.ts +29 -2
  72. package/dist/parser/ast.d.ts.map +1 -1
  73. package/dist/parser/ast.js.map +1 -1
  74. package/dist/parser/index.d.ts +6 -1
  75. package/dist/parser/index.d.ts.map +1 -1
  76. package/dist/parser/index.js +76 -22
  77. package/dist/parser/index.js.map +1 -1
  78. package/dist/parser/paths.d.ts +15 -0
  79. package/dist/parser/paths.d.ts.map +1 -0
  80. package/dist/parser/paths.js +34 -0
  81. package/dist/parser/paths.js.map +1 -0
  82. package/dist/parser/run.d.ts +24 -0
  83. package/dist/parser/run.d.ts.map +1 -0
  84. package/dist/parser/run.js +55 -0
  85. package/dist/parser/run.js.map +1 -0
  86. package/dist/parser/validator.d.ts +12 -0
  87. package/dist/parser/validator.d.ts.map +1 -1
  88. package/dist/parser/validator.js +218 -2
  89. package/dist/parser/validator.js.map +1 -1
  90. package/dist/scaffold/__tests__/safety.test.d.ts +11 -0
  91. package/dist/scaffold/__tests__/safety.test.d.ts.map +1 -0
  92. package/dist/scaffold/__tests__/safety.test.js +184 -0
  93. package/dist/scaffold/__tests__/safety.test.js.map +1 -0
  94. package/dist/scaffold/__tests__/scaffold.test.js +2 -1
  95. package/dist/scaffold/__tests__/scaffold.test.js.map +1 -1
  96. package/dist/scaffold/__tests__/settings-merge.test.js +40 -9
  97. package/dist/scaffold/__tests__/settings-merge.test.js.map +1 -1
  98. package/dist/scaffold/incremental.d.ts +49 -13
  99. package/dist/scaffold/incremental.d.ts.map +1 -1
  100. package/dist/scaffold/incremental.js +235 -95
  101. package/dist/scaffold/incremental.js.map +1 -1
  102. package/dist/scaffold/manifest.d.ts +8 -1
  103. package/dist/scaffold/manifest.d.ts.map +1 -1
  104. package/dist/scaffold/manifest.js +32 -8
  105. package/dist/scaffold/manifest.js.map +1 -1
  106. package/dist/scaffold/merge.d.ts +15 -4
  107. package/dist/scaffold/merge.d.ts.map +1 -1
  108. package/dist/scaffold/merge.js +109 -18
  109. package/dist/scaffold/merge.js.map +1 -1
  110. package/dist/scaffold/types.d.ts +17 -0
  111. package/dist/scaffold/types.d.ts.map +1 -1
  112. package/package.json +1 -1
  113. package/spec/grammar.md +20 -4
  114. package/spec/testing.md +81 -0
  115. package/spec/validation.md +6 -2
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Live: real Claude Code loads what scaffold generates.
3
+ *
4
+ * Unit tests check the files we write; only Claude Code can say whether it
5
+ * reads them. This scaffolds every example (and the hazards fixture) into a
6
+ * temporary project, starts a headless `claude -p` session there, and checks
7
+ * the session's init report: every generated agent, skill and command is
8
+ * registered, and the session itself succeeds.
9
+ *
10
+ * It found the first bug the moment it ran: settings `env` carried the literal
11
+ * text "${ANTHROPIC_API_KEY}", which Claude Code applied as the key, so every
12
+ * session in 6 of the 10 example projects failed with "Invalid API key".
13
+ *
14
+ * Opt-in, because it needs the `claude` CLI and makes one small model call per
15
+ * project: AT_LIVE=1 npx vitest run src/__tests__/live
16
+ * The session is isolated from the developer's own setup: user settings (and
17
+ * their hooks), MCP servers and session history are all left out.
18
+ */
19
+ import { describe, it, expect } from "vitest";
20
+ import { execFileSync, spawnSync } from "node:child_process";
21
+ import * as fs from "node:fs";
22
+ import * as os from "node:os";
23
+ import { resolve, dirname, join, basename } from "node:path";
24
+ import { fileURLToPath } from "node:url";
25
+ const here = dirname(fileURLToPath(import.meta.url));
26
+ const root = resolve(here, "../../..");
27
+ const CLI = resolve(root, "dist/cli/index.js");
28
+ const LIVE = process.env.AT_LIVE === "1";
29
+ /**
30
+ * The installed Claude Code, not a copy under some node_modules/.bin: npx puts
31
+ * every ancestor's node_modules/.bin first on PATH, and a stale 1.0.x found
32
+ * there rejected today's flags. AT_CLAUDE_BIN overrides.
33
+ */
34
+ function claudeBin() {
35
+ if (process.env.AT_CLAUDE_BIN)
36
+ return process.env.AT_CLAUDE_BIN;
37
+ for (const dir of (process.env.PATH ?? "").split(":")) {
38
+ if (dir.includes("node_modules"))
39
+ continue;
40
+ const candidate = join(dir, "claude");
41
+ if (fs.existsSync(candidate))
42
+ return candidate;
43
+ }
44
+ return "claude";
45
+ }
46
+ function runClaude(cwd) {
47
+ const proc = spawnSync(claudeBin(), [
48
+ "-p",
49
+ "Reply with the single word OK.",
50
+ "--setting-sources", "project,local",
51
+ "--strict-mcp-config",
52
+ "--no-session-persistence",
53
+ "--model", "haiku",
54
+ "--max-turns", "1",
55
+ "--output-format", "stream-json",
56
+ "--verbose",
57
+ ], { cwd, encoding: "utf8", timeout: 180_000 });
58
+ const lines = (proc.stdout ?? "").split("\n").filter((l) => l.trim().startsWith("{"));
59
+ const events = lines.map((l) => JSON.parse(l));
60
+ const init = events.find((e) => e.type === "system" && e.subtype === "init");
61
+ const result = events.find((e) => e.type === "result");
62
+ if (!init || !result) {
63
+ throw new Error(`claude produced no init/result (exit ${proc.status}): ${proc.stderr}`);
64
+ }
65
+ return { init, result, stderr: proc.stderr ?? "" };
66
+ }
67
+ /**
68
+ * `name:` from a generated agent file's frontmatter, or null when it has none.
69
+ * Human nodes are written to .claude/agents/<id>/AGENT.md without frontmatter;
70
+ * Claude Code ignores such a file (open finding H1: they should compile to an
71
+ * approval step), so only files that declare a subagent are expected to load.
72
+ */
73
+ function frontmatterName(path) {
74
+ const m = fs.readFileSync(path, "utf-8").match(/^---\n[\s\S]*?^name:\s*"?([^"\n]+)"?\s*$/m);
75
+ return m ? m[1].trim() : null;
76
+ }
77
+ const projects = [
78
+ ...fs.readdirSync(resolve(root, "examples")).filter((f) => f.endsWith(".at")).map((f) => resolve(root, "examples", f)),
79
+ resolve(here, "../e2e/fixtures/hazards.at"),
80
+ ];
81
+ describe.skipIf(!LIVE)("[L1] Claude Code loads every scaffolded project", () => {
82
+ it.each(projects.map((p) => [basename(p), p]))("%s", (_name, atFile) => {
83
+ const project = fs.mkdtempSync(join(os.tmpdir(), "at-live-"));
84
+ try {
85
+ execFileSync(process.execPath, [CLI, "scaffold", atFile, "--target", "claude-code", "--output", project], {
86
+ stdio: "ignore",
87
+ });
88
+ const run = runClaude(project);
89
+ const agentsDir = join(project, ".claude/agents");
90
+ const agents = fs.existsSync(agentsDir)
91
+ ? fs.readdirSync(agentsDir)
92
+ .map((d) => frontmatterName(join(agentsDir, d, "AGENT.md")))
93
+ .filter((n) => n !== null)
94
+ : [];
95
+ const skillsDir = join(project, ".claude/skills");
96
+ const skills = fs.existsSync(skillsDir)
97
+ ? fs.readdirSync(skillsDir).filter((d) => fs.existsSync(join(skillsDir, d, "SKILL.md")))
98
+ : [];
99
+ const commandsDir = join(project, ".claude/commands");
100
+ const commands = fs.existsSync(commandsDir)
101
+ ? fs.readdirSync(commandsDir).filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, ""))
102
+ : [];
103
+ expect(run.result.is_error, `session failed: ${run.result.result}`).toBe(false);
104
+ for (const a of agents)
105
+ expect(run.init.agents, `agent ${a}`).toContain(a);
106
+ for (const s of skills)
107
+ expect(run.init.skills, `skill ${s}`).toContain(s);
108
+ for (const c of commands)
109
+ expect(run.init.slash_commands, `command ${c}`).toContain(c);
110
+ }
111
+ finally {
112
+ fs.rmSync(project, { recursive: true, force: true });
113
+ }
114
+ }, 200_000);
115
+ });
116
+ describe.skipIf(!LIVE)("[L2] Claude Code loads a hand-built project after import then scaffold", () => {
117
+ it("handbuilt fixture", () => {
118
+ const tmp = fs.mkdtempSync(join(os.tmpdir(), "at-live-rt-"));
119
+ const project = join(tmp, "project");
120
+ try {
121
+ fs.cpSync(resolve(here, "../e2e/fixtures/handbuilt"), project, { recursive: true });
122
+ execFileSync(process.execPath, [CLI, "import", "--target", "claude-code", "--dir", project, "--name", "handbuilt", "--output", tmp], { stdio: "ignore" });
123
+ execFileSync(process.execPath, [CLI, "scaffold", join(tmp, "handbuilt.at"), "--target", "claude-code", "--output", project], { stdio: "ignore" });
124
+ const run = runClaude(project);
125
+ expect(run.result.is_error, `session failed: ${run.result.result}`).toBe(false);
126
+ for (const a of ["reviewer", "writer"])
127
+ expect(run.init.agents, `agent ${a}`).toContain(a);
128
+ expect(run.init.skills).toContain("story");
129
+ expect(run.init.slash_commands).toContain("ship");
130
+ }
131
+ finally {
132
+ fs.rmSync(tmp, { recursive: true, force: true });
133
+ }
134
+ }, 200_000);
135
+ });
136
+ //# sourceMappingURL=claude-code.live.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.live.test.js","sourceRoot":"","sources":["../../../src/__tests__/live/claude-code.live.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=zz-debug.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zz-debug.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/live/zz-debug.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import { it } from "vitest";
2
+ import { spawnSync } from "node:child_process";
3
+ it("debug", () => { const r = spawnSync("sh", ["-c", "command -v claude; claude --version; echo $PATH | tr ':' '\n' | head -5"], { encoding: "utf8" }); console.log(r.stdout, r.stderr); });
4
+ //# sourceMappingURL=zz-debug.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zz-debug.test.js","sourceRoot":"","sources":["../../../src/__tests__/live/zz-debug.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,yEAAyE,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * What the suite does NOT yet guarantee — visible in every run.
3
+ *
4
+ * Each todo is an open finding from spec/testing.md, under the same id. When
5
+ * one is fixed, replace its todo with a real test (keeping the id in the name)
6
+ * and move the row to Guarantees. spec-testing-sync.test.ts keeps the two
7
+ * lists in step.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=open-findings.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-findings.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/open-findings.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * What the suite does NOT yet guarantee — visible in every run.
3
+ *
4
+ * Each todo is an open finding from spec/testing.md, under the same id. When
5
+ * one is fixed, replace its todo with a real test (keeping the id in the name)
6
+ * and move the row to Guarantees. spec-testing-sync.test.ts keeps the two
7
+ * lists in step.
8
+ */
9
+ import { describe, it } from "vitest";
10
+ describe("open findings (spec/testing.md)", () => {
11
+ it.todo("[N11] sync pulls generated prose back into the .at, so the prompt grows each cycle");
12
+ it.todo("[N12] A skill `prompt: \"path\"` emits `See: path`; the instructions never reach Claude");
13
+ it.todo("[A3] `kind: external` actions ignore `commands:` and emit a TODO stub");
14
+ it.todo("[A3b] Tools with a real `script:` get stub copies");
15
+ it.todo("[B4] Schedule scripts call the wrong action path; `every:` becomes a commented-out cron line");
16
+ it.todo("[H1] Human nodes compile to frontmatter-less AGENT.md files that Claude Code ignores; they should be an approval step");
17
+ it.todo("[N4] `.mcp.json` drops `type`, so a url server is skipped");
18
+ it.todo("[N5] `type: prompt` hooks are emitted as commands");
19
+ it.todo("[N6] Universal events (AgentStop, ToolUse, Error) are emitted unmapped, so the hook never fires");
20
+ it.todo("[N7] MCP tool names: whole-server and dotted names mis-converted; permissions not converted");
21
+ it.todo("[N8] V27 accepts `confirm` and `bypass`, emitted as invalid `permissionMode`");
22
+ it.todo("[N9] A hook matcher with a regex breaks the agent file's YAML");
23
+ it.todo("[N10] `invocation: manual` drops the description, so the agent is not registered");
24
+ it.todo("[N14] `settings.ask` is parsed but never emitted");
25
+ it.todo("[N16] Model ids are not checked per target");
26
+ it.todo("[N17] Unquoted YAML scalars change type (`yes` becomes `true`)");
27
+ it.todo("[N18] No reference checks for `skills:`, `mcp-servers:`, skill `agent:`");
28
+ it.todo("[N19] Hook stubs carry the date, so output is not byte-stable");
29
+ it.todo("[C1] `scaffold --dry-run` says settings.json and .mcp.json would be \"config merged\" but does not show the diff (content session, 2026-10-29)");
30
+ it.todo("[C2] Scaffold adds `.claude/CLAUDE.md`, which competes with a project's root CLAUDE.md (content session, 2026-10-29)");
31
+ it.todo("[C3] Run-line parsing takes placeholders and file arguments as scripts (`SLUG`, `frames-critic.json`) and plans stubs for them (content session, 2026-10-29)");
32
+ it.todo("[C4] A gate whose `after:` is an action or a human compiles to no enforceable hook (10 gates in content-harness)");
33
+ it.todo("[B5] An agent's permissions read as per-agent grants; Claude Code allow rules are session-wide");
34
+ it.todo("[F1] Missing first-class fields: agent effort, initialPrompt, omitClaudeMd, cache TTL, color; skill when_to_use, arguments, paths, shell");
35
+ it.todo("[F2] Hooks as a typed union (http, mcp_tool, agent; once, async, if)");
36
+ });
37
+ //# sourceMappingURL=open-findings.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-findings.test.js","sourceRoot":"","sources":["../../src/__tests__/open-findings.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEtC,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,IAAI,CAAC,oFAAoF,CAAC,CAAC;IAC9F,EAAE,CAAC,IAAI,CAAC,yFAAyF,CAAC,CAAC;IACnG,EAAE,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;IACjF,EAAE,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAC7D,EAAE,CAAC,IAAI,CAAC,8FAA8F,CAAC,CAAC;IACxG,EAAE,CAAC,IAAI,CAAC,uHAAuH,CAAC,CAAC;IACjI,EAAE,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IACrE,EAAE,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAC7D,EAAE,CAAC,IAAI,CAAC,iGAAiG,CAAC,CAAC;IAC3G,EAAE,CAAC,IAAI,CAAC,6FAA6F,CAAC,CAAC;IACvG,EAAE,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;IACxF,EAAE,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IACzE,EAAE,CAAC,IAAI,CAAC,kFAAkF,CAAC,CAAC;IAC5F,EAAE,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;IAC5D,EAAE,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;IACtD,EAAE,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;IAC1E,EAAE,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;IACnF,EAAE,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;IACzE,EAAE,CAAC,IAAI,CAAC,gJAAgJ,CAAC,CAAC;IAC1J,EAAE,CAAC,IAAI,CAAC,sHAAsH,CAAC,CAAC;IAChI,EAAE,CAAC,IAAI,CAAC,8JAA8J,CAAC,CAAC;IACxK,EAAE,CAAC,IAAI,CAAC,kHAAkH,CAAC,CAAC;IAC5H,EAAE,CAAC,IAAI,CAAC,gGAAgG,CAAC,CAAC;IAC1G,EAAE,CAAC,IAAI,CAAC,0IAA0I,CAAC,CAAC;IACpJ,EAAE,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;AAClF,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * spec/testing.md and the tests cannot drift apart.
3
+ *
4
+ * Every guarantee id in the spec must name at least one test, and every id a
5
+ * test claims must be in the spec, as a guarantee or as an open finding. Same
6
+ * idea as spec-sync.test.ts for validation rules: drift is a missing test, not
7
+ * a discipline problem.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=spec-testing-sync.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-testing-sync.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/spec-testing-sync.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * spec/testing.md and the tests cannot drift apart.
3
+ *
4
+ * Every guarantee id in the spec must name at least one test, and every id a
5
+ * test claims must be in the spec, as a guarantee or as an open finding. Same
6
+ * idea as spec-sync.test.ts for validation rules: drift is a missing test, not
7
+ * a discipline problem.
8
+ */
9
+ import { describe, it, expect } from "vitest";
10
+ import { readFileSync, readdirSync } from "node:fs";
11
+ import { resolve, dirname, join } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
14
+ const spec = readFileSync(resolve(root, "spec/testing.md"), "utf8");
15
+ const section = (title) => {
16
+ const start = spec.indexOf(title);
17
+ const next = spec.indexOf("\n## ", start + title.length);
18
+ return spec.slice(start, next === -1 ? undefined : next);
19
+ };
20
+ const idsIn = (md) => [...md.matchAll(/^\| ([A-Z]+\d+) \|/gm)].map((m) => m[1]);
21
+ const guarantees = idsIn(section("## Guarantees"));
22
+ const open = idsIn(section("## Open findings"));
23
+ function testFiles(dir) {
24
+ return readdirSync(dir, { withFileTypes: true }).flatMap((e) => {
25
+ const p = join(dir, e.name);
26
+ if (e.isDirectory())
27
+ return e.name === "node_modules" ? [] : testFiles(p);
28
+ return e.name.endsWith(".test.ts") ? [p] : [];
29
+ });
30
+ }
31
+ const sources = testFiles(resolve(root, "src")).map((f) => readFileSync(f, "utf8"));
32
+ const realTags = new Set(sources.flatMap((s) => [...s.matchAll(/\b(?:it|it\.each\([^)]*\)|describe(?:\.skipIf\([^)]*\))?)\(\s*["'`]\[([A-Z]+\d+)\]/g)].map((m) => m[1])));
33
+ const todoTags = new Set(sources.flatMap((s) => [...s.matchAll(/it\.todo\(\s*["'`]\[([A-Z]+\d+)\]/g)].map((m) => m[1])));
34
+ describe("spec/testing.md tracks the tests", () => {
35
+ it("lists guarantees and open findings", () => {
36
+ expect(guarantees.length).toBeGreaterThan(0);
37
+ expect(open.length).toBeGreaterThan(0);
38
+ });
39
+ it("every guarantee has a real test carrying its id", () => {
40
+ const missing = guarantees.filter((id) => !realTags.has(id));
41
+ expect(missing, `guarantees with no test: ${missing.join(", ")}`).toEqual([]);
42
+ });
43
+ it("every open finding is a visible todo", () => {
44
+ const missing = open.filter((id) => !todoTags.has(id));
45
+ expect(missing, `open findings with no it.todo: ${missing.join(", ")}`).toEqual([]);
46
+ });
47
+ it("no test claims an id the spec does not list", () => {
48
+ const known = new Set([...guarantees, ...open]);
49
+ const unknown = [...realTags, ...todoTags].filter((id) => !known.has(id));
50
+ expect(unknown, `ids not in spec/testing.md: ${unknown.join(", ")}`).toEqual([]);
51
+ });
52
+ it("a fixed finding is not still listed as open", () => {
53
+ const both = open.filter((id) => realTags.has(id));
54
+ expect(both, `real tests exist for open ids (move them to Guarantees): ${both.join(", ")}`).toEqual([]);
55
+ });
56
+ });
57
+ //# sourceMappingURL=spec-testing-sync.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-testing-sync.test.js","sourceRoot":"","sources":["../../src/__tests__/spec-testing-sync.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A command hook's `run:` → the settings.json command.
3
+ *
4
+ * Before: a project path ("./x/y.py --flag") was run by bash whatever its
5
+ * extension, and relative to the hook's cwd, which is the session's and moves.
6
+ * Now it runs by its extension's interpreter through $CLAUDE_PROJECT_DIR.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=hook-command.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-command.test.d.ts","sourceRoot":"","sources":["../../../src/bindings/__tests__/hook-command.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * A command hook's `run:` → the settings.json command.
3
+ *
4
+ * Before: a project path ("./x/y.py --flag") was run by bash whatever its
5
+ * extension, and relative to the hook's cwd, which is the session's and moves.
6
+ * Now it runs by its extension's interpreter through $CLAUDE_PROJECT_DIR.
7
+ */
8
+ import { describe, it, expect } from "vitest";
9
+ import { parse } from "../../parser/index.js";
10
+ import { hookCommand, settingsHooksOf } from "../claude-code.js";
11
+ import { bindings } from "../index.js";
12
+ describe("hookCommand", () => {
13
+ it("runs a project .py path by python3 from the project root, arguments kept", () => {
14
+ expect(hookCommand("./harness-plugin/hooks/router.py --pane", "t")).toBe('python3 "$CLAUDE_PROJECT_DIR/harness-plugin/hooks/router.py" --pane');
15
+ });
16
+ it("runs a dotted .sh path by bash from the project root", () => {
17
+ expect(hookCommand(".claude/hooks/gates/gate.sh", "t")).toBe('bash "$CLAUDE_PROJECT_DIR/.claude/hooks/gates/gate.sh"');
18
+ expect(hookCommand("./scripts/x.sh", "t")).toBe('bash "$CLAUDE_PROJECT_DIR/scripts/x.sh"');
19
+ });
20
+ it("runs by the interpreter the run: names, kept as written", () => {
21
+ expect(hookCommand("/opt/homebrew/bin/python3 ./harness-plugin/hooks/router.py --pane", "t")).toBe('/opt/homebrew/bin/python3 "$CLAUDE_PROJECT_DIR/harness-plugin/hooks/router.py" --pane');
22
+ expect(hookCommand("sh .claude/x.sh", "t")).toBe('sh "$CLAUDE_PROJECT_DIR/.claude/x.sh"');
23
+ });
24
+ it("keeps an absolute path as written", () => {
25
+ expect(hookCommand("/opt/x/y.py -q", "t")).toBe("python3 /opt/x/y.py -q");
26
+ });
27
+ it("keeps any other run: in the skill's scripts folder by basename, as before", () => {
28
+ expect(hookCommand("gate.sh", "mini")).toBe("bash .claude/skills/mini/scripts/gate.sh");
29
+ expect(hookCommand("scripts/post-write.sh", "mini")).toBe("bash .claude/skills/mini/scripts/post-write.sh");
30
+ });
31
+ });
32
+ describe("settingsHooksOf", () => {
33
+ const ast = parse([
34
+ "topology mini : [pipeline] {",
35
+ " meta {",
36
+ ' version: "1.0.0"',
37
+ ' description: "hooks"',
38
+ " }",
39
+ " orchestrator {",
40
+ " model: sonnet",
41
+ " }",
42
+ " agent a {",
43
+ " model: sonnet",
44
+ ' description: "a"',
45
+ " }",
46
+ " flow { a }",
47
+ " hooks {",
48
+ " hook pane {",
49
+ " on: PreToolUse",
50
+ ' matcher: "SendMessage"',
51
+ ' run: "./harness-plugin/hooks/router.py --pane"',
52
+ " type: command",
53
+ " timeout: 3",
54
+ " }",
55
+ " hook stop {",
56
+ " on: Stop",
57
+ ' run: "/opt/homebrew/bin/python3 .claude/hooks/end_check.py"',
58
+ " type: command",
59
+ " }",
60
+ " hook redact {",
61
+ " on: tool.call",
62
+ ' run: "harness-plugin/hooks/register.ts"',
63
+ " type: mod",
64
+ " flag: X",
65
+ " }",
66
+ " }",
67
+ "}",
68
+ ].join("\n"));
69
+ it("emits the command hooks only, with matcher and timeout (seconds, as written)", () => {
70
+ expect(settingsHooksOf(ast)).toEqual({
71
+ PreToolUse: [
72
+ {
73
+ matcher: "SendMessage",
74
+ hooks: [{ type: "command", command: 'python3 "$CLAUDE_PROJECT_DIR/harness-plugin/hooks/router.py" --pane', timeout: 3 }],
75
+ },
76
+ ],
77
+ Stop: [{ hooks: [{ type: "command", command: '/opt/homebrew/bin/python3 "$CLAUDE_PROJECT_DIR/.claude/hooks/end_check.py"' }] }],
78
+ });
79
+ });
80
+ it("writes a dotted hook's stub at its path, never at a path holding its arguments", () => {
81
+ const paths = bindings["claude-code"].scaffold(ast).map((f) => f.path);
82
+ expect(paths).toContain("./harness-plugin/hooks/router.py");
83
+ expect(paths.some((p) => p.includes("--pane"))).toBe(false);
84
+ expect(paths).toContain(".claude/hooks/end_check.py");
85
+ expect(paths.some((p) => p.includes("homebrew")), "never a stub at the interpreter").toBe(false);
86
+ });
87
+ });
88
+ //# sourceMappingURL=hook-command.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-command.test.js","sourceRoot":"","sources":["../../../src/bindings/__tests__/hook-command.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `type: mod` hooks on claude-code → a plugin directory, never settings.json.
3
+ *
4
+ * The engine loads ONE module per plugin (measured, Claude Code 2.1.283), so
5
+ * the plugin's single entry is a generated register.ts that registers every
6
+ * Mod. Command hooks keep going to settings.json exactly as before.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=mod-plugin.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod-plugin.test.d.ts","sourceRoot":"","sources":["../../../src/bindings/__tests__/mod-plugin.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * `type: mod` hooks on claude-code → a plugin directory, never settings.json.
3
+ *
4
+ * The engine loads ONE module per plugin (measured, Claude Code 2.1.283), so
5
+ * the plugin's single entry is a generated register.ts that registers every
6
+ * Mod. Command hooks keep going to settings.json exactly as before.
7
+ */
8
+ import { describe, it, expect, vi } from "vitest";
9
+ import { parse } from "../../parser/index.js";
10
+ import { validate } from "../../parser/validator.js";
11
+ import { claudeCodeBinding, bindings } from "../index.js";
12
+ const topology = (hooks) => [
13
+ "topology mini : [pipeline] {",
14
+ " meta {",
15
+ ' version: "1.2.0"',
16
+ ' description: "mods"',
17
+ " }",
18
+ " orchestrator {",
19
+ " model: sonnet",
20
+ " }",
21
+ " agent a {",
22
+ " model: sonnet",
23
+ ' description: "a"',
24
+ " }",
25
+ " flow { a }",
26
+ " hooks {",
27
+ hooks,
28
+ " }",
29
+ "}",
30
+ ].join("\n");
31
+ const REDACT = [
32
+ " hook redact {",
33
+ " on: tool.call",
34
+ ' run: "harness-plugin/hooks/redact.ts"',
35
+ " type: mod",
36
+ " flag: MY_MOD_REDACT",
37
+ " }",
38
+ ].join("\n");
39
+ const STATE = [
40
+ " hook state {",
41
+ " on: prompt.submit",
42
+ ' run: "harness-plugin/hooks/state_mod.ts"',
43
+ " type: mod",
44
+ " flag: MY_MOD_STATE",
45
+ " }",
46
+ ].join("\n");
47
+ const GATE = [
48
+ " hook gate {",
49
+ " on: PreToolUse",
50
+ ' matcher: "Bash"',
51
+ ' run: "gate.sh"',
52
+ " }",
53
+ ].join("\n");
54
+ const PLUGIN = "harness-plugin"; // the folder that holds the Mods' modules
55
+ const scaffold = (src) => claudeCodeBinding.scaffold(parse(src));
56
+ const file = (files, p) => files.find((f) => f.path === p);
57
+ describe("claude-code: Mods become a plugin", () => {
58
+ const files = scaffold(topology([REDACT, STATE, GATE].join("\n")));
59
+ it("emits the plugin's four files", () => {
60
+ expect(files.filter((f) => f.path.startsWith(PLUGIN + "/")).map((f) => f.path).sort()).toEqual([
61
+ `${PLUGIN}/.claude-plugin/plugin.json`,
62
+ `${PLUGIN}/hooks/at-register.ts`,
63
+ `${PLUGIN}/hooks/hooks.json`,
64
+ `${PLUGIN}/launch-mods.sh`,
65
+ ]);
66
+ });
67
+ it("names ONE entry module in hooks.json", () => {
68
+ expect(JSON.parse(file(files, `${PLUGIN}/hooks/hooks.json`).content).modules).toEqual(["./at-register.ts"]);
69
+ const manifest = JSON.parse(file(files, `${PLUGIN}/.claude-plugin/plugin.json`).content);
70
+ expect(manifest).toMatchObject({ name: "mini-mods", version: "1.2.0" });
71
+ });
72
+ it("registers every Mod from the entry, passing on as it came", () => {
73
+ const reg = file(files, `${PLUGIN}/hooks/at-register.ts`).content;
74
+ // Inside the plugin folder: the engine refuses an import outside it (2.1.283).
75
+ expect(reg).toContain("import { register as mod_redact } from './redact'");
76
+ expect(reg).toContain("import { register as mod_state } from './state_mod'");
77
+ expect(reg).not.toContain("../");
78
+ // The engine refuses a wrapped or kept `on`, and a module has no `process`
79
+ // (both measured, 2.1.283): the entry passes `on` as it came.
80
+ expect(reg).toContain(" mod_redact(on)");
81
+ expect(reg).toContain(" mod_state(on)");
82
+ expect(reg).toContain("the Mod reads its own switch, MY_MOD_REDACT");
83
+ expect(reg).not.toContain("process.");
84
+ expect(reg).not.toMatch(/\bon\)\s*=>|behind\(/);
85
+ expect(reg).toContain("export function register(on: On)");
86
+ });
87
+ it("launches with the engine switch, each flag and --plugin-dir", () => {
88
+ const launch = file(files, `${PLUGIN}/launch-mods.sh`);
89
+ expect(launch.executable).toBe(true);
90
+ expect(launch.content).toContain("export CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1");
91
+ expect(launch.content).toContain('export MY_MOD_REDACT="${MY_MOD_REDACT:-1}"');
92
+ expect(launch.content).toContain('export MY_MOD_STATE="${MY_MOD_STATE:-1}"');
93
+ expect(launch.content).toContain('exec claude --plugin-dir "$PLUGIN_DIR" "$@"');
94
+ });
95
+ it("keeps Mods out of settings.json; the command hook stays there", () => {
96
+ const hooks = JSON.parse(file(files, ".claude/settings.json").content).hooks;
97
+ expect(Object.keys(hooks)).toEqual(["PreToolUse"]);
98
+ expect(hooks.PreToolUse[0].hooks[0]).toEqual({ type: "command", command: "bash .claude/skills/mini/scripts/gate.sh" });
99
+ expect(JSON.stringify(hooks)).not.toContain("mod");
100
+ });
101
+ it("never writes over a Mod's own module", () => {
102
+ expect(files.some((f) => f.path.includes("redact") || f.path.includes("state_mod"))).toBe(false);
103
+ });
104
+ it("never overwrites a hand-written hooks/register.ts", () => {
105
+ expect(files.some((f) => f.path.endsWith("/register.ts"))).toBe(false);
106
+ });
107
+ });
108
+ describe("claude-code: the plugin root holds the modules", () => {
109
+ it("is the project root when the Mods share no folder, still without ../", () => {
110
+ const src = topology([REDACT, STATE.replace("harness-plugin/hooks/state_mod.ts", "mods/state_mod.ts")].join("\n"));
111
+ const files = scaffold(src);
112
+ expect(files.map((f) => f.path)).toContain("hooks/at-register.ts");
113
+ expect(files.map((f) => f.path)).toContain(".claude-plugin/plugin.json");
114
+ const reg = file(files, "hooks/at-register.ts").content;
115
+ expect(reg).toContain("from '../harness-plugin/hooks/redact'");
116
+ expect(reg).toContain("from '../mods/state_mod'");
117
+ });
118
+ it("keeps a nested folder that is not hooks/", () => {
119
+ const src = topology(REDACT.replace("harness-plugin/hooks/redact.ts", "tools/mods/redact.ts"));
120
+ expect(file(scaffold(src), "tools/mods/hooks/at-register.ts").content).toContain("from '../redact'");
121
+ });
122
+ it("skips the plugin, with a warning, when a run: path is absolute", () => {
123
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
124
+ const files = scaffold(topology(REDACT.replace("harness-plugin/hooks/redact.ts", "/opt/redact.ts")));
125
+ expect(files.some((f) => f.path.endsWith("at-register.ts"))).toBe(false);
126
+ expect(warn.mock.calls.some((c) => String(c[0]).includes("is absolute"))).toBe(true);
127
+ warn.mockRestore();
128
+ });
129
+ });
130
+ describe("claude-code: no Mods, no change", () => {
131
+ it("emits no plugin for a topology without Mods", () => {
132
+ const files = scaffold(topology(GATE));
133
+ expect(files.some((f) => f.path.startsWith(".claude/plugins/"))).toBe(false);
134
+ });
135
+ it("emits the same files with or without the Mods, apart from the plugin", () => {
136
+ const plain = scaffold(topology(GATE));
137
+ const withMods = scaffold(topology([REDACT, GATE].join("\n")));
138
+ const strip = (fs) => fs.filter((f) => !f.path.startsWith(PLUGIN)).map((f) => [f.path, f.content]);
139
+ expect(strip(withMods)).toEqual(strip(plain));
140
+ });
141
+ });
142
+ describe("other targets cannot host a Mod", () => {
143
+ it("drops it with a warning instead of writing a shell command for a .ts file", () => {
144
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
145
+ for (const [name, b] of Object.entries(bindings)) {
146
+ if (name === "claude-code")
147
+ continue;
148
+ const out = b.scaffold(parse(topology([REDACT, GATE].join("\n"))));
149
+ expect(out.some((f) => f.content.includes("redact.ts")), name).toBe(false);
150
+ }
151
+ expect(warn.mock.calls.filter((c) => String(c[0]).includes("cannot host them"))).toHaveLength(7);
152
+ warn.mockRestore();
153
+ });
154
+ });
155
+ describe("V94: a Mod is engine-wide", () => {
156
+ it("refuses a mod declared inside an agent", () => {
157
+ const src = topology(GATE).replace(' description: "a"\n', ' description: "a"\n hooks {\n hook m {\n on: tool.call\n run: "m.ts"\n type: mod\n }\n }\n');
158
+ const v94 = validate(parse(src)).filter((r) => r.rule === "V94");
159
+ expect(v94.map((r) => r.message)).toEqual([
160
+ 'Hook "a.m" is a mod inside an agent — a Mod runs in the engine for the whole session; declare it in the topology\'s hooks block.',
161
+ ]);
162
+ });
163
+ });
164
+ //# sourceMappingURL=mod-plugin.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod-plugin.test.js","sourceRoot":"","sources":["../../../src/bindings/__tests__/mod-plugin.test.ts"],"names":[],"mappings":"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"}