@magnusekdahl/parallix 1.0.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 +140 -0
- package/LICENSE +661 -0
- package/README.md +196 -0
- package/config/agents.json +25 -0
- package/config/agents.local.json.template +8 -0
- package/config/state-map.json +4 -0
- package/config/state-map.json.template +31 -0
- package/config/workflow.config.schema.json +98 -0
- package/data/.gitkeep +0 -0
- package/docs/adr/0031-ai-agent-instruction-boundary-and-command-floor.md +114 -0
- package/docs/adr/0032-mission-refinement-state-and-usage-budget-signals.md +135 -0
- package/docs/adr/0034-module-and-skill-invocation-model.md +202 -0
- package/docs/adr/0036-mission-sizing-and-dependency-wave-heuristics.md +79 -0
- package/docs/adr/0037-ai-workflow-coordination-architecture.md +162 -0
- package/docs/adr/0041-integration-pipeline-gates.md +165 -0
- package/docs/adr/0042-workflow-cli-color-rendering-approach.md +106 -0
- package/docs/adr/0043-git-target-resolution-strategy.md +185 -0
- package/docs/adr/0044-workflow-distribution-model.md +277 -0
- package/docs/adr/0045-parallax-branch-model.md +182 -0
- package/docs/adr/0046-npm-publish-process-and-security.md +138 -0
- package/docs/adr/index.md +20 -0
- package/docs/agents.md +212 -0
- package/docs/authority-reference.md +298 -0
- package/docs/forgejo-setup.md +31 -0
- package/docs/migration/extraction.md +61 -0
- package/docs/migration/task-classification.md +36 -0
- package/docs/operator-setup.md +76 -0
- package/docs/readme-rewrite-benchmark.md +188 -0
- package/docs/use-cases.md +105 -0
- package/examples/README.md +62 -0
- package/examples/run-enterprise-tarball-workflow-smoke.sh +257 -0
- package/examples/run-verify-env-smoke.sh +40 -0
- package/index.js +250 -0
- package/lib/README.md +13 -0
- package/lib/agents/agents.js +867 -0
- package/lib/agents/claude-telemetry.js +233 -0
- package/lib/agents/claude.js +139 -0
- package/lib/agents/codex-telemetry.js +202 -0
- package/lib/agents/codex.js +219 -0
- package/lib/agents/limit-hit.js +252 -0
- package/lib/agents/mistral-telemetry.js +44 -0
- package/lib/agents/mistral.js +68 -0
- package/lib/agents/opencode-export.js +110 -0
- package/lib/agents/opencode-telemetry.js +356 -0
- package/lib/agents/opencode.js +218 -0
- package/lib/agents/stage-telemetry.js +37 -0
- package/lib/commands/active.js +625 -0
- package/lib/commands/checkpoint.js +76 -0
- package/lib/commands/config.js +39 -0
- package/lib/commands/coverage-gate.js +358 -0
- package/lib/commands/diff.js +119 -0
- package/lib/commands/draft.js +854 -0
- package/lib/commands/handoff.js +501 -0
- package/lib/commands/integrate.js +1528 -0
- package/lib/commands/mission-start.js +246 -0
- package/lib/commands/rebase.js +597 -0
- package/lib/commands/repair-handoff.js +227 -0
- package/lib/commands/resolve-conflict.js +109 -0
- package/lib/commands/review.js +13 -0
- package/lib/commands/setup-review.js +13 -0
- package/lib/commands/setup.js +3 -0
- package/lib/commands/stats-backfill.js +395 -0
- package/lib/commands/stats.js +1601 -0
- package/lib/commands/status.js +183 -0
- package/lib/commands/verify.js +1 -0
- package/lib/core/fmt.js +202 -0
- package/lib/core/git.js +73 -0
- package/lib/core/gitignore.js +110 -0
- package/lib/core/mission-utils.js +1017 -0
- package/lib/core/persistent-data-migration.js +201 -0
- package/lib/core/product-config.js +508 -0
- package/lib/core/runtime-matrix.js +82 -0
- package/lib/core/spawn-tee.js +173 -0
- package/lib/core/state-map.js +89 -0
- package/lib/core/storage.js +165 -0
- package/lib/core/verification.js +149 -0
- package/lib/index.js +77 -0
- package/lib/review/rebase.js +163 -0
- package/lib/review/review-adapter.js +135 -0
- package/lib/review/review-artifacts.js +619 -0
- package/lib/review/review-commands.js +1375 -0
- package/lib/review/review-events.js +1007 -0
- package/lib/review/review-loop.js +1004 -0
- package/lib/review/review-polling.js +141 -0
- package/lib/review/review-prompts.js +212 -0
- package/lib/review/review-state.js +280 -0
- package/lib/review/review.js +96 -0
- package/lib/tools/backlog.js +680 -0
- package/lib/tools/forgejo.js +1585 -0
- package/lib/tools/gatekeeper.js +106 -0
- package/lib/tools/sessions.js +74 -0
- package/lib/tools/setup-review.js +1053 -0
- package/package.json +56 -0
- package/prompts/act-on-review-verbose.md +20 -0
- package/prompts/act-on-review.md +22 -0
- package/prompts/draft.md +20 -0
- package/prompts/execute.md +24 -0
- package/prompts/portfolio.md +30 -0
- package/prompts/review-verbose.md +20 -0
- package/prompts/review.md +17 -0
- package/px.js +236 -0
- package/templates/AGENTS-snippet.md +14 -0
- package/templates/AGENTS.md.template +34 -0
- package/templates/CLAUDE.md.template +27 -0
- package/templates/CODEX.md.template +38 -0
- package/templates/MISTRAL.md.template +24 -0
- package/templates/claude-commands/act-on-review.md +3 -0
- package/templates/claude-commands/area-review.md +3 -0
- package/templates/claude-commands/draft.md +6 -0
- package/templates/claude-commands/execute.md +6 -0
- package/templates/claude-commands/integrate.md +4 -0
- package/templates/claude-commands/portfolio.md +5 -0
- package/templates/claude-commands/review.md +4 -0
- package/templates/codex/config.toml +6 -0
- package/templates/mission-scaffold.md +39 -0
- package/templates/vibe/skills/act-on-review/SKILL.md +16 -0
- package/templates/vibe/skills/area-review/SKILL.md +16 -0
- package/templates/vibe/skills/draft/SKILL.md +16 -0
- package/templates/vibe/skills/execute/SKILL.md +16 -0
- package/templates/vibe/skills/integrate/SKILL.md +16 -0
- package/templates/vibe/skills/portfolio/SKILL.md +21 -0
- package/templates/vibe/skills/review/SKILL.md +16 -0
- package/tools/setup-forgejo-docker.sh +84 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fmt = require('../core/fmt');
|
|
4
|
+
const { getCurrentBranch, getLastCommit, run } = require('../core/git');
|
|
5
|
+
const { resolveTaskFile, getTaskStatus, reportTaskResolution } = require('../tools/backlog');
|
|
6
|
+
const { adapterChecklist, evaluateRepositoryReadiness } = require('../core/product-config');
|
|
7
|
+
const { evaluateReviewSetup } = require('../tools/setup-review');
|
|
8
|
+
const { toVirtual } = require('../core/state-map');
|
|
9
|
+
const { findMissionDir, findCheckpoints, getFirstLine, inferSlug, getMissionYear, conventionalWorktreePath, resolveMissionBaseBranch, getPrimaryBranch } = require('../core/mission-utils');
|
|
10
|
+
const { getPrStatus } = require('../tools/forgejo');
|
|
11
|
+
const { isForgejoReviewEnabled } = require('../core/product-config');
|
|
12
|
+
const stats = require('./stats');
|
|
13
|
+
|
|
14
|
+
function missionStart(args, options = {}) {
|
|
15
|
+
const log = options.log || fmt.log.plain;
|
|
16
|
+
const error = options.error || fmt.log.plainError;
|
|
17
|
+
const cwdFn = options.cwdFn || (() => process.cwd());
|
|
18
|
+
const getCurrentBranchFn = options.getCurrentBranchFn || getCurrentBranch;
|
|
19
|
+
const resolveTaskFileFn = options.resolveTaskFileFn || resolveTaskFile;
|
|
20
|
+
const getTaskStatusFn = options.getTaskStatusFn || getTaskStatus;
|
|
21
|
+
const toVirtualFn = options.toVirtualFn || toVirtual;
|
|
22
|
+
const findMissionDirFn = options.findMissionDirFn || findMissionDir;
|
|
23
|
+
const findCheckpointsFn = options.findCheckpointsFn || findCheckpoints;
|
|
24
|
+
const getFirstLineFn = options.getFirstLineFn || getFirstLine;
|
|
25
|
+
const inferSlugFn = options.inferSlugFn || inferSlug;
|
|
26
|
+
const getMissionYearFn = options.getMissionYearFn || getMissionYear;
|
|
27
|
+
const conventionalWorktreePathFn = options.conventionalWorktreePathFn || conventionalWorktreePath;
|
|
28
|
+
const getLastCommitFn = options.getLastCommitFn || getLastCommit;
|
|
29
|
+
const getPrStatusFn = options.getPrStatusFn || getPrStatus;
|
|
30
|
+
const evaluateRepositoryReadinessFn = options.evaluateRepositoryReadinessFn || evaluateRepositoryReadiness;
|
|
31
|
+
const evaluateReviewSetupFn = options.evaluateReviewSetupFn || evaluateReviewSetup;
|
|
32
|
+
const adapterChecklistFn = options.adapterChecklistFn || adapterChecklist;
|
|
33
|
+
const resolveMissionClassificationFn = options.resolveMissionClassificationFn || stats.resolveMissionClassification;
|
|
34
|
+
const isForgejoReviewEnabledFn = options.isForgejoReviewEnabledFn || isForgejoReviewEnabled;
|
|
35
|
+
const fsExistsSync = options.fsExistsSync || fs.existsSync;
|
|
36
|
+
const resolveMissionBaseBranchFn = options.resolveMissionBaseBranchFn || resolveMissionBaseBranch;
|
|
37
|
+
const getPrimaryBranchFn = options.getPrimaryBranchFn || getPrimaryBranch;
|
|
38
|
+
const runFn = options.runFn || run;
|
|
39
|
+
|
|
40
|
+
const explicitSlug = args[0];
|
|
41
|
+
const slug = inferSlugFn(explicitSlug);
|
|
42
|
+
const isVerifyOnly = options.command === 'verify-env' || !slug;
|
|
43
|
+
const returnResult = Boolean(options.returnResult);
|
|
44
|
+
|
|
45
|
+
if (isVerifyOnly) {
|
|
46
|
+
log(fmt.status('INFO', 'Running environment diagnostics (verify-env)...'));
|
|
47
|
+
} else {
|
|
48
|
+
log(fmt.status('INFO', `Running mission startup preflight for: ${fmt.slug(slug)}`));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let overallFail = false;
|
|
52
|
+
const remediationSteps = [];
|
|
53
|
+
|
|
54
|
+
// Check 1: PWD
|
|
55
|
+
const cwd = cwdFn();
|
|
56
|
+
const reportReviewSetup = () => {
|
|
57
|
+
const reviewSetup = evaluateReviewSetupFn(cwd);
|
|
58
|
+
if (reviewSetup.required && reviewSetup.ok) {
|
|
59
|
+
log(fmt.status('PASS', 'Forgejo review setup: token files, auth, and git remote are ready.'));
|
|
60
|
+
} else if (reviewSetup.required && !reviewSetup.ok) {
|
|
61
|
+
log(fmt.status('WARN', 'Forgejo review setup: review actions are not ready yet.'));
|
|
62
|
+
for (const issue of reviewSetup.issues) {
|
|
63
|
+
log(fmt.status('INFO', issue));
|
|
64
|
+
}
|
|
65
|
+
for (const step of reviewSetup.steps) {
|
|
66
|
+
log(fmt.status('INFO', step));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const reportRepositoryReadiness = () => {
|
|
72
|
+
const readiness = evaluateRepositoryReadinessFn(cwd);
|
|
73
|
+
if (readiness.mode === 'default') {
|
|
74
|
+
log(fmt.status('PASS', 'Workflow config: using built-in defaults (create workflow.config.json to override).'));
|
|
75
|
+
reportReviewSetup();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (readiness.mode === 'configured') {
|
|
79
|
+
log(fmt.status('PASS', `Workflow config: ${readiness.configPath}`));
|
|
80
|
+
log(fmt.status('PASS', 'Repository adapters: override sections are valid.'));
|
|
81
|
+
reportReviewSetup();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// mode === 'invalid'
|
|
86
|
+
log(fmt.status('FAIL', `Workflow config: ${readiness.configPath || 'workflow.config.json'}`));
|
|
87
|
+
for (const issue of readiness.issues) {
|
|
88
|
+
log(fmt.status('INFO', issue));
|
|
89
|
+
}
|
|
90
|
+
for (const step of adapterChecklistFn()) {
|
|
91
|
+
log(fmt.status('INFO', step));
|
|
92
|
+
}
|
|
93
|
+
overallFail = true;
|
|
94
|
+
remediationSteps.push('Fix workflow.config.json: ensure it is valid JSON and adapters is an object with valid subsections.');
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (isVerifyOnly) {
|
|
98
|
+
log(fmt.status('PASS', `PWD: ${fmt.path(cwd)}`));
|
|
99
|
+
} else {
|
|
100
|
+
const expectedPath = conventionalWorktreePathFn(slug);
|
|
101
|
+
if (cwd === expectedPath || cwd.endsWith(slug)) {
|
|
102
|
+
log(fmt.status('PASS', `PWD: matches expected mission worktree path ${fmt.path(expectedPath)}`));
|
|
103
|
+
} else {
|
|
104
|
+
log(fmt.status('FAIL', `PWD: ${fmt.path(cwd)} does not match expected mission worktree path ${fmt.path(expectedPath)}`));
|
|
105
|
+
overallFail = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check 2: Branch
|
|
110
|
+
const currentBranch = getCurrentBranchFn();
|
|
111
|
+
if (isVerifyOnly) {
|
|
112
|
+
log(fmt.status('PASS', `Branch: ${fmt.branch(currentBranch)}`));
|
|
113
|
+
} else {
|
|
114
|
+
const expectedBranch = `mission/${slug}`;
|
|
115
|
+
if (currentBranch === expectedBranch) {
|
|
116
|
+
log(fmt.status('PASS', `Branch: ${fmt.branch(currentBranch)}`));
|
|
117
|
+
} else {
|
|
118
|
+
log(fmt.status('FAIL', `Branch: ${fmt.branch(currentBranch)} does not match expected mission branch ${fmt.branch(expectedBranch)}`));
|
|
119
|
+
overallFail = true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (isVerifyOnly) {
|
|
123
|
+
reportRepositoryReadiness();
|
|
124
|
+
}
|
|
125
|
+
// Check 3: Backlog task
|
|
126
|
+
if (slug) {
|
|
127
|
+
const taskResolution = resolveTaskFileFn(slug);
|
|
128
|
+
if (taskResolution.ok) {
|
|
129
|
+
const status = getTaskStatusFn(taskResolution.taskFile);
|
|
130
|
+
const virtualStatus = toVirtualFn(status);
|
|
131
|
+
|
|
132
|
+
if (!isVerifyOnly) {
|
|
133
|
+
if (virtualStatus === 'ready' || virtualStatus === 'active' || virtualStatus === 'review') {
|
|
134
|
+
log(fmt.status('PASS', `Backlog task status: ${status}`));
|
|
135
|
+
} else if (virtualStatus === 'backlog' || virtualStatus === 'draft') {
|
|
136
|
+
log(fmt.status('WARN', `Backlog task status: ${status} (expected 'ready', 'active', or 'review')`));
|
|
137
|
+
} else {
|
|
138
|
+
log(fmt.status('FAIL', `Backlog task status: ${status} (mission already complete)`));
|
|
139
|
+
overallFail = true;
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
log(fmt.status('PASS', `Backlog task status: ${status}`));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const { classification } = resolveMissionClassificationFn(slug);
|
|
147
|
+
log(fmt.status('PASS', `Backlog classification: ${classification}`));
|
|
148
|
+
} catch (error) {
|
|
149
|
+
log(fmt.status('FAIL', `Backlog classification: ${error.message}`));
|
|
150
|
+
overallFail = true;
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
reportTaskResolution(taskResolution, slug, log);
|
|
154
|
+
overallFail = true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check 4: Mission docs + base branch
|
|
159
|
+
if (!isVerifyOnly) {
|
|
160
|
+
const missionDir = findMissionDirFn(slug);
|
|
161
|
+
if (missionDir) {
|
|
162
|
+
const missionFile = path.join(missionDir, 'MISSION.md');
|
|
163
|
+
if (fsExistsSync(missionFile)) {
|
|
164
|
+
const checkpoints = findCheckpointsFn(missionDir);
|
|
165
|
+
if (checkpoints.length > 0) {
|
|
166
|
+
const lastCP = checkpoints[checkpoints.length - 1];
|
|
167
|
+
const firstLine = getFirstLineFn(lastCP);
|
|
168
|
+
log(fmt.status('PASS', `Mission doc: found MISSION.md. Most recent checkpoint: ${fmt.path(path.basename(lastCP))} (${firstLine})`));
|
|
169
|
+
} else {
|
|
170
|
+
log(fmt.status('WARN', `Mission doc: found MISSION.md but no checkpoints yet. Start from CP-1.`));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Base branch validation: when a mission records a non-primary base,
|
|
174
|
+
// verify the base branch exists locally so integrate won't fail silently.
|
|
175
|
+
const primaryBranch = getPrimaryBranchFn();
|
|
176
|
+
let recordedBase;
|
|
177
|
+
try {
|
|
178
|
+
recordedBase = resolveMissionBaseBranchFn(slug, process.cwd());
|
|
179
|
+
} catch (_) {
|
|
180
|
+
recordedBase = null;
|
|
181
|
+
}
|
|
182
|
+
if (recordedBase && recordedBase !== primaryBranch) {
|
|
183
|
+
const checkResult = runFn(['-C', cwd, 'show-ref', '--verify', '--quiet', `refs/heads/${recordedBase}`], { cwd });
|
|
184
|
+
if (!checkResult || checkResult.status !== 0) {
|
|
185
|
+
log(fmt.status('FAIL', `Preflight: base branch '${recordedBase}' recorded in MISSION.md does not exist locally. Create or fetch the '${recordedBase}' base branch before starting this mission.`));
|
|
186
|
+
overallFail = true;
|
|
187
|
+
} else {
|
|
188
|
+
log(fmt.status('PASS', `Preflight: base branch '${recordedBase}' exists locally.`));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
log(fmt.status('FAIL', `Mission doc: found directory but MISSION.md is missing in ${fmt.path(missionDir)}`));
|
|
193
|
+
overallFail = true;
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
log(fmt.status('FAIL', `Mission doc: directory not found in docs/missions/${getMissionYearFn(slug)}/ for slug ${fmt.slug(slug)}`));
|
|
197
|
+
overallFail = true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check 5: Last commit
|
|
202
|
+
const lastCommit = getLastCommitFn();
|
|
203
|
+
log(fmt.status('PASS', `Last commit: ${fmt.sha(lastCommit.sha.substring(0, 8))} - ${lastCommit.subject} (${lastCommit.date})`));
|
|
204
|
+
|
|
205
|
+
// Check 6: Forgejo PR (skipped when review provider is not forgejo)
|
|
206
|
+
if (!isVerifyOnly && isForgejoReviewEnabledFn(cwd)) {
|
|
207
|
+
const pr = getPrStatusFn(`mission/${slug}`);
|
|
208
|
+
if (!pr.exists) {
|
|
209
|
+
log(fmt.status('PASS', `Forgejo PR: no PR found (ready for startup)`));
|
|
210
|
+
} else if (pr.state === 'open') {
|
|
211
|
+
log(fmt.status('PASS', `Forgejo PR: found OPEN PR (#${pr.number})`));
|
|
212
|
+
} else {
|
|
213
|
+
log(fmt.status('PASS', `Forgejo PR: found ${pr.state ? pr.state.toUpperCase() : 'UNKNOWN'} PR (#${pr.number})`));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return completePreflightOrExit(overallFail, returnResult, { log, error, remediationSteps });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Extracted so callers can test the returnResult path without live git dependencies.
|
|
221
|
+
function completePreflightOrExit(overallFail, returnResult, options = {}) {
|
|
222
|
+
const error = options.error || fmt.log.plainError;
|
|
223
|
+
const log = options.log || fmt.log.plain;
|
|
224
|
+
const remediationSteps = options.remediationSteps || [];
|
|
225
|
+
if (overallFail) {
|
|
226
|
+
let msg = '\n' + fmt.status('FAIL', 'Environment verdict: NOT USABLE');
|
|
227
|
+
if (remediationSteps.length > 0) {
|
|
228
|
+
msg += ' — remediation:';
|
|
229
|
+
for (const step of remediationSteps) {
|
|
230
|
+
msg += `\n - ${step}`;
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
msg += ' — fix blockers above before proceeding.';
|
|
234
|
+
}
|
|
235
|
+
error(msg);
|
|
236
|
+
if (returnResult) return { pass: false };
|
|
237
|
+
process.exit(1);
|
|
238
|
+
} else {
|
|
239
|
+
log('\n' + fmt.status('PASS', 'Environment verdict: USABLE — this repository is ready for workflow commands.'));
|
|
240
|
+
if (returnResult) return { pass: true };
|
|
241
|
+
process.exit(0);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = missionStart;
|
|
246
|
+
module.exports.completePreflightOrExit = completePreflightOrExit;
|