@covibes/zeroshot 5.2.1 → 5.4.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/CHANGELOG.md +174 -189
- package/README.md +226 -195
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +3145 -2366
- package/cli/lib/first-run.js +40 -3
- package/cli/message-formatters-normal.js +28 -6
- package/cluster-templates/base-templates/debug-workflow.json +24 -78
- package/cluster-templates/base-templates/full-workflow.json +99 -316
- package/cluster-templates/base-templates/single-worker.json +23 -15
- package/cluster-templates/base-templates/worker-validator.json +105 -36
- package/cluster-templates/conductor-bootstrap.json +9 -7
- package/lib/docker-config.js +14 -1
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +10 -7
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +298 -15
- package/lib/stream-json-parser.js +4 -238
- package/package.json +27 -6
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/validate-templates.js +100 -0
- package/src/agent/agent-config.js +140 -63
- package/src/agent/agent-context-builder.js +336 -165
- package/src/agent/agent-hook-executor.js +337 -67
- package/src/agent/agent-lifecycle.js +386 -287
- package/src/agent/agent-stuck-detector.js +7 -7
- package/src/agent/agent-task-executor.js +944 -683
- package/src/agent/output-extraction.js +217 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent-wrapper.js +112 -31
- package/src/agents/git-pusher-template.js +285 -0
- package/src/claude-task-runner.js +145 -44
- package/src/config-router.js +13 -13
- package/src/config-validator.js +1049 -563
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +499 -320
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +273 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +179 -0
- package/src/issue-providers/gitlab-provider.js +241 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +239 -0
- package/src/ledger.js +50 -11
- package/src/lib/safe-exec.js +88 -0
- package/src/orchestrator.js +1348 -757
- package/src/preflight.js +306 -149
- package/src/process-metrics.js +98 -56
- package/src/providers/anthropic/cli-builder.js +73 -0
- package/src/providers/anthropic/index.js +204 -0
- package/src/providers/anthropic/models.js +23 -0
- package/src/providers/anthropic/output-parser.js +177 -0
- package/src/providers/base-provider.js +251 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/cli-builder.js +55 -0
- package/src/providers/google/index.js +116 -0
- package/src/providers/google/models.js +24 -0
- package/src/providers/google/output-parser.js +101 -0
- package/src/providers/index.js +91 -0
- package/src/providers/openai/cli-builder.js +133 -0
- package/src/providers/openai/index.js +136 -0
- package/src/providers/openai/models.js +21 -0
- package/src/providers/openai/output-parser.js +143 -0
- package/src/providers/opencode/cli-builder.js +42 -0
- package/src/providers/opencode/index.js +103 -0
- package/src/providers/opencode/models.js +55 -0
- package/src/providers/opencode/output-parser.js +122 -0
- package/src/schemas/sub-cluster.js +68 -39
- package/src/status-footer.js +94 -48
- package/src/sub-cluster-wrapper.js +92 -36
- package/src/task-runner.js +8 -6
- package/src/template-resolver.js +12 -9
- package/src/tui/data-poller.js +123 -99
- package/src/tui/formatters.js +4 -3
- package/src/tui/keybindings.js +259 -318
- package/src/tui/layout.js +20 -3
- package/src/tui/renderer.js +11 -21
- package/task-lib/attachable-watcher.js +150 -111
- package/task-lib/claude-recovery.js +156 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/list.js +3 -3
- package/task-lib/commands/logs.js +231 -189
- package/task-lib/commands/resume.js +3 -2
- package/task-lib/commands/run.js +12 -3
- package/task-lib/commands/schedules.js +111 -61
- package/task-lib/config.js +0 -2
- package/task-lib/runner.js +128 -50
- package/task-lib/scheduler.js +3 -3
- package/task-lib/store.js +464 -152
- package/task-lib/tui/formatters.js +94 -45
- package/task-lib/tui/renderer.js +13 -3
- package/task-lib/tui.js +73 -32
- package/task-lib/watcher.js +157 -100
- package/src/agents/git-pusher-agent.json +0 -20
- package/src/github.js +0 -103
package/src/agent-wrapper.js
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
const LogicEngine = require('./logic-engine');
|
|
14
14
|
const { validateAgentConfig } = require('./agent/agent-config');
|
|
15
15
|
const { loadSettings, validateModelAgainstMax } = require('../lib/settings');
|
|
16
|
+
const { normalizeProviderName } = require('../lib/provider-names');
|
|
17
|
+
const { getProvider } = require('./providers');
|
|
16
18
|
const { buildContext } = require('./agent/agent-context-builder');
|
|
17
19
|
const { findMatchingTrigger, evaluateTrigger } = require('./agent/agent-trigger-evaluator');
|
|
18
20
|
const { executeHook } = require('./agent/agent-hook-executor');
|
|
@@ -69,6 +71,8 @@ class AgentWrapper {
|
|
|
69
71
|
this.unsubscribe = null;
|
|
70
72
|
/** @type {number | null} */
|
|
71
73
|
this.lastTaskEndTime = null; // Track when last task completed (for context filtering)
|
|
74
|
+
/** @type {number | null} */
|
|
75
|
+
this.lastAgentStartTime = null; // Track when agent last began executing (for context filtering)
|
|
72
76
|
|
|
73
77
|
// LIVENESS DETECTION - Track output freshness to detect stuck agents
|
|
74
78
|
/** @type {number | null} */
|
|
@@ -87,9 +91,12 @@ class AgentWrapper {
|
|
|
87
91
|
// TaskRunner DI - create mockSpawnFn wrapper
|
|
88
92
|
const taskRunner = options.taskRunner;
|
|
89
93
|
this.mockSpawnFn = (args, { context }) => {
|
|
94
|
+
const spec = this._resolveModelSpec();
|
|
90
95
|
return taskRunner.run(context, {
|
|
91
96
|
agentId: this.id,
|
|
92
97
|
model: this._selectModel(),
|
|
98
|
+
modelSpec: spec,
|
|
99
|
+
provider: this._resolveProvider(),
|
|
93
100
|
});
|
|
94
101
|
};
|
|
95
102
|
} else {
|
|
@@ -116,6 +123,90 @@ class AgentWrapper {
|
|
|
116
123
|
}
|
|
117
124
|
}
|
|
118
125
|
|
|
126
|
+
_resolveProvider() {
|
|
127
|
+
const settings = loadSettings();
|
|
128
|
+
const clusterConfig = this.cluster?.config || {};
|
|
129
|
+
|
|
130
|
+
const resolved =
|
|
131
|
+
clusterConfig.forceProvider ||
|
|
132
|
+
this.config.provider ||
|
|
133
|
+
clusterConfig.defaultProvider ||
|
|
134
|
+
settings.defaultProvider ||
|
|
135
|
+
'claude';
|
|
136
|
+
|
|
137
|
+
return normalizeProviderName(resolved) || 'claude';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_resolveModelSpec() {
|
|
141
|
+
const settings = loadSettings();
|
|
142
|
+
const providerName = this._resolveProvider();
|
|
143
|
+
const provider = getProvider(providerName);
|
|
144
|
+
const clusterConfig = this.cluster?.config || {};
|
|
145
|
+
const providerSettings = settings.providerSettings?.[providerName] || {};
|
|
146
|
+
const levelOverrides = providerSettings.levelOverrides || {};
|
|
147
|
+
const minLevel = providerSettings.minLevel;
|
|
148
|
+
const maxLevel = providerSettings.maxLevel;
|
|
149
|
+
const forcedLevel =
|
|
150
|
+
clusterConfig.forceProvider === providerName ? clusterConfig.forceLevel : null;
|
|
151
|
+
|
|
152
|
+
const applyReasoningOverride = (spec, override) => {
|
|
153
|
+
if (!override) return spec;
|
|
154
|
+
return { ...spec, reasoningEffort: override };
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
if (this.modelConfig.type === 'rules') {
|
|
158
|
+
for (const rule of this.modelConfig.rules) {
|
|
159
|
+
if (this._matchesIterationRange(rule.iterations)) {
|
|
160
|
+
if (rule.model) {
|
|
161
|
+
return {
|
|
162
|
+
level: 'custom',
|
|
163
|
+
model: rule.model,
|
|
164
|
+
reasoningEffort: rule.reasoningEffort || this.config.reasoningEffort,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (rule.modelLevel) {
|
|
168
|
+
const level = provider.validateLevel(rule.modelLevel, minLevel, maxLevel);
|
|
169
|
+
const spec = provider.resolveModelSpec(level, levelOverrides);
|
|
170
|
+
return applyReasoningOverride(
|
|
171
|
+
{ ...spec, level },
|
|
172
|
+
rule.reasoningEffort || this.config.reasoningEffort
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Agent ${this.id}: No model rule matched iteration ${this.iteration}. ` +
|
|
180
|
+
`Add a catch-all rule like { "iterations": "all", "modelLevel": "level2" }`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (this.modelConfig.model) {
|
|
185
|
+
return {
|
|
186
|
+
level: 'custom',
|
|
187
|
+
model: this.modelConfig.model,
|
|
188
|
+
reasoningEffort: this.config.reasoningEffort,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (forcedLevel) {
|
|
193
|
+
const level = provider.validateLevel(forcedLevel, minLevel, maxLevel);
|
|
194
|
+
const spec = provider.resolveModelSpec(level, levelOverrides);
|
|
195
|
+
return applyReasoningOverride({ ...spec, level }, this.config.reasoningEffort);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (this.modelConfig.modelLevel) {
|
|
199
|
+
const level = provider.validateLevel(this.modelConfig.modelLevel, minLevel, maxLevel);
|
|
200
|
+
const spec = provider.resolveModelSpec(level, levelOverrides);
|
|
201
|
+
return applyReasoningOverride({ ...spec, level }, this.config.reasoningEffort);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const defaultLevel = providerSettings.defaultLevel || provider.getDefaultLevel();
|
|
205
|
+
const level = provider.validateLevel(defaultLevel, minLevel, maxLevel);
|
|
206
|
+
const spec = provider.resolveModelSpec(level, levelOverrides);
|
|
207
|
+
return applyReasoningOverride({ ...spec, level }, this.config.reasoningEffort);
|
|
208
|
+
}
|
|
209
|
+
|
|
119
210
|
/**
|
|
120
211
|
* Publish a message to the message bus, always including sender_model
|
|
121
212
|
* @private
|
|
@@ -126,6 +217,7 @@ class AgentWrapper {
|
|
|
126
217
|
cluster_id: this.cluster.id,
|
|
127
218
|
sender: this.id,
|
|
128
219
|
sender_model: this._selectModel(),
|
|
220
|
+
sender_provider: this._resolveProvider(),
|
|
129
221
|
});
|
|
130
222
|
}
|
|
131
223
|
|
|
@@ -145,6 +237,7 @@ class AgentWrapper {
|
|
|
145
237
|
role: this.role,
|
|
146
238
|
state: this.state,
|
|
147
239
|
model: this._selectModel(),
|
|
240
|
+
provider: this._resolveProvider(),
|
|
148
241
|
...details,
|
|
149
242
|
},
|
|
150
243
|
},
|
|
@@ -153,44 +246,21 @@ class AgentWrapper {
|
|
|
153
246
|
|
|
154
247
|
/**
|
|
155
248
|
* Select model based on current iteration and agent config
|
|
156
|
-
* Enforces maxModel
|
|
157
|
-
* @returns {string}
|
|
249
|
+
* Enforces legacy maxModel/minModel for Claude's haiku/sonnet/opus
|
|
250
|
+
* @returns {string|null}
|
|
158
251
|
* @private
|
|
159
252
|
*/
|
|
160
253
|
_selectModel() {
|
|
254
|
+
const spec = this._resolveModelSpec();
|
|
161
255
|
const settings = loadSettings();
|
|
162
256
|
const maxModel = settings.maxModel || 'sonnet';
|
|
257
|
+
const minModel = settings.minModel || null;
|
|
163
258
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// Get requested model from config
|
|
167
|
-
if (this.modelConfig.type === 'static') {
|
|
168
|
-
requestedModel = this.modelConfig.model;
|
|
169
|
-
} else if (this.modelConfig.type === 'rules') {
|
|
170
|
-
// Dynamic rules: evaluate based on iteration
|
|
171
|
-
for (const rule of this.modelConfig.rules) {
|
|
172
|
-
if (this._matchesIterationRange(rule.iterations)) {
|
|
173
|
-
requestedModel = rule.model;
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// No match for rules: fail fast (config error)
|
|
179
|
-
if (!requestedModel) {
|
|
180
|
-
throw new Error(
|
|
181
|
-
`Agent ${this.id}: No model rule matched iteration ${this.iteration}. ` +
|
|
182
|
-
`Add a catch-all rule like { "iterations": "all", "model": "sonnet" }`
|
|
183
|
-
);
|
|
184
|
-
}
|
|
259
|
+
if (spec.model && ['opus', 'sonnet', 'haiku'].includes(spec.model)) {
|
|
260
|
+
return validateModelAgainstMax(spec.model, maxModel, minModel);
|
|
185
261
|
}
|
|
186
262
|
|
|
187
|
-
|
|
188
|
-
if (!requestedModel) {
|
|
189
|
-
return maxModel;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Enforce ceiling - will throw if requestedModel > maxModel
|
|
193
|
-
return validateModelAgainstMax(requestedModel, maxModel);
|
|
263
|
+
return spec.model || null;
|
|
194
264
|
}
|
|
195
265
|
|
|
196
266
|
/**
|
|
@@ -336,7 +406,8 @@ class AgentWrapper {
|
|
|
336
406
|
* @private
|
|
337
407
|
*/
|
|
338
408
|
_buildContext(triggeringMessage) {
|
|
339
|
-
|
|
409
|
+
const previousAgentStart = this.lastAgentStartTime;
|
|
410
|
+
const context = buildContext({
|
|
340
411
|
id: this.id,
|
|
341
412
|
role: this.role,
|
|
342
413
|
iteration: this.iteration,
|
|
@@ -344,12 +415,18 @@ class AgentWrapper {
|
|
|
344
415
|
messageBus: this.messageBus,
|
|
345
416
|
cluster: this.cluster,
|
|
346
417
|
lastTaskEndTime: this.lastTaskEndTime,
|
|
418
|
+
lastAgentStartTime: previousAgentStart,
|
|
347
419
|
triggeringMessage,
|
|
348
420
|
selectedPrompt: this._selectPrompt(),
|
|
349
421
|
// Pass isolation state for conditional git restriction
|
|
350
422
|
worktree: this.worktree,
|
|
351
423
|
isolation: this.isolation,
|
|
352
424
|
});
|
|
425
|
+
|
|
426
|
+
// Record when this iteration started so future "since: last_agent_start" filters work
|
|
427
|
+
this.lastAgentStartTime = Date.now();
|
|
428
|
+
|
|
429
|
+
return context;
|
|
353
430
|
}
|
|
354
431
|
|
|
355
432
|
/**
|
|
@@ -424,6 +501,7 @@ class AgentWrapper {
|
|
|
424
501
|
* Parse agent output to extract structured result data
|
|
425
502
|
* GENERIC - returns whatever structured output the agent provides
|
|
426
503
|
* Works with any agent schema (planner, validator, worker, etc.)
|
|
504
|
+
* Falls back to reformatting if extraction fails
|
|
427
505
|
* @private
|
|
428
506
|
*/
|
|
429
507
|
_parseResultOutput(output) {
|
|
@@ -464,10 +542,13 @@ class AgentWrapper {
|
|
|
464
542
|
* Get current agent state
|
|
465
543
|
*/
|
|
466
544
|
getState() {
|
|
545
|
+
const modelSpec = this._resolveModelSpec();
|
|
467
546
|
return {
|
|
468
547
|
id: this.id,
|
|
469
548
|
role: this.role,
|
|
470
549
|
model: this._selectModel(),
|
|
550
|
+
provider: this._resolveProvider(),
|
|
551
|
+
modelSpec,
|
|
471
552
|
state: this.state,
|
|
472
553
|
iteration: this.iteration,
|
|
473
554
|
maxIterations: this.maxIterations,
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Pusher Agent Template
|
|
3
|
+
*
|
|
4
|
+
* Generates platform-specific git-pusher agent configurations.
|
|
5
|
+
* Eliminates duplication across github/gitlab/azure JSON files.
|
|
6
|
+
*
|
|
7
|
+
* Single source of truth for:
|
|
8
|
+
* - Trigger logic (validation consensus detection)
|
|
9
|
+
* - Agent structure (id, role, modelLevel, output)
|
|
10
|
+
* - Prompt template with platform-specific commands
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Shared trigger logic for detecting when all validators have approved.
|
|
15
|
+
* This is the SINGLE source of truth - no more duplicating across 3 JSON files.
|
|
16
|
+
*/
|
|
17
|
+
const SHARED_TRIGGER_SCRIPT = `const validators = cluster.getAgentsByRole('validator');
|
|
18
|
+
const lastPush = ledger.findLast({ topic: 'IMPLEMENTATION_READY' });
|
|
19
|
+
if (!lastPush) return false;
|
|
20
|
+
if (validators.length === 0) return true;
|
|
21
|
+
const results = ledger.query({ topic: 'VALIDATION_RESULT', since: lastPush.timestamp });
|
|
22
|
+
if (results.length < validators.length) return false;
|
|
23
|
+
const allApproved = results.every(r => r.content?.data?.approved === 'true' || r.content?.data?.approved === true);
|
|
24
|
+
if (!allApproved) return false;
|
|
25
|
+
const hasRealEvidence = results.every(r => {
|
|
26
|
+
const criteria = r.content?.data?.criteriaResults || [];
|
|
27
|
+
return criteria.every(c => {
|
|
28
|
+
return c.evidence?.command && typeof c.evidence?.exitCode === 'number' && c.evidence?.output?.length > 10;
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
return hasRealEvidence;`;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Platform-specific CLI commands and terminology
|
|
35
|
+
*/
|
|
36
|
+
const PLATFORM_CONFIGS = {
|
|
37
|
+
github: {
|
|
38
|
+
prName: 'PR',
|
|
39
|
+
prNameLower: 'pull request',
|
|
40
|
+
createCmd: 'gh pr create --title "feat: {{issue_title}}" --body "Closes #{{issue_number}}"',
|
|
41
|
+
mergeCmd: 'gh pr merge --merge --auto',
|
|
42
|
+
mergeFallbackCmd: 'gh pr merge --merge',
|
|
43
|
+
prUrlExample: 'https://github.com/owner/repo/pull/123',
|
|
44
|
+
outputFields: { urlField: 'pr_url', numberField: 'pr_number', mergedField: 'merged' },
|
|
45
|
+
},
|
|
46
|
+
gitlab: {
|
|
47
|
+
prName: 'MR',
|
|
48
|
+
prNameLower: 'merge request',
|
|
49
|
+
createCmd:
|
|
50
|
+
'glab mr create --title "feat: {{issue_title}}" --description "Closes #{{issue_number}}"',
|
|
51
|
+
mergeCmd: 'glab mr merge --auto-merge',
|
|
52
|
+
mergeFallbackCmd: 'glab mr merge',
|
|
53
|
+
prUrlExample: 'https://gitlab.com/owner/repo/-/merge_requests/123',
|
|
54
|
+
outputFields: { urlField: 'mr_url', numberField: 'mr_number', mergedField: 'merged' },
|
|
55
|
+
},
|
|
56
|
+
'azure-devops': {
|
|
57
|
+
prName: 'PR',
|
|
58
|
+
prNameLower: 'pull request',
|
|
59
|
+
createCmd:
|
|
60
|
+
'az repos pr create --title "feat: {{issue_title}}" --description "Closes #{{issue_number}}"',
|
|
61
|
+
mergeCmd: 'az repos pr update --id <PR_ID> --auto-complete true',
|
|
62
|
+
mergeFallbackCmd: 'az repos pr update --id <PR_ID> --status completed',
|
|
63
|
+
prUrlExample: 'https://dev.azure.com/org/project/_git/repo/pullrequest/123',
|
|
64
|
+
outputFields: {
|
|
65
|
+
urlField: 'pr_url',
|
|
66
|
+
numberField: 'pr_number',
|
|
67
|
+
mergedField: 'merged',
|
|
68
|
+
autoCompleteField: 'auto_complete',
|
|
69
|
+
},
|
|
70
|
+
// Azure requires extracting PR ID from create output
|
|
71
|
+
requiresPrIdExtraction: true,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate the prompt for a specific platform
|
|
77
|
+
* @param {Object} config - Platform configuration from PLATFORM_CONFIGS
|
|
78
|
+
* @returns {string} The complete prompt with platform-specific commands
|
|
79
|
+
*/
|
|
80
|
+
function generatePrompt(config) {
|
|
81
|
+
const {
|
|
82
|
+
prName,
|
|
83
|
+
prNameLower,
|
|
84
|
+
createCmd,
|
|
85
|
+
mergeCmd,
|
|
86
|
+
mergeFallbackCmd,
|
|
87
|
+
prUrlExample,
|
|
88
|
+
outputFields,
|
|
89
|
+
requiresPrIdExtraction,
|
|
90
|
+
} = config;
|
|
91
|
+
|
|
92
|
+
// Azure-specific instructions for PR ID extraction
|
|
93
|
+
const azurePrIdNote = requiresPrIdExtraction
|
|
94
|
+
? `\n\n💡 IMPORTANT: The output will contain the PR ID. You MUST extract it for the next step.
|
|
95
|
+
Look for output like: "Created PR 123" or parse the URL for the PR number.
|
|
96
|
+
Save the PR ID to a variable for step 6.`
|
|
97
|
+
: '';
|
|
98
|
+
|
|
99
|
+
// Azure uses different merge terminology
|
|
100
|
+
const mergeDescription = requiresPrIdExtraction
|
|
101
|
+
? 'SET AUTO-COMPLETE (MANDATORY - THIS IS NOT OPTIONAL)'
|
|
102
|
+
: `MERGE THE ${prName} (MANDATORY - THIS IS NOT OPTIONAL)`;
|
|
103
|
+
|
|
104
|
+
const mergeExplanation = requiresPrIdExtraction
|
|
105
|
+
? `Replace <PR_ID> with the actual PR number from step 5.
|
|
106
|
+
This enables auto-complete (auto-merge when CI passes).
|
|
107
|
+
|
|
108
|
+
If auto-complete is not available or you need to merge immediately:`
|
|
109
|
+
: `This sets auto-merge. If it fails (e.g., no auto-merge enabled), try:`;
|
|
110
|
+
|
|
111
|
+
const postMergeStatus = requiresPrIdExtraction
|
|
112
|
+
? 'PR IS CREATED AND AUTO-COMPLETE IS SET'
|
|
113
|
+
: `${prName} IS MERGED`;
|
|
114
|
+
|
|
115
|
+
const finalOutputNote = requiresPrIdExtraction
|
|
116
|
+
? `ONLY after the PR is created and auto-complete is set, output:
|
|
117
|
+
\`\`\`json
|
|
118
|
+
{"${outputFields.urlField}": "${prUrlExample}", "${outputFields.numberField}": 123, "merged": false, "auto_complete": true}
|
|
119
|
+
\`\`\`
|
|
120
|
+
|
|
121
|
+
If truly no changes exist, output:
|
|
122
|
+
\`\`\`json
|
|
123
|
+
{"${outputFields.urlField}": null, "${outputFields.numberField}": null, "merged": false, "auto_complete": false}
|
|
124
|
+
\`\`\``
|
|
125
|
+
: `ONLY after the ${prName} is MERGED, output:
|
|
126
|
+
\`\`\`json
|
|
127
|
+
{"${outputFields.urlField}": "${prUrlExample}", "${outputFields.numberField}": 123, "merged": true}
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
If truly no changes exist, output:
|
|
131
|
+
\`\`\`json
|
|
132
|
+
{"${outputFields.urlField}": null, "${outputFields.numberField}": null, "merged": false}
|
|
133
|
+
\`\`\``;
|
|
134
|
+
|
|
135
|
+
return `🚨 CRITICAL: ALL VALIDATORS APPROVED. YOU MUST CREATE A ${prName} AND GET IT MERGED. DO NOT STOP UNTIL THE ${prName} IS MERGED. 🚨
|
|
136
|
+
|
|
137
|
+
## MANDATORY STEPS - EXECUTE EACH ONE IN ORDER - DO NOT SKIP ANY STEP
|
|
138
|
+
|
|
139
|
+
### STEP 1: Stage ALL changes (MANDATORY)
|
|
140
|
+
\`\`\`bash
|
|
141
|
+
git add -A
|
|
142
|
+
\`\`\`
|
|
143
|
+
Run this command. Do not skip it.
|
|
144
|
+
|
|
145
|
+
### STEP 2: Check what's staged
|
|
146
|
+
\`\`\`bash
|
|
147
|
+
git status
|
|
148
|
+
\`\`\`
|
|
149
|
+
Run this. If nothing to commit, output JSON with ${outputFields.urlField}: null and stop.
|
|
150
|
+
|
|
151
|
+
### STEP 3: Commit the changes (MANDATORY if there are changes)
|
|
152
|
+
\`\`\`bash
|
|
153
|
+
git commit -m "feat: implement #{{issue_number}} - {{issue_title}}"
|
|
154
|
+
\`\`\`
|
|
155
|
+
Run this command. Do not skip it.
|
|
156
|
+
|
|
157
|
+
### STEP 4: Push to origin (MANDATORY)
|
|
158
|
+
\`\`\`bash
|
|
159
|
+
git push -u origin HEAD
|
|
160
|
+
\`\`\`
|
|
161
|
+
Run this. If it fails, check the error and fix it.
|
|
162
|
+
|
|
163
|
+
⚠️ AFTER PUSH YOU ARE NOT DONE! CONTINUE TO STEP 5! ⚠️
|
|
164
|
+
|
|
165
|
+
### STEP 5: CREATE THE ${prName.toUpperCase()} (MANDATORY - YOU MUST RUN THIS COMMAND)
|
|
166
|
+
\`\`\`bash
|
|
167
|
+
${createCmd}
|
|
168
|
+
\`\`\`
|
|
169
|
+
🚨 YOU MUST RUN \`${createCmd.split(' ').slice(0, 3).join(' ')}\`! Outputting a link is NOT creating a ${prName}! 🚨
|
|
170
|
+
The push output shows a "Create a ${prNameLower}" link - IGNORE IT.
|
|
171
|
+
You MUST run the \`${createCmd.split(' ').slice(0, 3).join(' ')}\` command above.${requiresPrIdExtraction ? '' : ` Save the actual ${prName} URL from the output.`}${azurePrIdNote}
|
|
172
|
+
|
|
173
|
+
⚠️ AFTER ${prName} CREATION YOU ARE NOT DONE! CONTINUE TO STEP 6! ⚠️
|
|
174
|
+
|
|
175
|
+
### STEP 6: ${mergeDescription}
|
|
176
|
+
\`\`\`bash
|
|
177
|
+
${mergeCmd}
|
|
178
|
+
\`\`\`
|
|
179
|
+
${mergeExplanation}
|
|
180
|
+
\`\`\`bash
|
|
181
|
+
${mergeFallbackCmd}
|
|
182
|
+
\`\`\`
|
|
183
|
+
|
|
184
|
+
🚨 IF MERGE FAILS DUE TO CONFLICTS - YOU MUST RESOLVE THEM:
|
|
185
|
+
a) Pull latest main and rebase:
|
|
186
|
+
\`\`\`bash
|
|
187
|
+
git fetch origin main
|
|
188
|
+
git rebase origin/main
|
|
189
|
+
\`\`\`
|
|
190
|
+
b) If conflicts appear - RESOLVE THEM IMMEDIATELY:
|
|
191
|
+
- Read the conflicting files
|
|
192
|
+
- Make intelligent decisions about what code to keep
|
|
193
|
+
- Edit the files to resolve conflicts
|
|
194
|
+
- \`git add <resolved-files>\`
|
|
195
|
+
- \`git rebase --continue\`
|
|
196
|
+
c) Force push the resolved branch:
|
|
197
|
+
\`\`\`bash
|
|
198
|
+
git push --force-with-lease
|
|
199
|
+
\`\`\`
|
|
200
|
+
d) Retry merge:
|
|
201
|
+
\`\`\`bash
|
|
202
|
+
${mergeFallbackCmd}
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
REPEAT UNTIL MERGED. DO NOT GIVE UP. DO NOT SKIP. THE ${prName} MUST BE ${requiresPrIdExtraction ? 'SET TO AUTO-COMPLETE' : 'MERGED'}.
|
|
206
|
+
If merge is blocked by CI, wait and retry. ${requiresPrIdExtraction ? 'The auto-complete will merge when CI passes.' : 'If blocked by reviews, set auto-merge.'}
|
|
207
|
+
|
|
208
|
+
## CRITICAL RULES
|
|
209
|
+
- Execute EVERY step in order (1, 2, 3, 4, 5, 6)
|
|
210
|
+
- Do NOT skip git add -A
|
|
211
|
+
- Do NOT skip git commit
|
|
212
|
+
- Do NOT skip ${createCmd.split(' ').slice(0, 3).join(' ')} - THE TASK IS NOT DONE UNTIL ${prName} EXISTS
|
|
213
|
+
- Do NOT skip ${mergeCmd.split(' ').slice(0, 4).join(' ')} - THE TASK IS NOT DONE UNTIL ${postMergeStatus}${requiresPrIdExtraction ? '\n- MUST extract PR ID from step 5 output to use in step 6' : ''}
|
|
214
|
+
- If push fails, debug and fix it
|
|
215
|
+
- If ${prName} creation fails, debug and fix it
|
|
216
|
+
- If ${requiresPrIdExtraction ? 'auto-complete' : 'merge'} fails, debug and fix it
|
|
217
|
+
- DO NOT OUTPUT JSON UNTIL ${postMergeStatus}
|
|
218
|
+
- A link from git push is NOT a ${prName} - you must run ${createCmd.split(' ').slice(0, 3).join(' ')}
|
|
219
|
+
|
|
220
|
+
## Final Output
|
|
221
|
+
${finalOutputNote}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Generate a git-pusher agent configuration for a specific platform
|
|
226
|
+
*
|
|
227
|
+
* @param {string} platform - Platform ID ('github', 'gitlab', 'azure-devops')
|
|
228
|
+
* @returns {Object} Agent configuration object
|
|
229
|
+
* @throws {Error} If platform is not supported
|
|
230
|
+
*/
|
|
231
|
+
function generateGitPusherAgent(platform) {
|
|
232
|
+
const config = PLATFORM_CONFIGS[platform];
|
|
233
|
+
|
|
234
|
+
if (!config) {
|
|
235
|
+
const supported = Object.keys(PLATFORM_CONFIGS).join(', ');
|
|
236
|
+
throw new Error(`Unsupported platform '${platform}'. Supported: ${supported}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
id: 'git-pusher',
|
|
241
|
+
role: 'completion-detector',
|
|
242
|
+
modelLevel: 'level2',
|
|
243
|
+
triggers: [
|
|
244
|
+
{
|
|
245
|
+
topic: 'VALIDATION_RESULT',
|
|
246
|
+
logic: {
|
|
247
|
+
engine: 'javascript',
|
|
248
|
+
script: SHARED_TRIGGER_SCRIPT,
|
|
249
|
+
},
|
|
250
|
+
action: 'execute_task',
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
prompt: generatePrompt(config),
|
|
254
|
+
output: {
|
|
255
|
+
topic: 'PR_CREATED',
|
|
256
|
+
publishAfter: 'CLUSTER_COMPLETE',
|
|
257
|
+
},
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get list of supported platforms for git-pusher
|
|
263
|
+
* @returns {string[]} Array of platform IDs
|
|
264
|
+
*/
|
|
265
|
+
function getSupportedPlatforms() {
|
|
266
|
+
return Object.keys(PLATFORM_CONFIGS);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Check if a platform supports git-pusher (PR/MR creation)
|
|
271
|
+
* @param {string} platform - Platform ID
|
|
272
|
+
* @returns {boolean}
|
|
273
|
+
*/
|
|
274
|
+
function isPlatformSupported(platform) {
|
|
275
|
+
return platform in PLATFORM_CONFIGS;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
module.exports = {
|
|
279
|
+
generateGitPusherAgent,
|
|
280
|
+
getSupportedPlatforms,
|
|
281
|
+
isPlatformSupported,
|
|
282
|
+
// Export for testing
|
|
283
|
+
SHARED_TRIGGER_SCRIPT,
|
|
284
|
+
PLATFORM_CONFIGS,
|
|
285
|
+
};
|