intentdna 1.2.2 → 1.3.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 (184) hide show
  1. package/.claude-plugin/hooks/hooks.json +12 -0
  2. package/.claude-plugin/plugin.json +5 -0
  3. package/dist/audit/index.d.ts +0 -1
  4. package/dist/audit/index.js +0 -1
  5. package/dist/cli/commands/compile.d.ts +0 -1
  6. package/dist/cli/commands/compile.js +0 -1
  7. package/dist/cli/commands/epigenetic.d.ts +0 -1
  8. package/dist/cli/commands/epigenetic.js +0 -1
  9. package/dist/cli/commands/evolve.d.ts +0 -1
  10. package/dist/cli/commands/evolve.js +0 -1
  11. package/dist/cli/commands/guard.d.ts +0 -1
  12. package/dist/cli/commands/guard.js +0 -3
  13. package/dist/cli/commands/import.d.ts +0 -1
  14. package/dist/cli/commands/import.js +0 -1
  15. package/dist/cli/commands/init.d.ts +0 -1
  16. package/dist/cli/commands/init.js +0 -1
  17. package/dist/cli/commands/run.d.ts +0 -1
  18. package/dist/cli/commands/run.js +4 -27
  19. package/dist/cli/commands/setup.d.ts +9 -0
  20. package/dist/cli/commands/setup.js +42 -0
  21. package/dist/cli/commands/show.d.ts +0 -1
  22. package/dist/cli/commands/show.js +0 -1
  23. package/dist/cli/commands/sync.d.ts +13 -1
  24. package/dist/cli/commands/sync.js +158 -33
  25. package/dist/cli/commands/validate.d.ts +0 -1
  26. package/dist/cli/commands/validate.js +0 -1
  27. package/dist/cli/commands/verify.d.ts +0 -1
  28. package/dist/cli/commands/verify.js +0 -1
  29. package/dist/cli/index.d.ts +0 -1
  30. package/dist/cli/index.js +9 -1
  31. package/dist/cli/util/format.d.ts +0 -1
  32. package/dist/cli/util/format.js +0 -1
  33. package/dist/cli/util/prompt.d.ts +0 -1
  34. package/dist/cli/util/prompt.js +0 -1
  35. package/dist/compiler/activate.d.ts +0 -1
  36. package/dist/compiler/activate.js +0 -1
  37. package/dist/compiler/cascade.d.ts +1 -3
  38. package/dist/compiler/cascade.js +1 -10
  39. package/dist/compiler/compile.d.ts +0 -1
  40. package/dist/compiler/compile.js +33 -17
  41. package/dist/compiler/index.d.ts +0 -1
  42. package/dist/compiler/index.js +0 -1
  43. package/dist/compiler/workflow.d.ts +0 -1
  44. package/dist/compiler/workflow.js +0 -2
  45. package/dist/evolution/collector.d.ts +0 -1
  46. package/dist/evolution/collector.js +0 -1
  47. package/dist/evolution/fitness.d.ts +0 -1
  48. package/dist/evolution/fitness.js +0 -1
  49. package/dist/evolution/index.d.ts +0 -1
  50. package/dist/evolution/index.js +0 -1
  51. package/dist/evolution/marker-gen.d.ts +0 -1
  52. package/dist/evolution/marker-gen.js +0 -1
  53. package/dist/evolution/store.d.ts +0 -1
  54. package/dist/evolution/store.js +0 -1
  55. package/dist/evolution/types.d.ts +0 -1
  56. package/dist/evolution/types.js +0 -1
  57. package/dist/hooks/cli.d.ts +17 -0
  58. package/dist/hooks/cli.js +156 -0
  59. package/dist/hooks/enforce.d.ts +106 -0
  60. package/dist/hooks/enforce.js +431 -0
  61. package/dist/hooks/index.d.ts +9 -0
  62. package/dist/hooks/index.js +12 -0
  63. package/dist/hooks/protocol.d.ts +73 -0
  64. package/dist/hooks/protocol.js +91 -0
  65. package/dist/hooks/state.d.ts +53 -0
  66. package/dist/hooks/state.js +93 -0
  67. package/dist/index.d.ts +0 -1
  68. package/dist/index.js +0 -1
  69. package/dist/log/logger.d.ts +0 -1
  70. package/dist/log/logger.js +0 -1
  71. package/dist/runtime/agent-md.d.ts +0 -1
  72. package/dist/runtime/agent-md.js +95 -105
  73. package/dist/runtime/claude-sdk.d.ts +0 -1
  74. package/dist/runtime/claude-sdk.js +0 -1
  75. package/dist/runtime/index.d.ts +3 -3
  76. package/dist/runtime/index.js +2 -2
  77. package/dist/runtime/markdown.d.ts +0 -1
  78. package/dist/runtime/markdown.js +0 -1
  79. package/dist/runtime/plugin-adapter.d.ts +42 -0
  80. package/dist/runtime/plugin-adapter.js +77 -0
  81. package/dist/runtime/settings-adapter.d.ts +8 -9
  82. package/dist/runtime/settings-adapter.js +57 -46
  83. package/dist/runtime/skill-adapter.d.ts +0 -1
  84. package/dist/runtime/skill-adapter.js +94 -71
  85. package/dist/runtime/workflow-runner.d.ts +0 -1
  86. package/dist/runtime/workflow-runner.js +0 -14
  87. package/dist/schema/index.d.ts +0 -1
  88. package/dist/schema/index.js +0 -1
  89. package/dist/schema/types.d.ts +33 -20
  90. package/dist/schema/types.js +0 -1
  91. package/dist/schema/validate.d.ts +0 -1
  92. package/dist/schema/validate.js +32 -19
  93. package/dist/schema/yaml-parser.d.ts +0 -1
  94. package/dist/schema/yaml-parser.js +0 -1
  95. package/dist/templates/code-review-pipeline.dna.yaml +0 -11
  96. package/dist/templates/flutter-behavior-lock.dna.yaml +0 -16
  97. package/dist/templates/flutter-refactoring-rescue.dna.yaml +0 -12
  98. package/dist/templates/flutter-rewrite.dna.yaml +0 -27
  99. package/dist/templates/full-pipeline.dna.yaml +0 -25
  100. package/dist/templates/multi-perspective-review.dna.yaml +0 -12
  101. package/dist/templates/subagent-parallel.dna.yaml +0 -16
  102. package/package.json +4 -2
  103. package/dist/audit/index.d.ts.map +0 -1
  104. package/dist/audit/index.js.map +0 -1
  105. package/dist/cli/commands/compile.d.ts.map +0 -1
  106. package/dist/cli/commands/compile.js.map +0 -1
  107. package/dist/cli/commands/epigenetic.d.ts.map +0 -1
  108. package/dist/cli/commands/epigenetic.js.map +0 -1
  109. package/dist/cli/commands/evolve.d.ts.map +0 -1
  110. package/dist/cli/commands/evolve.js.map +0 -1
  111. package/dist/cli/commands/guard.d.ts.map +0 -1
  112. package/dist/cli/commands/guard.js.map +0 -1
  113. package/dist/cli/commands/import.d.ts.map +0 -1
  114. package/dist/cli/commands/import.js.map +0 -1
  115. package/dist/cli/commands/init.d.ts.map +0 -1
  116. package/dist/cli/commands/init.js.map +0 -1
  117. package/dist/cli/commands/run.d.ts.map +0 -1
  118. package/dist/cli/commands/run.js.map +0 -1
  119. package/dist/cli/commands/show.d.ts.map +0 -1
  120. package/dist/cli/commands/show.js.map +0 -1
  121. package/dist/cli/commands/sync.d.ts.map +0 -1
  122. package/dist/cli/commands/sync.js.map +0 -1
  123. package/dist/cli/commands/validate.d.ts.map +0 -1
  124. package/dist/cli/commands/validate.js.map +0 -1
  125. package/dist/cli/commands/verify.d.ts.map +0 -1
  126. package/dist/cli/commands/verify.js.map +0 -1
  127. package/dist/cli/index.d.ts.map +0 -1
  128. package/dist/cli/index.js.map +0 -1
  129. package/dist/cli/util/format.d.ts.map +0 -1
  130. package/dist/cli/util/format.js.map +0 -1
  131. package/dist/cli/util/prompt.d.ts.map +0 -1
  132. package/dist/cli/util/prompt.js.map +0 -1
  133. package/dist/compiler/activate.d.ts.map +0 -1
  134. package/dist/compiler/activate.js.map +0 -1
  135. package/dist/compiler/cascade.d.ts.map +0 -1
  136. package/dist/compiler/cascade.js.map +0 -1
  137. package/dist/compiler/compile.d.ts.map +0 -1
  138. package/dist/compiler/compile.js.map +0 -1
  139. package/dist/compiler/index.d.ts.map +0 -1
  140. package/dist/compiler/index.js.map +0 -1
  141. package/dist/compiler/workflow.d.ts.map +0 -1
  142. package/dist/compiler/workflow.js.map +0 -1
  143. package/dist/evolution/collector.d.ts.map +0 -1
  144. package/dist/evolution/collector.js.map +0 -1
  145. package/dist/evolution/fitness.d.ts.map +0 -1
  146. package/dist/evolution/fitness.js.map +0 -1
  147. package/dist/evolution/index.d.ts.map +0 -1
  148. package/dist/evolution/index.js.map +0 -1
  149. package/dist/evolution/marker-gen.d.ts.map +0 -1
  150. package/dist/evolution/marker-gen.js.map +0 -1
  151. package/dist/evolution/store.d.ts.map +0 -1
  152. package/dist/evolution/store.js.map +0 -1
  153. package/dist/evolution/types.d.ts.map +0 -1
  154. package/dist/evolution/types.js.map +0 -1
  155. package/dist/index.d.ts.map +0 -1
  156. package/dist/index.js.map +0 -1
  157. package/dist/log/logger.d.ts.map +0 -1
  158. package/dist/log/logger.js.map +0 -1
  159. package/dist/runtime/agent-md.d.ts.map +0 -1
  160. package/dist/runtime/agent-md.js.map +0 -1
  161. package/dist/runtime/claude-sdk.d.ts.map +0 -1
  162. package/dist/runtime/claude-sdk.js.map +0 -1
  163. package/dist/runtime/hooks.d.ts +0 -67
  164. package/dist/runtime/hooks.d.ts.map +0 -1
  165. package/dist/runtime/hooks.js +0 -785
  166. package/dist/runtime/hooks.js.map +0 -1
  167. package/dist/runtime/index.d.ts.map +0 -1
  168. package/dist/runtime/index.js.map +0 -1
  169. package/dist/runtime/markdown.d.ts.map +0 -1
  170. package/dist/runtime/markdown.js.map +0 -1
  171. package/dist/runtime/settings-adapter.d.ts.map +0 -1
  172. package/dist/runtime/settings-adapter.js.map +0 -1
  173. package/dist/runtime/skill-adapter.d.ts.map +0 -1
  174. package/dist/runtime/skill-adapter.js.map +0 -1
  175. package/dist/runtime/workflow-runner.d.ts.map +0 -1
  176. package/dist/runtime/workflow-runner.js.map +0 -1
  177. package/dist/schema/index.d.ts.map +0 -1
  178. package/dist/schema/index.js.map +0 -1
  179. package/dist/schema/types.d.ts.map +0 -1
  180. package/dist/schema/types.js.map +0 -1
  181. package/dist/schema/validate.d.ts.map +0 -1
  182. package/dist/schema/validate.js.map +0 -1
  183. package/dist/schema/yaml-parser.d.ts.map +0 -1
  184. package/dist/schema/yaml-parser.js.map +0 -1
