agentweaver 0.1.0 → 0.1.3

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 (77) hide show
  1. package/README.md +30 -5
  2. package/dist/artifacts.js +24 -2
  3. package/dist/executors/claude-executor.js +46 -0
  4. package/dist/executors/claude-summary-executor.js +31 -0
  5. package/dist/executors/codex-docker-executor.js +27 -0
  6. package/dist/executors/codex-local-executor.js +25 -0
  7. package/dist/executors/command-check-executor.js +14 -0
  8. package/dist/executors/configs/claude-config.js +12 -0
  9. package/dist/executors/configs/claude-summary-config.js +8 -0
  10. package/dist/executors/configs/codex-docker-config.js +10 -0
  11. package/dist/executors/configs/codex-local-config.js +8 -0
  12. package/dist/executors/configs/jira-fetch-config.js +4 -0
  13. package/dist/executors/configs/process-config.js +3 -0
  14. package/dist/executors/configs/verify-build-config.js +7 -0
  15. package/dist/executors/jira-fetch-executor.js +11 -0
  16. package/dist/executors/process-executor.js +21 -0
  17. package/dist/executors/types.js +1 -0
  18. package/dist/executors/verify-build-executor.js +22 -0
  19. package/dist/index.js +456 -699
  20. package/dist/interactive-ui.js +536 -182
  21. package/dist/jira.js +3 -1
  22. package/dist/pipeline/auto-flow.js +9 -0
  23. package/dist/pipeline/build-failure-summary.js +6 -0
  24. package/dist/pipeline/checks.js +15 -0
  25. package/dist/pipeline/context.js +19 -0
  26. package/dist/pipeline/declarative-flow-runner.js +246 -0
  27. package/dist/pipeline/declarative-flows.js +24 -0
  28. package/dist/pipeline/flow-runner.js +13 -0
  29. package/dist/pipeline/flow-specs/auto.json +471 -0
  30. package/dist/pipeline/flow-specs/implement.json +47 -0
  31. package/dist/pipeline/flow-specs/plan.json +88 -0
  32. package/dist/pipeline/flow-specs/preflight.json +174 -0
  33. package/dist/pipeline/flow-specs/review-fix.json +76 -0
  34. package/dist/pipeline/flow-specs/review.json +233 -0
  35. package/dist/pipeline/flow-specs/test-fix.json +24 -0
  36. package/dist/pipeline/flow-specs/test-linter-fix.json +24 -0
  37. package/dist/pipeline/flow-specs/test.json +19 -0
  38. package/dist/pipeline/flow-types.js +1 -0
  39. package/dist/pipeline/flows/implement-flow.js +47 -0
  40. package/dist/pipeline/flows/plan-flow.js +42 -0
  41. package/dist/pipeline/flows/preflight-flow.js +19 -0
  42. package/dist/pipeline/flows/review-fix-flow.js +62 -0
  43. package/dist/pipeline/flows/review-flow.js +124 -0
  44. package/dist/pipeline/flows/test-fix-flow.js +12 -0
  45. package/dist/pipeline/flows/test-flow.js +32 -0
  46. package/dist/pipeline/node-registry.js +71 -0
  47. package/dist/pipeline/node-runner.js +20 -0
  48. package/dist/pipeline/nodes/build-failure-summary-node.js +71 -0
  49. package/dist/pipeline/nodes/claude-prompt-node.js +54 -0
  50. package/dist/pipeline/nodes/claude-summary-node.js +38 -0
  51. package/dist/pipeline/nodes/codex-docker-prompt-node.js +32 -0
  52. package/dist/pipeline/nodes/codex-local-prompt-node.js +32 -0
  53. package/dist/pipeline/nodes/command-check-node.js +10 -0
  54. package/dist/pipeline/nodes/file-check-node.js +15 -0
  55. package/dist/pipeline/nodes/implement-codex-node.js +16 -0
  56. package/dist/pipeline/nodes/jira-fetch-node.js +25 -0
  57. package/dist/pipeline/nodes/plan-codex-node.js +32 -0
  58. package/dist/pipeline/nodes/review-claude-node.js +38 -0
  59. package/dist/pipeline/nodes/review-reply-codex-node.js +40 -0
  60. package/dist/pipeline/nodes/summary-file-load-node.js +16 -0
  61. package/dist/pipeline/nodes/task-summary-node.js +42 -0
  62. package/dist/pipeline/nodes/verify-build-node.js +14 -0
  63. package/dist/pipeline/prompt-registry.js +22 -0
  64. package/dist/pipeline/prompt-runtime.js +18 -0
  65. package/dist/pipeline/registry.js +23 -0
  66. package/dist/pipeline/spec-compiler.js +200 -0
  67. package/dist/pipeline/spec-loader.js +14 -0
  68. package/dist/pipeline/spec-types.js +1 -0
  69. package/dist/pipeline/spec-validator.js +290 -0
  70. package/dist/pipeline/types.js +10 -0
  71. package/dist/pipeline/value-resolver.js +199 -0
  72. package/dist/prompts.js +1 -3
  73. package/dist/runtime/command-resolution.js +139 -0
  74. package/dist/runtime/docker-runtime.js +51 -0
  75. package/dist/runtime/process-runner.js +112 -0
  76. package/dist/tui.js +73 -0
  77. package/package.json +3 -2
