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.
- package/LICENSE +69 -0
- package/README.md +251 -0
- package/dist/audit/index.d.ts +109 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +144 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/cli/commands/compile.d.ts +14 -0
- package/dist/cli/commands/compile.d.ts.map +1 -0
- package/dist/cli/commands/compile.js +89 -0
- package/dist/cli/commands/compile.js.map +1 -0
- package/dist/cli/commands/epigenetic.d.ts +20 -0
- package/dist/cli/commands/epigenetic.d.ts.map +1 -0
- package/dist/cli/commands/epigenetic.js +112 -0
- package/dist/cli/commands/epigenetic.js.map +1 -0
- package/dist/cli/commands/evolve.d.ts +12 -0
- package/dist/cli/commands/evolve.d.ts.map +1 -0
- package/dist/cli/commands/evolve.js +52 -0
- package/dist/cli/commands/evolve.js.map +1 -0
- package/dist/cli/commands/guard.d.ts +22 -0
- package/dist/cli/commands/guard.d.ts.map +1 -0
- package/dist/cli/commands/guard.js +257 -0
- package/dist/cli/commands/guard.js.map +1 -0
- package/dist/cli/commands/import.d.ts +71 -0
- package/dist/cli/commands/import.d.ts.map +1 -0
- package/dist/cli/commands/import.js +428 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/init.d.ts +13 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +171 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/run.d.ts +59 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +350 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/show.d.ts +12 -0
- package/dist/cli/commands/show.d.ts.map +1 -0
- package/dist/cli/commands/show.js +78 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +31 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +307 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +11 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +30 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/commands/verify.d.ts +33 -0
- package/dist/cli/commands/verify.d.ts.map +1 -0
- package/dist/cli/commands/verify.js +102 -0
- package/dist/cli/commands/verify.js.map +1 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +358 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/util/format.d.ts +24 -0
- package/dist/cli/util/format.d.ts.map +1 -0
- package/dist/cli/util/format.js +67 -0
- package/dist/cli/util/format.js.map +1 -0
- package/dist/cli/util/prompt.d.ts +11 -0
- package/dist/cli/util/prompt.d.ts.map +1 -0
- package/dist/cli/util/prompt.js +51 -0
- package/dist/cli/util/prompt.js.map +1 -0
- package/dist/compiler/activate.d.ts +29 -0
- package/dist/compiler/activate.d.ts.map +1 -0
- package/dist/compiler/activate.js +141 -0
- package/dist/compiler/activate.js.map +1 -0
- package/dist/compiler/cascade.d.ts +27 -0
- package/dist/compiler/cascade.d.ts.map +1 -0
- package/dist/compiler/cascade.js +124 -0
- package/dist/compiler/cascade.js.map +1 -0
- package/dist/compiler/compile.d.ts +17 -0
- package/dist/compiler/compile.d.ts.map +1 -0
- package/dist/compiler/compile.js +219 -0
- package/dist/compiler/compile.js.map +1 -0
- package/dist/compiler/index.d.ts +28 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/compiler/index.js +51 -0
- package/dist/compiler/index.js.map +1 -0
- package/dist/compiler/workflow.d.ts +37 -0
- package/dist/compiler/workflow.d.ts.map +1 -0
- package/dist/compiler/workflow.js +274 -0
- package/dist/compiler/workflow.js.map +1 -0
- package/dist/evolution/collector.d.ts +52 -0
- package/dist/evolution/collector.d.ts.map +1 -0
- package/dist/evolution/collector.js +72 -0
- package/dist/evolution/collector.js.map +1 -0
- package/dist/evolution/fitness.d.ts +33 -0
- package/dist/evolution/fitness.d.ts.map +1 -0
- package/dist/evolution/fitness.js +96 -0
- package/dist/evolution/fitness.js.map +1 -0
- package/dist/evolution/index.d.ts +53 -0
- package/dist/evolution/index.d.ts.map +1 -0
- package/dist/evolution/index.js +69 -0
- package/dist/evolution/index.js.map +1 -0
- package/dist/evolution/marker-gen.d.ts +31 -0
- package/dist/evolution/marker-gen.d.ts.map +1 -0
- package/dist/evolution/marker-gen.js +141 -0
- package/dist/evolution/marker-gen.js.map +1 -0
- package/dist/evolution/store.d.ts +24 -0
- package/dist/evolution/store.d.ts.map +1 -0
- package/dist/evolution/store.js +78 -0
- package/dist/evolution/store.js.map +1 -0
- package/dist/evolution/types.d.ts +73 -0
- package/dist/evolution/types.d.ts.map +1 -0
- package/dist/evolution/types.js +14 -0
- package/dist/evolution/types.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/log/logger.d.ts +15 -0
- package/dist/log/logger.d.ts.map +1 -0
- package/dist/log/logger.js +36 -0
- package/dist/log/logger.js.map +1 -0
- package/dist/runtime/agent-md.d.ts +56 -0
- package/dist/runtime/agent-md.d.ts.map +1 -0
- package/dist/runtime/agent-md.js +257 -0
- package/dist/runtime/agent-md.js.map +1 -0
- package/dist/runtime/claude-sdk.d.ts +51 -0
- package/dist/runtime/claude-sdk.d.ts.map +1 -0
- package/dist/runtime/claude-sdk.js +78 -0
- package/dist/runtime/claude-sdk.js.map +1 -0
- package/dist/runtime/hooks.d.ts +67 -0
- package/dist/runtime/hooks.d.ts.map +1 -0
- package/dist/runtime/hooks.js +645 -0
- package/dist/runtime/hooks.js.map +1 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/markdown.d.ts +36 -0
- package/dist/runtime/markdown.d.ts.map +1 -0
- package/dist/runtime/markdown.js +214 -0
- package/dist/runtime/markdown.js.map +1 -0
- package/dist/runtime/settings-adapter.d.ts +33 -0
- package/dist/runtime/settings-adapter.d.ts.map +1 -0
- package/dist/runtime/settings-adapter.js +78 -0
- package/dist/runtime/settings-adapter.js.map +1 -0
- package/dist/runtime/skill-adapter.d.ts +22 -0
- package/dist/runtime/skill-adapter.d.ts.map +1 -0
- package/dist/runtime/skill-adapter.js +170 -0
- package/dist/runtime/skill-adapter.js.map +1 -0
- package/dist/runtime/workflow-runner.d.ts +53 -0
- package/dist/runtime/workflow-runner.d.ts.map +1 -0
- package/dist/runtime/workflow-runner.js +420 -0
- package/dist/runtime/workflow-runner.js.map +1 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/types.d.ts +275 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +11 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/schema/validate.d.ts +20 -0
- package/dist/schema/validate.d.ts.map +1 -0
- package/dist/schema/validate.js +379 -0
- package/dist/schema/validate.js.map +1 -0
- package/dist/schema/yaml-parser.d.ts +12 -0
- package/dist/schema/yaml-parser.d.ts.map +1 -0
- package/dist/schema/yaml-parser.js +298 -0
- package/dist/schema/yaml-parser.js.map +1 -0
- package/dist/species/default.dna.json +62 -0
- package/dist/templates/api-backend.dna.yaml +77 -0
- package/dist/templates/brainstorming-first.dna.yaml +71 -0
- package/dist/templates/branch-finishing.dna.yaml +54 -0
- package/dist/templates/code-review-pipeline.dna.yaml +91 -0
- package/dist/templates/devops-cicd.dna.yaml +67 -0
- package/dist/templates/documentation-writer.dna.yaml +71 -0
- package/dist/templates/enterprise-baseline.dna.yaml +66 -0
- package/dist/templates/frontend-quality.dna.yaml +88 -0
- package/dist/templates/full-pipeline.dna.yaml +129 -0
- package/dist/templates/mobile-dev.dna.yaml +68 -0
- package/dist/templates/monorepo-governance.dna.yaml +60 -0
- package/dist/templates/multi-perspective-review.dna.yaml +109 -0
- package/dist/templates/pr-submitter.dna.yaml +55 -0
- package/dist/templates/safe-refactoring.dna.yaml +65 -0
- package/dist/templates/secure-dev.dna.yaml +90 -0
- package/dist/templates/subagent-parallel.dna.yaml +98 -0
- package/dist/templates/systematic-debugging.dna.yaml +55 -0
- package/dist/templates/tdd-strict.dna.yaml +69 -0
- package/dist/templates/verification-loop.dna.yaml +53 -0
- package/dist/templates/yolo-with-guardrails.dna.yaml +56 -0
- 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
|