intentdna 1.0.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/LICENSE +69 -0
  2. package/README.md +251 -0
  3. package/dist/audit/index.d.ts +109 -0
  4. package/dist/audit/index.d.ts.map +1 -0
  5. package/dist/audit/index.js +144 -0
  6. package/dist/audit/index.js.map +1 -0
  7. package/dist/cli/commands/compile.d.ts +14 -0
  8. package/dist/cli/commands/compile.d.ts.map +1 -0
  9. package/dist/cli/commands/compile.js +89 -0
  10. package/dist/cli/commands/compile.js.map +1 -0
  11. package/dist/cli/commands/epigenetic.d.ts +20 -0
  12. package/dist/cli/commands/epigenetic.d.ts.map +1 -0
  13. package/dist/cli/commands/epigenetic.js +112 -0
  14. package/dist/cli/commands/epigenetic.js.map +1 -0
  15. package/dist/cli/commands/evolve.d.ts +12 -0
  16. package/dist/cli/commands/evolve.d.ts.map +1 -0
  17. package/dist/cli/commands/evolve.js +52 -0
  18. package/dist/cli/commands/evolve.js.map +1 -0
  19. package/dist/cli/commands/guard.d.ts +22 -0
  20. package/dist/cli/commands/guard.d.ts.map +1 -0
  21. package/dist/cli/commands/guard.js +257 -0
  22. package/dist/cli/commands/guard.js.map +1 -0
  23. package/dist/cli/commands/import.d.ts +71 -0
  24. package/dist/cli/commands/import.d.ts.map +1 -0
  25. package/dist/cli/commands/import.js +428 -0
  26. package/dist/cli/commands/import.js.map +1 -0
  27. package/dist/cli/commands/init.d.ts +13 -0
  28. package/dist/cli/commands/init.d.ts.map +1 -0
  29. package/dist/cli/commands/init.js +171 -0
  30. package/dist/cli/commands/init.js.map +1 -0
  31. package/dist/cli/commands/run.d.ts +59 -0
  32. package/dist/cli/commands/run.d.ts.map +1 -0
  33. package/dist/cli/commands/run.js +350 -0
  34. package/dist/cli/commands/run.js.map +1 -0
  35. package/dist/cli/commands/show.d.ts +12 -0
  36. package/dist/cli/commands/show.d.ts.map +1 -0
  37. package/dist/cli/commands/show.js +78 -0
  38. package/dist/cli/commands/show.js.map +1 -0
  39. package/dist/cli/commands/sync.d.ts +31 -0
  40. package/dist/cli/commands/sync.d.ts.map +1 -0
  41. package/dist/cli/commands/sync.js +307 -0
  42. package/dist/cli/commands/sync.js.map +1 -0
  43. package/dist/cli/commands/validate.d.ts +11 -0
  44. package/dist/cli/commands/validate.d.ts.map +1 -0
  45. package/dist/cli/commands/validate.js +30 -0
  46. package/dist/cli/commands/validate.js.map +1 -0
  47. package/dist/cli/commands/verify.d.ts +33 -0
  48. package/dist/cli/commands/verify.d.ts.map +1 -0
  49. package/dist/cli/commands/verify.js +102 -0
  50. package/dist/cli/commands/verify.js.map +1 -0
  51. package/dist/cli/index.d.ts +15 -0
  52. package/dist/cli/index.d.ts.map +1 -0
  53. package/dist/cli/index.js +358 -0
  54. package/dist/cli/index.js.map +1 -0
  55. package/dist/cli/util/format.d.ts +24 -0
  56. package/dist/cli/util/format.d.ts.map +1 -0
  57. package/dist/cli/util/format.js +67 -0
  58. package/dist/cli/util/format.js.map +1 -0
  59. package/dist/cli/util/prompt.d.ts +11 -0
  60. package/dist/cli/util/prompt.d.ts.map +1 -0
  61. package/dist/cli/util/prompt.js +51 -0
  62. package/dist/cli/util/prompt.js.map +1 -0
  63. package/dist/compiler/activate.d.ts +29 -0
  64. package/dist/compiler/activate.d.ts.map +1 -0
  65. package/dist/compiler/activate.js +141 -0
  66. package/dist/compiler/activate.js.map +1 -0
  67. package/dist/compiler/cascade.d.ts +27 -0
  68. package/dist/compiler/cascade.d.ts.map +1 -0
  69. package/dist/compiler/cascade.js +124 -0
  70. package/dist/compiler/cascade.js.map +1 -0
  71. package/dist/compiler/compile.d.ts +17 -0
  72. package/dist/compiler/compile.d.ts.map +1 -0
  73. package/dist/compiler/compile.js +219 -0
  74. package/dist/compiler/compile.js.map +1 -0
  75. package/dist/compiler/index.d.ts +28 -0
  76. package/dist/compiler/index.d.ts.map +1 -0
  77. package/dist/compiler/index.js +51 -0
  78. package/dist/compiler/index.js.map +1 -0
  79. package/dist/compiler/workflow.d.ts +37 -0
  80. package/dist/compiler/workflow.d.ts.map +1 -0
  81. package/dist/compiler/workflow.js +274 -0
  82. package/dist/compiler/workflow.js.map +1 -0
  83. package/dist/evolution/collector.d.ts +52 -0
  84. package/dist/evolution/collector.d.ts.map +1 -0
  85. package/dist/evolution/collector.js +72 -0
  86. package/dist/evolution/collector.js.map +1 -0
  87. package/dist/evolution/fitness.d.ts +33 -0
  88. package/dist/evolution/fitness.d.ts.map +1 -0
  89. package/dist/evolution/fitness.js +96 -0
  90. package/dist/evolution/fitness.js.map +1 -0
  91. package/dist/evolution/index.d.ts +53 -0
  92. package/dist/evolution/index.d.ts.map +1 -0
  93. package/dist/evolution/index.js +69 -0
  94. package/dist/evolution/index.js.map +1 -0
  95. package/dist/evolution/marker-gen.d.ts +31 -0
  96. package/dist/evolution/marker-gen.d.ts.map +1 -0
  97. package/dist/evolution/marker-gen.js +141 -0
  98. package/dist/evolution/marker-gen.js.map +1 -0
  99. package/dist/evolution/store.d.ts +24 -0
  100. package/dist/evolution/store.d.ts.map +1 -0
  101. package/dist/evolution/store.js +78 -0
  102. package/dist/evolution/store.js.map +1 -0
  103. package/dist/evolution/types.d.ts +73 -0
  104. package/dist/evolution/types.d.ts.map +1 -0
  105. package/dist/evolution/types.js +14 -0
  106. package/dist/evolution/types.js.map +1 -0
  107. package/dist/index.d.ts +4 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +4 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/log/logger.d.ts +15 -0
  112. package/dist/log/logger.d.ts.map +1 -0
  113. package/dist/log/logger.js +36 -0
  114. package/dist/log/logger.js.map +1 -0
  115. package/dist/runtime/agent-md.d.ts +56 -0
  116. package/dist/runtime/agent-md.d.ts.map +1 -0
  117. package/dist/runtime/agent-md.js +257 -0
  118. package/dist/runtime/agent-md.js.map +1 -0
  119. package/dist/runtime/claude-sdk.d.ts +51 -0
  120. package/dist/runtime/claude-sdk.d.ts.map +1 -0
  121. package/dist/runtime/claude-sdk.js +78 -0
  122. package/dist/runtime/claude-sdk.js.map +1 -0
  123. package/dist/runtime/hooks.d.ts +67 -0
  124. package/dist/runtime/hooks.d.ts.map +1 -0
  125. package/dist/runtime/hooks.js +645 -0
  126. package/dist/runtime/hooks.js.map +1 -0
  127. package/dist/runtime/index.d.ts +9 -0
  128. package/dist/runtime/index.d.ts.map +1 -0
  129. package/dist/runtime/index.js +5 -0
  130. package/dist/runtime/index.js.map +1 -0
  131. package/dist/runtime/markdown.d.ts +36 -0
  132. package/dist/runtime/markdown.d.ts.map +1 -0
  133. package/dist/runtime/markdown.js +214 -0
  134. package/dist/runtime/markdown.js.map +1 -0
  135. package/dist/runtime/settings-adapter.d.ts +33 -0
  136. package/dist/runtime/settings-adapter.d.ts.map +1 -0
  137. package/dist/runtime/settings-adapter.js +78 -0
  138. package/dist/runtime/settings-adapter.js.map +1 -0
  139. package/dist/runtime/skill-adapter.d.ts +22 -0
  140. package/dist/runtime/skill-adapter.d.ts.map +1 -0
  141. package/dist/runtime/skill-adapter.js +170 -0
  142. package/dist/runtime/skill-adapter.js.map +1 -0
  143. package/dist/runtime/workflow-runner.d.ts +53 -0
  144. package/dist/runtime/workflow-runner.d.ts.map +1 -0
  145. package/dist/runtime/workflow-runner.js +420 -0
  146. package/dist/runtime/workflow-runner.js.map +1 -0
  147. package/dist/schema/index.d.ts +2 -0
  148. package/dist/schema/index.d.ts.map +1 -0
  149. package/dist/schema/index.js +2 -0
  150. package/dist/schema/index.js.map +1 -0
  151. package/dist/schema/types.d.ts +275 -0
  152. package/dist/schema/types.d.ts.map +1 -0
  153. package/dist/schema/types.js +11 -0
  154. package/dist/schema/types.js.map +1 -0
  155. package/dist/schema/validate.d.ts +20 -0
  156. package/dist/schema/validate.d.ts.map +1 -0
  157. package/dist/schema/validate.js +379 -0
  158. package/dist/schema/validate.js.map +1 -0
  159. package/dist/schema/yaml-parser.d.ts +12 -0
  160. package/dist/schema/yaml-parser.d.ts.map +1 -0
  161. package/dist/schema/yaml-parser.js +298 -0
  162. package/dist/schema/yaml-parser.js.map +1 -0
  163. package/dist/species/default.dna.json +62 -0
  164. package/dist/templates/api-backend.dna.yaml +77 -0
  165. package/dist/templates/brainstorming-first.dna.yaml +71 -0
  166. package/dist/templates/branch-finishing.dna.yaml +54 -0
  167. package/dist/templates/code-review-pipeline.dna.yaml +91 -0
  168. package/dist/templates/devops-cicd.dna.yaml +67 -0
  169. package/dist/templates/documentation-writer.dna.yaml +71 -0
  170. package/dist/templates/enterprise-baseline.dna.yaml +66 -0
  171. package/dist/templates/frontend-quality.dna.yaml +88 -0
  172. package/dist/templates/full-pipeline.dna.yaml +129 -0
  173. package/dist/templates/mobile-dev.dna.yaml +68 -0
  174. package/dist/templates/monorepo-governance.dna.yaml +60 -0
  175. package/dist/templates/multi-perspective-review.dna.yaml +109 -0
  176. package/dist/templates/pr-submitter.dna.yaml +55 -0
  177. package/dist/templates/safe-refactoring.dna.yaml +65 -0
  178. package/dist/templates/secure-dev.dna.yaml +90 -0
  179. package/dist/templates/subagent-parallel.dna.yaml +98 -0
  180. package/dist/templates/systematic-debugging.dna.yaml +55 -0
  181. package/dist/templates/tdd-strict.dna.yaml +69 -0
  182. package/dist/templates/verification-loop.dna.yaml +53 -0
  183. package/dist/templates/yolo-with-guardrails.dna.yaml +56 -0
  184. package/package.json +43 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Intent DNA — Skill Adapter