@@ -0,0 +1,38 @@
1
+ import { artifactFile, designFile, planFile } from "../../artifacts.js";
2
+ import { REVIEW_PROMPT_TEMPLATE, formatPrompt, formatTemplate } from "../../prompts.js";
3
+ import { printInfo, printPrompt } from "../../tui.js";
4
+ import { toExecutorContext } from "../types.js";
5
+ export const reviewClaudeNode = {
6
+ kind: "review-claude",
7
+ version: 1,
8
+ async run(context, params) {
9
+ const reviewFile = artifactFile("review", params.taskKey, params.iteration);
10
+ const prompt = formatPrompt(formatTemplate(REVIEW_PROMPT_TEMPLATE, {
11
+ jira_task_file: params.jiraTaskFile,
12
+ design_file: designFile(params.taskKey),
13
+ plan_file: planFile(params.taskKey),
14
+ review_file: reviewFile,
15
+ }), params.extraPrompt);
16
+ printInfo(`Running Claude review mode (iteration ${params.iteration})`);
17
+ printPrompt("Claude", prompt);
18
+ const executor = context.executors.get("claude");
19
+ const value = await executor.execute(toExecutorContext(context), {
20
+ prompt,
21
+ command: params.claudeCmd,
22
+ env: { ...context.env },
23
+ }, executor.defaultConfig);
24
+ return {
25
+ value,
26
+ outputs: [{ kind: "artifact", path: reviewFile, required: true }],
27
+ };
28
+ },
29
+ checks(_context, params) {
30
+ return [
31
+ {
32
+ kind: "require-artifacts",
33
+ paths: [artifactFile("review", params.taskKey, params.iteration)],
34
+ message: "Claude review did not produce the required review artifact.",
35
+ },
36
+ ];
37
+ },
38
+ };
@@ -0,0 +1,40 @@
1
+ import { artifactFile, designFile, planFile } from "../../artifacts.js";
2
+ import { REVIEW_REPLY_PROMPT_TEMPLATE, formatPrompt, formatTemplate } from "../../prompts.js";
3
+ import { printInfo, printPrompt } from "../../tui.js";
4
+ import { toExecutorContext } from "../types.js";
5
+ export const reviewReplyCodexNode = {
6
+ kind: "review-reply-codex",
7
+ version: 1,
8
+ async run(context, params) {
9
+ const reviewFile = artifactFile("review", params.taskKey, params.iteration);
10
+ const reviewReplyFile = artifactFile("review-reply", params.taskKey, params.iteration);
11
+ const prompt = formatPrompt(formatTemplate(REVIEW_REPLY_PROMPT_TEMPLATE, {
12
+ review_file: reviewFile,
13
+ jira_task_file: params.jiraTaskFile,
14
+ design_file: designFile(params.taskKey),
15
+ plan_file: planFile(params.taskKey),
16
+ review_reply_file: reviewReplyFile,
17
+ }), params.extraPrompt);
18
+ printInfo(`Running Codex review reply mode (iteration ${params.iteration})`);
19
+ printPrompt("Codex", prompt);
20
+ const executor = context.executors.get("codex-local");
21
+ const value = await executor.execute(toExecutorContext(context), {
22
+ prompt,
23
+ command: params.codexCmd,
24
+ env: { ...context.env },
25
+ }, executor.defaultConfig);
26
+ return {
27
+ value,
28
+ outputs: [{ kind: "artifact", path: reviewReplyFile, required: true }],
29
+ };
30
+ },
31
+ checks(_context, params) {
32
+ return [
33
+ {
34
+ kind: "require-artifacts",
35
+ paths: [artifactFile("review-reply", params.taskKey, params.iteration)],
36
+ message: "Codex review reply did not produce the required review-reply artifact.",
37
+ },
38
+ ];
39
+ },
40
+ };
@@ -0,0 +1,16 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { printSummary } from "../../tui.js";
3
+ export const summaryFileLoadNode = {
4
+ kind: "summary-file-load",
5
+ version: 1,
6
+ async run(context, params) {
7
+ const text = readFileSync(params.path, "utf8").trim();
8
+ context.setSummary?.(text);
9
+ if (params.title) {
10
+ printSummary(params.title, text);
11
+ }
12
+ return {
13
+ value: { text },
14
+ };
15
+ },
16
+ };
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { taskSummaryFile } from "../../artifacts.js";
3
+ import { requireArtifacts } from "../../artifacts.js";
4
+ import { TASK_SUMMARY_PROMPT_TEMPLATE, formatTemplate } from "../../prompts.js";
5
+ import { toExecutorContext } from "../types.js";
6
+ export const taskSummaryNode = {
7
+ kind: "task-summary",
8
+ version: 1,
9
+ async run(context, params) {
10
+ const outputFile = taskSummaryFile(params.taskKey);
11
+ const prompt = formatTemplate(TASK_SUMMARY_PROMPT_TEMPLATE, {
12
+ jira_task_file: params.jiraTaskFile,
13
+ task_summary_file: outputFile,
14
+ });
15
+ const executor = context.executors.get("claude");
16
+ const value = await executor.execute(toExecutorContext(context), {
17
+ prompt,
18
+ ...(params.model ? { model: params.model } : {}),
19
+ ...(params.claudeCmd ? { command: params.claudeCmd } : {}),
20
+ env: { ...context.env },
21
+ }, executor.defaultConfig);
22
+ requireArtifacts([outputFile], `Claude summary did not produce ${outputFile}.`);
23
+ const artifactText = readFileSync(outputFile, "utf8").trim();
24
+ context.setSummary?.(artifactText);
25
+ return {
26
+ value: {
27
+ ...value,
28
+ artifactText,
29
+ },
30
+ outputs: [{ kind: "artifact", path: outputFile, required: true }],
31
+ };
32
+ },
33
+ checks(_context, params) {
34
+ return [
35
+ {
36
+ kind: "require-artifacts",
37
+ paths: [taskSummaryFile(params.taskKey)],
38
+ message: `Claude summary did not produce ${taskSummaryFile(params.taskKey)}.`,
39
+ },
40
+ ];
41
+ },
42
+ };
@@ -0,0 +1,14 @@
1
+ import { printInfo } from "../../tui.js";
2
+ import { toExecutorContext } from "../types.js";
3
+ export const verifyBuildNode = {
4
+ kind: "verify-build",
5
+ version: 1,
6
+ async run(context, params) {
7
+ printInfo(params.labelText);
8
+ const executor = context.executors.get("verify-build");
9
+ const value = await executor.execute(toExecutorContext(context), {
10
+ dockerComposeFile: params.dockerComposeFile,
11
+ }, executor.defaultConfig);
12
+ return { value };
13
+ },
14
+ };
@@ -0,0 +1,22 @@
1
+ import { IMPLEMENT_PROMPT_TEMPLATE, PLAN_PROMPT_TEMPLATE, REVIEW_FIX_PROMPT_TEMPLATE, REVIEW_PROMPT_TEMPLATE, REVIEW_REPLY_PROMPT_TEMPLATE, REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE, REVIEW_SUMMARY_PROMPT_TEMPLATE, TASK_SUMMARY_PROMPT_TEMPLATE, TEST_FIX_PROMPT_TEMPLATE, TEST_LINTER_FIX_PROMPT_TEMPLATE, } from "../prompts.js";
2
+ const promptTemplates = {
3
+ implement: IMPLEMENT_PROMPT_TEMPLATE,
4
+ plan: PLAN_PROMPT_TEMPLATE,
5
+ review: REVIEW_PROMPT_TEMPLATE,
6
+ "review-fix": REVIEW_FIX_PROMPT_TEMPLATE,
7
+ "review-reply": REVIEW_REPLY_PROMPT_TEMPLATE,
8
+ "review-reply-summary": REVIEW_REPLY_SUMMARY_PROMPT_TEMPLATE,
9
+ "review-summary": REVIEW_SUMMARY_PROMPT_TEMPLATE,
10
+ "task-summary": TASK_SUMMARY_PROMPT_TEMPLATE,
11
+ "test-fix": TEST_FIX_PROMPT_TEMPLATE,
12
+ "test-linter-fix": TEST_LINTER_FIX_PROMPT_TEMPLATE,
13
+ };
14
+ export function isPromptTemplateRef(value) {
15
+ return value in promptTemplates;
16
+ }
17
+ export function getPromptTemplate(ref) {
18
+ return promptTemplates[ref];
19
+ }
20
+ export function promptTemplateRefs() {
21
+ return Object.keys(promptTemplates);
22
+ }
@@ -0,0 +1,18 @@
1
+ import { TaskRunnerError } from "../errors.js";
2
+ import { formatPrompt, formatTemplate } from "../prompts.js";
3
+ import { getPromptTemplate } from "./prompt-registry.js";
4
+ import { resolveValue } from "./value-resolver.js";
5
+ export function renderPrompt(binding, context) {
6
+ const baseTemplate = binding.inlineTemplate ?? (binding.templateRef ? getPromptTemplate(binding.templateRef) : null);
7
+ if (!baseTemplate) {
8
+ throw new TaskRunnerError("Prompt binding must define templateRef or inlineTemplate");
9
+ }
10
+ const vars = Object.fromEntries(Object.entries(binding.vars ?? {}).map(([key, value]) => [key, String(resolveValue(value, context))]));
11
+ const basePrompt = formatTemplate(baseTemplate, vars);
12
+ const resolvedExtraPrompt = binding.extraPrompt ? resolveValue(binding.extraPrompt, context) : null;
13
+ const extraPrompt = resolvedExtraPrompt === null || resolvedExtraPrompt === undefined ? null : String(resolvedExtraPrompt);
14
+ if ((binding.format ?? "task-prompt") === "plain") {
15
+ return basePrompt;
16
+ }
17
+ return formatPrompt(basePrompt, extraPrompt);
18
+ }
@@ -0,0 +1,23 @@
1
+ import { commandCheckExecutor } from "../executors/command-check-executor.js";
2
+ import { claudeExecutor } from "../executors/claude-executor.js";
3
+ import { codexDockerExecutor } from "../executors/codex-docker-executor.js";
4
+ import { codexLocalExecutor } from "../executors/codex-local-executor.js";
5
+ import { jiraFetchExecutor } from "../executors/jira-fetch-executor.js";
6
+ import { processExecutor } from "../executors/process-executor.js";
7
+ import { verifyBuildExecutor } from "../executors/verify-build-executor.js";
8
+ const builtInExecutors = {
9
+ process: processExecutor,
10
+ "command-check": commandCheckExecutor,
11
+ "jira-fetch": jiraFetchExecutor,
12
+ "codex-local": codexLocalExecutor,
13
+ "codex-docker": codexDockerExecutor,
14
+ claude: claudeExecutor,
15
+ "verify-build": verifyBuildExecutor,
16
+ };
17
+ export function createExecutorRegistry() {
18
+ return {
19
+ get(id) {
20
+ return builtInExecutors[id];
21
+ },
22
+ };
23
+ }
@@ -0,0 +1,200 @@
1
+ function interpolateText(template, repeatVars) {
2
+ let result = template;
3
+ for (const [key, value] of Object.entries(repeatVars)) {
4
+ result = result.replaceAll(`\${${key}}`, String(value));
5
+ }
6
+ return result;
7
+ }
8
+ function interpolateValueSpec(value, repeatVars) {
9
+ if ("const" in value || "artifact" in value || "artifactList" in value || "list" in value) {
10
+ if ("artifact" in value) {
11
+ return {
12
+ artifact: {
13
+ ...value.artifact,
14
+ taskKey: interpolateValueSpec(value.artifact.taskKey, repeatVars),
15
+ ...(value.artifact.iteration ? { iteration: interpolateValueSpec(value.artifact.iteration, repeatVars) } : {}),
16
+ },
17
+ };
18
+ }
19
+ if ("artifactList" in value) {
20
+ return {
21
+ artifactList: {
22
+ ...value.artifactList,
23
+ taskKey: interpolateValueSpec(value.artifactList.taskKey, repeatVars),
24
+ },
25
+ };
26
+ }
27
+ if ("list" in value) {
28
+ return {
29
+ list: value.list.map((candidate) => interpolateValueSpec(candidate, repeatVars)),
30
+ };
31
+ }
32
+ return value;
33
+ }
34
+ if ("ref" in value) {
35
+ return {
36
+ ref: interpolateText(value.ref, repeatVars),
37
+ };
38
+ }
39
+ if ("template" in value) {
40
+ return {
41
+ template: interpolateText(value.template, repeatVars),
42
+ ...(value.vars
43
+ ? {
44
+ vars: Object.fromEntries(Object.entries(value.vars).map(([key, candidate]) => [key, interpolateValueSpec(candidate, repeatVars)])),
45
+ }
46
+ : {}),
47
+ };
48
+ }
49
+ if ("appendPrompt" in value) {
50
+ return {
51
+ appendPrompt: {
52
+ ...(value.appendPrompt.base ? { base: interpolateValueSpec(value.appendPrompt.base, repeatVars) } : {}),
53
+ suffix: interpolateValueSpec(value.appendPrompt.suffix, repeatVars),
54
+ },
55
+ };
56
+ }
57
+ if ("concat" in value) {
58
+ return {
59
+ concat: value.concat.map((candidate) => interpolateValueSpec(candidate, repeatVars)),
60
+ };
61
+ }
62
+ return value;
63
+ }
64
+ function interpolateCondition(condition, repeatVars) {
65
+ if (!condition) {
66
+ return undefined;
67
+ }
68
+ if ("ref" in condition) {
69
+ return {
70
+ ref: interpolateText(condition.ref, repeatVars),
71
+ };
72
+ }
73
+ if ("not" in condition) {
74
+ return {
75
+ not: interpolateCondition(condition.not, repeatVars),
76
+ };
77
+ }
78
+ if ("all" in condition) {
79
+ return {
80
+ all: condition.all.map((candidate) => interpolateCondition(candidate, repeatVars)),
81
+ };
82
+ }
83
+ if ("any" in condition) {
84
+ return {
85
+ any: condition.any.map((candidate) => interpolateCondition(candidate, repeatVars)),
86
+ };
87
+ }
88
+ if ("equals" in condition) {
89
+ return {
90
+ equals: [
91
+ interpolateValueSpec(condition.equals[0], repeatVars),
92
+ interpolateValueSpec(condition.equals[1], repeatVars),
93
+ ],
94
+ };
95
+ }
96
+ if ("exists" in condition) {
97
+ return {
98
+ exists: interpolateValueSpec(condition.exists, repeatVars),
99
+ };
100
+ }
101
+ return condition;
102
+ }
103
+ function interpolatePrompt(prompt, repeatVars) {
104
+ return {
105
+ ...(prompt.templateRef ? { templateRef: prompt.templateRef } : {}),
106
+ ...(prompt.inlineTemplate ? { inlineTemplate: interpolateText(prompt.inlineTemplate, repeatVars) } : {}),
107
+ ...(prompt.vars
108
+ ? {
109
+ vars: Object.fromEntries(Object.entries(prompt.vars).map(([key, candidate]) => [key, interpolateValueSpec(candidate, repeatVars)])),
110
+ }
111
+ : {}),
112
+ ...(prompt.extraPrompt ? { extraPrompt: interpolateValueSpec(prompt.extraPrompt, repeatVars) } : {}),
113
+ ...(prompt.format ? { format: prompt.format } : {}),
114
+ };
115
+ }
116
+ function interpolateExpectation(expectation, repeatVars) {
117
+ if (expectation.kind === "require-artifacts") {
118
+ const when = expectation.when ? interpolateCondition(expectation.when, repeatVars) : undefined;
119
+ return {
120
+ kind: expectation.kind,
121
+ ...(when ? { when } : {}),
122
+ paths: interpolateValueSpec(expectation.paths, repeatVars),
123
+ message: interpolateText(expectation.message, repeatVars),
124
+ };
125
+ }
126
+ const when = expectation.when ? interpolateCondition(expectation.when, repeatVars) : undefined;
127
+ if (expectation.kind === "require-file") {
128
+ return {
129
+ kind: expectation.kind,
130
+ ...(when ? { when } : {}),
131
+ path: interpolateValueSpec(expectation.path, repeatVars),
132
+ message: interpolateText(expectation.message, repeatVars),
133
+ };
134
+ }
135
+ return {
136
+ kind: expectation.kind,
137
+ ...(when ? { when } : {}),
138
+ value: interpolateValueSpec(expectation.value, repeatVars),
139
+ ...(expectation.equals ? { equals: interpolateValueSpec(expectation.equals, repeatVars) } : {}),
140
+ message: interpolateText(expectation.message, repeatVars),
141
+ };
142
+ }
143
+ function interpolateAfterAction(action, repeatVars) {
144
+ const when = action.when ? interpolateCondition(action.when, repeatVars) : undefined;
145
+ return {
146
+ kind: action.kind,
147
+ ...(when ? { when } : {}),
148
+ path: interpolateValueSpec(action.path, repeatVars),
149
+ };
150
+ }
151
+ function expandPhase(phase, repeatVars) {
152
+ const phaseWhen = phase.when ? interpolateCondition(phase.when, repeatVars) : undefined;
153
+ return {
154
+ id: interpolateText(phase.id, repeatVars),
155
+ repeatVars,
156
+ ...(phaseWhen ? { when: phaseWhen } : {}),
157
+ steps: phase.steps.map((step) => {
158
+ const stepWhen = step.when ? interpolateCondition(step.when, repeatVars) : undefined;
159
+ const stopFlowIf = step.stopFlowIf ? interpolateCondition(step.stopFlowIf, repeatVars) : undefined;
160
+ return {
161
+ id: interpolateText(step.id, repeatVars),
162
+ node: step.node,
163
+ ...(stepWhen ? { when: stepWhen } : {}),
164
+ ...(step.prompt ? { prompt: interpolatePrompt(step.prompt, repeatVars) } : {}),
165
+ ...(step.params
166
+ ? {
167
+ params: Object.fromEntries(Object.entries(step.params).map(([key, value]) => [key, interpolateValueSpec(value, repeatVars)])),
168
+ }
169
+ : {}),
170
+ ...(step.expect ? { expect: step.expect.map((item) => interpolateExpectation(item, repeatVars)) } : {}),
171
+ ...(stopFlowIf ? { stopFlowIf } : {}),
172
+ ...(step.after ? { after: step.after.map((item) => interpolateAfterAction(item, repeatVars)) } : {}),
173
+ repeatVars,
174
+ };
175
+ }),
176
+ };
177
+ }
178
+ function expandRepeat(block) {
179
+ const phases = [];
180
+ for (let index = block.repeat.from; index <= block.repeat.to; index += 1) {
181
+ const repeatVars = {
182
+ [block.repeat.var]: index,
183
+ };
184
+ for (const phase of block.phases) {
185
+ phases.push(expandPhase(phase, repeatVars));
186
+ }
187
+ }
188
+ return phases;
189
+ }
190
+ export function compileFlowSpec(spec) {
191
+ const phases = [];
192
+ for (const item of spec.phases) {
193
+ if ("repeat" in item) {
194
+ phases.push(...expandRepeat(item));
195
+ continue;
196
+ }
197
+ phases.push(expandPhase(item, {}));
198
+ }
199
+ return phases;
200
+ }
@@ -0,0 +1,14 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { TaskRunnerError } from "../errors.js";
5
+ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
6
+ export function loadFlowSpecSync(fileName) {
7
+ const filePath = path.join(MODULE_DIR, "flow-specs", fileName);
8
+ try {
9
+ return JSON.parse(readFileSync(filePath, "utf8"));
10
+ }
11
+ catch (error) {
12
+ throw new TaskRunnerError(`Failed to load flow spec ${filePath}: ${error.message}`);
13
+ }
14
+ }
@@ -0,0 +1 @@
1
+ export {};