agentweaver 0.1.16 → 0.1.18

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 (74) hide show
  1. package/README.md +148 -27
  2. package/dist/artifacts.js +114 -3
  3. package/dist/doctor/checks/executors.js +2 -2
  4. package/dist/flow-state.js +138 -1
  5. package/dist/index.js +421 -82
  6. package/dist/interactive/controller.js +305 -36
  7. package/dist/interactive/ink/index.js +24 -3
  8. package/dist/interactive/state.js +1 -0
  9. package/dist/interactive/tree.js +2 -2
  10. package/dist/interactive/web/index.js +179 -0
  11. package/dist/interactive/web/protocol.js +154 -0
  12. package/dist/interactive/web/server.js +575 -0
  13. package/dist/interactive/web/static/app.js +709 -0
  14. package/dist/interactive/web/static/index.html +77 -0
  15. package/dist/interactive/web/static/styles.css +2 -0
  16. package/dist/interactive/web/static/styles.input.css +469 -0
  17. package/dist/pipeline/auto-flow.js +9 -6
  18. package/dist/pipeline/context.js +6 -5
  19. package/dist/pipeline/declarative-flows.js +39 -20
  20. package/dist/pipeline/flow-catalog.js +40 -14
  21. package/dist/pipeline/flow-specs/auto-common-guided.json +313 -0
  22. package/dist/pipeline/flow-specs/auto-common.json +4 -1
  23. package/dist/pipeline/flow-specs/auto-golang.json +27 -1
  24. package/dist/pipeline/flow-specs/design-review/design-review-loop.json +15 -1
  25. package/dist/pipeline/flow-specs/design-review.json +2 -0
  26. package/dist/pipeline/flow-specs/implement.json +3 -1
  27. package/dist/pipeline/flow-specs/plan.json +8 -2
  28. package/dist/pipeline/flow-specs/playbook-init.json +199 -0
  29. package/dist/pipeline/flow-specs/review/review-fix.json +3 -1
  30. package/dist/pipeline/flow-specs/review/review-loop.json +4 -0
  31. package/dist/pipeline/flow-specs/review/review.json +2 -0
  32. package/dist/pipeline/launch-profile-config.js +30 -18
  33. package/dist/pipeline/node-contract.js +1 -0
  34. package/dist/pipeline/node-registry.js +119 -5
  35. package/dist/pipeline/nodes/flow-run-node.js +200 -173
  36. package/dist/pipeline/nodes/llm-prompt-node.js +15 -33
  37. package/dist/pipeline/nodes/playbook-ensure-node.js +115 -0
  38. package/dist/pipeline/nodes/playbook-inventory-node.js +51 -0
  39. package/dist/pipeline/nodes/playbook-questions-form-node.js +166 -0
  40. package/dist/pipeline/nodes/playbook-write-node.js +243 -0
  41. package/dist/pipeline/nodes/project-guidance-node.js +69 -0
  42. package/dist/pipeline/plugin-loader.js +389 -0
  43. package/dist/pipeline/plugin-types.js +1 -0
  44. package/dist/pipeline/prompt-registry.js +4 -1
  45. package/dist/pipeline/prompt-runtime.js +6 -2
  46. package/dist/pipeline/registry.js +71 -4
  47. package/dist/pipeline/spec-compiler.js +1 -0
  48. package/dist/pipeline/spec-loader.js +14 -0
  49. package/dist/pipeline/spec-types.js +19 -0
  50. package/dist/pipeline/spec-validator.js +6 -0
  51. package/dist/pipeline/value-resolver.js +41 -2
  52. package/dist/playbook/practice-candidates.js +12 -0
  53. package/dist/playbook/repo-inventory.js +208 -0
  54. package/dist/plugin-sdk.js +1 -0
  55. package/dist/prompts.js +31 -0
  56. package/dist/runtime/artifact-registry.js +3 -0
  57. package/dist/runtime/execution-routing.js +25 -19
  58. package/dist/runtime/interactive-execution-routing.js +66 -57
  59. package/dist/runtime/playbook.js +485 -0
  60. package/dist/runtime/project-guidance.js +339 -0
  61. package/dist/structured-artifact-schema-registry.js +8 -0
  62. package/dist/structured-artifact-schemas.json +235 -0
  63. package/dist/structured-artifacts.js +7 -1
  64. package/docs/declarative-workflows.md +565 -0
  65. package/docs/example/.flows/examples/claude-example.json +50 -0
  66. package/docs/example/.plugins/claude-example-plugin/index.js +149 -0
  67. package/docs/example/.plugins/claude-example-plugin/plugin.json +8 -0
  68. package/docs/examples/.flows/claude-example.json +50 -0
  69. package/docs/examples/.plugins/claude-example-plugin/index.js +149 -0
  70. package/docs/examples/.plugins/claude-example-plugin/plugin.json +8 -0
  71. package/docs/features.md +77 -0
  72. package/docs/playbook.md +327 -0
  73. package/docs/plugin-sdk.md +731 -0
  74. package/package.json +13 -4
