@syntesseraai/opencode-feature-factory 0.10.0 → 0.10.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/README.md +17 -16
- package/agents/building.md +1 -1
- package/agents/documenting.md +1 -1
- package/agents/planning.md +1 -1
- package/agents/reviewing.md +1 -1
- package/dist/tools/mini-loop.js +16 -6
- package/dist/tools/pipeline.js +30 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,35 +119,36 @@ Both `ff_pipeline` and `ff_mini_loop` tools run asynchronously with real-time pr
|
|
|
119
119
|
- **Background orchestration**: The full pipeline or mini-loop runs in a detached `Promise`. All child session orchestration (fan-out, gates, loops) remains identical — it just executes after the tool returns.
|
|
120
120
|
- **Progress updates via `promptAsync(noReply: true)`**: After each major phase completes, a structured notification is injected into the parent session as a visible chat message. These appear in the OpenCode TUI without triggering an LLM turn.
|
|
121
121
|
- **Phase-by-phase visibility**: Users see updates for planning, building, each review iteration gate decision, each documentation iteration, and the final completion report.
|
|
122
|
-
- **Error notifications**: If the background orchestration throws, a
|
|
122
|
+
- **Error notifications**: If the background orchestration throws, a `# Pipeline: Error` or `# Mini-Loop: Error` notification is sent with the last phase and error message.
|
|
123
123
|
- **`context.metadata()` retained**: All existing metadata calls remain in place for future-proofing (when OpenCode's TUI renders tool metadata natively).
|
|
124
124
|
|
|
125
125
|
### Notification Format
|
|
126
126
|
|
|
127
|
-
Pipeline
|
|
127
|
+
Pipeline notifications use plain-text markdown headers with phase START/END bracketing and per-iteration gate details:
|
|
128
128
|
|
|
129
129
|
```
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
# Pipeline: Reviewing — Iteration 2/10
|
|
131
|
+
|
|
132
132
|
Status: APPROVED
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
Confidence: 97%
|
|
134
|
+
Unresolved Issues: 0
|
|
135
|
+
Duration: 45.3s
|
|
136
|
+
Feedback: N/A
|
|
136
137
|
```
|
|
137
138
|
|
|
138
|
-
Mini-loop
|
|
139
|
+
Mini-loop notifications follow the same pattern:
|
|
139
140
|
|
|
140
141
|
```
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Status:
|
|
144
|
-
Confidence:
|
|
145
|
-
|
|
146
|
-
Duration:
|
|
147
|
-
|
|
142
|
+
# Mini-Loop: Building — Iteration 1/10
|
|
143
|
+
|
|
144
|
+
Status: REWORK
|
|
145
|
+
Confidence: 82%
|
|
146
|
+
Unresolved Issues: 2
|
|
147
|
+
Duration: 23.1s
|
|
148
|
+
Feedback: Fix type errors in handler.ts
|
|
148
149
|
```
|
|
149
150
|
|
|
150
|
-
Final reports
|
|
151
|
+
Final reports use `# Pipeline: Complete` or `# Mini-Loop: Complete` headers containing the full markdown report. Errors use `# Pipeline: Error` or `# Mini-Loop: Error`.
|
|
151
152
|
|
|
152
153
|
## Related Docs
|
|
153
154
|
|
package/agents/building.md
CHANGED
package/agents/documenting.md
CHANGED
package/agents/planning.md
CHANGED
package/agents/reviewing.md
CHANGED
package/dist/tools/mini-loop.js
CHANGED
|
@@ -79,6 +79,8 @@ export function createMiniLoopTool(client) {
|
|
|
79
79
|
const implementationIterationDetails = [];
|
|
80
80
|
let implGate = { decision: 'REWORK', feedback: requirements };
|
|
81
81
|
let lastImplRaw = '';
|
|
82
|
+
// Phase start notification
|
|
83
|
+
await notify(`# Mini-Loop: Building started\n\nStarting implementation phase...\n`);
|
|
82
84
|
for (let implIter = 0; implIter < 10 && implGate.decision === 'REWORK'; implIter++) {
|
|
83
85
|
const iteration = implIter + 1;
|
|
84
86
|
const buildTitle = `ff-mini-build-${iteration}`;
|
|
@@ -165,10 +167,12 @@ export function createMiniLoopTool(client) {
|
|
|
165
167
|
`- **Gate**: ${implGate.decision} (confidence: ${review.confidence}, change requested: ${review.changeRequested ? 'yes' : 'no'}, unresolved: ${review.unresolvedIssues})\n` +
|
|
166
168
|
`- **Feedback**: ${feedback}`);
|
|
167
169
|
// Notify each implementation iteration gate decision
|
|
168
|
-
await notify(
|
|
170
|
+
await notify(`# Mini-Loop: Building — Iteration ${iteration}/10\n\nStatus: ${implGate.decision}\nConfidence: ${review.confidence}%\nUnresolved Issues: ${review.unresolvedIssues}\nDuration: ${formatElapsed(implementationStartMs, Date.now())}\nFeedback: ${feedback}\n`);
|
|
169
171
|
if (implGate.decision === 'ESCALATE') {
|
|
170
172
|
const implementationEndMs = Date.now();
|
|
171
173
|
addReport('Implementation', `${implementationIterationDetails.join('\n\n')}\n\n**Outcome**: ESCALATE: ${implGate.reason}\n**Phase time**: ${formatElapsed(implementationStartMs, implementationEndMs)}`);
|
|
174
|
+
// Phase end notification
|
|
175
|
+
await notify(`# Mini-Loop: Building ended\n\nOutcome: ESCALATE\nReason: ${implGate.reason}\nIterations: ${iteration}\nPhase time: ${formatElapsed(implementationStartMs, implementationEndMs)}\n`);
|
|
172
176
|
context.metadata({
|
|
173
177
|
title: '⚠️ Mini-loop finished with issues',
|
|
174
178
|
metadata: {
|
|
@@ -178,7 +182,7 @@ export function createMiniLoopTool(client) {
|
|
|
178
182
|
},
|
|
179
183
|
});
|
|
180
184
|
addReport('Complete', `Mini-loop finished with issues.\n**Total time**: ${formatElapsed(totalStartMs, Date.now())}`);
|
|
181
|
-
await notify(
|
|
185
|
+
await notify(`# Mini-Loop: Complete\n\n${report.join('\n\n')}\n`);
|
|
182
186
|
return;
|
|
183
187
|
}
|
|
184
188
|
// REWORK continues the loop
|
|
@@ -187,6 +191,8 @@ export function createMiniLoopTool(client) {
|
|
|
187
191
|
addReport('Implementation', `${implementationIterationDetails.join('\n\n')}\n\n**Outcome**: ${implGate.decision === 'APPROVED'
|
|
188
192
|
? 'APPROVED'
|
|
189
193
|
: `REWORK exhausted (10 iterations). Last feedback: ${implGate.feedback}`}\n**Phase time**: ${formatElapsed(implementationStartMs, implementationEndMs)}`);
|
|
194
|
+
// Phase end notification
|
|
195
|
+
await notify(`# Mini-Loop: Building ended\n\nOutcome: ${implGate.decision}\nIterations: ${implGate.decision === 'APPROVED' ? 'converged' : '10 (exhausted)'}\nPhase time: ${formatElapsed(implementationStartMs, implementationEndMs)}\n`);
|
|
190
196
|
if (implGate.decision !== 'APPROVED') {
|
|
191
197
|
context.metadata({
|
|
192
198
|
title: '⚠️ Mini-loop finished with issues',
|
|
@@ -197,7 +203,7 @@ export function createMiniLoopTool(client) {
|
|
|
197
203
|
},
|
|
198
204
|
});
|
|
199
205
|
addReport('Complete', `Mini-loop finished with issues.\n**Total time**: ${formatElapsed(totalStartMs, Date.now())}`);
|
|
200
|
-
await notify(
|
|
206
|
+
await notify(`# Mini-Loop: Complete\n\n${report.join('\n\n')}\n`);
|
|
201
207
|
return;
|
|
202
208
|
}
|
|
203
209
|
// ===================================================================
|
|
@@ -208,6 +214,8 @@ export function createMiniLoopTool(client) {
|
|
|
208
214
|
const documentationIterationDetails = [];
|
|
209
215
|
let docInput = lastImplRaw;
|
|
210
216
|
let docGate = { decision: 'REWORK' };
|
|
217
|
+
// Phase start notification
|
|
218
|
+
await notify(`# Mini-Loop: Documentation started\n\nStarting documentation phase...\n`);
|
|
211
219
|
for (let docIter = 0; docIter < 5 && docGate.decision === 'REWORK'; docIter++) {
|
|
212
220
|
const iteration = docIter + 1;
|
|
213
221
|
const writeTitle = `ff-mini-doc-write-${iteration}`;
|
|
@@ -295,7 +303,7 @@ export function createMiniLoopTool(client) {
|
|
|
295
303
|
`- **Gate**: ${docGate.decision} (confidence: ${docReview.confidence}, unresolved: ${docReview.unresolvedIssues})\n` +
|
|
296
304
|
`- **Feedback**: ${feedback}`);
|
|
297
305
|
// Notify each documentation iteration gate decision
|
|
298
|
-
await notify(
|
|
306
|
+
await notify(`# Mini-Loop: Documentation — Iteration ${iteration}/5\n\nStatus: ${docGate.decision}\nConfidence: ${docReview.confidence}%\nUnresolved Issues: ${docReview.unresolvedIssues}\nDuration: ${formatElapsed(documentationStartMs, Date.now())}\nFeedback: ${feedback}\n`);
|
|
299
307
|
if (docGate.decision === 'REWORK') {
|
|
300
308
|
docInput = `${docInput}\n\nDocumentation review feedback:\n${docGate.feedback}`;
|
|
301
309
|
}
|
|
@@ -306,6 +314,8 @@ export function createMiniLoopTool(client) {
|
|
|
306
314
|
: docGate.decision === 'ESCALATE'
|
|
307
315
|
? `ESCALATE: ${docGate.reason}`
|
|
308
316
|
: `REWORK exhausted (5 iterations). Last feedback: ${docGate.feedback}`}\n**Phase time**: ${formatElapsed(documentationStartMs, documentationEndMs)}`);
|
|
317
|
+
// Phase end notification
|
|
318
|
+
await notify(`# Mini-Loop: Documentation ended\n\nOutcome: ${docGate.decision}\nConfidence: ${docGate.decision === 'APPROVED' ? 'converged' : 'N/A'}\nPhase time: ${formatElapsed(documentationStartMs, documentationEndMs)}\n`);
|
|
309
319
|
const totalEndMs = Date.now();
|
|
310
320
|
const completedWithoutIssues = docGate.decision === 'APPROVED';
|
|
311
321
|
context.metadata({
|
|
@@ -320,12 +330,12 @@ export function createMiniLoopTool(client) {
|
|
|
320
330
|
});
|
|
321
331
|
addReport('Complete', `${completedWithoutIssues ? 'Mini-loop finished successfully.' : 'Mini-loop finished with issues.'}\n**Total time**: ${formatElapsed(totalStartMs, totalEndMs)}`);
|
|
322
332
|
// Send final completion report as notification
|
|
323
|
-
await notify(
|
|
333
|
+
await notify(`# Mini-Loop: Complete\n\n${report.join('\n\n')}\n`);
|
|
324
334
|
}; // end asyncOrchestration
|
|
325
335
|
// Launch orchestration in background — fire-and-forget
|
|
326
336
|
void asyncOrchestration().catch(async (err) => {
|
|
327
337
|
const message = err instanceof Error ? err.message : String(err);
|
|
328
|
-
await notifyParent(client, sessionId, agent,
|
|
338
|
+
await notifyParent(client, sessionId, agent, `# Mini-Loop: Error\n\nPhase: ${lastPhase}\nError: ${message}\n`);
|
|
329
339
|
});
|
|
330
340
|
// Return immediately with acknowledgment
|
|
331
341
|
const summary = requirements.length > 120 ? requirements.slice(0, 120) + '...' : requirements;
|
package/dist/tools/pipeline.js
CHANGED
|
@@ -108,6 +108,8 @@ export function createPipelineTool(client) {
|
|
|
108
108
|
const planningIterationDetails = [];
|
|
109
109
|
let planningGate = { decision: 'REWORK', feedback: requirements };
|
|
110
110
|
let finalPlan = '';
|
|
111
|
+
// Phase start notification
|
|
112
|
+
await notify(`# Pipeline: Planning started\n\nStarting planning phase with ${planModels.length} models...\n`);
|
|
111
113
|
for (let planIter = 0; planIter < 5 && planningGate.decision === 'REWORK'; planIter++) {
|
|
112
114
|
const iteration = planIter + 1;
|
|
113
115
|
const synthesisTitle = `ff-plan-synthesis-${iteration}`;
|
|
@@ -197,9 +199,13 @@ export function createPipelineTool(client) {
|
|
|
197
199
|
`- **Synthesis**: ${synthesisTitle} (${formatElapsed(synthesisStartMs, synthesisEndMs)})\n` +
|
|
198
200
|
`- **Gate**: ${planningGate.decision} (score: ${consensus.consensusScore})\n` +
|
|
199
201
|
`- **Feedback**: ${feedback}`);
|
|
202
|
+
// Notify each planning iteration gate decision
|
|
203
|
+
await notify(`# Pipeline: Planning — Iteration ${iteration}/5\n\nStatus: ${planningGate.decision}\nConsensus Score: ${consensus.consensusScore}%\nDuration: ${formatElapsed(planningStartMs, Date.now())}\nFeedback: ${feedback}\n`);
|
|
200
204
|
if (planningGate.decision === 'BLOCKED') {
|
|
201
205
|
const planningEndMs = Date.now();
|
|
202
206
|
addReport('Planning', `${planningIterationDetails.join('\n\n')}\n\n**Outcome**: BLOCKED: ${planningGate.reason}\n**Phase time**: ${formatElapsed(planningStartMs, planningEndMs)}`);
|
|
207
|
+
// Phase end notification
|
|
208
|
+
await notify(`# Pipeline: Planning ended\n\nOutcome: BLOCKED\nReason: ${planningGate.reason}\nConsensus Score: ${consensus.consensusScore}%\nIterations: ${iteration}/5\nPhase time: ${formatElapsed(planningStartMs, planningEndMs)}\n`);
|
|
203
209
|
context.metadata({
|
|
204
210
|
title: '⚠️ Pipeline finished with issues',
|
|
205
211
|
metadata: {
|
|
@@ -209,8 +215,7 @@ export function createPipelineTool(client) {
|
|
|
209
215
|
},
|
|
210
216
|
});
|
|
211
217
|
addReport('Complete', `Pipeline finished with issues.\n**Total time**: ${formatElapsed(totalStartMs, Date.now())}`);
|
|
212
|
-
await notify(
|
|
213
|
-
await notify(`<ff_pipeline_complete>\n${report.join('\n\n')}\n</ff_pipeline_complete>`);
|
|
218
|
+
await notify(`# Pipeline: Complete\n\n${report.join('\n\n')}\n`);
|
|
214
219
|
return;
|
|
215
220
|
}
|
|
216
221
|
// REWORK continues the loop
|
|
@@ -219,8 +224,8 @@ export function createPipelineTool(client) {
|
|
|
219
224
|
addReport('Planning', `${planningIterationDetails.join('\n\n')}\n\n**Outcome**: ${planningGate.decision === 'APPROVED'
|
|
220
225
|
? 'APPROVED'
|
|
221
226
|
: `REWORK exhausted (5 iterations). Last feedback: ${planningGate.feedback}`}\n**Phase time**: ${formatElapsed(planningStartMs, planningEndMs)}`);
|
|
222
|
-
//
|
|
223
|
-
await notify(
|
|
227
|
+
// Phase end notification
|
|
228
|
+
await notify(`# Pipeline: Planning ended\n\nOutcome: ${planningGate.decision}\nDuration: ${formatElapsed(planningStartMs, planningEndMs)}\nNext Phase: ${planningGate.decision === 'APPROVED' ? 'Building' : 'Finished'}\n`);
|
|
224
229
|
if (planningGate.decision !== 'APPROVED') {
|
|
225
230
|
context.metadata({
|
|
226
231
|
title: '⚠️ Pipeline finished with issues',
|
|
@@ -231,7 +236,7 @@ export function createPipelineTool(client) {
|
|
|
231
236
|
},
|
|
232
237
|
});
|
|
233
238
|
addReport('Complete', `Pipeline finished with issues.\n**Total time**: ${formatElapsed(totalStartMs, Date.now())}`);
|
|
234
|
-
await notify(
|
|
239
|
+
await notify(`# Pipeline: Complete\n\n${report.join('\n\n')}\n`);
|
|
235
240
|
return;
|
|
236
241
|
}
|
|
237
242
|
// ===================================================================
|
|
@@ -239,6 +244,8 @@ export function createPipelineTool(client) {
|
|
|
239
244
|
// ===================================================================
|
|
240
245
|
lastPhase = 'Building';
|
|
241
246
|
const buildingStartMs = Date.now();
|
|
247
|
+
// Phase start notification
|
|
248
|
+
await notify(`# Pipeline: Building started\n\nStarting build phase (breakdown → validate → implement)...\n`);
|
|
242
249
|
context.metadata({
|
|
243
250
|
title: '⏳ Breaking down tasks...',
|
|
244
251
|
metadata: {
|
|
@@ -296,8 +303,8 @@ export function createPipelineTool(client) {
|
|
|
296
303
|
`- **Tests passed**: ${implementation.testsPassed}\n` +
|
|
297
304
|
`- **Open issues**: ${implementation.openIssues.length > 0 ? implementation.openIssues.join('; ') : 'none'}\n\n` +
|
|
298
305
|
`**Phase time**: ${formatElapsed(buildingStartMs, buildingEndMs)}`);
|
|
299
|
-
//
|
|
300
|
-
await notify(
|
|
306
|
+
// Phase end notification
|
|
307
|
+
await notify(`# Pipeline: Building ended\n\nOutcome: COMPLETE\nFiles Changed: ${implementation.filesChanged.length}\nTests Passed: ${implementation.testsPassed}\nOpen Issues: ${implementation.openIssues.length > 0 ? implementation.openIssues.join('; ') : 'none'}\nPhase time: ${formatElapsed(buildingStartMs, buildingEndMs)}\n`);
|
|
301
308
|
// ===================================================================
|
|
302
309
|
// PHASE 3: REVIEWING (triage → fan-out review → synthesize → gate, loop up to 10)
|
|
303
310
|
// ===================================================================
|
|
@@ -306,6 +313,8 @@ export function createPipelineTool(client) {
|
|
|
306
313
|
const reviewIterationDetails = [];
|
|
307
314
|
let reviewInput = implRaw;
|
|
308
315
|
let reviewGate = { decision: 'REWORK' };
|
|
316
|
+
// Phase start notification
|
|
317
|
+
await notify(`# Pipeline: Reviewing started\n\nStarting review phase with ${revModels.length} models...\n`);
|
|
309
318
|
for (let revIter = 0; revIter < 10 && reviewGate.decision === 'REWORK'; revIter++) {
|
|
310
319
|
const iteration = revIter + 1;
|
|
311
320
|
const triageTitle = `ff-review-triage-${iteration}`;
|
|
@@ -435,10 +444,12 @@ export function createPipelineTool(client) {
|
|
|
435
444
|
`${reworkLine}\n` +
|
|
436
445
|
`- **Feedback**: ${feedback}`);
|
|
437
446
|
// Notify each review iteration gate decision
|
|
438
|
-
await notify(
|
|
447
|
+
await notify(`# Pipeline: Reviewing — Iteration ${iteration}/10\n\nStatus: ${reviewGate.decision}\nConfidence: ${synthesis.overallConfidence}%\nUnresolved Issues: ${synthesis.unresolvedIssues}\nDuration: ${formatElapsed(reviewStartMs, Date.now())}\nFeedback: ${feedback}\n`);
|
|
439
448
|
if (reviewGate.decision === 'ESCALATE') {
|
|
440
449
|
const reviewEndMs = Date.now();
|
|
441
450
|
addReport('Reviewing', `${reviewIterationDetails.join('\n\n')}\n\n**Outcome**: ESCALATE: ${reviewGate.reason}\n**Phase time**: ${formatElapsed(reviewStartMs, reviewEndMs)}`);
|
|
451
|
+
// Phase end notification
|
|
452
|
+
await notify(`# Pipeline: Reviewing ended\n\nOutcome: ESCALATE\nReason: ${reviewGate.reason}\nIterations: ${iteration}\nPhase time: ${formatElapsed(reviewStartMs, reviewEndMs)}\n`);
|
|
442
453
|
context.metadata({
|
|
443
454
|
title: '⚠️ Pipeline finished with issues',
|
|
444
455
|
metadata: {
|
|
@@ -448,7 +459,7 @@ export function createPipelineTool(client) {
|
|
|
448
459
|
},
|
|
449
460
|
});
|
|
450
461
|
addReport('Complete', `Pipeline finished with issues.\n**Total time**: ${formatElapsed(totalStartMs, Date.now())}`);
|
|
451
|
-
await notify(
|
|
462
|
+
await notify(`# Pipeline: Complete\n\n${report.join('\n\n')}\n`);
|
|
452
463
|
return;
|
|
453
464
|
}
|
|
454
465
|
}
|
|
@@ -456,6 +467,8 @@ export function createPipelineTool(client) {
|
|
|
456
467
|
addReport('Reviewing', `${reviewIterationDetails.join('\n\n')}\n\n**Outcome**: ${reviewGate.decision === 'APPROVED'
|
|
457
468
|
? 'APPROVED'
|
|
458
469
|
: `REWORK exhausted (10 iterations). Last feedback: ${reviewGate.feedback}`}\n**Phase time**: ${formatElapsed(reviewStartMs, reviewEndMs)}`);
|
|
470
|
+
// Phase end notification
|
|
471
|
+
await notify(`# Pipeline: Reviewing ended\n\nOutcome: ${reviewGate.decision}\nDuration: ${formatElapsed(reviewStartMs, reviewEndMs)}\n`);
|
|
459
472
|
if (reviewGate.decision !== 'APPROVED') {
|
|
460
473
|
context.metadata({
|
|
461
474
|
title: '⚠️ Pipeline finished with issues',
|
|
@@ -466,7 +479,7 @@ export function createPipelineTool(client) {
|
|
|
466
479
|
},
|
|
467
480
|
});
|
|
468
481
|
addReport('Complete', `Pipeline finished with issues.\n**Total time**: ${formatElapsed(totalStartMs, Date.now())}`);
|
|
469
|
-
await notify(
|
|
482
|
+
await notify(`# Pipeline: Complete\n\n${report.join('\n\n')}\n`);
|
|
470
483
|
return;
|
|
471
484
|
}
|
|
472
485
|
// ===================================================================
|
|
@@ -477,6 +490,8 @@ export function createPipelineTool(client) {
|
|
|
477
490
|
const documentationIterationDetails = [];
|
|
478
491
|
let docInput = `Implementation report:\n${implRaw}\n\nReview synthesis:\n${reviewInput}`;
|
|
479
492
|
let docGate = { decision: 'REWORK' };
|
|
493
|
+
// Phase start notification
|
|
494
|
+
await notify(`# Pipeline: Documentation started\n\nStarting documentation phase...\n`);
|
|
480
495
|
for (let docIter = 0; docIter < 5 && docGate.decision === 'REWORK'; docIter++) {
|
|
481
496
|
const iteration = docIter + 1;
|
|
482
497
|
const writeTitle = `ff-doc-write-${iteration}`;
|
|
@@ -567,7 +582,7 @@ export function createPipelineTool(client) {
|
|
|
567
582
|
`- **Gate**: ${docGate.decision} (confidence: ${docReview.confidence}, unresolved: ${docReview.unresolvedIssues})\n` +
|
|
568
583
|
`- **Feedback**: ${feedback}`);
|
|
569
584
|
// Notify each documentation iteration gate decision
|
|
570
|
-
await notify(
|
|
585
|
+
await notify(`# Pipeline: Documentation — Iteration ${iteration}/5\n\nStatus: ${docGate.decision}\nConfidence: ${docReview.confidence}%\nUnresolved Issues: ${docReview.unresolvedIssues}\nDuration: ${formatElapsed(documentationStartMs, Date.now())}\nFeedback: ${feedback}\n`);
|
|
571
586
|
if (docGate.decision === 'REWORK') {
|
|
572
587
|
// Feed feedback into the next iteration
|
|
573
588
|
docInput = `${docInput}\n\nDocumentation review feedback:\n${docGate.feedback}`;
|
|
@@ -579,6 +594,8 @@ export function createPipelineTool(client) {
|
|
|
579
594
|
: docGate.decision === 'ESCALATE'
|
|
580
595
|
? `ESCALATE: ${docGate.reason}`
|
|
581
596
|
: `REWORK exhausted (5 iterations). Last feedback: ${docGate.feedback}`}\n**Phase time**: ${formatElapsed(documentationStartMs, documentationEndMs)}`);
|
|
597
|
+
// Phase end notification
|
|
598
|
+
await notify(`# Pipeline: Documentation ended\n\nOutcome: ${docGate.decision}\nPhase time: ${formatElapsed(documentationStartMs, documentationEndMs)}\n`);
|
|
582
599
|
// ===================================================================
|
|
583
600
|
// FINAL REPORT
|
|
584
601
|
// ===================================================================
|
|
@@ -596,12 +613,12 @@ export function createPipelineTool(client) {
|
|
|
596
613
|
});
|
|
597
614
|
addReport('Complete', `${completedWithoutIssues ? 'Pipeline finished successfully.' : 'Pipeline finished with issues.'}\n**Total time**: ${formatElapsed(totalStartMs, totalEndMs)}`);
|
|
598
615
|
// Send final completion report as notification
|
|
599
|
-
await notify(
|
|
616
|
+
await notify(`# Pipeline: Complete\n\n${report.join('\n\n')}\n`);
|
|
600
617
|
}; // end asyncOrchestration
|
|
601
618
|
// Launch orchestration in background — fire-and-forget
|
|
602
619
|
void asyncOrchestration().catch(async (err) => {
|
|
603
620
|
const message = err instanceof Error ? err.message : String(err);
|
|
604
|
-
await notifyParent(client, sessionId, agent,
|
|
621
|
+
await notifyParent(client, sessionId, agent, `# Pipeline: Error\n\nPhase: ${lastPhase}\nError: ${message}\n`);
|
|
605
622
|
});
|
|
606
623
|
// Return immediately with acknowledgment
|
|
607
624
|
const summary = requirements.length > 120 ? requirements.slice(0, 120) + '...' : requirements;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@syntesseraai/opencode-feature-factory",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "OpenCode plugin for Feature Factory agents - provides sub-agents and skills for validation, review, security, and architecture assessment",
|
|
7
7
|
"license": "MIT",
|