@@ -0,0 +1,12 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook PreToolUse", "timeout": 5000 }] }],
4
+ "PostToolUse": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook PostToolUse", "timeout": 3000 }] }],
5
+ "UserPromptSubmit": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook UserPromptSubmit", "timeout": 5000 }] }],
6
+ "SubagentStop": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook SubagentStop", "timeout": 3000 }] }],
7
+ "PreCompact": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook PreCompact", "timeout": 3000 }] }],
8
+ "Notification": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook Notification", "timeout": 3000 }] }],
9
+ "Stop": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook Stop", "timeout": 3000 }] }],
10
+ "SessionStart": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "dna-hook SessionStart", "timeout": 5000 }] }]
11
+ }
12
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "intentdna",
3
+ "version": "0.6.0",
4
+ "description": "Declarative policy layer for AI agent governance"
5
+ }
@@ -106,4 +106,3 @@ export declare function generateAuditSummary(logDir: string, from: string, to: s
106
106
  * Render audit summary as markdown.
107
107
  */
108
108
  export declare function renderAuditReport(summary: AuditSummary): string;
109
- //# sourceMappingURL=index.d.ts.map
@@ -141,4 +141,3 @@ export function renderAuditReport(summary) {
141
141
  }
142
142
  return lines.join("\n");
143
143
  }