@@ -1,27 +1,31 @@
1
1
  import path from "node:path";
2
- import { createNodeRegistry } from "./node-registry.js";
3
- import { createExecutorRegistry } from "./registry.js";
2
+ import { createPipelineRegistryContext } from "./plugin-loader.js";
4
3
  import { compileFlowSpec } from "./spec-compiler.js";
5
- import { listBuiltInFlowSpecFiles, listProjectFlowSpecFiles, loadFlowSpecSync, projectFlowSpecsDir, resolveBuiltInFlowSpecPath } from "./spec-loader.js";
4
+ import { globalFlowSpecsDir, listBuiltInFlowSpecFiles, listGlobalFlowSpecFiles, listProjectFlowSpecFiles, loadFlowSpecSync, projectFlowSpecsDir, resolveBuiltInFlowSpecPath, } from "./spec-loader.js";
6
5
  import { validateExpandedPhases, validateFlowSpec } from "./spec-validator.js";
7
6
  const cache = new Map();
8
7
  function toFlowSpecSource(ref) {
9
- return ref.source === "built-in" ? { source: "built-in", fileName: ref.fileName } : { source: "project-local", filePath: ref.filePath };
8
+ return ref.source === "built-in"
9
+ ? { source: "built-in", fileName: ref.fileName }
10
+ : { source: ref.source, filePath: ref.filePath };
10
11
  }
