@caseyharalson/orrery 0.7.1
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/.devcontainer.example/Dockerfile +149 -0
- package/.devcontainer.example/devcontainer.json +61 -0
- package/.devcontainer.example/init-firewall.sh +175 -0
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/agent/skills/discovery/SKILL.md +428 -0
- package/agent/skills/discovery/schemas/plan-schema.yaml +138 -0
- package/agent/skills/orrery-execute/SKILL.md +107 -0
- package/agent/skills/orrery-report/SKILL.md +119 -0
- package/agent/skills/orrery-review/SKILL.md +105 -0
- package/agent/skills/orrery-verify/SKILL.md +105 -0
- package/agent/skills/refine-plan/SKILL.md +291 -0
- package/agent/skills/simulate-plan/SKILL.md +244 -0
- package/bin/orrery.js +5 -0
- package/lib/cli/commands/help.js +21 -0
- package/lib/cli/commands/ingest-plan.js +56 -0
- package/lib/cli/commands/init.js +21 -0
- package/lib/cli/commands/install-devcontainer.js +97 -0
- package/lib/cli/commands/install-skills.js +182 -0
- package/lib/cli/commands/orchestrate.js +27 -0
- package/lib/cli/commands/resume.js +146 -0
- package/lib/cli/commands/status.js +137 -0
- package/lib/cli/commands/validate-plan.js +288 -0
- package/lib/cli/index.js +57 -0
- package/lib/orchestration/agent-invoker.js +595 -0
- package/lib/orchestration/condensed-plan.js +128 -0
- package/lib/orchestration/config.js +213 -0
- package/lib/orchestration/dependency-resolver.js +149 -0
- package/lib/orchestration/edit-invoker.js +115 -0
- package/lib/orchestration/index.js +1065 -0
- package/lib/orchestration/plan-loader.js +212 -0
- package/lib/orchestration/progress-tracker.js +208 -0
- package/lib/orchestration/report-format.js +80 -0
- package/lib/orchestration/review-invoker.js +305 -0
- package/lib/utils/agent-detector.js +47 -0
- package/lib/utils/git.js +297 -0
- package/lib/utils/paths.js +43 -0
- package/lib/utils/plan-detect.js +24 -0
- package/lib/utils/skill-copier.js +79 -0
- package/package.json +58 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator Configuration
|
|
3
|
+
*
|
|
4
|
+
* This file configures the plan orchestrator including agent commands,
|
|
5
|
+
* concurrency settings, and directory paths.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { getFormatInstructions } = require("./report-format");
|
|
9
|
+
const { detectInstalledAgents } = require("../utils/agent-detector");
|
|
10
|
+
|
|
11
|
+
// Helper function to get agent priority from environment or default
|
|
12
|
+
function getAgentPriority() {
|
|
13
|
+
const envPriority = process.env.ORRERY_AGENT_PRIORITY;
|
|
14
|
+
if (envPriority && envPriority.trim()) {
|
|
15
|
+
return envPriority
|
|
16
|
+
.trim()
|
|
17
|
+
.split(",")
|
|
18
|
+
.map((s) => s.trim());
|
|
19
|
+
}
|
|
20
|
+
return ["codex", "gemini", "claude"];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Filter agent priority to only include agents that are installed (have config directories)
|
|
24
|
+
function getInstalledAgentPriority() {
|
|
25
|
+
const requestedAgents = getAgentPriority();
|
|
26
|
+
const installedAgents = detectInstalledAgents();
|
|
27
|
+
|
|
28
|
+
const filteredAgents = requestedAgents.filter((agent) =>
|
|
29
|
+
installedAgents.includes(agent)
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
// Warn about agents in priority that aren't installed
|
|
33
|
+
const skippedAgents = requestedAgents.filter(
|
|
34
|
+
(agent) => !installedAgents.includes(agent)
|
|
35
|
+
);
|
|
36
|
+
if (skippedAgents.length > 0) {
|
|
37
|
+
const missingDirs = skippedAgents.map((a) => `~/.${a}`).join(", ");
|
|
38
|
+
console.warn(
|
|
39
|
+
`[config] Skipping unconfigured agents: ${skippedAgents.join(", ")} (missing: ${missingDirs})`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (filteredAgents.length === 0) {
|
|
44
|
+
console.warn(
|
|
45
|
+
`[config] No configured agents found. Install an agent CLI and create its config directory (e.g., ~/.claude, ~/.codex, ~/.gemini)`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return filteredAgents;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Shared prompt for all worker agents
|
|
53
|
+
const WORKER_PROMPT = `You are a Worker Agent executing plan steps.
|
|
54
|
+
|
|
55
|
+
Plan file: {planFile}
|
|
56
|
+
Steps to execute: {stepIds}
|
|
57
|
+
|
|
58
|
+
## Plan Format Note
|
|
59
|
+
|
|
60
|
+
The plan file may be a condensed version containing only your assigned steps
|
|
61
|
+
and their completed dependencies (indicated by \`condensed: true\` in metadata).
|
|
62
|
+
All necessary context is included - do not reference the source plan.
|
|
63
|
+
|
|
64
|
+
## Workflow
|
|
65
|
+
|
|
66
|
+
For each step:
|
|
67
|
+
|
|
68
|
+
1. Read the plan file to understand the step's requirements, criteria, and files
|
|
69
|
+
2. Execute: Implement the changes following project conventions. Do NOT commit - the orchestrator handles commits.
|
|
70
|
+
3. Verify: Run tests and confirm acceptance criteria are met. Fix issues before proceeding.
|
|
71
|
+
4. Report: Output a JSON result for the step (see format below)
|
|
72
|
+
|
|
73
|
+
Use /orrery-execute, /orrery-verify, and /orrery-report skills for detailed guidance on each phase.
|
|
74
|
+
|
|
75
|
+
${getFormatInstructions()}
|
|
76
|
+
|
|
77
|
+
## Exit Codes
|
|
78
|
+
|
|
79
|
+
- Exit 0: All steps completed successfully
|
|
80
|
+
- Exit 1: One or more steps blocked
|
|
81
|
+
|
|
82
|
+
## Rules
|
|
83
|
+
|
|
84
|
+
- The plan file is READ-ONLY—never modify it
|
|
85
|
+
- Complete each step fully before starting the next
|
|
86
|
+
- Output clean JSON to stdout—no extra text or markdown wrapping`;
|
|
87
|
+
|
|
88
|
+
const REVIEW_PROMPT = `You are a Review Agent. Evaluate the changes for the completed plan step.
|
|
89
|
+
|
|
90
|
+
## Context
|
|
91
|
+
|
|
92
|
+
Step context:
|
|
93
|
+
{stepContext}
|
|
94
|
+
|
|
95
|
+
Modified files:
|
|
96
|
+
{files}
|
|
97
|
+
|
|
98
|
+
Diff:
|
|
99
|
+
{diff}
|
|
100
|
+
|
|
101
|
+
## Instructions
|
|
102
|
+
|
|
103
|
+
- Identify bugs, regressions, missing edge cases, or unmet acceptance criteria.
|
|
104
|
+
- If changes are acceptable, respond with approval.
|
|
105
|
+
- If changes need edits, provide specific, actionable feedback.
|
|
106
|
+
- Keep feedback concise and grounded in the diff.
|
|
107
|
+
|
|
108
|
+
## Output Format (JSON, single line)
|
|
109
|
+
|
|
110
|
+
{"status":"approved","summary":"..."}
|
|
111
|
+
OR
|
|
112
|
+
{"status":"changes_requested","summary":"...","comments":["...","..."]}`;
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
// Agent configurations (keyed by agent name)
|
|
116
|
+
agents: {
|
|
117
|
+
claude: {
|
|
118
|
+
command: "claude",
|
|
119
|
+
args: [
|
|
120
|
+
"--model",
|
|
121
|
+
"sonnet",
|
|
122
|
+
"--dangerously-skip-permissions",
|
|
123
|
+
"-p",
|
|
124
|
+
WORKER_PROMPT
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
codex: {
|
|
128
|
+
command: "codex",
|
|
129
|
+
args: ["exec", "--yolo", WORKER_PROMPT],
|
|
130
|
+
// Codex writes progress to stderr and final result to stdout
|
|
131
|
+
stderrIsProgress: true
|
|
132
|
+
},
|
|
133
|
+
gemini: {
|
|
134
|
+
command: "gemini",
|
|
135
|
+
args: ["--yolo", "-p", WORKER_PROMPT],
|
|
136
|
+
// Gemini writes progress to stderr and final result to stdout
|
|
137
|
+
stderrIsProgress: true
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
// Default agent to use when failover is disabled
|
|
142
|
+
defaultAgent: "codex",
|
|
143
|
+
|
|
144
|
+
// Agent priority list for failover (tried in order, filtered to installed agents)
|
|
145
|
+
agentPriority: getInstalledAgentPriority(),
|
|
146
|
+
|
|
147
|
+
// Failover configuration
|
|
148
|
+
failover: {
|
|
149
|
+
// Enable/disable failover behavior
|
|
150
|
+
enabled: true,
|
|
151
|
+
|
|
152
|
+
// Timeout in milliseconds before trying next agent (15 minutes)
|
|
153
|
+
timeoutMs: 900000,
|
|
154
|
+
|
|
155
|
+
// Patterns to detect failover-triggering errors from stderr
|
|
156
|
+
errorPatterns: {
|
|
157
|
+
// API/connection errors
|
|
158
|
+
apiError: [
|
|
159
|
+
/API error/i,
|
|
160
|
+
/connection refused/i,
|
|
161
|
+
/ECONNRESET/i,
|
|
162
|
+
/ETIMEDOUT/i,
|
|
163
|
+
/network error/i,
|
|
164
|
+
/rate limit/i,
|
|
165
|
+
/429/,
|
|
166
|
+
/502/,
|
|
167
|
+
/503/
|
|
168
|
+
],
|
|
169
|
+
// Token/context limit errors
|
|
170
|
+
tokenLimit: [
|
|
171
|
+
/token limit/i,
|
|
172
|
+
/context.*(limit|length|exceeded)/i,
|
|
173
|
+
/maximum.*tokens/i,
|
|
174
|
+
/too long/i
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
// Concurrency control
|
|
180
|
+
concurrency: {
|
|
181
|
+
maxParallel: 1, // Parallel disabled - see TODO below
|
|
182
|
+
// TODO: To re-enable parallel execution:
|
|
183
|
+
// 1. Set maxParallel > 1
|
|
184
|
+
// 2. Bundle commits: after ALL parallel agents complete, commit once with combined messages
|
|
185
|
+
// 3. Update processPlan() to collect all parallel results before committing
|
|
186
|
+
pollInterval: 5000
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
// Retry policy for failed steps
|
|
190
|
+
retry: {
|
|
191
|
+
// Maximum retry attempts per step
|
|
192
|
+
maxAttempts: 1,
|
|
193
|
+
// Delay in ms before retrying
|
|
194
|
+
backoffMs: 5000
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
// Logging options
|
|
198
|
+
logging: {
|
|
199
|
+
// Show agent stdout in real-time
|
|
200
|
+
streamOutput: true,
|
|
201
|
+
// Prefix format for agent output ("[step-1]" or "[step-1,step-2]")
|
|
202
|
+
prefixFormat: "step"
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
// Review/edit loop configuration
|
|
206
|
+
review: {
|
|
207
|
+
enabled: false,
|
|
208
|
+
maxIterations: 3,
|
|
209
|
+
prompt: REVIEW_PROMPT
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
REVIEW_PROMPT
|
|
213
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dependency resolution utilities for plan step execution ordering.
|
|
5
|
+
* Uses Kahn's algorithm for topological sorting.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get steps that are ready to execute (pending with all deps satisfied)
|
|
10
|
+
* @param {Object} plan - The plan object
|
|
11
|
+
* @returns {Array} - Array of step objects ready to execute
|
|
12
|
+
*/
|
|
13
|
+
function getReadySteps(plan) {
|
|
14
|
+
const completed = plan.getCompletedSteps();
|
|
15
|
+
|
|
16
|
+
return plan.steps.filter((step) => {
|
|
17
|
+
// Must be pending
|
|
18
|
+
if (step.status !== "pending") return false;
|
|
19
|
+
|
|
20
|
+
// All dependencies must be complete (not pending, not blocked, not in_progress)
|
|
21
|
+
const deps = step.deps || [];
|
|
22
|
+
return deps.every((depId) => completed.has(depId));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Group steps for parallel execution based on dependency levels.
|
|
28
|
+
* Uses Kahn's algorithm to find execution groups.
|
|
29
|
+
* @param {Array} steps - Array of step objects (with id, deps, parallel fields)
|
|
30
|
+
* @returns {Array<Array>} - Array of step groups, each group can run in parallel
|
|
31
|
+
*/
|
|
32
|
+
function resolveExecutionGroups(steps) {
|
|
33
|
+
const stepMap = new Map(steps.map((s) => [s.id, s]));
|
|
34
|
+
const remaining = new Set(steps.map((s) => s.id));
|
|
35
|
+
const groups = [];
|
|
36
|
+
|
|
37
|
+
while (remaining.size > 0) {
|
|
38
|
+
// Find all steps with no remaining dependencies
|
|
39
|
+
const ready = [...remaining].filter((id) => {
|
|
40
|
+
const step = stepMap.get(id);
|
|
41
|
+
const deps = step.deps || [];
|
|
42
|
+
return deps.every((dep) => !remaining.has(dep));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (ready.length === 0 && remaining.size > 0) {
|
|
46
|
+
const cycleSteps = [...remaining].join(", ");
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Circular dependency detected among steps: ${cycleSteps}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Separate parallel and serial steps
|
|
53
|
+
const parallelSteps = ready
|
|
54
|
+
.filter((id) => stepMap.get(id).parallel === true)
|
|
55
|
+
.map((id) => stepMap.get(id));
|
|
56
|
+
|
|
57
|
+
const serialSteps = ready
|
|
58
|
+
.filter((id) => stepMap.get(id).parallel !== true)
|
|
59
|
+
.map((id) => stepMap.get(id));
|
|
60
|
+
|
|
61
|
+
// Add parallel steps as a single group (can run together)
|
|
62
|
+
if (parallelSteps.length > 0) {
|
|
63
|
+
groups.push(parallelSteps);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Add serial steps as individual groups (run one at a time)
|
|
67
|
+
for (const step of serialSteps) {
|
|
68
|
+
groups.push([step]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Remove processed steps from remaining
|
|
72
|
+
for (const id of ready) {
|
|
73
|
+
remaining.delete(id);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return groups;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if a plan has any circular dependencies
|
|
82
|
+
* @param {Object} plan - The plan object
|
|
83
|
+
* @returns {{hasCycle: boolean, cycleSteps?: string[]}} - Result with cycle info
|
|
84
|
+
*/
|
|
85
|
+
function detectCycles(plan) {
|
|
86
|
+
try {
|
|
87
|
+
resolveExecutionGroups(plan.steps);
|
|
88
|
+
return { hasCycle: false };
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (error.message.includes("Circular dependency")) {
|
|
91
|
+
// Extract step IDs from error message
|
|
92
|
+
const match = error.message.match(/steps: (.+)$/);
|
|
93
|
+
const cycleSteps = match ? match[1].split(", ") : [];
|
|
94
|
+
return { hasCycle: true, cycleSteps };
|
|
95
|
+
}
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get steps that are blocked due to a specific step being blocked
|
|
102
|
+
* (i.e., steps that depend on the blocked step directly or transitively)
|
|
103
|
+
* @param {Object} plan - The plan object
|
|
104
|
+
* @param {string} blockedStepId - ID of the blocked step
|
|
105
|
+
* @returns {string[]} - Array of step IDs that are blocked as a result
|
|
106
|
+
*/
|
|
107
|
+
function getBlockedDependents(plan, blockedStepId) {
|
|
108
|
+
const dependents = new Set();
|
|
109
|
+
|
|
110
|
+
function findDependents(stepId) {
|
|
111
|
+
for (const step of plan.steps) {
|
|
112
|
+
const deps = step.deps || [];
|
|
113
|
+
if (deps.includes(stepId) && !dependents.has(step.id)) {
|
|
114
|
+
dependents.add(step.id);
|
|
115
|
+
findDependents(step.id);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
findDependents(blockedStepId);
|
|
121
|
+
return [...dependents];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Partition ready steps into groups respecting maxParallel limit
|
|
126
|
+
* @param {Array} readySteps - Steps ready to execute
|
|
127
|
+
* @param {number} maxParallel - Maximum concurrent steps
|
|
128
|
+
* @param {number} currentlyRunning - Number of steps currently in progress
|
|
129
|
+
* @returns {{parallel: Array, serial: Array}} - Steps grouped by execution type
|
|
130
|
+
*/
|
|
131
|
+
function partitionSteps(readySteps, maxParallel, currentlyRunning = 0) {
|
|
132
|
+
const availableSlots = Math.max(0, maxParallel - currentlyRunning);
|
|
133
|
+
|
|
134
|
+
const parallel = readySteps.filter((s) => s.parallel === true);
|
|
135
|
+
const serial = readySteps.filter((s) => s.parallel !== true);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
parallel: parallel.slice(0, availableSlots),
|
|
139
|
+
serial: serial.slice(0, Math.max(0, availableSlots - parallel.length))
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
module.exports = {
|
|
144
|
+
getReadySteps,
|
|
145
|
+
resolveExecutionGroups,
|
|
146
|
+
detectCycles,
|
|
147
|
+
getBlockedDependents,
|
|
148
|
+
partitionSteps
|
|
149
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const {
|
|
2
|
+
invokeAgentWithFailover,
|
|
3
|
+
parseAgentResults
|
|
4
|
+
} = require("./agent-invoker");
|
|
5
|
+
|
|
6
|
+
function getWorkerPromptTemplate(config) {
|
|
7
|
+
if (config && typeof config.WORKER_PROMPT === "string") {
|
|
8
|
+
return config.WORKER_PROMPT;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const agents = (config && config.agents) || {};
|
|
12
|
+
const preferredAgent =
|
|
13
|
+
(config && config.defaultAgent && agents[config.defaultAgent]) ||
|
|
14
|
+
Object.values(agents)[0];
|
|
15
|
+
|
|
16
|
+
if (preferredAgent && Array.isArray(preferredAgent.args)) {
|
|
17
|
+
const lastArg = preferredAgent.args[preferredAgent.args.length - 1];
|
|
18
|
+
if (typeof lastArg === "string") {
|
|
19
|
+
return lastArg;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function formatFeedbackList(feedback) {
|
|
27
|
+
if (!Array.isArray(feedback) || feedback.length === 0) {
|
|
28
|
+
return "No review feedback items provided.";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return feedback
|
|
32
|
+
.map((entry, index) => {
|
|
33
|
+
const item =
|
|
34
|
+
entry && typeof entry === "object" ? entry : { comment: String(entry) };
|
|
35
|
+
const file =
|
|
36
|
+
typeof item.file === "string" && item.file.trim()
|
|
37
|
+
? item.file.trim()
|
|
38
|
+
: "(not specified)";
|
|
39
|
+
const line = Number.isFinite(item.line) ? ` line: ${item.line}` : "";
|
|
40
|
+
const severity =
|
|
41
|
+
typeof item.severity === "string" && item.severity.trim()
|
|
42
|
+
? item.severity.trim()
|
|
43
|
+
: "suggestion";
|
|
44
|
+
const comment =
|
|
45
|
+
typeof item.comment === "string" && item.comment.trim()
|
|
46
|
+
? item.comment.trim()
|
|
47
|
+
: "(no comment provided)";
|
|
48
|
+
|
|
49
|
+
return `${index + 1}. file: ${file}${line} severity: ${severity} comment: ${comment}`;
|
|
50
|
+
})
|
|
51
|
+
.join("\n");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function buildEditPrompt(template, planFile, stepIds, feedback) {
|
|
55
|
+
const stepIdsStr = Array.isArray(stepIds)
|
|
56
|
+
? stepIds.join(",")
|
|
57
|
+
: String(stepIds);
|
|
58
|
+
const basePrompt = String(template || "")
|
|
59
|
+
.replace("{planFile}", planFile)
|
|
60
|
+
.replace("{stepIds}", stepIdsStr);
|
|
61
|
+
|
|
62
|
+
const feedbackSection = formatFeedbackList(feedback);
|
|
63
|
+
|
|
64
|
+
return `${basePrompt}\n\n## Review Feedback\n${feedbackSection}\n\n## Instructions\nAddress all review feedback items above before reporting the step as complete.`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function buildEditConfig(config, prompt) {
|
|
68
|
+
const agents = {};
|
|
69
|
+
|
|
70
|
+
for (const [name, agentConfig] of Object.entries(config.agents || {})) {
|
|
71
|
+
const args = Array.isArray(agentConfig.args)
|
|
72
|
+
? agentConfig.args.slice()
|
|
73
|
+
: [];
|
|
74
|
+
if (args.length > 0) {
|
|
75
|
+
args[args.length - 1] = prompt;
|
|
76
|
+
}
|
|
77
|
+
agents[name] = {
|
|
78
|
+
...agentConfig,
|
|
79
|
+
args
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
...config,
|
|
85
|
+
agents
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function invokeEditAgent(
|
|
90
|
+
config,
|
|
91
|
+
planFile,
|
|
92
|
+
stepIds,
|
|
93
|
+
feedback,
|
|
94
|
+
repoRoot,
|
|
95
|
+
options = {}
|
|
96
|
+
) {
|
|
97
|
+
const template = getWorkerPromptTemplate(config);
|
|
98
|
+
const prompt = buildEditPrompt(template, planFile, stepIds, feedback);
|
|
99
|
+
const editConfig = buildEditConfig(config, prompt);
|
|
100
|
+
|
|
101
|
+
const handle = invokeAgentWithFailover(
|
|
102
|
+
editConfig,
|
|
103
|
+
planFile,
|
|
104
|
+
stepIds,
|
|
105
|
+
repoRoot,
|
|
106
|
+
options
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const result = await handle.completion;
|
|
110
|
+
return parseAgentResults(result.stdout || "");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
invokeEditAgent
|
|
115
|
+
};
|