144
- //# sourceMappingURL=index.js.map
@@ -11,4 +11,3 @@ export interface CompileOptions {
11
11
  ir: boolean;
12
12
  }
13
13
  export declare function runCompile(opts: CompileOptions): Promise<number>;
14
- //# sourceMappingURL=compile.d.ts.map
@@ -86,4 +86,3 @@ export async function runCompile(opts) {
86
86
  return 1;
87
87
  }
88
88
  }
89
- //# sourceMappingURL=compile.js.map
@@ -17,4 +17,3 @@ export interface EpigeneticOptions {
17
17
  json?: boolean;
18
18
  }
19
19
  export declare function runEpigenetic(opts: EpigeneticOptions): Promise<number>;
20
- //# sourceMappingURL=epigenetic.d.ts.map
@@ -109,4 +109,3 @@ async function doMarkers(engine, opts) {
109
109
  }
110
110
  return 0;
111
111
  }
112
- //# sourceMappingURL=epigenetic.js.map
@@ -9,4 +9,3 @@ export interface EvolveOptions {
9
9
  storeDir?: string;
10
10
  }
11
11
  export declare function runEvolve(opts: EvolveOptions): Promise<number>;
12
- //# sourceMappingURL=evolve.d.ts.map
@@ -49,4 +49,3 @@ export async function runEvolve(opts) {
49
49
  return 1;
50
50
  }
51
51
  }
52
- //# sourceMappingURL=evolve.js.map
@@ -19,4 +19,3 @@ export interface GuardOptions {
19
19
  remove: boolean;
20
20
  }
21
21
  export declare function runGuard(opts: GuardOptions): Promise<number>;
22
- //# sourceMappingURL=guard.d.ts.map
@@ -177,7 +177,6 @@ export async function runGuard(opts) {
177
177
  const r = await runSync({
178
178
  files: [], target: "claude-md",
179
179
  inject: join(cwd, "CLAUDE.md"),
180
- hooksDir: join(cwd, ".claude", "hooks"),
181
180
  evolve: false, remove: true,
182
181
  });
183
182
  if (r !== 0)
@@ -215,7 +214,6 @@ export async function runGuard(opts) {
215
214
  files: [tmpFile],
216
215
  target: "claude-md",
217
216
  inject: join(cwd, "CLAUDE.md"),
218
- hooksDir: join(cwd, ".claude", "hooks"),
219
217
  evolve: false, remove: false,
220
218
  });
221
219
  if (r !== 0)
@@ -254,4 +252,3 @@ export async function runGuard(opts) {
254
252
  await rm(tmpFile, { force: true });
255
253
  }
256
254
  }
257
- //# sourceMappingURL=guard.js.map
@@ -68,4 +68,3 @@ export declare function parseClaudeMD(content: string): {
68
68
  export declare function importHarness(dir: string): Promise<ImportResult>;
69
69
  export declare function generateDNAYaml(result: ImportResult, projectName?: string): string;
70
70
  export declare function runImport(opts: ImportOptions): Promise<number>;
71
- //# sourceMappingURL=import.d.ts.map
@@ -426,4 +426,3 @@ async function safeReaddir(path) {
426
426
  return [];
427
427
  }
428
428
  }