3
+ *
4
+ * Compiles DNA roles + workflow + genes into Claude Code skill files
5
+ * (.claude/skills/<name>/SKILL.md).
6
+ *
7
+ * Skills are compiled views of DNA's structured definitions — not stored content.
8
+ */
9
+ import { mkdir, writeFile, readdir, readFile, rm } from "node:fs/promises";
10
+ import { join } from "node:path";
11
+ import { toKebabCase } from "./agent-md.js";
12
+ const SENTINEL = "<!-- intentdna:managed — do not edit manually -->";
13
+ // ── Compile Workflow → Skill ───────────────────────────────
14
+ /**
15
+ * Compile a WorkflowPlan + roles + IR into a SKILL.md file.
16
+ */
17
+ export function compileWorkflowToSkill(plan, roles, ir) {
18
+ const lines = [];
19
+ // Frontmatter
20
+ lines.push("---");
21
+ lines.push(`name: ${plan.name}`);
22
+ lines.push(`description: "${escapeYaml(plan.description)}"`);
23
+ lines.push("---");
24
+ lines.push("");
25
+ // Sentinel
26
+ lines.push(SENTINEL);
27
+ lines.push(`<!-- Compiled: ${new Date().toISOString()} -->`);
28
+ lines.push("");
29
+ // Title
30
+ lines.push(`# /${plan.name} [task-id]`);
31
+ lines.push("");
32
+ lines.push(plan.description);
33
+ lines.push("");
34
+ // Roles table
35
+ lines.push("## Roles");
36
+ lines.push("");
37
+ lines.push("| Role | Agent | Tools | Deny | Scope (write) |");
38
+ lines.push("|------|-------|-------|------|---------------|");
39
+ const usedRoles = new Set(plan.steps.map((s) => s.role));
40
+ for (const roleName of usedRoles) {
41
+ const role = roles[roleName];
42
+ if (!role)
43
+ continue;
44
+ const agent = `dna-${toKebabCase(roleName)}`;
45
+ const tools = role.tool_permissions?.allow?.join(", ") || "all";
46
+ const deny = role.tool_permissions?.deny?.join(", ") || "—";
47
+ const scope = role.scope?.write?.join(", ") || "—";
48
+ lines.push(`| ${roleName} | ${agent} | ${tools} | ${deny} | ${scope} |`);
49
+ }
50
+ lines.push("");
51
+ // Process
52
+ lines.push("## Process");
53
+ lines.push("");
54
+ for (let i = 0; i < plan.parallel_groups.length; i++) {
55
+ const group = plan.parallel_groups[i];
56
+ const isParallel = group.step_ids.length > 1;
57
+ if (isParallel) {
58
+ lines.push(`### Group ${i + 1} (parallel)`);
59
+ }
60
+ for (const stepId of group.step_ids) {
61
+ const step = plan.steps.find((s) => s.id === stepId);
62
+ const agent = `@dna-${toKebabCase(step.role)}`;
63
+ const runIf = step.run_if ? ` (${step.run_if})` : "";
64
+ const optional = step.optional ? " (optional)" : "";
65
+ lines.push(`${i + 1}. **${step.id}** → ${agent}${runIf}${optional}`);
66
+ lines.push(` ${step.description}`);
67
+ }
68
+ }
69
+ lines.push("");
70
+ // Retry
71
+ if (plan.retry.max_retries > 0) {
72
+ lines.push("## Retry Policy");
73
+ lines.push("");
74
+ lines.push(`- Max retries: ${plan.retry.max_retries}`);
75
+ if (plan.retry.retry_from) {
76
+ lines.push(`- Retry from: ${plan.retry.retry_from}`);
77
+ }
78
+ for (const t of plan.transitions) {
79
+ if (t.condition === "fail") {
80
+ lines.push(`- On **${t.from}** fail → go to **${t.to}**`);
81
+ }
82
+ }
83
+ lines.push("");
84
+ }
85
+ // Guidelines from IR
86
+ if (ir && ir.prompt_directives.length > 0) {
87
+ lines.push("## Guidelines");
88
+ lines.push("");
89
+ const high = ir.prompt_directives.filter((d) => d.priority === "high");
90
+ const medium = ir.prompt_directives.filter((d) => d.priority === "medium");
91
+ if (high.length > 0) {
92
+ lines.push("### Critical");
93
+ for (const d of high) {
94
+ lines.push(`- **${d.text}**`);
95
+ }
96
+ lines.push("");
97
+ }
98
+ if (medium.length > 0) {
99
+ lines.push("### Standard");
100
+ for (const d of medium) {
101
+ lines.push(`- ${d.text}`);
102
+ }
103
+ lines.push("");
104
+ }
105
+ }
106
+ // Role instructions
107
+ lines.push("## Role Instructions");
108
+ lines.push("");
109
+ for (const roleName of usedRoles) {
110
+ const role = roles[roleName];
111
+ if (!role)
112
+ continue;
113
+ lines.push(`### ${roleName}`);
114
+ lines.push("");
115
+ if (role.instructions && role.instructions.length > 0) {
116
+ for (let j = 0; j < role.instructions.length; j++) {
117
+ lines.push(`${j + 1}. ${role.instructions[j]}`);
118
+ }
119
+ }
120
+ if (role.post_checks && role.post_checks.length > 0) {
121
+ lines.push("");
122
+ lines.push("Post checks: " + role.post_checks.join(", "));
123
+ }
124
+ lines.push("");
125
+ }
126
+ const content = lines.join("\n") + "\n";
127
+ const dirName = plan.name;
128
+ const fileName = join(dirName, "SKILL.md");
129
+ return { name: plan.name, fileName, dirName, content };
130
+ }
131
+ // ── File Operations ────────────────────────────────────────
132
+ export async function writeSkillFiles(results, outputDir) {
133
+ const written = [];
134
+ for (const result of results) {
135
+ const dir = join(outputDir, result.dirName);
136
+ await mkdir(dir, { recursive: true });
137
+ const path = join(dir, "SKILL.md");
138
+ await writeFile(path, result.content, "utf-8");
139
+ written.push(path);
140
+ }
141
+ return written;
142
+ }
143
+ export async function removeSkillFiles(outputDir) {
144
+ let removed = 0;
145
+ let entries;
146
+ try {
147
+ entries = await readdir(outputDir);
148
+ }
149
+ catch {
150
+ return 0;
151
+ }
152
+ for (const entry of entries) {
153
+ const skillPath = join(outputDir, entry, "SKILL.md");
154
+ try {
155
+ const content = await readFile(skillPath, "utf-8");
156
+ if (content.includes(SENTINEL)) {
157
+ await rm(join(outputDir, entry), { recursive: true });
158
+ removed++;
159
+ }
160
+ }
161
+ catch {
162
+ // Skip
163
+ }
164
+ }
165
+ return removed;
166
+ }
167
+ function escapeYaml(s) {
168
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
169
+ }
170
+ //# sourceMappingURL=skill-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-adapter.js","sourceRoot":"","sources":["../../src/runtime/skill-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Intent DNA -- Workflow Shell Script Runner
3
+ *
4
+ * Compiles a WorkflowPlan into an executable bash script.
5
+ * Each step invokes `claude -p --agent <role-name>`.
6
+ * Parallel groups use background jobs + wait.
7
+ * Retry loops wrap the execution with round tracking.
8
+ *
9
+ * Output: a self-contained shell script with set -euo pipefail,
10
+ * run_agent helper, parallel group execution, retry logic, and
11
+ * transition-based flow control.
12
+ */
13
+ import type { WorkflowPlan } from "../schema/types.js";
14
+ /** Options for workflow shell script generation */
15
+ export interface WorkflowShellOptions {
16
+ /** Agent name prefix (default "dna-") */
17
+ agentPrefix?: string;
18
+ /** Max budget per agent in USD (default 10) */
19
+ maxBudgetPerAgent?: number;
20
+ /** Output format for agent results (default "json") */
21
+ outputFormat?: "json" | "text";
22
+ /** Permission mode (default "bypassPermissions") */
23
+ permissionMode?: string;
24
+ /** Log directory relative to project root (default ".") */
25
+ logDir?: string;
26
+ /** Include pause/resume support (default false) */
27
+ pauseSupport?: boolean;
28
+ }
29
+ /** Result of workflow shell script generation */
30
+ export interface WorkflowShellResult {
31
+ /** The generated script content */
32
+ script: string;
33
+ /** File name for the script */
34
+ fileName: string;
35
+ }
36
+ /**
37
+ * Compile a WorkflowPlan into an executable shell script.
38
+ * Pure function, no side effects.
39
+ */
40
+ export declare function compileWorkflowToShell(plan: WorkflowPlan, options?: WorkflowShellOptions): WorkflowShellResult;
41
+ /**
42
+ * Write workflow shell script to the file system.
43
+ * Creates the directory if it doesn't exist.
44
+ * Returns the path of the written file.
45
+ */
46
+ export declare function writeWorkflowScript(result: WorkflowShellResult, outputDir: string): Promise<string>;
47
+ /**
48
+ * Remove Intent DNA generated workflow scripts from a directory.
49
+ * Only removes files that contain the sentinel comment.
50
+ * Returns number of files removed.
51
+ */
52
+ export declare function removeWorkflowScripts(outputDir: string): Promise<number>;
53
+ //# sourceMappingURL=workflow-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-runner.d.ts","sourceRoot":"","sources":["../../src/runtime/workflow-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,oBAAoB,CAAC;AAO5B,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAkVD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,YAAY,EAClB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,mBAAmB,CAwFrB;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,mBAAmB,EAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
@@ -0,0 +1,420 @@
1
+ /**
2
+ * Intent DNA -- Workflow Shell Script Runner
3
+ *
4
+ * Compiles a WorkflowPlan into an executable bash script.
5
+ * Each step invokes `claude -p --agent <role-name>`.
6
+ * Parallel groups use background jobs + wait.
7
+ * Retry loops wrap the execution with round tracking.
8
+ *
9
+ * Output: a self-contained shell script with set -euo pipefail,
10
+ * run_agent helper, parallel group execution, retry logic, and
11
+ * transition-based flow control.
12
+ */
13
+ import { mkdir, writeFile, readFile, readdir, rm } from "node:fs/promises";
14
+ import { join } from "node:path";
15
+ import { toKebabCase } from "./agent-md.js";
16
+ const SENTINEL_COMMENT = "# intentdna:managed -- do not edit manually";
17
+ // ── Internal helpers ────────────────────────────────────────
18
+ /**
19
+ * Sanitize a step ID for use as a bash variable name.
20
+ * Replaces any non-alphanumeric/underscore character with underscore.
21
+ */
22
+ function sanitizeForBash(id) {
23
+ return id.replace(/[^a-zA-Z0-9_]/g, "_");
24
+ }
25
+ /**
26
+ * Get the agent name for a role, using toKebabCase and the configured prefix.
27
+ */
28
+ function agentName(role, prefix) {
29
+ return `${prefix}${toKebabCase(role)}`;
30
+ }
31
+ /**
32
+ * Escape a string for safe use inside a single-quoted shell string.
33
+ */
34
+ function escapeShellSingleQuote(s) {
35
+ return s.replace(/'/g, "'\\''");
36
+ }
37
+ /**
38
+ * Compile a run_if expression into a bash conditional string.
39
+ * Returns null for unconditional execution.
40
+ */
41
+ function compileRunIf(runIf) {
42
+ if (runIf === null)
43
+ return null;
44
+ // "round == N"
45
+ const eqMatch = runIf.match(/^round\s*==\s*(\d+)$/);
46
+ if (eqMatch) {
47
+ return `[ "$ROUND" -eq ${eqMatch[1]} ]`;
48
+ }
49
+ // "round > N"
50
+ const gtMatch = runIf.match(/^round\s*>\s*(\d+)$/);
51
+ if (gtMatch) {
52
+ return `[ "$ROUND" -gt ${gtMatch[1]} ]`;
53
+ }
54
+ // "retry" is equivalent to "round > 1"
55
+ if (runIf === "retry") {
56
+ return '[ "$ROUND" -gt 1 ]';
57
+ }
58
+ // Unsupported expressions -- return as comment hint
59
+ return null;
60
+ }
61
+ /**
62
+ * Build a step map for looking up steps by ID.
63
+ */
64
+ function buildStepMap(steps) {
65
+ const map = new Map();
66
+ for (const s of steps) {
67
+ map.set(s.id, s);
68
+ }
69
+ return map;
70
+ }
71
+ /**
72
+ * Find the topological index of a step ID in the step list.
73
+ */
74
+ function stepIndex(steps, id) {
75
+ return steps.findIndex((s) => s.id === id);
76
+ }
77
+ // ── Script generators ──────────────────────────────────────
78
+ function generateHeader(plan, options) {
79
+ const lines = [];
80
+ lines.push("#!/bin/bash");
81
+ lines.push(SENTINEL_COMMENT);
82
+ lines.push(`# Workflow: ${plan.name}`);
83
+ lines.push(`# Compiled: ${plan.compiled_at}`);
84
+ lines.push(`# Source: ${plan.source_workflow}`);
85
+ lines.push("set -euo pipefail");
86
+ lines.push("");
87
+ lines.push('PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"');
88
+ lines.push('cd "$PROJECT_ROOT"');
89
+ lines.push("");
90
+ // Check jq availability
91
+ lines.push("# Check dependencies");
92
+ lines.push('if ! command -v jq &>/dev/null; then');
93
+ lines.push(' echo "Error: jq is required but not installed." >&2');
94
+ lines.push(" exit 1");
95
+ lines.push("fi");
96
+ lines.push('if ! command -v claude &>/dev/null; then');
97
+ lines.push(' echo "Error: claude CLI is required but not installed." >&2');
98
+ lines.push(" exit 1");
99
+ lines.push("fi");
100
+ lines.push("");
101
+ // Variables
102
+ lines.push("# Configuration");
103
+ lines.push(`MAX_BUDGET="${options.maxBudgetPerAgent}"`);
104
+ lines.push(`LOG_DIR="${options.logDir}"`);
105
+ lines.push('mkdir -p "$LOG_DIR"');
106
+ lines.push("");
107
+ // Pause support
108
+ if (options.pauseSupport) {
109
+ lines.push('PAUSE_FILE=".claude/dev-pause"');
110
+ lines.push("check_pause() {");
111
+ lines.push(' if [ -f "$PAUSE_FILE" ]; then');
112
+ lines.push(' echo "[$(date \'+%H:%M:%S\')] Paused. Remove $PAUSE_FILE to continue."');
113
+ lines.push(" exit 0");
114
+ lines.push(" fi");
115
+ lines.push("}");
116
+ lines.push("");
117
+ }
118
+ return lines;
119
+ }
120
+ function generateRunAgentFn(options) {
121
+ const lines = [];
122
+ lines.push("# Run a single agent step");
123
+ lines.push("run_agent() {");
124
+ lines.push(' local agent="$1"');
125
+ lines.push(' local prompt="$2"');
126
+ lines.push(' local step_id="$3"');
127
+ lines.push(' local result_file="${LOG_DIR}/${step_id}-result.json"');
128
+ lines.push("");
129
+ lines.push(' echo "[$(date \'+%H:%M:%S\')] Running step: $step_id (agent: $agent)"');
130
+ lines.push("");
131
+ lines.push(' echo "$prompt" | claude -p \\');
132
+ lines.push(' --agent "$agent" \\');
133
+ lines.push(` --output-format ${options.outputFormat} \\`);
134
+ lines.push(' --max-budget-usd "$MAX_BUDGET" \\');
135
+ lines.push(` --permission-mode ${options.permissionMode} \\`);
136
+ lines.push(' --session-id "$(uuidgen | tr \'[:upper:]\' \'[:lower:]\')" \\');
137
+ lines.push(' --no-session-persistence \\');
138
+ lines.push(' > "$result_file" 2>&1 || true');
139
+ lines.push("");
140
+ lines.push(' local is_error=$(jq -r \'.is_error // false\' "$result_file" 2>/dev/null || echo "false")');
141
+ lines.push(' if [ "$is_error" = "true" ]; then');
142
+ lines.push(' echo "[$(date \'+%H:%M:%S\')] Step $step_id FAILED"');
143
+ lines.push(" return 1");
144
+ lines.push(" fi");
145
+ lines.push(' echo "[$(date \'+%H:%M:%S\')] Step $step_id PASSED"');
146
+ lines.push(" return 0");
147
+ lines.push("}");
148
+ return lines;
149
+ }
150
+ /**
151
+ * Generate execution code for a single step, with run_if and optional handling.
152
+ * Returns lines of bash code.
153
+ */
154
+ function generateStepExecution(step, options, plan, isParallel) {
155
+ const lines = [];
156
+ const varId = sanitizeForBash(step.id);
157
+ const agent = agentName(step.role, options.agentPrefix);
158
+ const prompt = escapeShellSingleQuote(step.prompt ?? step.description);
159
+ const bashCondition = compileRunIf(step.run_if);
160
+ // Determine if this step needs a retry_from skip guard.
161
+ // If plan has retry_from set, and this step is before retry_from in
162
+ // topological order, and step has no explicit run_if, inject a round==1 guard.
163
+ let retrySkipGuard = false;
164
+ if (plan.retry.max_retries > 0 && plan.retry.retry_from !== null && step.run_if === null) {
165
+ const retryFromIdx = stepIndex(plan.steps, plan.retry.retry_from);
166
+ const thisIdx = stepIndex(plan.steps, step.id);
167
+ if (thisIdx >= 0 && retryFromIdx >= 0 && thisIdx < retryFromIdx) {
168
+ retrySkipGuard = true;
169
+ }
170
+ }
171
+ const effectiveCondition = retrySkipGuard ? '[ "$ROUND" -eq 1 ]' : bashCondition;
172
+ // Build the agent call
173
+ const agentCall = step.optional
174
+ ? `run_agent "${agent}" '${prompt}' "${step.id}" || {\n STEP_${varId}_STATUS=1\n echo "[$(date '+%H:%M:%S')] Optional step '${step.id}' failed, continuing..."\n }`
175
+ : `run_agent "${agent}" '${prompt}' "${step.id}" || STEP_${varId}_STATUS=1`;
176
+ if (effectiveCondition) {
177
+ const condLabel = retrySkipGuard ? "round == 1 (retry_from skip)" : step.run_if;
178
+ lines.push(` # Step: ${step.id} (run_if: ${condLabel})`);
179
+ lines.push(` if ${effectiveCondition}; then`);
180
+ lines.push(` ${agentCall}`);
181
+ lines.push(" else");
182
+ lines.push(` echo "[$(date '+%H:%M:%S')] Skipping step: ${step.id} (run_if: ${condLabel}, current round: $ROUND)"`);
183
+ lines.push(" fi");
184
+ }
185
+ else {
186
+ lines.push(` # Step: ${step.id}`);
187
+ if (step.optional) {
188
+ lines.push(` run_agent "${agent}" '${prompt}' "${step.id}" || {`);
189
+ lines.push(` STEP_${varId}_STATUS=1`);
190
+ lines.push(` echo "[$(date '+%H:%M:%S')] Optional step '${step.id}' failed, continuing..."`);
191
+ lines.push(" }");
192
+ }
193
+ else {
194
+ lines.push(` run_agent "${agent}" '${prompt}' "${step.id}" || STEP_${varId}_STATUS=1`);
195
+ }
196
+ }
197
+ return lines;
198
+ }
199
+ /**
200
+ * Generate execution code for a parallel group.
201
+ */
202
+ function generateGroupExecution(group, stepMap, options, plan) {
203
+ const lines = [];
204
+ const isParallel = group.step_ids.length > 1;
205
+ if (isParallel) {
206
+ lines.push(` # Group ${group.group_index}: ${group.step_ids.join(", ")} (parallel)`);
207
+ }
208
+ else {
209
+ lines.push(` # Group ${group.group_index}: ${group.step_ids[0]} (sequential)`);
210
+ }
211
+ // Initialize status variables
212
+ for (const id of group.step_ids) {
213
+ const varId = sanitizeForBash(id);
214
+ lines.push(` STEP_${varId}_STATUS=0`);
215
+ }
216
+ if (isParallel) {
217
+ // Background jobs
218
+ for (const id of group.step_ids) {
219
+ const step = stepMap.get(id);
220
+ const varId = sanitizeForBash(id);
221
+ const agent = agentName(step.role, options.agentPrefix);
222
+ const prompt = escapeShellSingleQuote(step.prompt ?? step.description);
223
+ lines.push("");
224
+ lines.push(` run_agent "${agent}" '${prompt}' "${step.id}" &`);
225
+ lines.push(` PID_${varId}=$!`);
226
+ }
227
+ lines.push("");
228
+ // Wait for all
229
+ for (const id of group.step_ids) {
230
+ const step = stepMap.get(id);
231
+ const varId = sanitizeForBash(id);
232
+ if (step.optional) {
233
+ lines.push(` wait $PID_${varId} || {`);
234
+ lines.push(` STEP_${varId}_STATUS=1`);
235
+ lines.push(` echo "[$(date '+%H:%M:%S')] Optional step '${step.id}' failed, continuing..."`);
236
+ lines.push(" }");
237
+ }
238
+ else {
239
+ lines.push(` wait $PID_${varId} || STEP_${varId}_STATUS=1`);
240
+ }
241
+ }
242
+ }
243
+ else {
244
+ // Sequential -- single step
245
+ const step = stepMap.get(group.step_ids[0]);
246
+ lines.push("");
247
+ lines.push(...generateStepExecution(step, options, plan, false));
248
+ }
249
+ return lines;
250
+ }
251
+ /**
252
+ * Generate transition check code after all groups execute.
253
+ */
254
+ function generateTransitionChecks(transitions, plan) {
255
+ const lines = [];
256
+ const failTransitions = transitions.filter((t) => t.condition === "fail" || t.condition === "error");
257
+ if (failTransitions.length === 0)
258
+ return lines;
259
+ lines.push("");
260
+ lines.push(" # Transition checks");
261
+ for (const t of failTransitions) {
262
+ const varId = sanitizeForBash(t.from);
263
+ lines.push(` if [ "$STEP_${varId}_STATUS" -ne 0 ]; then`);
264
+ lines.push(` # Transition: ${t.from} --${t.condition}--> ${t.to}`);
265
+ lines.push(" RETRY_NEEDED=true");
266
+ lines.push(" fi");
267
+ }
268
+ return lines;
269
+ }
270
+ /**
271
+ * Generate the complete check for non-optional step failures (that don't have
272
+ * explicit fail transitions) to trigger exit or retry.
273
+ */
274
+ function generateFailureChecks(plan) {
275
+ const lines = [];
276
+ const failTransitionFroms = new Set(plan.transitions.filter((t) => t.condition === "fail" || t.condition === "error").map((t) => t.from));
277
+ const nonOptionalSteps = plan.steps.filter((s) => !s.optional && !failTransitionFroms.has(s.id));
278
+ if (nonOptionalSteps.length === 0)
279
+ return lines;
280
+ lines.push("");
281
+ lines.push(" # Check for non-optional step failures");
282
+ for (const step of nonOptionalSteps) {
283
+ const varId = sanitizeForBash(step.id);
284
+ if (plan.retry.max_retries > 0) {
285
+ lines.push(` if [ "$STEP_${varId}_STATUS" -ne 0 ]; then`);
286
+ lines.push(" RETRY_NEEDED=true");
287
+ lines.push(" fi");
288
+ }
289
+ else {
290
+ lines.push(` if [ "$STEP_${varId}_STATUS" -ne 0 ]; then`);
291
+ lines.push(` echo "[$(date '+%H:%M:%S')] Step '${step.id}' failed."`);
292
+ lines.push(" exit 1");
293
+ lines.push(" fi");
294
+ }
295
+ }
296
+ return lines;
297
+ }
298
+ // ── Public API ──────────────────────────────────────────────
299
+ /**
300
+ * Compile a WorkflowPlan into an executable shell script.
301
+ * Pure function, no side effects.
302
+ */
303
+ export function compileWorkflowToShell(plan, options) {
304
+ const opts = {
305
+ agentPrefix: options?.agentPrefix ?? "dna-",
306
+ maxBudgetPerAgent: options?.maxBudgetPerAgent ?? 10,
307
+ outputFormat: options?.outputFormat ?? "json",
308
+ permissionMode: options?.permissionMode ?? "bypassPermissions",
309
+ logDir: options?.logDir ?? ".",
310
+ pauseSupport: options?.pauseSupport ?? false,
311
+ };
312
+ const stepMap = buildStepMap(plan.steps);
313
+ const lines = [];
314
+ // Header
315
+ lines.push(...generateHeader(plan, opts));
316
+ // run_agent function
317
+ lines.push(...generateRunAgentFn(opts));
318
+ lines.push("");
319
+ // Main execution
320
+ if (plan.retry.max_retries > 0) {
321
+ // With retry loop
322
+ lines.push("# ── Main execution (with retry) ──");
323
+ lines.push("ROUND=1");
324
+ lines.push(`MAX_RETRIES=${plan.retry.max_retries}`);
325
+ lines.push("");
326
+ lines.push('while [ "$ROUND" -le $((MAX_RETRIES + 1)) ]; do');
327
+ lines.push(' echo "=== Round $ROUND ==="');
328
+ lines.push(" RETRY_NEEDED=false");
329
+ if (opts.pauseSupport) {
330
+ lines.push(" check_pause");
331
+ }
332
+ lines.push("");
333
+ // Group blocks
334
+ for (const group of plan.parallel_groups) {
335
+ lines.push(...generateGroupExecution(group, stepMap, opts, plan));
336
+ lines.push("");
337
+ }
338
+ // Transition checks
339
+ lines.push(...generateTransitionChecks(plan.transitions, plan));
340
+ // Failure checks
341
+ lines.push(...generateFailureChecks(plan));
342
+ lines.push("");
343
+ lines.push(' if [ "$RETRY_NEEDED" = false ]; then');
344
+ lines.push(' echo "All steps passed."');
345
+ lines.push(" break");
346
+ lines.push(" fi");
347
+ lines.push("");
348
+ lines.push(" ROUND=$((ROUND + 1))");
349
+ lines.push(' if [ "$ROUND" -gt $((MAX_RETRIES + 1)) ]; then');
350
+ lines.push(' echo "Max retries ($MAX_RETRIES) exhausted."');
351
+ lines.push(" exit 1");
352
+ lines.push(" fi");
353
+ lines.push("");
354
+ lines.push(' echo "Retrying from round $ROUND..."');
355
+ lines.push("done");
356
+ }
357
+ else {
358
+ // No retry -- direct execution
359
+ lines.push("# ── Main execution (no retry) ──");
360
+ lines.push('echo "=== Running workflow: ' + escapeShellSingleQuote(plan.name) + ' ==="');
361
+ lines.push("");
362
+ // Group blocks
363
+ for (const group of plan.parallel_groups) {
364
+ lines.push(...generateGroupExecution(group, stepMap, opts, plan));
365
+ lines.push("");
366
+ }
367
+ // Failure checks (exit on failure, no retry)
368
+ lines.push(...generateFailureChecks(plan));
369
+ lines.push("");
370
+ lines.push('echo "All steps passed."');
371
+ }
372
+ lines.push("");
373
+ const script = lines.join("\n") + "\n";
374
+ const fileName = toKebabCase(plan.name) + ".sh";
375
+ return { script, fileName };
376
+ }
377
+ /**
378
+ * Write workflow shell script to the file system.
379
+ * Creates the directory if it doesn't exist.
380
+ * Returns the path of the written file.
381
+ */
382
+ export async function writeWorkflowScript(result, outputDir) {
383
+ await mkdir(outputDir, { recursive: true });
384
+ const path = join(outputDir, result.fileName);
385
+ await writeFile(path, result.script, { mode: 0o755 });
386
+ return path;
387
+ }
388
+ /**
389
+ * Remove Intent DNA generated workflow scripts from a directory.
390
+ * Only removes files that contain the sentinel comment.
391
+ * Returns number of files removed.
392
+ */
393
+ export async function removeWorkflowScripts(outputDir) {
394
+ let entries;
395
+ try {
396
+ entries = await readdir(outputDir);
397
+ }
398
+ catch {
399
+ // Directory doesn't exist
400
+ return 0;
401
+ }
402
+ let removed = 0;
403
+ for (const entry of entries) {
404
+ if (!entry.endsWith(".sh"))
405
+ continue;
406
+ const path = join(outputDir, entry);
407
+ try {
408
+ const content = await readFile(path, "utf-8");
409
+ if (content.includes(SENTINEL_COMMENT)) {
410
+ await rm(path);
411
+ removed++;
412
+ }
413
+ }
414
+ catch {
415
+ // Skip files that can't be read
416
+ }
417
+ }
418
+ return removed;
419
+ }
420
+ //# sourceMappingURL=workflow-runner.js.map