@magnusekdahl/parallix 1.2.1 → 1.3.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/index.js +190 -185
- package/lib/commands/active.js +502 -579
- package/lib/commands/active.ts +665 -0
- package/lib/commands/checkpoint.js +98 -74
- package/lib/commands/checkpoint.ts +79 -0
- package/lib/commands/config.js +61 -31
- package/lib/commands/config.ts +47 -0
- package/lib/commands/coverage-gate.js +271 -246
- package/lib/commands/coverage-gate.ts +362 -0
- package/lib/commands/diff.js +140 -119
- package/lib/commands/diff.ts +125 -0
- package/lib/commands/draft.js +770 -923
- package/lib/commands/draft.ts +1021 -0
- package/lib/commands/handoff.js +588 -608
- package/lib/commands/handoff.ts +694 -0
- package/lib/commands/integrate.js +1331 -1468
- package/lib/commands/integrate.ts +1688 -0
- package/lib/commands/mission-start.js +279 -237
- package/lib/commands/mission-start.ts +263 -0
- package/lib/commands/rebase.js +606 -582
- package/lib/commands/rebase.ts +635 -0
- package/lib/commands/repair-handoff.js +181 -179
- package/lib/commands/repair-handoff.ts +228 -0
- package/lib/commands/resolve-conflict.js +121 -102
- package/lib/commands/resolve-conflict.ts +112 -0
- package/lib/commands/review.js +3 -4
- package/lib/commands/review.ts +14 -0
- package/lib/commands/setup-review.js +4 -5
- package/lib/commands/setup-review.ts +14 -0
- package/lib/commands/setup.js +3 -3
- package/lib/commands/setup.ts +2 -0
- package/lib/commands/stats-backfill.js +394 -359
- package/lib/commands/stats-backfill.ts +418 -0
- package/lib/commands/stats.js +1424 -1665
- package/lib/commands/stats.ts +2186 -0
- package/lib/commands/status.js +219 -200
- package/lib/commands/status.ts +219 -0
- package/lib/commands/verify.js +3 -1
- package/lib/commands/verify.ts +2 -0
- package/lib/core/nels.js +112 -127
- package/lib/core/nels.ts +189 -0
- package/lib/core/subagent-limit.js +15 -16
- package/lib/core/subagent-limit.ts +25 -0
- package/lib/index.js +142 -59
- package/lib/index.ts +134 -0
- package/lib/review/rebase.js +142 -145
- package/lib/review/rebase.ts +188 -0
- package/lib/review/review-adapter.js +99 -87
- package/lib/review/review-adapter.ts +198 -0
- package/lib/review/review-artifacts.js +486 -629
- package/lib/review/review-artifacts.ts +622 -0
- package/lib/review/review-commands.js +1220 -1373
- package/lib/review/review-commands.ts +1579 -0
- package/lib/review/review-events.js +544 -886
- package/lib/review/review-events.ts +954 -0
- package/lib/review/review-loop.js +945 -1020
- package/lib/review/review-loop.ts +1174 -0
- package/lib/review/review-polling.js +86 -115
- package/lib/review/review-polling.ts +194 -0
- package/lib/review/review-prompts.js +154 -189
- package/lib/review/review-prompts.ts +182 -0
- package/lib/review/review-state.js +246 -225
- package/lib/review/review-state.ts +353 -0
- package/lib/review/review.js +63 -84
- package/lib/review/review.ts +138 -0
- package/lib/tools/backlog.js +694 -680
- package/lib/tools/backlog.ts +835 -0
- package/lib/tools/forgejo.js +1270 -1405
- package/lib/tools/forgejo.ts +1793 -0
- package/lib/tools/gatekeeper.js +106 -96
- package/lib/tools/gatekeeper.ts +124 -0
- package/lib/tools/redgreen.js +200 -181
- package/lib/tools/redgreen.ts +221 -0
- package/lib/tools/sessions.js +58 -52
- package/lib/tools/sessions.ts +81 -0
- package/lib/tools/setup-review.js +902 -975
- package/lib/tools/setup-review.ts +1152 -0
- package/package.json +4 -4
- package/px.js +226 -208
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Assemble review and act-on-review prompts for the autonomous review loop.
|
|
3
4
|
* Owned by the Node workflow harness (ADR 0037 / task-089).
|
|
@@ -6,61 +7,78 @@
|
|
|
6
7
|
* (same pattern as active.js / draft.js). Full verbose prompts are kept
|
|
7
8
|
* for --dry-run diagnostics and human/manual invocation.
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.PROMPT_ENTRYPOINTS = void 0;
|
|
45
|
+
exports.reviewEntrypoint = reviewEntrypoint;
|
|
46
|
+
exports.actOnReviewEntrypoint = actOnReviewEntrypoint;
|
|
47
|
+
exports.buildReviewPrompt = buildReviewPrompt;
|
|
48
|
+
exports.buildActOnReviewPrompt = buildActOnReviewPrompt;
|
|
49
|
+
exports.buildCompactReviewPrompt = buildCompactReviewPrompt;
|
|
50
|
+
exports.buildCompactActOnReviewPrompt = buildCompactActOnReviewPrompt;
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const mission_utils_js_1 = require("../core/mission-utils.js");
|
|
54
|
+
const review_artifacts_js_1 = require("./review-artifacts.js");
|
|
15
55
|
const REVIEW_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'review.md');
|
|
16
56
|
const ACT_ON_REVIEW_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'act-on-review.md');
|
|
17
57
|
const REVIEW_VERBOSE_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'review-verbose.md');
|
|
18
58
|
const ACT_ON_REVIEW_VERBOSE_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'act-on-review-verbose.md');
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* @typedef {{review: string, actOnReview: string}} PromptEntry
|
|
26
|
-
* @typedef {{codex: PromptEntry, claude: PromptEntry, mistral: PromptEntry, custom: PromptEntry, autonomous: PromptEntry}} PromptEntrypoints
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/** @type {PromptEntrypoints} */
|
|
30
|
-
const PROMPT_ENTRYPOINTS = {
|
|
31
|
-
codex: { review: '$review all', actOnReview: '$act-on-review' },
|
|
32
|
-
claude: { review: '/review all', actOnReview: '/act-on-review' },
|
|
33
|
-
mistral: { review: '$review all', actOnReview: '$act-on-review' },
|
|
34
|
-
custom: { review: '$review all', actOnReview: '$act-on-review' },
|
|
35
|
-
autonomous: { review: '$review all', actOnReview: '$act-on-review' }
|
|
59
|
+
exports.PROMPT_ENTRYPOINTS = {
|
|
60
|
+
codex: { review: '$review all', actOnReview: '$act-on-review' },
|
|
61
|
+
claude: { review: '/review all', actOnReview: '/act-on-review' },
|
|
62
|
+
mistral: { review: '$review all', actOnReview: '/act-on-review' },
|
|
63
|
+
custom: { review: '$review all', actOnReview: '/act-on-review' },
|
|
64
|
+
autonomous: { review: '$review all', actOnReview: '/act-on-review' }
|
|
36
65
|
};
|
|
37
|
-
|
|
38
66
|
/** @param {string} agent */
|
|
39
67
|
function reviewEntrypoint(agent) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
68
|
+
const entry = exports.PROMPT_ENTRYPOINTS[agent];
|
|
69
|
+
if (!entry) {
|
|
70
|
+
throw new Error(`Unknown agent family for review entrypoint: ${agent}`);
|
|
71
|
+
}
|
|
72
|
+
return entry.review;
|
|
43
73
|
}
|
|
44
|
-
|
|
45
74
|
/** @param {string} agent */
|
|
46
75
|
function actOnReviewEntrypoint(agent) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
76
|
+
const entry = exports.PROMPT_ENTRYPOINTS[agent];
|
|
77
|
+
if (!entry) {
|
|
78
|
+
throw new Error(`Unknown agent family for act-on-review entrypoint: ${agent}`);
|
|
79
|
+
}
|
|
80
|
+
return entry.actOnReview;
|
|
50
81
|
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Build the prompt sent to the reviewer agent for a given round.
|
|
54
|
-
*
|
|
55
|
-
* @param {object} opts
|
|
56
|
-
* @param {string} opts.reviewer - Reviewer agent family (codex|claude)
|
|
57
|
-
* @param {string} opts.branch - Mission branch name (e.g. 'mission/task-089')
|
|
58
|
-
* @param {string} opts.implementer - Implementer agent family
|
|
59
|
-
* @param {string} opts.focus - Review focus (default: 'all')
|
|
60
|
-
* @param {number} opts.attempt - Round number (1-based)
|
|
61
|
-
* @param {string} [opts.repoRoot] - Absolute repo root path
|
|
62
|
-
* @returns {string}
|
|
63
|
-
*/
|
|
64
82
|
/**
|
|
65
83
|
* @param {string} slug
|
|
66
84
|
* @param {string} [repoRoot]
|
|
@@ -68,176 +86,123 @@ function actOnReviewEntrypoint(agent) {
|
|
|
68
86
|
* @returns {string}
|
|
69
87
|
*/
|
|
70
88
|
function resolveMissionPath(slug, repoRoot, missionPathOverride) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
89
|
+
if (missionPathOverride) {
|
|
90
|
+
return missionPathOverride;
|
|
91
|
+
}
|
|
92
|
+
const root = repoRoot || process.cwd();
|
|
93
|
+
try {
|
|
94
|
+
return (0, mission_utils_js_1.missionPathForSlug)(root, slug);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
const year = (0, mission_utils_js_1.getMissionYear)(slug, root);
|
|
98
|
+
return path.join(root, 'docs', 'missions', String(year), slug, 'MISSION.md');
|
|
99
|
+
}
|
|
82
100
|
}
|
|
83
|
-
|
|
84
101
|
/** @param {string} [repoRoot] */
|
|
85
102
|
function resolvePrimaryBranch(repoRoot) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
try {
|
|
104
|
+
return (0, mission_utils_js_1.getPrimaryBranch)(repoRoot || process.cwd());
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return 'main';
|
|
108
|
+
}
|
|
91
109
|
}
|
|
92
|
-
|
|
93
110
|
/**
|
|
94
111
|
* @param {{reviewer: string, branch: string, implementer: string, focus?: string, attempt: number, repoRoot?: string, missionPath?: string}} opts
|
|
95
112
|
* @returns {string}
|
|
96
113
|
*/
|
|
97
114
|
function buildReviewPrompt({ reviewer, branch, implementer, focus = 'all', attempt, repoRoot = '', missionPath: missionPathOverride }) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
.replaceAll('{{repo_line}}', repoLine ? repoLine.trim() + '\n- ' : '');
|
|
115
|
+
const entrypoint = reviewEntrypoint(reviewer);
|
|
116
|
+
const repoLine = repoRoot ? `\noperate from repo root: ${repoRoot}` : '';
|
|
117
|
+
const slug = branch.replace(/^mission\//, '');
|
|
118
|
+
const year = (0, mission_utils_js_1.getMissionYear)(slug, repoRoot || process.cwd());
|
|
119
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
120
|
+
const artifactDir = (0, review_artifacts_js_1.resolveArtifactDir)(repoRoot || process.cwd());
|
|
121
|
+
const template = fs.readFileSync(REVIEW_VERBOSE_PROMPT_PATH, 'utf8');
|
|
122
|
+
return template
|
|
123
|
+
.replaceAll('{{branch}}', branch)
|
|
124
|
+
.replaceAll('{{reviewer}}', reviewer)
|
|
125
|
+
.replaceAll('{{implementer}}', implementer)
|
|
126
|
+
.replaceAll('{{focus}}', focus)
|
|
127
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
128
|
+
.replaceAll('{{slug}}', slug)
|
|
129
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
130
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
131
|
+
.replaceAll('{{primaryBranch}}', resolvePrimaryBranch(repoRoot))
|
|
132
|
+
.replaceAll('{{review_entrypoint}}', entrypoint)
|
|
133
|
+
.replaceAll('YYYY', year)
|
|
134
|
+
.replaceAll('{{repo_line}}', repoLine ? repoLine.trim() + '\n- ' : '');
|
|
119
135
|
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Build the prompt sent back to the implementer agent after a review round.
|
|
123
|
-
*
|
|
124
|
-
* @param {object} opts
|
|
125
|
-
* @param {string} opts.implementer - Implementer agent family
|
|
126
|
-
* @param {string} opts.branch - Mission branch name
|
|
127
|
-
* @param {number} opts.attempt - Round number (1-based)
|
|
128
|
-
* @param {string} [opts.repoRoot] - Absolute repo root path
|
|
129
|
-
* @returns {string}
|
|
130
|
-
*/
|
|
131
136
|
/**
|
|
132
137
|
* @param {{implementer: string, branch: string, attempt: number, repoRoot?: string, missionPath?: string}} opts
|
|
133
138
|
* @returns {string}
|
|
134
139
|
*/
|
|
135
140
|
function buildActOnReviewPrompt({ implementer, branch, attempt, repoRoot = '', missionPath: missionPathOverride }) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
.replaceAll('{{repo_line}}', repoLine ? repoLine.trim() + '\n- ' : '');
|
|
141
|
+
const entrypoint = actOnReviewEntrypoint(implementer);
|
|
142
|
+
const repoLine = repoRoot ? `\noperate from repo root: ${repoRoot}` : '';
|
|
143
|
+
const slug = branch.replace(/^mission\//, '');
|
|
144
|
+
const year = (0, mission_utils_js_1.getMissionYear)(slug, repoRoot || process.cwd());
|
|
145
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
146
|
+
const artifactDir = (0, review_artifacts_js_1.resolveArtifactDir)(repoRoot || process.cwd());
|
|
147
|
+
const template = fs.readFileSync(ACT_ON_REVIEW_VERBOSE_PROMPT_PATH, 'utf8');
|
|
148
|
+
return template
|
|
149
|
+
.replaceAll('{{branch}}', branch)
|
|
150
|
+
.replaceAll('{{implementer}}', implementer)
|
|
151
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
152
|
+
.replaceAll('{{slug}}', slug)
|
|
153
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
154
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
155
|
+
.replaceAll('{{primaryBranch}}', resolvePrimaryBranch(repoRoot))
|
|
156
|
+
.replaceAll('{{act_on_review_entrypoint}}', entrypoint)
|
|
157
|
+
.replaceAll('YYYY', year)
|
|
158
|
+
.replaceAll('{{repo_line}}', repoLine ? repoLine.trim() + '\n- ' : '');
|
|
155
159
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Node-invoked reviewer prompt built from parallix/prompts/review.md template.
|
|
160
|
-
* Uses the same template-substitution pattern as active.js (execute.md).
|
|
161
|
-
*
|
|
162
|
-
* @param {object} opts
|
|
163
|
-
* @param {string} opts.reviewer - Reviewer agent family
|
|
164
|
-
* @param {string} opts.branch - Mission branch name
|
|
165
|
-
* @param {string} opts.implementer - Implementer agent family
|
|
166
|
-
* @param {string} opts.focus - Review focus (default: 'all')
|
|
167
|
-
* @param {number} opts.attempt - Round number (1-based)
|
|
168
|
-
* @param {string} [opts.repoRoot] - Absolute repo root path (unused but kept for API symmetry)
|
|
169
|
-
* @param {string} [opts.actualReviewer] - The actual agent family launched (overrides reviewer for identity text, TASK-1051)
|
|
170
|
-
* @returns {string}
|
|
171
|
-
*/
|
|
172
160
|
/**
|
|
173
161
|
* @param {{reviewer: string, branch: string, implementer: string, focus?: string, attempt: number, actualReviewer?: string, repoRoot?: string, missionPath?: string}} opts
|
|
174
162
|
* @returns {string}
|
|
175
163
|
*/
|
|
176
164
|
function buildCompactReviewPrompt({ reviewer, branch, implementer, focus = 'all', attempt, actualReviewer, repoRoot = '', missionPath: missionPathOverride }) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
165
|
+
const slug = branch.replace(/^mission\//, '');
|
|
166
|
+
const finalReviewer = actualReviewer || reviewer;
|
|
167
|
+
const year = (0, mission_utils_js_1.getMissionYear)(slug, repoRoot || process.cwd());
|
|
168
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
169
|
+
const primaryBranch = resolvePrimaryBranch(repoRoot);
|
|
170
|
+
const artifactDir = (0, review_artifacts_js_1.resolveArtifactDir)(repoRoot || process.cwd());
|
|
171
|
+
const template = fs.readFileSync(REVIEW_PROMPT_PATH, 'utf8');
|
|
172
|
+
return template
|
|
173
|
+
.replaceAll('{{branch}}', branch)
|
|
174
|
+
.replaceAll('{{reviewer}}', finalReviewer)
|
|
175
|
+
.replaceAll('{{implementer}}', implementer)
|
|
176
|
+
.replaceAll('{{focus}}', focus)
|
|
177
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
178
|
+
.replaceAll('{{slug}}', slug)
|
|
179
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
180
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
181
|
+
.replaceAll('{{primaryBranch}}', primaryBranch)
|
|
182
|
+
.replaceAll('YYYY', year)
|
|
183
|
+
.replaceAll('{{review_entrypoint}}', reviewEntrypoint(finalReviewer));
|
|
196
184
|
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Node-invoked act-on-review prompt built from parallix/prompts/act-on-review.md template.
|
|
200
|
-
*
|
|
201
|
-
* @param {object} opts
|
|
202
|
-
* @param {string} opts.implementer - Implementer agent family
|
|
203
|
-
* @param {string} opts.branch - Mission branch name
|
|
204
|
-
* @param {number} opts.attempt - Round number (1-based)
|
|
205
|
-
* @param {string} [opts.reviewOutcome] - Outcome of latest review (e.g. 'REQUEST_CHANGES')
|
|
206
|
-
* @param {string} [opts.repoRoot] - Absolute repo root path
|
|
207
|
-
* @param {string} [opts.actualImplementer] - The actual agent family launched (overrides implementer for identity text, TASK-1051)
|
|
208
|
-
* @returns {string}
|
|
209
|
-
*/
|
|
210
185
|
/**
|
|
211
186
|
* @param {{implementer: string, branch: string, attempt: number, reviewOutcome?: string, actualImplementer?: string, repoRoot?: string, missionPath?: string}} opts
|
|
212
187
|
* @returns {string}
|
|
213
188
|
*/
|
|
214
189
|
function buildCompactActOnReviewPrompt({ implementer, branch, attempt, reviewOutcome = '?', actualImplementer, repoRoot = '', missionPath: missionPathOverride }) {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
190
|
+
const slug = branch.replace(/^mission\//, '');
|
|
191
|
+
const finalImplementer = actualImplementer || implementer;
|
|
192
|
+
const year = (0, mission_utils_js_1.getMissionYear)(slug, repoRoot || process.cwd());
|
|
193
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
194
|
+
const primaryBranch = resolvePrimaryBranch(repoRoot);
|
|
195
|
+
const artifactDir = (0, review_artifacts_js_1.resolveArtifactDir)(repoRoot || process.cwd());
|
|
196
|
+
const template = fs.readFileSync(ACT_ON_REVIEW_PROMPT_PATH, 'utf8');
|
|
197
|
+
return template
|
|
198
|
+
.replaceAll('{{branch}}', branch)
|
|
199
|
+
.replaceAll('{{implementer}}', finalImplementer)
|
|
200
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
201
|
+
.replaceAll('{{slug}}', slug)
|
|
202
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
203
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
204
|
+
.replaceAll('{{primaryBranch}}', primaryBranch)
|
|
205
|
+
.replaceAll('{{review_outcome}}', reviewOutcome)
|
|
206
|
+
.replaceAll('YYYY', year)
|
|
207
|
+
.replaceAll('{{act_on_review_entrypoint}}', actOnReviewEntrypoint(finalImplementer));
|
|
233
208
|
}
|
|
234
|
-
|
|
235
|
-
module.exports = {
|
|
236
|
-
PROMPT_ENTRYPOINTS,
|
|
237
|
-
reviewEntrypoint,
|
|
238
|
-
actOnReviewEntrypoint,
|
|
239
|
-
buildReviewPrompt,
|
|
240
|
-
buildActOnReviewPrompt,
|
|
241
|
-
buildCompactReviewPrompt,
|
|
242
|
-
buildCompactActOnReviewPrompt
|
|
243
|
-
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assemble review and act-on-review prompts for the autonomous review loop.
|
|
3
|
+
* Owned by the Node workflow harness (ADR 0037 / task-089).
|
|
4
|
+
*
|
|
5
|
+
* Node-invoked agent prompts read from parallix/prompts/*.md templates
|
|
6
|
+
* (same pattern as active.js / draft.js). Full verbose prompts are kept
|
|
7
|
+
* for --dry-run diagnostics and human/manual invocation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as fs from 'fs';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { getMissionYear, getPrimaryBranch, missionPathForSlug } from '../core/mission-utils.js';
|
|
13
|
+
import { resolveArtifactDir } from './review-artifacts.js';
|
|
14
|
+
|
|
15
|
+
const REVIEW_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'review.md');
|
|
16
|
+
const ACT_ON_REVIEW_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'act-on-review.md');
|
|
17
|
+
const REVIEW_VERBOSE_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'review-verbose.md');
|
|
18
|
+
const ACT_ON_REVIEW_VERBOSE_PROMPT_PATH = path.join(__dirname, '..', '..', 'prompts', 'act-on-review-verbose.md');
|
|
19
|
+
|
|
20
|
+
type PromptEntry = { review: string; actOnReview: string };
|
|
21
|
+
type PromptEntrypoints = { codex: PromptEntry; claude: PromptEntry; mistral: PromptEntry; custom: PromptEntry; autonomous: PromptEntry };
|
|
22
|
+
|
|
23
|
+
export const PROMPT_ENTRYPOINTS: PromptEntrypoints = {
|
|
24
|
+
codex: { review: '$review all', actOnReview: '$act-on-review' },
|
|
25
|
+
claude: { review: '/review all', actOnReview: '/act-on-review' },
|
|
26
|
+
mistral: { review: '$review all', actOnReview: '/act-on-review' },
|
|
27
|
+
custom: { review: '$review all', actOnReview: '/act-on-review' },
|
|
28
|
+
autonomous: { review: '$review all', actOnReview: '/act-on-review' }
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/** @param {string} agent */
|
|
32
|
+
export function reviewEntrypoint(agent: string): string {
|
|
33
|
+
const entry = (PROMPT_ENTRYPOINTS as Record<string, PromptEntry>)[agent];
|
|
34
|
+
if (!entry) { throw new Error(`Unknown agent family for review entrypoint: ${agent}`); }
|
|
35
|
+
return entry.review;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** @param {string} agent */
|
|
39
|
+
export function actOnReviewEntrypoint(agent: string): string {
|
|
40
|
+
const entry = (PROMPT_ENTRYPOINTS as Record<string, PromptEntry>)[agent];
|
|
41
|
+
if (!entry) { throw new Error(`Unknown agent family for act-on-review entrypoint: ${agent}`); }
|
|
42
|
+
return entry.actOnReview;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {string} slug
|
|
47
|
+
* @param {string} [repoRoot]
|
|
48
|
+
* @param {string} [missionPathOverride]
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function resolveMissionPath(slug: string, repoRoot?: string, missionPathOverride?: string): string {
|
|
52
|
+
if (missionPathOverride) { return missionPathOverride; }
|
|
53
|
+
const root = repoRoot || process.cwd();
|
|
54
|
+
try {
|
|
55
|
+
return missionPathForSlug(root, slug);
|
|
56
|
+
} catch {
|
|
57
|
+
const year = getMissionYear(slug, root);
|
|
58
|
+
return path.join(root, 'docs', 'missions', String(year), slug, 'MISSION.md');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** @param {string} [repoRoot] */
|
|
63
|
+
function resolvePrimaryBranch(repoRoot?: string): string {
|
|
64
|
+
try {
|
|
65
|
+
return getPrimaryBranch(repoRoot || process.cwd());
|
|
66
|
+
} catch {
|
|
67
|
+
return 'main';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {{reviewer: string, branch: string, implementer: string, focus?: string, attempt: number, repoRoot?: string, missionPath?: string}} opts
|
|
73
|
+
* @returns {string}
|
|
74
|
+
*/
|
|
75
|
+
export function buildReviewPrompt({ reviewer, branch, implementer, focus = 'all', attempt, repoRoot = '', missionPath: missionPathOverride }: {
|
|
76
|
+
reviewer: string; branch: string; implementer: string; focus?: string; attempt: number; repoRoot?: string; missionPath?: string;
|
|
77
|
+
}): string {
|
|
78
|
+
const entrypoint = reviewEntrypoint(reviewer);
|
|
79
|
+
const repoLine = repoRoot ? `\noperate from repo root: ${repoRoot}` : '';
|
|
80
|
+
const slug = branch.replace(/^mission\//, '');
|
|
81
|
+
const year = getMissionYear(slug, repoRoot || process.cwd());
|
|
82
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
83
|
+
const artifactDir = resolveArtifactDir(repoRoot || process.cwd());
|
|
84
|
+
|
|
85
|
+
const template = fs.readFileSync(REVIEW_VERBOSE_PROMPT_PATH, 'utf8');
|
|
86
|
+
return template
|
|
87
|
+
.replaceAll('{{branch}}', branch)
|
|
88
|
+
.replaceAll('{{reviewer}}', reviewer)
|
|
89
|
+
.replaceAll('{{implementer}}', implementer)
|
|
90
|
+
.replaceAll('{{focus}}', focus)
|
|
91
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
92
|
+
.replaceAll('{{slug}}', slug)
|
|
93
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
94
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
95
|
+
.replaceAll('{{primaryBranch}}', resolvePrimaryBranch(repoRoot))
|
|
96
|
+
.replaceAll('{{review_entrypoint}}', entrypoint)
|
|
97
|
+
.replaceAll('YYYY', year)
|
|
98
|
+
.replaceAll('{{repo_line}}', repoLine ? repoLine.trim() + '\n- ' : '');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {{implementer: string, branch: string, attempt: number, repoRoot?: string, missionPath?: string}} opts
|
|
103
|
+
* @returns {string}
|
|
104
|
+
*/
|
|
105
|
+
export function buildActOnReviewPrompt({ implementer, branch, attempt, repoRoot = '', missionPath: missionPathOverride }: {
|
|
106
|
+
implementer: string; branch: string; attempt: number; repoRoot?: string; missionPath?: string;
|
|
107
|
+
}): string {
|
|
108
|
+
const entrypoint = actOnReviewEntrypoint(implementer);
|
|
109
|
+
const repoLine = repoRoot ? `\noperate from repo root: ${repoRoot}` : '';
|
|
110
|
+
const slug = branch.replace(/^mission\//, '');
|
|
111
|
+
const year = getMissionYear(slug, repoRoot || process.cwd());
|
|
112
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
113
|
+
const artifactDir = resolveArtifactDir(repoRoot || process.cwd());
|
|
114
|
+
|
|
115
|
+
const template = fs.readFileSync(ACT_ON_REVIEW_VERBOSE_PROMPT_PATH, 'utf8');
|
|
116
|
+
return template
|
|
117
|
+
.replaceAll('{{branch}}', branch)
|
|
118
|
+
.replaceAll('{{implementer}}', implementer)
|
|
119
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
120
|
+
.replaceAll('{{slug}}', slug)
|
|
121
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
122
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
123
|
+
.replaceAll('{{primaryBranch}}', resolvePrimaryBranch(repoRoot))
|
|
124
|
+
.replaceAll('{{act_on_review_entrypoint}}', entrypoint)
|
|
125
|
+
.replaceAll('YYYY', year)
|
|
126
|
+
.replaceAll('{{repo_line}}', repoLine ? repoLine.trim() + '\n- ' : '');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {{reviewer: string, branch: string, implementer: string, focus?: string, attempt: number, actualReviewer?: string, repoRoot?: string, missionPath?: string}} opts
|
|
131
|
+
* @returns {string}
|
|
132
|
+
*/
|
|
133
|
+
export function buildCompactReviewPrompt({ reviewer, branch, implementer, focus = 'all', attempt, actualReviewer, repoRoot = '', missionPath: missionPathOverride }: {
|
|
134
|
+
reviewer: string; branch: string; implementer: string; focus?: string; attempt: number; actualReviewer?: string; repoRoot?: string; missionPath?: string;
|
|
135
|
+
}): string {
|
|
136
|
+
const slug = branch.replace(/^mission\//, '');
|
|
137
|
+
const finalReviewer = actualReviewer || reviewer;
|
|
138
|
+
const year = getMissionYear(slug, repoRoot || process.cwd());
|
|
139
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
140
|
+
const primaryBranch = resolvePrimaryBranch(repoRoot);
|
|
141
|
+
const artifactDir = resolveArtifactDir(repoRoot || process.cwd());
|
|
142
|
+
const template = fs.readFileSync(REVIEW_PROMPT_PATH, 'utf8');
|
|
143
|
+
return template
|
|
144
|
+
.replaceAll('{{branch}}', branch)
|
|
145
|
+
.replaceAll('{{reviewer}}', finalReviewer)
|
|
146
|
+
.replaceAll('{{implementer}}', implementer)
|
|
147
|
+
.replaceAll('{{focus}}', focus)
|
|
148
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
149
|
+
.replaceAll('{{slug}}', slug)
|
|
150
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
151
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
152
|
+
.replaceAll('{{primaryBranch}}', primaryBranch)
|
|
153
|
+
.replaceAll('YYYY', year)
|
|
154
|
+
.replaceAll('{{review_entrypoint}}', reviewEntrypoint(finalReviewer));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {{implementer: string, branch: string, attempt: number, reviewOutcome?: string, actualImplementer?: string, repoRoot?: string, missionPath?: string}} opts
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
export function buildCompactActOnReviewPrompt({ implementer, branch, attempt, reviewOutcome = '?', actualImplementer, repoRoot = '', missionPath: missionPathOverride }: {
|
|
162
|
+
implementer: string; branch: string; attempt: number; reviewOutcome?: string; actualImplementer?: string; repoRoot?: string; missionPath?: string;
|
|
163
|
+
}): string {
|
|
164
|
+
const slug = branch.replace(/^mission\//, '');
|
|
165
|
+
const finalImplementer = actualImplementer || implementer;
|
|
166
|
+
const year = getMissionYear(slug, repoRoot || process.cwd());
|
|
167
|
+
const missionPath = resolveMissionPath(slug, repoRoot, missionPathOverride);
|
|
168
|
+
const primaryBranch = resolvePrimaryBranch(repoRoot);
|
|
169
|
+
const artifactDir = resolveArtifactDir(repoRoot || process.cwd());
|
|
170
|
+
const template = fs.readFileSync(ACT_ON_REVIEW_PROMPT_PATH, 'utf8');
|
|
171
|
+
return template
|
|
172
|
+
.replaceAll('{{branch}}', branch)
|
|
173
|
+
.replaceAll('{{implementer}}', finalImplementer)
|
|
174
|
+
.replaceAll('{{attempt}}', String(attempt))
|
|
175
|
+
.replaceAll('{{slug}}', slug)
|
|
176
|
+
.replaceAll('{{missionPath}}', missionPath)
|
|
177
|
+
.replaceAll('{{artifactDir}}', artifactDir)
|
|
178
|
+
.replaceAll('{{primaryBranch}}', primaryBranch)
|
|
179
|
+
.replaceAll('{{review_outcome}}', reviewOutcome)
|
|
180
|
+
.replaceAll('YYYY', year)
|
|
181
|
+
.replaceAll('{{act_on_review_entrypoint}}', actOnReviewEntrypoint(finalImplementer));
|
|
182
|
+
}
|