429
- //# sourceMappingURL=import.js.map
@@ -12,4 +12,3 @@ export interface InitOptions {
12
12
  upgrade?: string | boolean;
13
13
  }
14
14
  export declare function runInit(opts: InitOptions): Promise<number>;
15
- //# sourceMappingURL=init.d.ts.map
@@ -364,4 +364,3 @@ export async function runInit(opts) {
364
364
  return 1;
365
365
  }
366
366
  }
367
- //# sourceMappingURL=init.js.map
@@ -56,4 +56,3 @@ export declare function executeGroup(group: ParallelGroup, allSteps: WorkflowSte
56
56
  export declare function checkRetryNeeded(stepResults: Map<string, StepResult>, transitions: TransitionRule[]): boolean;
57
57
  export declare function runRun(opts: RunOptions): Promise<number>;
58
58
  export {};
59
- //# sourceMappingURL=run.d.ts.map
@@ -13,7 +13,7 @@ import { loadDNA, compileFromFiles } from "../../compiler/index.js";
13
13
  import { cascadeDNA } from "../../compiler/cascade.js";
14
14
  import { compileWorkflow } from "../../compiler/workflow.js";
15
15
  import { compileAllRolesToAgentMD, writeAgentMDFiles, toKebabCase, } from "../../runtime/agent-md.js";
16
- import { compileToHooks, writeHookScripts } from "../../runtime/hooks.js";
16
+ import { createCompiledIR, writeCompiledIR } from "../../runtime/plugin-adapter.js";
17
17
  // ── Template & Condition Helpers ───────────────────────────
18
18
  /**
19
19
  * Replace {{var}} placeholders in a template string.
@@ -127,21 +127,6 @@ export async function executeStep(step, vars, opts) {
127
127
  "--no-session-persistence",
128
128
  ];
129
129
  log(`Running step: ${step.id} (agent: ${agentName})`);
130
- // Write mode file if step declares a mode
131
- if (step.mode) {
132
- const modeData = JSON.stringify({
133
- active_mode: step.mode,
134
- set_at: new Date().toISOString(),
135
- set_by: `run:${step.id}`,
136
- });
137
- try {
138
- const { mkdir: mkdirAsync, writeFile: writeFileAsync } = await import("node:fs/promises");
139
- await mkdirAsync(".dna", { recursive: true });
140
- await writeFileAsync(".dna/mode", modeData, "utf-8");
141
- log(`Mode set: ${step.mode}`);
142
- }
143
- catch { /* best effort */ }
144
- }
145
130
  try {
146
131
  const output = await spawnAsync("claude", args, prompt);
147
132
  await writeFile(resultFile, output, "utf-8");
@@ -271,7 +256,6 @@ export async function runRun(opts) {
271
256
  const maxBudget = opts.maxBudget ?? 5;
272
257
  const permissionMode = opts.permissionMode ?? "bypassPermissions";
273
258
  const agentsDir = opts.agentsDir ?? ".claude/agents";
274
- const hooksDir = opts.hooksDir ?? ".claude/hooks";
275
259
  try {
276
260
  // ── Step 1: Load & compile DNA ───────────────────────
277
261
  log("Loading DNA files...");
@@ -311,9 +295,9 @@ export async function runRun(opts) {
311
295
  const agentResults = compileAllRolesToAgentMD(cascaded.roles, ir);
312
296
  await writeAgentMDFiles(agentResults, agentsDir);
313
297
  log(`Generated ${agentResults.length} agent file(s) in ${agentsDir}`);
314
- const hookScripts = compileToHooks(ir);
315
- await writeHookScripts(hooksDir, hookScripts);
316
- log(`Generated hook scripts in ${hooksDir}`);
298
+ const hookScripts = createCompiledIR(ir, cascaded.roles);
299
+ await writeCompiledIR(process.cwd(), hookScripts);
300
+ log("Synced compiled IR for dna-hook enforcement");
317
301
  // ── Step 5: Prepare log directory ────────────────────
318
302
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
319
303
  const logDir = opts.logDir ?? join(".intentdna", "runs", `${opts.workflowName}-${timestamp}`);
@@ -367,12 +351,6 @@ export async function runRun(opts) {
367
351
  // All passed
368
352
  break;
369
353
  }
370
- // Clean up mode file after workflow completes
371
- try {
372
- const { rm: rmAsync } = await import("node:fs/promises");
373
- await rmAsync(".dna/mode", { force: true });
374
- }
375
- catch { /* best effort */ }
376
354
  return printSummary(stepResults, round, logDir, 0);
377
355
  }
378
356
  catch (err) {
@@ -428,4 +406,3 @@ function printSummary(results, rounds, logDir, exitCode) {
428
406
  process.stderr.write(`\nTotal: ${passed} passed, ${failed} failed, ${skipped} skipped\n`);
429
407
  return exitCode;
430
408
  }
431
- //# sourceMappingURL=run.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * dna setup
3
+ *
4
+ * Registers intentdna as a Claude Code plugin.
5
+ * Auto-detects whether claude CLI is available and prints guidance accordingly.
6
+ */
7
+ export interface SetupOptions {
8
+ }
9
+ export declare function runSetup(_opts: SetupOptions): Promise<number>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * dna setup
3
+ *
4
+ * Registers intentdna as a Claude Code plugin.
5
+ * Auto-detects whether claude CLI is available and prints guidance accordingly.
6
+ */
7
+ import { execSync } from "node:child_process";
8
+ export async function runSetup(_opts) {
9
+ let claudeAvailable = false;
10
+ try {
11
+ execSync("claude --version", { stdio: "ignore" });
12
+ claudeAvailable = true;
13
+ }
14
+ catch {
15
+ // claude CLI not found — fall through to bin mode
16
+ }
17
+ if (claudeAvailable) {
18
+ process.stderr.write("Claude Code CLI detected.\n");
19
+ process.stderr.write("\n");
20
+ process.stderr.write("Intent DNA plugin mode:\n");
21
+ process.stderr.write(" The .claude-plugin/ directory contains plugin.json and hook scripts.\n");
22
+ process.stderr.write(" To register as a plugin, run:\n");
23
+ process.stderr.write("\n");
24
+ let npmRoot = "";
25
+ try {
26
+ npmRoot = execSync("npm root -g", { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
27
+ }
28
+ catch {
29
+ // ignore
30
+ }
31
+ const pluginPath = npmRoot ? `${npmRoot}/intentdna` : "<npm-root>/intentdna";
32
+ process.stderr.write(` claude plugin add ${pluginPath}\n`);
33
+ process.stderr.write("\n");
34
+ process.stderr.write(" Or run 'dna sync --plugin' to activate hooks via settings.json.\n");
35
+ process.stderr.write("Intent DNA plugin registered. Run 'dna sync' to activate.\n");
36
+ }
37
+ else {
38
+ process.stderr.write("Claude Code CLI not detected. Using bin mode.\n");
39
+ process.stderr.write("Run 'dna sync' to register dna-hook in settings.json\n");
40
+ }
41
+ return 0;
42
+ }
@@ -9,4 +9,3 @@ export interface ShowOptions {
9
9
  json: boolean;
10
10
  }
11
11
  export declare function runShow(opts: ShowOptions): Promise<number>;
12
- //# sourceMappingURL=show.d.ts.map
@@ -75,4 +75,3 @@ export async function runShow(opts) {
75
75
  return 1;
76
76
  }
77
77
  }
78
- //# sourceMappingURL=show.js.map
@@ -26,6 +26,18 @@ export interface SyncOptions {
26
26
  settingsPath?: string;
27
27
  lockFile?: string;
28
28
  remove: boolean;
29
+ plugin?: boolean;
29
30
  }
31
+ /**
32
+ * Detect whether intentdna is running as a Claude Code plugin or standalone binary.
33
+ *
34
+ * Plugin mode: claude plugin system manages hook dispatch
35
+ * → generate ir.json + agents + skills + .mcp.json
36
+ * → do NOT write settings.json (hooks provided by plugin framework)
37
+ *
38
+ * Bin mode: fallback when plugin not installed
39
+ * → same as above
40
+ * → ALSO write settings.json to register dna-hook events
41
+ */
42
+ export declare function detectMode(): Promise<"plugin" | "bin">;
30
43
  export declare function runSync(opts: SyncOptions): Promise<number>;
31
- //# sourceMappingURL=sync.d.ts.map
@@ -17,14 +17,41 @@ import { fileURLToPath } from "node:url";
17
17
  import { createInterface } from "node:readline";
18
18
  import { loadDNA, compileFromFiles } from "../../compiler/index.js";
19
19
  import { compileToMarkdown, injectIntoFile, removeFromFile } from "../../runtime/markdown.js";
20
- import { compileToHooks, writeHookScripts, removeHookScripts } from "../../runtime/hooks.js";
21
20
  import { compileAllRolesToAgentMD, writeAgentMDFiles, removeAgentMDFiles } from "../../runtime/agent-md.js";
22
21
  import { removeWorkflowScripts } from "../../runtime/workflow-runner.js";
23
22
  import { compileWorkflowToSkill, writeSkillFiles } from "../../runtime/skill-adapter.js";
24
- import { compileSettingsFromHooks, mergeSettingsFile } from "../../runtime/settings-adapter.js";
23
+ import { compilePluginSettings, detectEnabledEvents, mergeSettingsFile } from "../../runtime/settings-adapter.js";
24
+ import { createCompiledIR, writeCompiledIR } from "../../runtime/plugin-adapter.js";
25
25
  import { cascadeDNA } from "../../compiler/cascade.js";
26
26
  import { EvolutionEngine } from "../../evolution/index.js";
27
27
  const VALID_TARGETS = ["claude-md", "soul-md", "cursorrules", "system-prompt"];
28
+ /**
29
+ * Detect whether intentdna is running as a Claude Code plugin or standalone binary.
30
+ *
31
+ * Plugin mode: claude plugin system manages hook dispatch
32
+ * → generate ir.json + agents + skills + .mcp.json
33
+ * → do NOT write settings.json (hooks provided by plugin framework)
34
+ *
35
+ * Bin mode: fallback when plugin not installed
36
+ * → same as above
37
+ * → ALSO write settings.json to register dna-hook events
38
+ */
39
+ export async function detectMode() {
40
+ // Check: is intentdna registered as a Claude Code plugin?
41
+ try {
42
+ const { execSync } = await import("node:child_process");
43
+ const result = execSync("claude plugin list 2>/dev/null", {
44
+ encoding: "utf-8",
45
+ timeout: 5000,
46
+ });
47
+ if (result.includes("intentdna"))
48
+ return "plugin";
49
+ }
50
+ catch {
51
+ // claude CLI not available or plugin not registered
52
+ }
53
+ return "bin";
54
+ }
28
55
  async function fileExists(path) {
29
56
  try {
30
57
  await stat(path);
@@ -35,6 +62,33 @@ async function fileExists(path) {
35
62
  }
36
63
  }
37
64
  const DNA_SENTINEL = "intentdna:managed";
65
+ const LEGACY_BASH_HOOKS = [
66
+ "dna-pre-tool-use.sh",
67
+ "dna-post-tool-use.sh",
68
+ "dna-user-prompt-submit.sh",
69
+ "dna-subagent-stop.sh",
70
+ "dna-pre-compact.sh",
71
+ "dna-notification.sh",
72
+ ];
73
+ /**
74
+ * Remove legacy bash hook scripts written by previous Intent DNA versions.
75
+ * Only removes files that contain the sentinel comment.
76
+ */
77
+ async function removeLegacyBashHooks(hooksDir) {
78
+ let removed = 0;
79
+ for (const file of LEGACY_BASH_HOOKS) {
80
+ const path = resolve(hooksDir, file);
81
+ try {
82
+ const content = await readFile(path, "utf-8");
83
+ if (content.includes("intentdna:managed")) {
84
+ await unlink(path);
85
+ removed++;
86
+ }
87
+ }
88
+ catch { /* File doesn't exist, skip */ }
89
+ }
90
+ return removed;
91
+ }
38
92
  /**
39
93
  * Remove stale DNA-managed files from a directory.
40
94
  * Scans for files/dirs containing the sentinel marker and deletes them.
@@ -212,11 +266,18 @@ export async function runSync(opts) {
212
266
  const hasClaudeMd = await fileExists(resolve(cwd, "CLAUDE.md"));
213
267
  if (hasClaudeDir || hasClaudeMd || opts.files.length > 0) {
214
268
  opts.inject = resolve(cwd, "CLAUDE.md");
215
- opts.hooksDir = resolve(cwd, ".claude", "hooks");
216
269
  opts.agentsDir = resolve(cwd, ".claude", "agents");
217
270
  opts.skillsDir = resolve(cwd, ".claude", "skills");
218
271
  opts.settingsPath = resolve(cwd, ".claude", "settings.json");
219
- process.stderr.write(`Auto-detect: Claude Code → CLAUDE.md + hooks + agents + skills + settings\n`);
272
+ process.stderr.write(`Auto-detect: Claude Code → CLAUDE.md + agents + skills + settings\n`);
273
+ }
274
+ }
275
+ // Auto-detect plugin vs bin mode if --plugin not explicitly set
276
+ if (!opts.remove && opts.plugin === undefined) {
277
+ const mode = await detectMode();
278
+ if (mode === "plugin") {
279
+ opts.plugin = true;
280
+ process.stderr.write("Auto-detect: plugin mode (intentdna registered as Claude Code plugin)\n");
220
281
  }
221
282
  }
222
283
  // Handle --remove
@@ -235,12 +296,12 @@ export async function runSync(opts) {
235
296
  }
236
297
  }
237
298
  if (opts.hooksDir) {
238
- const count = await removeHookScripts(opts.hooksDir);
299
+ const count = await removeLegacyBashHooks(opts.hooksDir);
239
300
  if (count > 0) {
240
- process.stderr.write(`Removed ${count} Intent DNA hook script(s) from ${opts.hooksDir}\n`);
301
+ process.stderr.write(`Removed ${count} legacy bash hook(s) from ${opts.hooksDir}\n`);
241
302
  }
242
303
  else {
243
- process.stderr.write(`No Intent DNA hook scripts found in ${opts.hooksDir}\n`);
304
+ process.stderr.write(`No legacy bash hooks found in ${opts.hooksDir}\n`);
244
305
  }
245
306
  }
246
307
  if (opts.agentsDir) {
@@ -314,26 +375,37 @@ export async function runSync(opts) {
314
375
  }
315
376
  trackedOutputs.push(resolve(opts.inject));
316
377
  }
317
- // Step 4: Generate hook scripts
318
- if (opts.hooksDir) {
319
- const scripts = compileToHooks(ir);
320
- const written = await writeHookScripts(opts.hooksDir, scripts);
321
- if (written.length > 0) {
322
- process.stderr.write(`Generated ${written.length} hook script(s) in ${opts.hooksDir}\n`);
323
- for (const f of written) {
324
- process.stderr.write(` ${f}\n`);
325
- }
326
- trackedOutputs.push(...written.map(f => resolve(f)));
327
- // Report role scope information
328
- if (ir.roles_scope_map && ir.roles_scope_map.length > 0) {
329
- const roleNames = ir.roles_scope_map.map(r => r.role_name).join(", ");
330
- process.stderr.write(` ${ir.roles_scope_map.length} role scope(s) compiled: ${roleNames}\n`);
331
- }
378
+ // Step 3.5: Write compiled IR (needed for both plugin and bin mode)
379
+ {
380
+ const cwd = process.cwd();
381
+ const dnas = await Promise.all(expandedFiles.map(loadDNA));
382
+ const cascadedForIR = cascadeDNA(dnas);
383
+ const roles = cascadedForIR.roles;
384
+ const compiled = createCompiledIR(ir, roles);
385
+ const irPath = await writeCompiledIR(cwd, compiled);
386
+ process.stderr.write(`Wrote compiled IR: ${irPath}\n`);
387
+ trackedOutputs.push(resolve(irPath));
388
+ // Clean up old bash hooks if they exist
389
+ const oldHooksDir = resolve(cwd, ".claude", "hooks");
390
+ const removed = await removeLegacyBashHooks(oldHooksDir);
391
+ if (removed > 0) {
392
+ process.stderr.write(`Cleaned ${removed} legacy bash hook(s) from ${oldHooksDir}\n`);
332
393
  }
333
- else {
334
- process.stderr.write(`No hook-enforceable constraints found\n`);
394
+ }
395
+ // Step 3.6: Register hooks in settings.json (bin mode only)
396
+ // Plugin mode: hooks managed by plugin framework — skip settings.json
397
+ if (!opts.plugin && opts.settingsPath) {
398
+ const enabledEvents = detectEnabledEvents(ir);
399
+ if (enabledEvents.length > 0) {
400
+ const pluginSettings = compilePluginSettings(enabledEvents);
401
+ await mergeSettingsFile(pluginSettings, opts.settingsPath);
402
+ process.stderr.write(`Bin mode: registered ${enabledEvents.length} hook event(s) in ${opts.settingsPath}\n`);
403
+ trackedOutputs.push(resolve(opts.settingsPath));
335
404
  }
336
405
  }
406
+ else if (opts.plugin && opts.settingsPath) {
407
+ process.stderr.write(`Plugin mode: hooks managed by plugin framework (skipping settings.json)\n`);
408
+ }
337
409
  // Step 5: Generate agent MD files
338
410
  if (opts.agentsDir) {
339
411
  // Clean stale DNA-managed agents before regenerating
@@ -414,6 +486,20 @@ export async function runSync(opts) {
414
486
  }
415
487
  const configPath = await autoDetectDNA();
416
488
  const variables = await resolveVariables(rawVars, configPath);
489
+ // Step 8: Generate .claude/.mcp.json from MCP dependencies (inside skillsDir block, shares variables)
490
+ {
491
+ const mcpDeps = {};
492
+ for (const d of dnas) {
493
+ if (d.mcp)
494
+ Object.assign(mcpDeps, d.mcp);
495
+ }
496
+ if (Object.keys(mcpDeps).length > 0) {
497
+ const cwd = process.cwd();
498
+ const mcpJsonPath = resolve(cwd, ".claude", ".mcp.json");
499
+ await writeMCPConfig(mcpDeps, mcpJsonPath, variables);
500
+ process.stderr.write(`MCP: wrote ${Object.keys(mcpDeps).length} server(s) to ${mcpJsonPath}\n`);
501
+ }
502
+ }
417
503
  if (Object.keys(workflows).length === 0) {
418
504
  process.stderr.write("No workflow defined — no skills to generate\n");
419
505
  }
@@ -436,14 +522,6 @@ export async function runSync(opts) {
436
522
  }
437
523
  }
438
524
  }
439
- // Step 8: Generate/merge settings file
440
- if (opts.settingsPath && opts.hooksDir) {
441
- const hookScripts = compileToHooks(ir);
442
- const dnaSettings = compileSettingsFromHooks(opts.hooksDir, hookScripts);
443
- await mergeSettingsFile(dnaSettings, opts.settingsPath);
444
- process.stderr.write(`Merged DNA hook config into ${opts.settingsPath}\n`);
445
- trackedOutputs.push(resolve(opts.settingsPath));
446
- }
447
525
  // Also output the compiled content to stderr for visibility
448
526
  const directiveCount = ir.prompt_directives.length;
449
527
  const gateCount = ir.pre_execution_gates.length;
@@ -462,4 +540,51 @@ export async function runSync(opts) {
462
540
  return 1;
463
541
  }
464
542
  }
465
- //# sourceMappingURL=sync.js.map
543
+ /**
544
+ * Write MCP server configuration to .claude/.mcp.json.
545
+ * Merges with existing config — DNA-declared servers are added/updated,
546
+ * but existing user-configured servers are NOT overwritten.
547
+ */
548
+ async function writeMCPConfig(mcpDeps, mcpJsonPath, variables) {
549
+ // Read existing .mcp.json
550
+ let existing = {};
551
+ try {
552
+ const raw = await readFile(mcpJsonPath, "utf-8");
553
+ existing = JSON.parse(raw);
554
+ }
555
+ catch { /* doesn't exist yet */ }
556
+ const mcpServers = (existing.mcpServers ?? {});
557
+ for (const [name, server] of Object.entries(mcpDeps)) {
558
+ // Don't overwrite existing user-configured servers
559
+ if (mcpServers[name])
560
+ continue;
561
+ const entry = {};
562
+ if (server.command) {
563
+ entry.command = server.command;
564
+ if (server.args)
565
+ entry.args = server.args;
566
+ }
567
+ if (server.url) {
568
+ entry.url = server.url;
569
+ }
570
+ if (server.env) {
571
+ // Substitute {{variable}} placeholders
572
+ const resolvedEnv = {};
573
+ for (const [k, v] of Object.entries(server.env)) {
574
+ resolvedEnv[k] = substituteVariables(v, variables);
575
+ }
576
+ entry.env = resolvedEnv;
577
+ }
578
+ if (server.timeout) {
579
+ entry.timeout = server.timeout;
580
+ }
581
+ mcpServers[name] = entry;
582
+ }
583
+ existing.mcpServers = mcpServers;
584
+ await mkdir(dirname(mcpJsonPath), { recursive: true });
585
+ await writeFileAsync(mcpJsonPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
586
+ }
587
+ /** Replace {{var}} placeholders with resolved variable values */
588
+ function substituteVariables(template, vars) {
589
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
590
+ }
@@ -8,4 +8,3 @@ export interface ValidateOptions {
8
8
  quiet: boolean;
9
9
  }
10
10
  export declare function runValidate(opts: ValidateOptions): Promise<number>;
11
- //# sourceMappingURL=validate.d.ts.map
@@ -27,4 +27,3 @@ export async function runValidate(opts) {
27
27
  }
28
28
  return allValid ? 0 : 1;
29
29
  }
30
- //# sourceMappingURL=validate.js.map
@@ -30,4 +30,3 @@ export declare function sha256(content: string): string;
30
30
  export declare function parseLockFile(raw: string): LockFile;
31
31
  export declare function verifyLock(lock: LockFile): Promise<VerifyResult[]>;
32
32
  export declare function runVerify(opts: VerifyOptions): Promise<number>;
33
- //# sourceMappingURL=verify.d.ts.map
@@ -99,4 +99,3 @@ export async function runVerify(opts) {
99
99
  return 0;
100
100
  }
101
101
  }
102
- //# sourceMappingURL=verify.js.map
@@ -12,4 +12,3 @@
12
12
  * help Show this help message
13
13
  */
14
14
  export {};
15
- //# sourceMappingURL=index.d.ts.map
package/dist/cli/index.js CHANGED
@@ -17,6 +17,7 @@ const HELP = `Intent DNA CLI v0.3.0
17
17
  Usage: dna <command> [options]
18
18
 
19
19
  Commands:
20
+ setup Register intentdna as a Claude Code plugin (auto-detect mode)
20
21
  guard Zero-config guardrails — detect environment, apply safety rules
21
22
  sync Compile + inject DNA into target file (CLAUDE.md, SOUL.md, etc.)
22
23
  verify Verify synced files match .dna/lock checksums (drift detection)
@@ -87,6 +88,7 @@ async function main() {
87
88
  settings: { type: "string" },
88
89
  lock: { type: "string", default: ".dna/lock" },
89
90
  remove: { type: "boolean", default: false },
91
+ plugin: { type: "boolean", default: false },
90
92
  },
91
93
  allowPositionals: true,
92
94
  strict: false,
@@ -108,6 +110,7 @@ async function main() {
108
110
  settingsPath: values.settings,
109
111
  lockFile: values.lock,
110
112
  remove: values.remove,
113
+ plugin: values.plugin,
111
114
  });
112
115
  process.exit(code);
113
116
  break;
@@ -353,6 +356,12 @@ async function main() {
353
356
  process.exit(code);
354
357
  break;
355
358
  }
359
+ case "setup": {
360
+ const { runSetup } = await import("./commands/setup.js");
361
+ const code = await runSetup({});
362
+ process.exit(code);
363
+ break;
364
+ }
356
365
  default:
357
366
  process.stderr.write(`Unknown command: ${command}\n\n`);
358
367
  process.stderr.write(HELP);
@@ -363,4 +372,3 @@ main().catch((err) => {
363
372
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
364
373
  process.exit(1);
365
374
  });
366
- //# sourceMappingURL=index.js.map
@@ -21,4 +21,3 @@ export declare function formatCodon(codon: Codon): string;
21
21
  * Format a gene with its expression state for display.
22
22
  */
23
23
  export declare function formatGene(name: string, gene: ActivatedGene): string;
24
- //# sourceMappingURL=format.d.ts.map
@@ -64,4 +64,3 @@ export function formatGene(name, gene) {
64
64
  }
65
65
  return lines.join("\n");
66
66
  }
67
- //# sourceMappingURL=format.js.map
@@ -8,4 +8,3 @@ export declare function askString(question: string, defaultVal?: string): Promis
8
8
  export declare function askNumber(question: string, defaultVal: number): Promise<number>;
9
9
  export declare function askChoice(question: string, choices: string[], defaultVal?: string): Promise<string>;
10
10
  export declare function askYesNo(question: string, defaultVal: boolean): Promise<boolean>;
11
- //# sourceMappingURL=prompt.d.ts.map
@@ -48,4 +48,3 @@ export async function askYesNo(question, defaultVal) {
48
48
  return defaultVal;
49
49
  return answer.toLowerCase().startsWith("y");
50
50
  }
51
- //# sourceMappingURL=prompt.js.map
@@ -26,4 +26,3 @@ export interface ActivatedGene {
26
26
  * Returns the final gene expression state ready for compilation to IR.
27
27
  */
28
28
  export declare function activateDNA(cascaded: CascadedDNA, contextName?: string | null, activeRole?: string | null): ActivatedDNA;
29
- //# sourceMappingURL=activate.d.ts.map