@syntesseraai/opencode-feature-factory 0.6.20 → 0.7.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/command/mini-loop/documentation/run.md +3 -1
- package/command/mini-loop/implementation/run.md +3 -1
- package/command/mini-loop/start.md +3 -1
- package/command/pipeline/building/run.md +3 -1
- package/command/pipeline/documentation/run.md +3 -1
- package/command/pipeline/planning/run.md +3 -1
- package/command/pipeline/reviewing/run.md +3 -1
- package/command/pipeline/start.md +3 -1
- package/dist/command-config.d.ts +38 -0
- package/dist/command-config.js +128 -0
- package/dist/index.js +18 -2
- package/dist/tools/index.d.ts +16 -0
- package/dist/tools/index.js +21 -0
- package/dist/tools/mini-loop.d.ts +24 -0
- package/dist/tools/mini-loop.js +133 -0
- package/dist/tools/parsers.d.ts +28 -0
- package/dist/tools/parsers.js +142 -0
- package/dist/tools/pipeline.d.ts +32 -0
- package/dist/tools/pipeline.js +227 -0
- package/dist/tools/prompts.d.ts +24 -0
- package/dist/tools/prompts.js +182 -0
- package/dist/workflow/fan-out.d.ts +84 -0
- package/dist/workflow/fan-out.js +83 -0
- package/dist/workflow/gate-evaluator.d.ts +50 -0
- package/dist/workflow/gate-evaluator.js +118 -0
- package/dist/workflow/orchestrator.d.ts +9 -0
- package/dist/workflow/orchestrator.js +9 -0
- package/dist/workflow/types.d.ts +148 -0
- package/dist/workflow/types.js +62 -0
- package/package.json +1 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response parsers that extract structured data from LLM text output.
|
|
3
|
+
*
|
|
4
|
+
* These are intentionally tolerant — they fall back to raw text when
|
|
5
|
+
* structured sections cannot be found. The gate evaluators work on
|
|
6
|
+
* the parsed numbers/booleans, not on freeform text.
|
|
7
|
+
*/
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Generic helpers
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
/** Extract the first integer found after a label in the text. */
|
|
12
|
+
export function extractNumber(text, label, fallback = 0) {
|
|
13
|
+
// Try patterns like "CONSENSUS_SCORE: 85", "confidence: 92", "CONFIDENCE=95"
|
|
14
|
+
const patterns = [
|
|
15
|
+
new RegExp(`${label}[=:\\s]+([0-9]+)`, 'i'),
|
|
16
|
+
new RegExp(`${label}[^0-9]*([0-9]+)`, 'i'),
|
|
17
|
+
];
|
|
18
|
+
for (const re of patterns) {
|
|
19
|
+
const m = text.match(re);
|
|
20
|
+
if (m)
|
|
21
|
+
return parseInt(m[1], 10);
|
|
22
|
+
}
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
/** Extract text between two section headers (markdown-style). */
|
|
26
|
+
export function extractSection(text, heading) {
|
|
27
|
+
// Try numbered list style: "1. HEADING" or "## HEADING" or "**HEADING**"
|
|
28
|
+
const patterns = [
|
|
29
|
+
new RegExp(`(?:^|\\n)(?:\\d+\\.\\s*)?\\*{0,2}${heading}\\*{0,2}[:\\s]*\\n([\\s\\S]*?)(?=\\n(?:\\d+\\.\\s*)?\\*{0,2}[A-Z_]+|$)`, 'i'),
|
|
30
|
+
new RegExp(`(?:^|\\n)#{1,3}\\s*${heading}[:\\s]*\\n([\\s\\S]*?)(?=\\n#{1,3}\\s|$)`, 'i'),
|
|
31
|
+
];
|
|
32
|
+
for (const re of patterns) {
|
|
33
|
+
const m = text.match(re);
|
|
34
|
+
if (m)
|
|
35
|
+
return m[1].trim();
|
|
36
|
+
}
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
/** Check if a YES/NO field is YES. */
|
|
40
|
+
export function isYes(text, label) {
|
|
41
|
+
const m = text.match(new RegExp(`${label}[=:\\s]*(YES|NO)`, 'i'));
|
|
42
|
+
return m ? m[1].toUpperCase() === 'YES' : false;
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Planning parsers
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
export function parsePlanProposal(tag, raw) {
|
|
48
|
+
return {
|
|
49
|
+
tag,
|
|
50
|
+
requirementsSummary: extractSection(raw, 'REQUIREMENTS_SUMMARY') || raw.slice(0, 200),
|
|
51
|
+
architectureValidation: extractSection(raw, 'ARCHITECTURE_VALIDATION'),
|
|
52
|
+
implementationSteps: extractSection(raw, 'IMPLEMENTATION_STEPS'),
|
|
53
|
+
risksAndMitigations: extractSection(raw, 'RISKS_AND_MITIGATIONS'),
|
|
54
|
+
testingStrategy: extractSection(raw, 'TESTING_AND_VALIDATION_STRATEGY'),
|
|
55
|
+
raw,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function parseConsensusPlan(raw) {
|
|
59
|
+
return {
|
|
60
|
+
consensusScore: extractNumber(raw, 'CONSENSUS_SCORE'),
|
|
61
|
+
agreedElements: extractSection(raw, 'AGREED_ELEMENTS'),
|
|
62
|
+
divergentElements: extractSection(raw, 'DIVERGENT_ELEMENTS'),
|
|
63
|
+
synthesizedPlan: extractSection(raw, 'SYNTHESIZED_PLAN'),
|
|
64
|
+
openQuestions: extractSection(raw, 'OPEN_QUESTIONS'),
|
|
65
|
+
raw,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Review parsers
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
export function parseReviewReport(tag, raw) {
|
|
72
|
+
return {
|
|
73
|
+
tag,
|
|
74
|
+
findings: raw,
|
|
75
|
+
confidence: extractNumber(raw, 'confidence'),
|
|
76
|
+
raw,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function parseReviewSynthesis(raw) {
|
|
80
|
+
const confidence = extractNumber(raw, 'confidence');
|
|
81
|
+
const unresolvedIssues = extractNumber(raw, 'unresolved');
|
|
82
|
+
const verdictMatch = raw.match(/verdict[=:\s]*(APPROVED|REWORK_REQUIRED)/i);
|
|
83
|
+
const verdict = (verdictMatch?.[1]?.toUpperCase() === 'APPROVED' ? 'APPROVED' : 'REWORK_REQUIRED');
|
|
84
|
+
const reworkSection = extractSection(raw, 'rework');
|
|
85
|
+
return {
|
|
86
|
+
overallConfidence: confidence,
|
|
87
|
+
consolidatedFindings: raw,
|
|
88
|
+
unresolvedIssues,
|
|
89
|
+
verdict,
|
|
90
|
+
reworkInstructions: reworkSection || undefined,
|
|
91
|
+
raw,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Doc parsers
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
export function parseDocUpdate(raw) {
|
|
98
|
+
return {
|
|
99
|
+
filesChanged: [], // The LLM lists these in the raw text; we keep the raw for the gate
|
|
100
|
+
rationale: raw,
|
|
101
|
+
raw,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function parseDocReview(raw) {
|
|
105
|
+
const confidence = extractNumber(raw, 'confidence');
|
|
106
|
+
const unresolvedIssues = extractNumber(raw, 'unresolved');
|
|
107
|
+
const verdictMatch = raw.match(/verdict[=:\s]*(APPROVED|REWORK_REQUIRED)/i);
|
|
108
|
+
const verdict = (verdictMatch?.[1]?.toUpperCase() === 'APPROVED' ? 'APPROVED' : 'REWORK_REQUIRED');
|
|
109
|
+
const reworkSection = extractSection(raw, 'rework');
|
|
110
|
+
return {
|
|
111
|
+
verdict,
|
|
112
|
+
unresolvedIssues,
|
|
113
|
+
confidence,
|
|
114
|
+
reworkInstructions: reworkSection || undefined,
|
|
115
|
+
raw,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
// Implementation parsers
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
export function parseImplementationReport(raw) {
|
|
122
|
+
return {
|
|
123
|
+
filesChanged: [],
|
|
124
|
+
testsRun: [],
|
|
125
|
+
testsPassed: !raw.toLowerCase().includes('failed'),
|
|
126
|
+
openIssues: [],
|
|
127
|
+
raw,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// Mini-loop review parser
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
export function parseMiniReview(raw) {
|
|
134
|
+
return {
|
|
135
|
+
confidence: extractNumber(raw, 'CONFIDENCE'),
|
|
136
|
+
changeRequested: isYes(raw, 'CHANGE_REQUESTED'),
|
|
137
|
+
unresolvedIssues: extractNumber(raw, 'UNRESOLVED_BLOCKING_ISSUES') ||
|
|
138
|
+
extractNumber(raw, 'UNRESOLVED_DOCUMENTATION_ISSUES'),
|
|
139
|
+
reworkInstructions: extractSection(raw, 'rework') || undefined,
|
|
140
|
+
raw,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ff_pipeline — Full plan → build → review → document workflow tool.
|
|
3
|
+
*
|
|
4
|
+
* This is an entrypoint tool exposed to the LLM. It orchestrates the
|
|
5
|
+
* complete Feature Factory pipeline using deterministic control flow
|
|
6
|
+
* (TypeScript loops and gates) while delegating creative work to
|
|
7
|
+
* model-specific prompts via the SDK client.
|
|
8
|
+
*/
|
|
9
|
+
import { type Client } from '../workflow/orchestrator.js';
|
|
10
|
+
export declare function createPipelineTool(client: Client): {
|
|
11
|
+
description: string;
|
|
12
|
+
args: {
|
|
13
|
+
requirements: import("zod").ZodString;
|
|
14
|
+
planning_models: import("zod").ZodOptional<import("zod").ZodString>;
|
|
15
|
+
review_models: import("zod").ZodOptional<import("zod").ZodString>;
|
|
16
|
+
orchestrator_model: import("zod").ZodOptional<import("zod").ZodString>;
|
|
17
|
+
build_model: import("zod").ZodOptional<import("zod").ZodString>;
|
|
18
|
+
validate_model: import("zod").ZodOptional<import("zod").ZodString>;
|
|
19
|
+
doc_model: import("zod").ZodOptional<import("zod").ZodString>;
|
|
20
|
+
doc_review_model: import("zod").ZodOptional<import("zod").ZodString>;
|
|
21
|
+
};
|
|
22
|
+
execute(args: {
|
|
23
|
+
requirements: string;
|
|
24
|
+
planning_models?: string | undefined;
|
|
25
|
+
review_models?: string | undefined;
|
|
26
|
+
orchestrator_model?: string | undefined;
|
|
27
|
+
build_model?: string | undefined;
|
|
28
|
+
validate_model?: string | undefined;
|
|
29
|
+
doc_model?: string | undefined;
|
|
30
|
+
doc_review_model?: string | undefined;
|
|
31
|
+
}, context: import("@opencode-ai/plugin/tool").ToolContext): Promise<string>;
|
|
32
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ff_pipeline — Full plan → build → review → document workflow tool.
|
|
3
|
+
*
|
|
4
|
+
* This is an entrypoint tool exposed to the LLM. It orchestrates the
|
|
5
|
+
* complete Feature Factory pipeline using deterministic control flow
|
|
6
|
+
* (TypeScript loops and gates) while delegating creative work to
|
|
7
|
+
* model-specific prompts via the SDK client.
|
|
8
|
+
*/
|
|
9
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
10
|
+
import { fanOut, promptSession, evaluatePlanningGate, evaluateReviewGate, evaluateDocGate, PLANNING_MODELS, REVIEW_MODELS, ORCHESTRATOR_MODEL, BUILD_MODEL, DOC_MODEL, VALIDATE_MODEL, DOC_REVIEW_MODEL, parseModelString, parseNamedModels, } from '../workflow/orchestrator.js';
|
|
11
|
+
import { planningPrompt, synthesisPrompt, breakdownPrompt, validateBatchPrompt, implementBatchPrompt, triagePrompt, reviewPrompt, reviewSynthesisPrompt, documentPrompt, docReviewPrompt, } from './prompts.js';
|
|
12
|
+
import { parseConsensusPlan, parseReviewSynthesis, parseImplementationReport, parseDocReview, } from './parsers.js';
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Tool factory — needs the SDK client from plugin init
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export function createPipelineTool(client) {
|
|
17
|
+
return tool({
|
|
18
|
+
description: 'Run the full Feature Factory pipeline: multi-model planning → build → review → documentation. ' +
|
|
19
|
+
'Returns a structured completion report. All model parameters are optional and use sensible defaults.',
|
|
20
|
+
args: {
|
|
21
|
+
requirements: tool.schema
|
|
22
|
+
.string()
|
|
23
|
+
.describe('The feature requirements or task description to implement'),
|
|
24
|
+
planning_models: tool.schema
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Comma-separated list of tag:provider/model for planning fan-out. ' +
|
|
28
|
+
'Example: "opus:anthropic/claude-opus-4-6, gemini:google/gemini-2.5-pro". ' +
|
|
29
|
+
'Defaults to opus + gemini + codex.'),
|
|
30
|
+
review_models: tool.schema
|
|
31
|
+
.string()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('Comma-separated list of tag:provider/model for review fan-out. ' +
|
|
34
|
+
'Defaults to the same models as planning_models.'),
|
|
35
|
+
orchestrator_model: tool.schema
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe('provider/model for synthesis and triage steps. Defaults to openai/gpt-5.4.'),
|
|
39
|
+
build_model: tool.schema
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe('provider/model for building and implementation. Defaults to openai/gpt-5.3-codex.'),
|
|
43
|
+
validate_model: tool.schema
|
|
44
|
+
.string()
|
|
45
|
+
.optional()
|
|
46
|
+
.describe('provider/model for batch validation. Defaults to opencode/gemini-3.1-pro.'),
|
|
47
|
+
doc_model: tool.schema
|
|
48
|
+
.string()
|
|
49
|
+
.optional()
|
|
50
|
+
.describe('provider/model for documentation writing. Defaults to the build model.'),
|
|
51
|
+
doc_review_model: tool.schema
|
|
52
|
+
.string()
|
|
53
|
+
.optional()
|
|
54
|
+
.describe('provider/model for documentation review. Defaults to the validate model.'),
|
|
55
|
+
},
|
|
56
|
+
async execute(args, context) {
|
|
57
|
+
const sessionId = context.sessionID;
|
|
58
|
+
const { requirements } = args;
|
|
59
|
+
// Resolve models — use provided overrides or fall back to defaults
|
|
60
|
+
const planModels = args.planning_models
|
|
61
|
+
? parseNamedModels(args.planning_models)
|
|
62
|
+
: PLANNING_MODELS;
|
|
63
|
+
const revModels = args.review_models
|
|
64
|
+
? parseNamedModels(args.review_models)
|
|
65
|
+
: args.planning_models
|
|
66
|
+
? planModels
|
|
67
|
+
: REVIEW_MODELS;
|
|
68
|
+
const orchestratorModel = args.orchestrator_model
|
|
69
|
+
? parseModelString(args.orchestrator_model)
|
|
70
|
+
: ORCHESTRATOR_MODEL;
|
|
71
|
+
const buildModel = args.build_model
|
|
72
|
+
? parseModelString(args.build_model)
|
|
73
|
+
: BUILD_MODEL;
|
|
74
|
+
const validateModel = args.validate_model
|
|
75
|
+
? parseModelString(args.validate_model)
|
|
76
|
+
: VALIDATE_MODEL;
|
|
77
|
+
const docModel = args.doc_model
|
|
78
|
+
? parseModelString(args.doc_model)
|
|
79
|
+
: args.build_model
|
|
80
|
+
? buildModel
|
|
81
|
+
: DOC_MODEL;
|
|
82
|
+
const docReviewModel = args.doc_review_model
|
|
83
|
+
? parseModelString(args.doc_review_model)
|
|
84
|
+
: args.validate_model
|
|
85
|
+
? validateModel
|
|
86
|
+
: DOC_REVIEW_MODEL;
|
|
87
|
+
const report = [];
|
|
88
|
+
const addReport = (phase, msg) => {
|
|
89
|
+
report.push(`## ${phase}\n${msg}`);
|
|
90
|
+
};
|
|
91
|
+
// ===================================================================
|
|
92
|
+
// PHASE 1: PLANNING (fan-out → synthesize → gate, loop up to 5)
|
|
93
|
+
// ===================================================================
|
|
94
|
+
let planningGate = { decision: 'REWORK', feedback: requirements };
|
|
95
|
+
let finalPlan = '';
|
|
96
|
+
for (let planIter = 0; planIter < 5 && planningGate.decision === 'REWORK'; planIter++) {
|
|
97
|
+
const planInput = planIter === 0
|
|
98
|
+
? requirements
|
|
99
|
+
: `${requirements}\n\nPrevious feedback:\n${planningGate.feedback}`;
|
|
100
|
+
// Fan-out: N models plan in parallel
|
|
101
|
+
const fanOutResults = await fanOut(client, sessionId, planModels, (tag) => planningPrompt(planInput, tag), 'planning');
|
|
102
|
+
// Synthesize consensus
|
|
103
|
+
const synthesisRaw = await promptSession(client, sessionId, synthesisPrompt(fanOutResults), {
|
|
104
|
+
model: orchestratorModel,
|
|
105
|
+
agent: 'planning',
|
|
106
|
+
title: `ff-plan-synthesis-${planIter + 1}`,
|
|
107
|
+
});
|
|
108
|
+
const consensus = parseConsensusPlan(synthesisRaw);
|
|
109
|
+
// Gate (deterministic)
|
|
110
|
+
planningGate = evaluatePlanningGate(consensus);
|
|
111
|
+
if (planningGate.decision === 'APPROVED') {
|
|
112
|
+
finalPlan = consensus.synthesizedPlan || synthesisRaw;
|
|
113
|
+
addReport('Planning', `APPROVED (score: ${consensus.consensusScore}, iteration: ${planIter + 1})`);
|
|
114
|
+
}
|
|
115
|
+
else if (planningGate.decision === 'BLOCKED') {
|
|
116
|
+
addReport('Planning', `BLOCKED: ${planningGate.reason}`);
|
|
117
|
+
return report.join('\n\n');
|
|
118
|
+
}
|
|
119
|
+
// REWORK continues the loop
|
|
120
|
+
}
|
|
121
|
+
if (planningGate.decision !== 'APPROVED') {
|
|
122
|
+
addReport('Planning', `REWORK exhausted (5 iterations). Last feedback:\n${planningGate.feedback}`);
|
|
123
|
+
return report.join('\n\n');
|
|
124
|
+
}
|
|
125
|
+
// ===================================================================
|
|
126
|
+
// PHASE 2: BUILDING (breakdown → validate → implement)
|
|
127
|
+
// ===================================================================
|
|
128
|
+
// Breakdown
|
|
129
|
+
const tasksRaw = await promptSession(client, sessionId, breakdownPrompt(finalPlan), {
|
|
130
|
+
model: buildModel,
|
|
131
|
+
agent: 'building',
|
|
132
|
+
title: 'ff-build-breakdown',
|
|
133
|
+
});
|
|
134
|
+
// Validate batches
|
|
135
|
+
const batchesRaw = await promptSession(client, sessionId, validateBatchPrompt(tasksRaw), {
|
|
136
|
+
model: validateModel,
|
|
137
|
+
agent: 'building',
|
|
138
|
+
title: 'ff-build-validate',
|
|
139
|
+
});
|
|
140
|
+
// Implement
|
|
141
|
+
const implRaw = await promptSession(client, sessionId, implementBatchPrompt(batchesRaw), {
|
|
142
|
+
model: buildModel,
|
|
143
|
+
agent: 'building',
|
|
144
|
+
title: 'ff-build-implement',
|
|
145
|
+
});
|
|
146
|
+
const implementation = parseImplementationReport(implRaw);
|
|
147
|
+
addReport('Building', `Implementation completed. Tests passed: ${implementation.testsPassed}`);
|
|
148
|
+
// ===================================================================
|
|
149
|
+
// PHASE 3: REVIEWING (triage → fan-out review → synthesize → gate, loop up to 10)
|
|
150
|
+
// ===================================================================
|
|
151
|
+
let reviewInput = implRaw;
|
|
152
|
+
let reviewGate = { decision: 'REWORK' };
|
|
153
|
+
for (let revIter = 0; revIter < 10 && reviewGate.decision === 'REWORK'; revIter++) {
|
|
154
|
+
// Triage
|
|
155
|
+
const brief = await promptSession(client, sessionId, triagePrompt(reviewInput), {
|
|
156
|
+
model: orchestratorModel,
|
|
157
|
+
agent: 'reviewing',
|
|
158
|
+
title: `ff-review-triage-${revIter + 1}`,
|
|
159
|
+
});
|
|
160
|
+
// Fan-out review
|
|
161
|
+
const reviewResults = await fanOut(client, sessionId, revModels, (tag) => reviewPrompt(brief, tag), 'reviewing');
|
|
162
|
+
// Synthesize
|
|
163
|
+
const synthRaw = await promptSession(client, sessionId, reviewSynthesisPrompt(reviewResults), {
|
|
164
|
+
model: orchestratorModel,
|
|
165
|
+
agent: 'reviewing',
|
|
166
|
+
title: `ff-review-synthesis-${revIter + 1}`,
|
|
167
|
+
});
|
|
168
|
+
const synthesis = parseReviewSynthesis(synthRaw);
|
|
169
|
+
// Gate (deterministic)
|
|
170
|
+
reviewGate = evaluateReviewGate(synthesis, revIter + 1);
|
|
171
|
+
if (reviewGate.decision === 'APPROVED') {
|
|
172
|
+
addReport('Reviewing', `APPROVED (confidence: ${synthesis.overallConfidence}, iteration: ${revIter + 1})`);
|
|
173
|
+
}
|
|
174
|
+
else if (reviewGate.decision === 'ESCALATE') {
|
|
175
|
+
addReport('Reviewing', `ESCALATE: ${reviewGate.reason}`);
|
|
176
|
+
return report.join('\n\n');
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
// REWORK — apply fixes, then re-review
|
|
180
|
+
const fixRaw = await promptSession(client, sessionId, implementBatchPrompt(`Rework required:\n${reviewGate.feedback}\n\nOriginal batches:\n${batchesRaw}`), { model: buildModel, agent: 'building', title: `ff-review-rework-${revIter + 1}` });
|
|
181
|
+
reviewInput = fixRaw;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (reviewGate.decision !== 'APPROVED') {
|
|
185
|
+
addReport('Reviewing', `REWORK exhausted (10 iterations). Last feedback:\n${reviewGate.feedback}`);
|
|
186
|
+
return report.join('\n\n');
|
|
187
|
+
}
|
|
188
|
+
// ===================================================================
|
|
189
|
+
// PHASE 4: DOCUMENTATION (document → review → gate, loop up to 5)
|
|
190
|
+
// ===================================================================
|
|
191
|
+
let docInput = `Implementation report:\n${implRaw}\n\nReview synthesis:\n${reviewInput}`;
|
|
192
|
+
let docGate = { decision: 'REWORK' };
|
|
193
|
+
for (let docIter = 0; docIter < 5 && docGate.decision === 'REWORK'; docIter++) {
|
|
194
|
+
// Write docs
|
|
195
|
+
const docRaw = await promptSession(client, sessionId, documentPrompt(docInput), {
|
|
196
|
+
model: docModel,
|
|
197
|
+
agent: 'documenting',
|
|
198
|
+
title: `ff-doc-write-${docIter + 1}`,
|
|
199
|
+
});
|
|
200
|
+
// Review docs
|
|
201
|
+
const docRevRaw = await promptSession(client, sessionId, docReviewPrompt(docRaw), {
|
|
202
|
+
model: docReviewModel,
|
|
203
|
+
agent: 'reviewing',
|
|
204
|
+
title: `ff-doc-review-${docIter + 1}`,
|
|
205
|
+
});
|
|
206
|
+
const docReview = parseDocReview(docRevRaw);
|
|
207
|
+
// Gate (deterministic)
|
|
208
|
+
docGate = evaluateDocGate(docReview, docIter + 1);
|
|
209
|
+
if (docGate.decision === 'APPROVED') {
|
|
210
|
+
addReport('Documentation', `APPROVED (confidence: ${docReview.confidence}, iteration: ${docIter + 1})`);
|
|
211
|
+
}
|
|
212
|
+
else if (docGate.decision === 'ESCALATE') {
|
|
213
|
+
addReport('Documentation', `ESCALATE: ${docGate.reason}`);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// Feed feedback into the next iteration
|
|
217
|
+
docInput = `${docInput}\n\nDocumentation review feedback:\n${docGate.feedback}`;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// ===================================================================
|
|
221
|
+
// FINAL REPORT
|
|
222
|
+
// ===================================================================
|
|
223
|
+
addReport('Complete', 'Pipeline finished.');
|
|
224
|
+
return report.join('\n\n');
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt templates for workflow tools.
|
|
3
|
+
*
|
|
4
|
+
* Centralised here so that tools stay lean and prompts are easy to review
|
|
5
|
+
* and tune independently.
|
|
6
|
+
*/
|
|
7
|
+
export declare function planningPrompt(requirements: string, modelTag: string): string;
|
|
8
|
+
export declare function synthesisPrompt(proposals: Array<{
|
|
9
|
+
tag: string;
|
|
10
|
+
raw: string;
|
|
11
|
+
}>): string;
|
|
12
|
+
export declare function breakdownPrompt(finalPlan: string): string;
|
|
13
|
+
export declare function validateBatchPrompt(tasks: string): string;
|
|
14
|
+
export declare function implementBatchPrompt(batches: string): string;
|
|
15
|
+
export declare function triagePrompt(implementationReport: string): string;
|
|
16
|
+
export declare function reviewPrompt(brief: string, modelTag: string): string;
|
|
17
|
+
export declare function reviewSynthesisPrompt(reviews: Array<{
|
|
18
|
+
tag: string;
|
|
19
|
+
raw: string;
|
|
20
|
+
}>): string;
|
|
21
|
+
export declare function documentPrompt(input: string): string;
|
|
22
|
+
export declare function docReviewPrompt(docUpdate: string): string;
|
|
23
|
+
export declare function miniBuildPrompt(requirements: string, reworkFeedback?: string): string;
|
|
24
|
+
export declare function miniReviewPrompt(implementationReport: string): string;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt templates for workflow tools.
|
|
3
|
+
*
|
|
4
|
+
* Centralised here so that tools stay lean and prompts are easy to review
|
|
5
|
+
* and tune independently.
|
|
6
|
+
*/
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Planning
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
export function planningPrompt(requirements, modelTag) {
|
|
11
|
+
return `[MODEL_TAG:${modelTag}]
|
|
12
|
+
|
|
13
|
+
Create a comprehensive implementation plan from the requirements below.
|
|
14
|
+
|
|
15
|
+
Requirements:
|
|
16
|
+
${requirements}
|
|
17
|
+
|
|
18
|
+
Return a structured proposal with these sections:
|
|
19
|
+
|
|
20
|
+
1. REQUIREMENTS_SUMMARY
|
|
21
|
+
2. ARCHITECTURE_VALIDATION
|
|
22
|
+
3. IMPLEMENTATION_STEPS
|
|
23
|
+
4. RISKS_AND_MITIGATIONS
|
|
24
|
+
5. TESTING_AND_VALIDATION_STRATEGY`;
|
|
25
|
+
}
|
|
26
|
+
export function synthesisPrompt(proposals) {
|
|
27
|
+
const formatted = proposals.map((p) => `--- ${p.tag} ---\n${p.raw}`).join('\n\n');
|
|
28
|
+
return `Synthesize the following model planning outputs into a consensus report.
|
|
29
|
+
|
|
30
|
+
${formatted}
|
|
31
|
+
|
|
32
|
+
Produce a consensus report with these sections:
|
|
33
|
+
|
|
34
|
+
1. CONSENSUS_SCORE (0-100)
|
|
35
|
+
2. AGREED_ELEMENTS
|
|
36
|
+
3. DIVERGENT_ELEMENTS
|
|
37
|
+
4. SYNTHESIZED_PLAN
|
|
38
|
+
5. OPEN_QUESTIONS`;
|
|
39
|
+
}
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Building
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
export function breakdownPrompt(finalPlan) {
|
|
44
|
+
return `Read the approved final plan below and produce atomic tasks.
|
|
45
|
+
|
|
46
|
+
${finalPlan}
|
|
47
|
+
|
|
48
|
+
Each task must include:
|
|
49
|
+
- task id
|
|
50
|
+
- title and description
|
|
51
|
+
- target files
|
|
52
|
+
- dependencies
|
|
53
|
+
- acceptance criteria
|
|
54
|
+
|
|
55
|
+
Return structured TASKS output.`;
|
|
56
|
+
}
|
|
57
|
+
export function validateBatchPrompt(tasks) {
|
|
58
|
+
return `Read the TASKS input below and create dependency-safe batches.
|
|
59
|
+
|
|
60
|
+
${tasks}
|
|
61
|
+
|
|
62
|
+
For each batch:
|
|
63
|
+
1. validate architecture and codebase fit
|
|
64
|
+
2. confirm or adjust file targets
|
|
65
|
+
3. flag architectural risks
|
|
66
|
+
4. mark whether tasks can run in parallel
|
|
67
|
+
|
|
68
|
+
Return structured BATCHES output.`;
|
|
69
|
+
}
|
|
70
|
+
export function implementBatchPrompt(batches) {
|
|
71
|
+
return `Read the BATCHES input below and implement each batch.
|
|
72
|
+
|
|
73
|
+
${batches}
|
|
74
|
+
|
|
75
|
+
Execution strategy:
|
|
76
|
+
1. Process batches in dependency order.
|
|
77
|
+
2. Within each batch, implement tasks that have no mutual dependency edges.
|
|
78
|
+
3. Tasks that share file targets or have explicit dependency edges must run sequentially.
|
|
79
|
+
4. Wait for all tasks in a batch to complete before starting the next batch.
|
|
80
|
+
|
|
81
|
+
For each task:
|
|
82
|
+
1. implement code changes
|
|
83
|
+
2. add/update tests
|
|
84
|
+
3. run lint/typecheck/tests for impacted scope
|
|
85
|
+
4. return a structured completion report
|
|
86
|
+
|
|
87
|
+
After all batches complete, merge the per-task completion reports into a single IMPLEMENTATION_REPORT output.`;
|
|
88
|
+
}
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Reviewing
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
export function triagePrompt(implementationReport) {
|
|
93
|
+
return `Prepare a review brief for the completed implementation.
|
|
94
|
+
|
|
95
|
+
${implementationReport}
|
|
96
|
+
|
|
97
|
+
Include:
|
|
98
|
+
- summary of implemented changes
|
|
99
|
+
- files changed
|
|
100
|
+
- focus areas and risk points
|
|
101
|
+
|
|
102
|
+
Return a structured REVIEW_BRIEF output.`;
|
|
103
|
+
}
|
|
104
|
+
export function reviewPrompt(brief, modelTag) {
|
|
105
|
+
return `[MODEL_TAG:${modelTag}]
|
|
106
|
+
|
|
107
|
+
Review the current task from the triage brief below for correctness, quality, architecture validity, testing, security, and edge cases.
|
|
108
|
+
|
|
109
|
+
${brief}
|
|
110
|
+
|
|
111
|
+
Classify findings as critical/high/medium/low and include a confidence score (0-100).
|
|
112
|
+
|
|
113
|
+
Return a structured review report.`;
|
|
114
|
+
}
|
|
115
|
+
export function reviewSynthesisPrompt(reviews) {
|
|
116
|
+
const formatted = reviews.map((r) => `--- ${r.tag} ---\n${r.raw}`).join('\n\n');
|
|
117
|
+
return `Read the three review inputs below and synthesize a single authoritative output.
|
|
118
|
+
|
|
119
|
+
${formatted}
|
|
120
|
+
|
|
121
|
+
Required output:
|
|
122
|
+
1. overall confidence (0-100)
|
|
123
|
+
2. consolidated deduplicated findings
|
|
124
|
+
3. unresolved issues list (count)
|
|
125
|
+
4. verdict APPROVED or REWORK_REQUIRED
|
|
126
|
+
5. explicit rework instructions when needed`;
|
|
127
|
+
}
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Documentation
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
export function documentPrompt(input) {
|
|
132
|
+
return `Document the approved code changes and update repository documentation.
|
|
133
|
+
|
|
134
|
+
${input}
|
|
135
|
+
|
|
136
|
+
Requirements:
|
|
137
|
+
1. Use the latest approved review outputs and implementation artifacts as source of truth.
|
|
138
|
+
2. Update all affected docs so behavior and operational steps match shipped code.
|
|
139
|
+
3. If this is a rework iteration, incorporate documentation reviewer feedback from the previous documentation review.
|
|
140
|
+
4. Summarize what docs were changed and why.
|
|
141
|
+
|
|
142
|
+
Return a structured documentation update summary.`;
|
|
143
|
+
}
|
|
144
|
+
export function docReviewPrompt(docUpdate) {
|
|
145
|
+
return `Review the latest documentation pass for completeness, correctness, and repository doc consistency.
|
|
146
|
+
|
|
147
|
+
${docUpdate}
|
|
148
|
+
|
|
149
|
+
Required output:
|
|
150
|
+
1. verdict APPROVED or REWORK_REQUIRED
|
|
151
|
+
2. unresolved documentation issues (count, integer)
|
|
152
|
+
3. explicit rework instructions
|
|
153
|
+
4. confidence score (0-100)`;
|
|
154
|
+
}
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Mini-loop specific
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
export function miniBuildPrompt(requirements, reworkFeedback) {
|
|
159
|
+
const rework = reworkFeedback
|
|
160
|
+
? `\n\nPrevious review feedback to address:\n${reworkFeedback}`
|
|
161
|
+
: '';
|
|
162
|
+
return `Implement the current mini-loop requirements below.
|
|
163
|
+
|
|
164
|
+
${requirements}${rework}
|
|
165
|
+
|
|
166
|
+
Requirements:
|
|
167
|
+
1. Apply requested code rework when present.
|
|
168
|
+
2. Add or update tests for behavioral changes.
|
|
169
|
+
3. Run lint/typecheck/tests only for impacted scope.
|
|
170
|
+
4. Return a concise implementation report with changed files, tests run, and known open issues.`;
|
|
171
|
+
}
|
|
172
|
+
export function miniReviewPrompt(implementationReport) {
|
|
173
|
+
return `Review the latest mini-loop implementation output below.
|
|
174
|
+
|
|
175
|
+
${implementationReport}
|
|
176
|
+
|
|
177
|
+
Required output fields:
|
|
178
|
+
1. CHANGE_REQUESTED=YES|NO
|
|
179
|
+
2. UNRESOLVED_BLOCKING_ISSUES=<integer>
|
|
180
|
+
3. CONFIDENCE=<0-100>
|
|
181
|
+
4. concise rework instructions when change is requested`;
|
|
182
|
+
}
|