11
- function cacheKey(ref) {
12
- return ref.source === "built-in" ? `built-in:${ref.fileName}` : `project-local:${path.resolve(ref.filePath)}`;
12
+ function cacheKey(ref, registryContext) {
13
+ const flowKey = ref.source === "built-in"
14
+ ? `built-in:${ref.fileName}`
15
+ : `${ref.source}:${path.resolve(ref.filePath)}`;
16
+ return `${registryContext.cacheKey}:${flowKey}`;
13
17
  }
14
- export function loadDeclarativeFlow(flow) {
18
+ export async function loadDeclarativeFlow(flow, options = {}) {
15
19
  const ref = typeof flow === "string" ? { source: "built-in", fileName: flow } : flow;
16
- const cached = cache.get(cacheKey(ref));
20
+ const cwd = path.resolve(options.cwd ?? options.registryContext?.cwd ?? process.cwd());
21
+ const registryContext = options.registryContext ?? await createPipelineRegistryContext(cwd);
22
+ const cached = cache.get(cacheKey(ref, registryContext));
17
23
  if (cached) {
18
24
  return cached;
19
25
  }
20
26
  const spec = loadFlowSpecSync(toFlowSpecSource(ref));
21
- const nodeRegistry = createNodeRegistry();
22
- const executorRegistry = createExecutorRegistry();
23
- validateFlowSpec(spec, nodeRegistry, executorRegistry, {
24
- resolveFlowByName: (fileName) => resolveNamedDeclarativeFlowRef(fileName, process.cwd()),
27
+ validateFlowSpec(spec, registryContext.nodes, registryContext.executors, {
28
+ resolveFlowByName: (fileName) => resolveNamedDeclarativeFlowRef(fileName, cwd),
25
29
  });
26
30
  const phases = compileFlowSpec(spec);
27
31
  validateExpandedPhases(phases);
@@ -36,33 +40,48 @@ export function loadDeclarativeFlow(flow) {
36
40
  fileName: ref.source === "built-in" ? ref.fileName : path.basename(ref.filePath),
37
41
  absolutePath: ref.source === "built-in" ? resolveBuiltInFlowSpecPath(ref.fileName) : path.resolve(ref.filePath),
38
42
  };
39
- cache.set(cacheKey(ref), loaded);
43
+ cache.set(cacheKey(ref, registryContext), loaded);
40
44
  return loaded;
41
45
  }
42
46
  export function resolveNamedDeclarativeFlowRef(fileName, cwd) {
43
47
  const projectMatches = listProjectFlowSpecFiles(cwd).filter((candidate) => path.basename(candidate) === fileName);
48
+ const globalMatches = listGlobalFlowSpecFiles().filter((candidate) => path.basename(candidate) === fileName);
44
49
  const builtInMatches = listBuiltInFlowSpecFiles().filter((candidate) => path.basename(candidate) === fileName);
45
- if (projectMatches.length > 0 && builtInMatches.length > 0) {
46
- throw new Error(`Ambiguous nested flow '${fileName}': both built-in and project-local specs exist in ${projectFlowSpecsDir(cwd)}.`);
50
+ const matches = [
51
+ ...builtInMatches.map((candidate) => ({ source: "built-in", candidate })),
52
+ ...globalMatches.map((candidate) => ({ source: "global", candidate })),
53
+ ...projectMatches.map((candidate) => ({ source: "project-local", candidate })),
54
+ ];
55
+ if (matches.length > 1) {
56
+ throw new Error(`Ambiguous nested flow '${fileName}': matches exist in built-in flows, ${globalFlowSpecsDir()}, or ${projectFlowSpecsDir(cwd)}. Use unique nested flow file names.`);
47
57
  }
48
58
  if (projectMatches.length > 1) {
49
59
  throw new Error(`Ambiguous project-local flow '${fileName}' in ${projectFlowSpecsDir(cwd)}.`);
50
60
  }
61
+ if (globalMatches.length > 1) {
62
+ throw new Error(`Ambiguous global flow '${fileName}' in ${globalFlowSpecsDir()}.`);
63
+ }
51
64
  if (builtInMatches.length > 1) {
52
65
  throw new Error(`Ambiguous built-in flow '${fileName}'. Use unique nested flow file names.`);
53
66
  }
54
67
  if (projectMatches[0]) {
55
68
  return { source: "project-local", filePath: projectMatches[0] };
56
69
  }
70
+ if (globalMatches[0]) {
71
+ return { source: "global", filePath: globalMatches[0] };
72
+ }
57
73
  if (builtInMatches[0]) {
58
74
  return { source: "built-in", fileName: builtInMatches[0] };
59
75
  }
60
76
  throw new Error(`Nested flow '${fileName}' was not found.`);
61
77
  }
62
- export function loadNamedDeclarativeFlow(fileName, cwd) {
63
- return loadDeclarativeFlow(resolveNamedDeclarativeFlowRef(fileName, cwd));
78
+ export async function loadNamedDeclarativeFlow(fileName, cwd, options = {}) {
79
+ return loadDeclarativeFlow(resolveNamedDeclarativeFlowRef(fileName, cwd), {
80
+ cwd,
81
+ ...(options.registryContext ? { registryContext: options.registryContext } : {}),
82
+ });
64
83
  }
65
- export function collectFlowRoutingGroups(flow, cwd, visited = new Set()) {
84
+ export async function collectFlowRoutingGroups(flow, cwd, visited = new Set(), options = {}) {
66
85
  if (visited.has(flow.absolutePath)) {
67
86
  return [];
68
87
  }
@@ -80,8 +99,8 @@ export function collectFlowRoutingGroups(flow, cwd, visited = new Set()) {
80
99
  if (!nestedFlowName || !("const" in nestedFlowName) || typeof nestedFlowName.const !== "string") {
81
100
  continue;
82
101
  }
83
- const nestedFlow = loadNamedDeclarativeFlow(nestedFlowName.const, cwd);
84
- for (const nestedGroup of collectFlowRoutingGroups(nestedFlow, cwd, visited)) {
102
+ const nestedFlow = await loadNamedDeclarativeFlow(nestedFlowName.const, cwd, options);
103
+ for (const nestedGroup of await collectFlowRoutingGroups(nestedFlow, cwd, visited, options)) {
85
104
  groups.add(nestedGroup);
86
105
  }
87
106
  }
@@ -2,9 +2,10 @@ import path from "node:path";
2
2
  import { TaskRunnerError } from "../errors.js";
3
3
  import { loadAutoGolangFlow } from "./auto-flow.js";
4
4
  import { collectFlowRoutingGroups, loadDeclarativeFlow } from "./declarative-flows.js";
5
- import { listBuiltInFlowSpecFiles, listProjectFlowSpecFiles, projectFlowSpecsDir } from "./spec-loader.js";
5
+ import { globalFlowSpecsDir, listBuiltInFlowSpecFiles, listGlobalFlowSpecFiles, listProjectFlowSpecFiles, projectFlowSpecsDir } from "./spec-loader.js";
6
6
  export const BUILT_IN_COMMAND_FLOW_IDS = [
7
7
  "auto-golang",
8
+ "auto-common-guided",
8
9
  "auto-common",
9
10
  "auto-simple",
10
11
  "bug-analyze",
@@ -17,6 +18,7 @@ export const BUILT_IN_COMMAND_FLOW_IDS = [
17
18
  "mr-description",
18
19
  "plan",
19
20
  "plan-revise",
21
+ "playbook-init",
20
22
  "task-describe",
21
23
  "implement",
22
24
  "review",
@@ -27,6 +29,7 @@ export const BUILT_IN_COMMAND_FLOW_IDS = [
27
29
  ];
28
30
  const BUILT_IN_COMMAND_FLOW_FILES = {
29
31
  "auto-golang": "auto-golang.json",
32
+ "auto-common-guided": "auto-common-guided.json",
30
33
  "auto-common": "auto-common.json",
31
34
  "auto-simple": "auto-simple.json",
32
35
  "bug-analyze": "bugz/bug-analyze.json",
@@ -39,6 +42,7 @@ const BUILT_IN_COMMAND_FLOW_FILES = {
39
42
  "mr-description": "gitlab/mr-description.json",
40
43
  plan: "plan.json",
41
44
  "plan-revise": "plan-revise.json",
45
+ "playbook-init": "playbook-init.json",
42
46
  "task-describe": "task-describe.json",
43
47
  implement: "implement.json",
44
48
  review: "review/review.json",
@@ -58,11 +62,13 @@ function builtInCommandIdForFile(fileName) {
58
62
  }
59
63
  return null;
60
64
  }
61
- function loadBuiltInCatalogEntry(fileName) {
65
+ async function loadBuiltInCatalogEntry(fileName, options) {
62
66
  const commandId = builtInCommandIdForFile(fileName);
63
67
  const relativePath = fileName.replace(/\.json$/i, "").split(/[\\/]+/).filter((segment) => segment.length > 0);
64
68
  const id = commandId ?? relativePath.join("/");
65
- const flow = id === "auto-golang" ? loadAutoGolangFlow() : loadDeclarativeFlow({ source: "built-in", fileName });
69
+ const flow = id === "auto-golang"
70
+ ? await loadAutoGolangFlow(options)
71
+ : await loadDeclarativeFlow({ source: "built-in", fileName }, options);
66
72
  return {
67
73
  id,
68
74
  source: "built-in",
@@ -72,8 +78,8 @@ function loadBuiltInCatalogEntry(fileName) {
72
78
  flow,
73
79
  };
74
80
  }
75
- function loadProjectCatalogEntry(cwd, filePath) {
76
- const flow = loadDeclarativeFlow({ source: "project-local", filePath });
81
+ async function loadProjectCatalogEntry(cwd, filePath, options) {
82
+ const flow = await loadDeclarativeFlow({ source: "project-local", filePath }, { ...options, cwd });
77
83
  const relativeFilePath = path.relative(projectFlowSpecsDir(cwd), path.resolve(filePath));
78
84
  const relativePathWithoutExt = relativeFilePath.replace(/\.json$/i, "");
79
85
  const relativeSegments = relativePathWithoutExt.split(path.sep).filter((segment) => segment.length > 0);
@@ -86,10 +92,30 @@ function loadProjectCatalogEntry(cwd, filePath) {
86
92
  flow,
87
93
  };
88
94
  }
89
- export function loadInteractiveFlowCatalog(cwd) {
90
- const entries = listBuiltInFlowSpecFiles().map((fileName) => loadBuiltInCatalogEntry(fileName));
95
+ async function loadGlobalCatalogEntry(filePath, options) {
96
+ const flow = await loadDeclarativeFlow({ source: "global", filePath }, options);
97
+ const relativeFilePath = path.relative(globalFlowSpecsDir(), path.resolve(filePath));
98
+ const relativePathWithoutExt = relativeFilePath.replace(/\.json$/i, "");
99
+ const relativeSegments = relativePathWithoutExt.split(path.sep).filter((segment) => segment.length > 0);
100
+ return {
101
+ id: relativeSegments.join("/"),
102
+ source: "global",
103
+ fileName: path.basename(filePath),
104
+ absolutePath: path.resolve(filePath),
105
+ treePath: ["global", ...relativeSegments],
106
+ flow,
107
+ };
108
+ }
109
+ export async function loadInteractiveFlowCatalog(cwd, options = {}) {
110
+ const entries = [];
111
+ for (const fileName of listBuiltInFlowSpecFiles()) {
112
+ entries.push(await loadBuiltInCatalogEntry(fileName, { ...options, cwd }));
113
+ }
114
+ for (const filePath of listGlobalFlowSpecFiles()) {
115
+ entries.push(await loadGlobalCatalogEntry(filePath, { ...options, cwd }));
116
+ }
91
117
  for (const filePath of listProjectFlowSpecFiles(cwd)) {
92
- entries.push(loadProjectCatalogEntry(cwd, filePath));
118
+ entries.push(await loadProjectCatalogEntry(cwd, filePath, { ...options, cwd }));
93
119
  }
94
120
  const visibleEntries = entries.filter((entry) => entry.flow.catalogVisibility !== "hidden");
95
121
  const byId = new Map();
@@ -111,13 +137,13 @@ export function isBuiltInCommandFlowId(flowId) {
111
137
  export function toDeclarativeFlowRef(entry) {
112
138
  return entry.source === "built-in"
113
139
  ? { source: "built-in", fileName: entry.fileName }
114
- : { source: "project-local", filePath: entry.absolutePath };
140
+ : { source: entry.source, filePath: entry.absolutePath };
115
141
  }
116
142
  export function flowRoutingKey(entry) {
117
- return entry.source === "project-local"
118
- ? `project-local:${entry.absolutePath}`
119
- : `built-in:${entry.id}`;
143
+ return entry.source === "built-in"
144
+ ? `built-in:${entry.id}`
145
+ : `${entry.source}:${entry.absolutePath}`;
120
146
  }
121
- export function flowRoutingGroups(entry, cwd) {
122
- return collectFlowRoutingGroups(entry.flow, cwd);
147
+ export async function flowRoutingGroups(entry, cwd, options = {}) {
148
+ return collectFlowRoutingGroups(entry.flow, cwd, new Set(), options);
123
149
  }
@@ -0,0 +1,313 @@
1
+ {
2
+ "kind": "auto-common-guided-flow",
3
+ "version": 1,
4
+ "description": "End-to-end auto-common pipeline with compact manifest.yaml project guidance generated before guided LLM phases.",
5
+ "phases": [
6
+ {
7
+ "id": "source",
8
+ "steps": [
9
+ {
10
+ "id": "fetch_jira_source",
11
+ "node": "flow-run",
12
+ "params": {
13
+ "fileName": { "const": "jira-fetch.json" },
14
+ "labelText": { "const": "Fetching Jira task source" },
15
+ "jiraApiUrl": { "ref": "params.jiraApiUrl" },
16
+ "taskKey": { "ref": "params.taskKey" }
17
+ }
18
+ }
19
+ ]
20
+ },
21
+ {
22
+ "id": "normalize",
23
+ "steps": [
24
+ {
25
+ "id": "run_normalize_source",
26
+ "node": "flow-run",
27
+ "params": {
28
+ "fileName": { "const": "normalize-task-source.json" },
29
+ "labelText": { "const": "Normalizing task source" },
30
+ "taskKey": { "ref": "params.taskKey" },
31
+ "iteration": { "ref": "params.taskContextIteration" },
32
+ "llmExecutor": { "ref": "params.llmExecutor" },
33
+ "llmModel": { "ref": "params.llmModel" },
34
+ "extraPrompt": { "ref": "params.extraPrompt" }
35
+ }
36
+ }
37
+ ]
38
+ },
39
+ {
40
+ "id": "playbook",
41
+ "steps": [
42
+ {
43
+ "id": "ensure_playbook",
44
+ "node": "playbook-ensure",
45
+ "params": {
46
+ "writeResultJsonFile": { "artifact": { "kind": "playbook-write-result-json-file", "taskKey": { "ref": "params.taskKey" } } },
47
+ "acceptPlaybookDraft": { "ref": "params.acceptPlaybookDraft" }
48
+ },
49
+ "stopFlowIf": {
50
+ "any": [
51
+ { "equals": [{ "ref": "steps.playbook.ensure_playbook.value.status" }, { "const": "blocked" }] },
52
+ { "equals": [{ "ref": "steps.playbook.ensure_playbook.value.status" }, { "const": "invalid_manifest" }] },
53
+ { "equals": [{ "ref": "steps.playbook.ensure_playbook.value.status" }, { "const": "partial_manifest" }] },
54
+ { "equals": [{ "ref": "steps.playbook.ensure_playbook.value.status" }, { "const": "not_accepted" }] },
55
+ { "equals": [{ "ref": "steps.playbook.ensure_playbook.value.status" }, { "const": "failed" }] }
56
+ ]
57
+ },
58
+ "stopFlowOutcome": "stopped"
59
+ },
60
+ {
61
+ "id": "run_playbook_init",
62
+ "node": "flow-run",
63
+ "when": { "ref": "steps.playbook.ensure_playbook.value.shouldRunPlaybookInit" },
64
+ "params": {
65
+ "fileName": { "const": "playbook-init.json" },
66
+ "labelText": { "const": "Initializing manifest-based project playbook" },
67
+ "taskKey": { "ref": "params.taskKey" },
68
+ "taskContextJsonFile": { "artifact": { "kind": "task-context-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.taskContextIteration" } } },
69
+ "markdownLanguage": { "ref": "params.mdLang" },
70
+ "iteration": { "ref": "params.planIteration" },
71
+ "launchMode": { "ref": "params.launchMode" },
72
+ "targetManifestPath": { "ref": "steps.playbook.ensure_playbook.value.manifestPath" },
73
+ "acceptPlaybookDraft": { "ref": "params.acceptPlaybookDraft" },
74
+ "llmExecutor": { "ref": "params.llmExecutor" },
75
+ "llmModel": { "ref": "params.llmModel" },
76
+ "extraPrompt": { "ref": "params.extraPrompt" }
77
+ }
78
+ },
79
+ {
80
+ "id": "verify_playbook",
81
+ "node": "playbook-ensure",
82
+ "when": { "ref": "steps.playbook.ensure_playbook.value.shouldRunPlaybookInit" },
83
+ "params": {
84
+ "writeResultJsonFile": { "artifact": { "kind": "playbook-write-result-json-file", "taskKey": { "ref": "params.taskKey" } } },
85
+ "acceptPlaybookDraft": { "ref": "params.acceptPlaybookDraft" },
86
+ "verifyAfterInit": { "const": true }
87
+ },
88
+ "stopFlowIf": {
89
+ "not": {
90
+ "any": [
91
+ { "equals": [{ "ref": "steps.playbook.verify_playbook.value.status" }, { "const": "written" }] },
92
+ { "equals": [{ "ref": "steps.playbook.verify_playbook.value.status" }, { "const": "skipped_valid_existing" }] },
93
+ { "equals": [{ "ref": "steps.playbook.verify_playbook.value.status" }, { "const": "dry_run_written" }] }
94
+ ]
95
+ }
96
+ },
97
+ "stopFlowOutcome": "stopped"
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ "id": "plan_guidance",
103
+ "steps": [
104
+ {
105
+ "id": "write_plan_guidance",
106
+ "node": "project-guidance",
107
+ "params": {
108
+ "taskContextJsonFile": { "artifact": { "kind": "task-context-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.taskContextIteration" } } },
109
+ "phase": { "const": "plan" },
110
+ "outputJsonFile": { "artifact": { "kind": "project-guidance-plan-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
111
+ "outputFile": { "artifact": { "kind": "project-guidance-plan-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
112
+ "markdownLanguage": { "ref": "params.mdLang" }
113
+ }
114
+ }
115
+ ]
116
+ },
117
+ {
118
+ "id": "plan",
119
+ "steps": [
120
+ {
121
+ "id": "run_plan_flow",
122
+ "node": "flow-run",
123
+ "params": {
124
+ "fileName": { "const": "plan.json" },
125
+ "labelText": { "const": "Running planning flow" },
126
+ "taskKey": { "ref": "params.taskKey" },
127
+ "taskContextIteration": { "ref": "params.taskContextIteration" },
128
+ "designIteration": { "ref": "params.designIteration" },
129
+ "planIteration": { "ref": "params.planIteration" },
130
+ "qaIteration": { "ref": "params.qaIteration" },
131
+ "projectGuidanceFile": { "ref": "steps.plan_guidance.write_plan_guidance.value.outputFile" },
132
+ "projectGuidanceJsonFile": { "ref": "steps.plan_guidance.write_plan_guidance.value.outputJsonFile" },
133
+ "llmExecutor": { "ref": "params.llmExecutor" },
134
+ "llmModel": { "ref": "params.llmModel" },
135
+ "extraPrompt": { "ref": "params.extraPrompt" },
136
+ "mdLang": { "ref": "params.mdLang" }
137
+ }
138
+ }
139
+ ]
140
+ },
141
+ {
142
+ "id": "design_review_guidance",
143
+ "steps": [
144
+ {
145
+ "id": "write_design_review_guidance",
146
+ "node": "project-guidance",
147
+ "params": {
148
+ "taskContextJsonFile": { "artifact": { "kind": "task-context-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.taskContextIteration" } } },
149
+ "phase": { "const": "design-review" },
150
+ "outputJsonFile": { "artifact": { "kind": "project-guidance-design-review-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
151
+ "outputFile": { "artifact": { "kind": "project-guidance-design-review-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
152
+ "markdownLanguage": { "ref": "params.mdLang" }
153
+ }
154
+ }
155
+ ]
156
+ },
157
+ {
158
+ "id": "design_review_loop",
159
+ "steps": [
160
+ {
161
+ "id": "run_design_review_loop",
162
+ "node": "flow-run",
163
+ "params": {
164
+ "fileName": { "const": "design-review-loop.json" },
165
+ "labelText": { "const": "Running design-review loop" },
166
+ "taskKey": { "ref": "params.taskKey" },
167
+ "baseIteration": { "ref": "params.designReviewBaseIteration" },
168
+ "workspaceDir": { "ref": "params.workspaceDir" },
169
+ "extraPrompt": { "ref": "params.extraPrompt" },
170
+ "projectGuidanceFile": { "ref": "steps.design_review_guidance.write_design_review_guidance.value.outputFile" },
171
+ "projectGuidanceJsonFile": { "ref": "steps.design_review_guidance.write_design_review_guidance.value.outputJsonFile" },
172
+ "llmExecutor": { "ref": "params.llmExecutor" },
173
+ "llmModel": { "ref": "params.llmModel" }
174
+ },
175
+ "stopFlowIf": {
176
+ "equals": [
177
+ { "ref": "steps.design_review_loop.run_design_review_loop.value.executionState.terminationOutcome" },
178
+ { "const": "stopped" }
179
+ ]
180
+ },
181
+ "stopFlowOutcome": "stopped"
182
+ }
183
+ ]
184
+ },
185
+ {
186
+ "id": "implement_guidance",
187
+ "steps": [
188
+ {
189
+ "id": "write_implement_guidance",
190
+ "node": "project-guidance",
191
+ "params": {
192
+ "taskContextJsonFile": { "artifact": { "kind": "task-context-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.taskContextIteration" } } },
193
+ "phase": { "const": "implement" },
194
+ "outputJsonFile": { "artifact": { "kind": "project-guidance-implement-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
195
+ "outputFile": { "artifact": { "kind": "project-guidance-implement-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
196
+ "markdownLanguage": { "ref": "params.mdLang" }
197
+ }
198
+ }
199
+ ]
200
+ },
201
+ {
202
+ "id": "implement",
203
+ "steps": [
204
+ {
205
+ "id": "resolve_planning_bundle",
206
+ "node": "planning-bundle",
207
+ "params": { "taskKey": { "ref": "params.taskKey" } }
208
+ },
209
+ {
210
+ "id": "run_implement",
211
+ "node": "llm-prompt",
212
+ "routingGroup": "implementation",
213
+ "prompt": {
214
+ "templateRef": "implement",
215
+ "vars": {
216
+ "design_file": { "ref": "steps.implement.resolve_planning_bundle.value.designFile" },
217
+ "design_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.designJsonFile" },
218
+ "plan_file": { "ref": "steps.implement.resolve_planning_bundle.value.planFile" },
219
+ "plan_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.planJsonFile" },
220
+ "qa_file": { "ref": "steps.implement.resolve_planning_bundle.value.qaFile" },
221
+ "qa_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.qaJsonFile" },
222
+ "project_guidance_file": { "ref": "steps.implement_guidance.write_implement_guidance.value.outputFile" },
223
+ "project_guidance_json_file": { "ref": "steps.implement_guidance.write_implement_guidance.value.outputJsonFile" }
224
+ },
225
+ "extraPrompt": { "ref": "params.extraPrompt" },
226
+ "format": "task-prompt"
227
+ },
228
+ "params": {
229
+ "labelText": { "const": "Running implementation mode locally" },
230
+ "model": { "ref": "params.llmModel" },
231
+ "executor": { "ref": "params.llmExecutor" }
232
+ }
233
+ },
234
+ {
235
+ "id": "notify_implement_complete",
236
+ "node": "telegram-notify",
237
+ "params": { "message": { "template": "Implementation phase for {taskKey} complete.", "vars": { "taskKey": { "ref": "params.taskKey" } } } }
238
+ }
239
+ ]
240
+ },
241
+ {
242
+ "id": "review_guidance",
243
+ "steps": [
244
+ {
245
+ "id": "write_review_guidance",
246
+ "node": "project-guidance",
247
+ "params": {
248
+ "taskContextJsonFile": { "artifact": { "kind": "task-context-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.taskContextIteration" } } },
249
+ "phase": { "const": "review" },
250
+ "outputJsonFile": { "artifact": { "kind": "project-guidance-review-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
251
+ "outputFile": { "artifact": { "kind": "project-guidance-review-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
252
+ "markdownLanguage": { "ref": "params.mdLang" }
253
+ }
254
+ }
255
+ ]
256
+ },
257
+ {
258
+ "id": "repair_guidance",
259
+ "steps": [
260
+ {
261
+ "id": "write_repair_guidance",
262
+ "node": "project-guidance",
263
+ "params": {
264
+ "taskContextJsonFile": { "artifact": { "kind": "task-context-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.taskContextIteration" } } },
265
+ "phase": { "const": "repair/review-fix" },
266
+ "outputJsonFile": { "artifact": { "kind": "project-guidance-repair-review-fix-json-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
267
+ "outputFile": { "artifact": { "kind": "project-guidance-repair-review-fix-file", "taskKey": { "ref": "params.taskKey" }, "iteration": { "ref": "params.planIteration" } } },
268
+ "markdownLanguage": { "ref": "params.mdLang" }
269
+ }
270
+ }
271
+ ]
272
+ },
273
+ {
274
+ "id": "review-loop",
275
+ "steps": [
276
+ {
277
+ "id": "run_review_loop",
278
+ "node": "flow-run",
279
+ "stopFlowIf": {
280
+ "not": {
281
+ "equals": [
282
+ { "ref": "steps.review-loop.run_review_loop.value.executionState.terminationOutcome" },
283
+ { "const": "success" }
284
+ ]
285
+ }
286
+ },
287
+ "stopFlowOutcome": "stopped",
288
+ "params": {
289
+ "fileName": { "const": "review-loop.json" },
290
+ "labelText": { "const": "Running review-loop" },
291
+ "taskKey": { "ref": "params.taskKey" },
292
+ "baseIteration": { "ref": "params.baseIteration" },
293
+ "workspaceDir": { "ref": "params.workspaceDir" },
294
+ "extraPrompt": { "ref": "params.extraPrompt" },
295
+ "reviewFixPoints": { "ref": "params.reviewFixPoints" },
296
+ "reviewBlockingSeverities": { "ref": "params.reviewBlockingSeverities" },
297
+ "projectGuidanceFile": { "ref": "steps.review_guidance.write_review_guidance.value.outputFile" },
298
+ "projectGuidanceJsonFile": { "ref": "steps.review_guidance.write_review_guidance.value.outputJsonFile" },
299
+ "repairProjectGuidanceFile": { "ref": "steps.repair_guidance.write_repair_guidance.value.outputFile" },
300
+ "repairProjectGuidanceJsonFile": { "ref": "steps.repair_guidance.write_repair_guidance.value.outputJsonFile" },
301
+ "llmExecutor": { "ref": "params.llmExecutor" },
302
+ "llmModel": { "ref": "params.llmModel" }
303
+ }
304
+ },
305
+ {
306
+ "id": "notify_task_complete",
307
+ "node": "telegram-notify",
308
+ "params": { "message": { "template": "Task {taskKey} complete.", "vars": { "taskKey": { "ref": "params.taskKey" } } } }
309
+ }
310
+ ]
311
+ }
312
+ ]
313
+ }
@@ -68,6 +68,7 @@
68
68
  "fileName": { "const": "design-review-loop.json" },
69
69
  "labelText": { "const": "Running design-review loop" },
70
70
  "taskKey": { "ref": "params.taskKey" },
71
+ "baseIteration": { "ref": "params.designReviewBaseIteration" },
71
72
  "workspaceDir": { "ref": "params.workspaceDir" },
72
73
  "extraPrompt": { "ref": "params.extraPrompt" },
73
74
  "llmExecutor": { "ref": "params.llmExecutor" },
@@ -105,7 +106,9 @@
105
106
  "plan_file": { "ref": "steps.implement.resolve_planning_bundle.value.planFile" },
106
107
  "plan_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.planJsonFile" },
107
108
  "qa_file": { "ref": "steps.implement.resolve_planning_bundle.value.qaFile" },
108
- "qa_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.qaJsonFile" }
109
+ "qa_json_file": { "ref": "steps.implement.resolve_planning_bundle.value.qaJsonFile" },
110
+ "project_guidance_file": { "const": "not provided" },
111
+ "project_guidance_json_file": { "const": "not provided" }
109
112
  },
110
113
  "extraPrompt": { "ref": "params.extraPrompt" },
111
114
  "format": "task-prompt"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "kind": "auto-flow",
3
3
  "version": 1,
4
- "description": "End-to-end resumable pipeline for Go projects. Runs the full sequence: Jira fetch → task source normalization → plan → implement → linter loop → test loop → review loop → final linter loop → final test loop. Supports --from to restart from a specific phase and auto-status/auto-reset for state management.",
4
+ "description": "End-to-end resumable pipeline for Go projects. Runs the full sequence: Jira fetch → task source normalization → plan → design-review loop → implement → linter loop → test loop → review loop → final linter loop → final test loop. Supports --from to restart from a specific phase and auto-status/auto-reset for state management.",
5
5
  "constants": {
6
6
  "autoReviewFixExtraPrompt": "Fix only blockers, criticals, and important findings"
7
7
  },
@@ -61,6 +61,32 @@
61
61
  }
62
62
  ]
63
63
  },
64
+ {
65
+ "id": "design_review_loop",
66
+ "steps": [
67
+ {
68
+ "id": "run_design_review_loop",
69
+ "node": "flow-run",
70
+ "params": {
71
+ "fileName": { "const": "design-review-loop.json" },
72
+ "labelText": { "const": "Running design-review loop" },
73
+ "taskKey": { "ref": "params.taskKey" },
74
+ "baseIteration": { "ref": "params.designReviewBaseIteration" },
75
+ "workspaceDir": { "ref": "params.workspaceDir" },
76
+ "extraPrompt": { "ref": "params.extraPrompt" },
77
+ "llmExecutor": { "ref": "params.llmExecutor" },
78
+ "llmModel": { "ref": "params.llmModel" }
79
+ },
80
+ "stopFlowIf": {
81
+ "equals": [
82
+ { "ref": "steps.design_review_loop.run_design_review_loop.value.executionState.terminationOutcome" },
83
+ { "const": "stopped" }
84
+ ]
85
+ },
86
+ "stopFlowOutcome": "stopped"
87
+ }
88
+ ]
89
+ },
64
90
  {
65
91
  "id": "implement",
66
92
  "steps": [
@@ -23,8 +23,16 @@
23
23
  "template": "Running design review (iteration ${iteration})"
24
24
  },
25
25
  "taskKey": { "ref": "params.taskKey" },
26
+ "iteration": {
27
+ "add": [
28
+ { "ref": "params.baseIteration" },
29
+ { "add": [{ "ref": "repeat.iteration" }, { "const": -1 }] }
30
+ ]
31
+ },
26
32
  "workspaceDir": { "ref": "params.workspaceDir" },
27
33
  "extraPrompt": { "ref": "params.extraPrompt" },
34
+ "projectGuidanceFile": { "ref": "params.projectGuidanceFile" },
35
+ "projectGuidanceJsonFile": { "ref": "params.projectGuidanceJsonFile" },
28
36
  "llmExecutor": { "ref": "params.llmExecutor" },
29
37
  "llmModel": { "ref": "params.llmModel" }
30
38
  }
@@ -33,7 +41,13 @@
33
41
  "id": "check_design_review_verdict",
34
42
  "node": "design-review-verdict",
35
43
  "params": {
36
- "taskKey": { "ref": "params.taskKey" }
44
+ "taskKey": { "ref": "params.taskKey" },
45
+ "iteration": {
46
+ "add": [
47
+ { "ref": "params.baseIteration" },
48
+ { "add": [{ "ref": "repeat.iteration" }, { "const": -1 }] }
49
+ ]
50
+ }
37
51
  }
38
52
  },
39
53
  {
@@ -134,6 +134,8 @@
134
134
  "plan_json_file": { "ref": "params.planJsonFile" },
135
135
  "qa_file": { "ref": "params.qaFile" },
136
136
  "qa_json_file": { "ref": "params.qaJsonFile" },
137
+ "project_guidance_file": { "ref": "params.projectGuidanceFile" },
138
+ "project_guidance_json_file": { "ref": "params.projectGuidanceJsonFile" },
137
139
  "review_file": {
138
140
  "artifact": {
139
141
  "kind": "design-review-file",