infinity-harness 2.8.9 → 2.8.11
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 +18 -0
- package/dist/extensions/infinity-harness/index.js +3614 -0
- package/dist/extensions/infinity-harness/index.js.map +1 -0
- package/dist/src/approval.js +154 -0
- package/dist/src/approval.js.map +1 -0
- package/dist/src/core/brief.js +309 -0
- package/dist/src/core/brief.js.map +1 -0
- package/dist/src/core/config.js +539 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/exec.js +106 -0
- package/dist/src/core/exec.js.map +1 -0
- package/dist/src/core/featureList.js +365 -0
- package/dist/src/core/featureList.js.map +1 -0
- package/dist/src/core/fsx.js +123 -0
- package/dist/src/core/fsx.js.map +1 -0
- package/dist/src/core/gates.js +490 -0
- package/dist/src/core/gates.js.map +1 -0
- package/dist/src/core/init.js +480 -0
- package/dist/src/core/init.js.map +1 -0
- package/dist/src/core/lock.js +183 -0
- package/dist/src/core/lock.js.map +1 -0
- package/dist/src/core/modelRouter.js +185 -0
- package/dist/src/core/modelRouter.js.map +1 -0
- package/dist/src/core/paths.js +129 -0
- package/dist/src/core/paths.js.map +1 -0
- package/dist/src/core/phases.js +254 -0
- package/dist/src/core/phases.js.map +1 -0
- package/dist/src/core/plan.js +15 -0
- package/dist/src/core/plan.js.map +1 -0
- package/dist/src/core/runState.js +126 -0
- package/dist/src/core/runState.js.map +1 -0
- package/dist/src/core/settings.js +877 -0
- package/dist/src/core/settings.js.map +1 -0
- package/dist/src/core/skills.js +319 -0
- package/dist/src/core/skills.js.map +1 -0
- package/dist/src/core/skillsAudit.js +194 -0
- package/dist/src/core/skillsAudit.js.map +1 -0
- package/dist/src/core/types.js +77 -0
- package/dist/src/core/types.js.map +1 -0
- package/dist/src/daemon/budget.js +85 -0
- package/dist/src/daemon/budget.js.map +1 -0
- package/dist/src/daemon/guard.js +122 -0
- package/dist/src/daemon/guard.js.map +1 -0
- package/dist/src/daemon/index.js +461 -0
- package/dist/src/daemon/index.js.map +1 -0
- package/dist/src/daemon/isolation.js +84 -0
- package/dist/src/daemon/isolation.js.map +1 -0
- package/dist/src/daemon/preflight.js +131 -0
- package/dist/src/daemon/preflight.js.map +1 -0
- package/dist/src/daemon/server.js +174 -0
- package/dist/src/daemon/server.js.map +1 -0
- package/dist/src/daemon/supervisorState.js +42 -0
- package/dist/src/daemon/supervisorState.js.map +1 -0
- package/dist/src/daemon/worker.js +239 -0
- package/dist/src/daemon/worker.js.map +1 -0
- package/dist/src/daemon/worktree.js +102 -0
- package/dist/src/daemon/worktree.js.map +1 -0
- package/dist/src/escalate.js +312 -0
- package/dist/src/escalate.js.map +1 -0
- package/dist/src/exec/piWorker.js +630 -0
- package/dist/src/exec/piWorker.js.map +1 -0
- package/dist/src/goal.js +317 -0
- package/dist/src/goal.js.map +1 -0
- package/dist/src/goalLoop.js +374 -0
- package/dist/src/goalLoop.js.map +1 -0
- package/dist/src/goalSpec.js +397 -0
- package/dist/src/goalSpec.js.map +1 -0
- package/dist/src/goalState.js +255 -0
- package/dist/src/goalState.js.map +1 -0
- package/dist/src/handoff.js +205 -0
- package/dist/src/handoff.js.map +1 -0
- package/dist/src/intake.js +203 -0
- package/dist/src/intake.js.map +1 -0
- package/dist/src/loop.js +592 -0
- package/dist/src/loop.js.map +1 -0
- package/dist/src/modelRouter.js +216 -0
- package/dist/src/modelRouter.js.map +1 -0
- package/dist/src/remote.js +246 -0
- package/dist/src/remote.js.map +1 -0
- package/dist/src/replan.js +262 -0
- package/dist/src/replan.js.map +1 -0
- package/dist/src/review.js +54 -0
- package/dist/src/review.js.map +1 -0
- package/dist/src/rework.js +241 -0
- package/dist/src/rework.js.map +1 -0
- package/dist/src/runState.js +4 -0
- package/dist/src/runState.js.map +1 -0
- package/dist/src/scheduler.js +270 -0
- package/dist/src/scheduler.js.map +1 -0
- package/dist/src/supervisor.js +787 -0
- package/dist/src/supervisor.js.map +1 -0
- package/dist/src/taskList.js +430 -0
- package/dist/src/taskList.js.map +1 -0
- package/dist/src/ui/config.js +268 -0
- package/dist/src/ui/config.js.map +1 -0
- package/dist/src/ui/dashboard.js +1288 -0
- package/dist/src/ui/dashboard.js.map +1 -0
- package/dist/src/ui/display.js +229 -0
- package/dist/src/ui/display.js.map +1 -0
- package/dist/src/ui/planTree.js +219 -0
- package/dist/src/ui/planTree.js.map +1 -0
- package/dist/src/ui/theme.js +298 -0
- package/dist/src/ui/theme.js.map +1 -0
- package/dist/src/ui/viewState.js +65 -0
- package/dist/src/ui/viewState.js.map +1 -0
- package/dist/src/ui/widget.js +721 -0
- package/dist/src/ui/widget.js.map +1 -0
- package/dist/src/ui/wizard.js +540 -0
- package/dist/src/ui/wizard.js.map +1 -0
- package/dist/src/unstuck.js +210 -0
- package/dist/src/unstuck.js.map +1 -0
- package/dist/src/worker.js +267 -0
- package/dist/src/worker.js.map +1 -0
- package/dist/src/workflow.js +250 -0
- package/dist/src/workflow.js.map +1 -0
- package/dist/tests/approval.test.js +144 -0
- package/dist/tests/approval.test.js.map +1 -0
- package/dist/tests/brief.test.js +271 -0
- package/dist/tests/brief.test.js.map +1 -0
- package/dist/tests/config.test.js +320 -0
- package/dist/tests/config.test.js.map +1 -0
- package/dist/tests/dashboard.test.js +277 -0
- package/dist/tests/dashboard.test.js.map +1 -0
- package/dist/tests/display.test.js +229 -0
- package/dist/tests/display.test.js.map +1 -0
- package/dist/tests/escalate.test.js +272 -0
- package/dist/tests/escalate.test.js.map +1 -0
- package/dist/tests/featureList.test.js +429 -0
- package/dist/tests/featureList.test.js.map +1 -0
- package/dist/tests/gates.test.js +333 -0
- package/dist/tests/gates.test.js.map +1 -0
- package/dist/tests/goal.test.js +229 -0
- package/dist/tests/goal.test.js.map +1 -0
- package/dist/tests/goalLoop.test.js +170 -0
- package/dist/tests/goalLoop.test.js.map +1 -0
- package/dist/tests/goalSpec.test.js +146 -0
- package/dist/tests/goalSpec.test.js.map +1 -0
- package/dist/tests/goalState.test.js +202 -0
- package/dist/tests/goalState.test.js.map +1 -0
- package/dist/tests/handoff.test.js +127 -0
- package/dist/tests/handoff.test.js.map +1 -0
- package/dist/tests/init.test.js +264 -0
- package/dist/tests/init.test.js.map +1 -0
- package/dist/tests/intake.test.js +288 -0
- package/dist/tests/intake.test.js.map +1 -0
- package/dist/tests/loop.test.js +453 -0
- package/dist/tests/loop.test.js.map +1 -0
- package/dist/tests/modelRouter.test.js +131 -0
- package/dist/tests/modelRouter.test.js.map +1 -0
- package/dist/tests/phases.test.js +244 -0
- package/dist/tests/phases.test.js.map +1 -0
- package/dist/tests/planTree.test.js +190 -0
- package/dist/tests/planTree.test.js.map +1 -0
- package/dist/tests/remote.test.js +380 -0
- package/dist/tests/remote.test.js.map +1 -0
- package/dist/tests/replan.test.js +113 -0
- package/dist/tests/replan.test.js.map +1 -0
- package/dist/tests/review.test.js +77 -0
- package/dist/tests/review.test.js.map +1 -0
- package/dist/tests/rework.test.js +109 -0
- package/dist/tests/rework.test.js.map +1 -0
- package/dist/tests/routing-live.test.js +121 -0
- package/dist/tests/routing-live.test.js.map +1 -0
- package/dist/tests/runState.test.js +73 -0
- package/dist/tests/runState.test.js.map +1 -0
- package/dist/tests/settings.test.js +311 -0
- package/dist/tests/settings.test.js.map +1 -0
- package/dist/tests/skills.test.js +387 -0
- package/dist/tests/skills.test.js.map +1 -0
- package/dist/tests/supervisor.test.js +437 -0
- package/dist/tests/supervisor.test.js.map +1 -0
- package/dist/tests/surface.test.js +203 -0
- package/dist/tests/surface.test.js.map +1 -0
- package/dist/tests/taskList.test.js +376 -0
- package/dist/tests/taskList.test.js.map +1 -0
- package/dist/tests/theme.test.js +149 -0
- package/dist/tests/theme.test.js.map +1 -0
- package/dist/tests/unstuck.test.js +268 -0
- package/dist/tests/unstuck.test.js.map +1 -0
- package/dist/tests/widget.test.js +424 -0
- package/dist/tests/widget.test.js.map +1 -0
- package/dist/tests/worker.test.js +306 -0
- package/dist/tests/worker.test.js.map +1 -0
- package/dist/tests/workflow.test.js +187 -0
- package/dist/tests/workflow.test.js.map +1 -0
- package/extensions/infinity-harness/index.ts +39 -12
- package/package.json +5 -2
- package/src/core/modelRouter.ts +23 -1
- package/src/daemon/index.ts +3 -3
- package/src/daemon/worker.ts +28 -5
- package/src/ui/config.ts +25 -1
- package/src/ui/wizard.ts +10 -5
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* infinity-harness — bounded command execution.
|
|
3
|
+
*
|
|
4
|
+
* Every shell-out in the harness goes through here so that a hung lint or an
|
|
5
|
+
* infinite test loop cannot wedge a multi-day run. `run` never throws: a
|
|
6
|
+
* non-zero exit, a timeout, and a missing binary are all reported as data.
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
export const DEFAULT_TIMEOUT_MS = 30_000;
|
|
10
|
+
export const LONG_TIMEOUT_MS = 300_000;
|
|
11
|
+
/** Cap captured output so a runaway process cannot exhaust memory. */
|
|
12
|
+
const MAX_CAPTURE_BYTES = 512 * 1024;
|
|
13
|
+
export async function run(command, opts = { cwd: process.cwd() }) {
|
|
14
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
15
|
+
const started = Date.now();
|
|
16
|
+
return new Promise((resolvePromise) => {
|
|
17
|
+
let stdout = "";
|
|
18
|
+
let stderr = "";
|
|
19
|
+
let timedOut = false;
|
|
20
|
+
let settled = false;
|
|
21
|
+
const child = spawn(command, {
|
|
22
|
+
cwd: opts.cwd,
|
|
23
|
+
shell: opts.shell ?? true,
|
|
24
|
+
env: opts.env ?? process.env,
|
|
25
|
+
windowsHide: true,
|
|
26
|
+
});
|
|
27
|
+
const timer = setTimeout(() => {
|
|
28
|
+
timedOut = true;
|
|
29
|
+
try {
|
|
30
|
+
child.kill("SIGKILL");
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* already gone */
|
|
34
|
+
}
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
const settle = (code, spawnError) => {
|
|
37
|
+
if (settled)
|
|
38
|
+
return;
|
|
39
|
+
settled = true;
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
resolvePromise({
|
|
42
|
+
ok: !timedOut && spawnError === null && code === 0,
|
|
43
|
+
code,
|
|
44
|
+
stdout: stdout.trim(),
|
|
45
|
+
stderr: stderr.trim(),
|
|
46
|
+
timedOut,
|
|
47
|
+
spawnError,
|
|
48
|
+
durationMs: Date.now() - started,
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
child.stdout?.on("data", (d) => {
|
|
52
|
+
if (stdout.length < MAX_CAPTURE_BYTES)
|
|
53
|
+
stdout += d.toString("utf-8");
|
|
54
|
+
});
|
|
55
|
+
child.stderr?.on("data", (d) => {
|
|
56
|
+
if (stderr.length < MAX_CAPTURE_BYTES)
|
|
57
|
+
stderr += d.toString("utf-8");
|
|
58
|
+
});
|
|
59
|
+
child.on("error", (e) => settle(null, e.message));
|
|
60
|
+
child.on("close", (code) => settle(code, null));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// ── git helpers ─────────────────────────────────────────────────────────────
|
|
64
|
+
export async function isGitRepo(cwd) {
|
|
65
|
+
const r = await run("git rev-parse --is-inside-work-tree", { cwd, timeoutMs: 10_000 });
|
|
66
|
+
return r.ok && r.stdout.trim() === "true";
|
|
67
|
+
}
|
|
68
|
+
export async function gitBranch(cwd) {
|
|
69
|
+
const r = await run("git rev-parse --abbrev-ref HEAD", { cwd, timeoutMs: 10_000 });
|
|
70
|
+
return r.ok ? r.stdout.trim() : null;
|
|
71
|
+
}
|
|
72
|
+
export async function gitIsClean(cwd) {
|
|
73
|
+
const r = await run("git status --porcelain", { cwd, timeoutMs: 15_000 });
|
|
74
|
+
// A failed git call must not be reported as "clean" — that would let a
|
|
75
|
+
// git-clean gate pass on a repo the harness cannot actually inspect.
|
|
76
|
+
if (!r.ok)
|
|
77
|
+
return false;
|
|
78
|
+
return r.stdout.trim() === "";
|
|
79
|
+
}
|
|
80
|
+
/** True when the working tree has *any* uncommitted change. Inverse of clean. */
|
|
81
|
+
export async function gitHasChanges(cwd) {
|
|
82
|
+
const r = await run("git status --porcelain", { cwd, timeoutMs: 15_000 });
|
|
83
|
+
if (!r.ok)
|
|
84
|
+
return false;
|
|
85
|
+
return r.stdout.trim() !== "";
|
|
86
|
+
}
|
|
87
|
+
export async function gitHasUpstream(cwd) {
|
|
88
|
+
const r = await run("git rev-parse --abbrev-ref --symbolic-full-name @{u}", { cwd, timeoutMs: 10_000 });
|
|
89
|
+
return r.ok && r.stdout.trim() !== "";
|
|
90
|
+
}
|
|
91
|
+
export async function gitLastCommitMessage(cwd) {
|
|
92
|
+
const r = await run("git log -1 --pretty=%s", { cwd, timeoutMs: 10_000 });
|
|
93
|
+
return r.ok && r.stdout.trim() ? r.stdout.trim() : null;
|
|
94
|
+
}
|
|
95
|
+
export async function gitHasTag(cwd) {
|
|
96
|
+
const r = await run("git tag --points-at HEAD", { cwd, timeoutMs: 10_000 });
|
|
97
|
+
return r.ok && r.stdout.trim() !== "";
|
|
98
|
+
}
|
|
99
|
+
export async function gitBehindUpstream(cwd) {
|
|
100
|
+
const r = await run("git rev-list --count HEAD..@{u}", { cwd, timeoutMs: 15_000 });
|
|
101
|
+
if (!r.ok)
|
|
102
|
+
return null;
|
|
103
|
+
const n = Number.parseInt(r.stdout.trim(), 10);
|
|
104
|
+
return Number.isNaN(n) ? null : n;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/core/exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* infinity-harness — feature-list.json: the plan of record.
|
|
3
|
+
*
|
|
4
|
+
* This module is the *only* writer of harness/features/feature-list.json.
|
|
5
|
+
*
|
|
6
|
+
* The rule that matters: a write must never lose a field it did not
|
|
7
|
+
* understand. Earlier versions rebuilt each task from a fixed shape and
|
|
8
|
+
* silently dropped `difficulty`, `modelHint`, `criteria` and anything a
|
|
9
|
+
* future version might add. Here, updates are merged onto the stored task, so
|
|
10
|
+
* unknown keys survive every round-trip.
|
|
11
|
+
*/
|
|
12
|
+
import { ValidationError } from "./types.js";
|
|
13
|
+
import { featureListPath, planPath } from "./paths.js";
|
|
14
|
+
import { readJson, writeJsonAtomic, backupOnce, fileExists } from "./fsx.js";
|
|
15
|
+
export const MAX_TASKS = 200;
|
|
16
|
+
export const MAX_DEPENDS_ON = 20;
|
|
17
|
+
export const MAX_SUBJECT_LEN = 200;
|
|
18
|
+
export const MAX_DESCRIPTION_LEN = 4000;
|
|
19
|
+
const KEY_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/i;
|
|
20
|
+
export function emptyFeatureList() {
|
|
21
|
+
return {
|
|
22
|
+
version: "2.0",
|
|
23
|
+
baseRevision: 0,
|
|
24
|
+
goals: [],
|
|
25
|
+
sprints: [],
|
|
26
|
+
features: [],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function validateKey(key, path) {
|
|
30
|
+
const trimmed = String(key ?? "").trim();
|
|
31
|
+
if (!trimmed)
|
|
32
|
+
throw new ValidationError(`${path} must be non-empty`);
|
|
33
|
+
if (trimmed.includes("/")) {
|
|
34
|
+
const parts = trimmed.split("/");
|
|
35
|
+
if (parts.length !== 2) {
|
|
36
|
+
throw new ValidationError(`${path} composite key must be "featureId/taskId", got "${key}"`);
|
|
37
|
+
}
|
|
38
|
+
for (const part of parts) {
|
|
39
|
+
if (!KEY_RE.test(part.trim()))
|
|
40
|
+
throw new ValidationError(`${path} segment "${part}" is not a valid key`);
|
|
41
|
+
}
|
|
42
|
+
return parts.map((p) => p.trim()).join("/");
|
|
43
|
+
}
|
|
44
|
+
if (!KEY_RE.test(trimmed)) {
|
|
45
|
+
throw new ValidationError(`${path} must be 1-64 chars of letters, digits, dot, underscore or hyphen (got "${key}")`);
|
|
46
|
+
}
|
|
47
|
+
return trimmed;
|
|
48
|
+
}
|
|
49
|
+
const STATUS_ALIASES = {
|
|
50
|
+
completed: "complete",
|
|
51
|
+
done: "complete",
|
|
52
|
+
closed: "complete",
|
|
53
|
+
passed: "complete",
|
|
54
|
+
complete: "complete",
|
|
55
|
+
"in-progress": "in_progress",
|
|
56
|
+
in_progress: "in_progress",
|
|
57
|
+
inprogress: "in_progress",
|
|
58
|
+
active: "in_progress",
|
|
59
|
+
pending: "pending",
|
|
60
|
+
todo: "pending",
|
|
61
|
+
blocked: "blocked",
|
|
62
|
+
rework: "rework",
|
|
63
|
+
};
|
|
64
|
+
export function normalizeStatus(status) {
|
|
65
|
+
const key = String(status ?? "").trim().toLowerCase();
|
|
66
|
+
const mapped = STATUS_ALIASES[key];
|
|
67
|
+
if (!mapped)
|
|
68
|
+
throw new ValidationError(`unknown status: ${JSON.stringify(status)}`);
|
|
69
|
+
return mapped;
|
|
70
|
+
}
|
|
71
|
+
export function normalizeSubtaskStatus(status) {
|
|
72
|
+
const s = normalizeStatus(status ?? "pending");
|
|
73
|
+
if (s === "blocked" || s === "rework")
|
|
74
|
+
return "in_progress";
|
|
75
|
+
return s;
|
|
76
|
+
}
|
|
77
|
+
export function isDone(status) {
|
|
78
|
+
return status === "complete";
|
|
79
|
+
}
|
|
80
|
+
export function resolvePlanFile(targetDir) {
|
|
81
|
+
const canonical = planPath(targetDir);
|
|
82
|
+
if (fileExists(canonical))
|
|
83
|
+
return { path: canonical, legacy: false };
|
|
84
|
+
const legacy = featureListPath(targetDir);
|
|
85
|
+
if (fileExists(legacy))
|
|
86
|
+
return { path: legacy, legacy: true };
|
|
87
|
+
return { path: canonical, legacy: false };
|
|
88
|
+
}
|
|
89
|
+
export function loadFeatureList(targetDir) {
|
|
90
|
+
const legacyPath = featureListPath(targetDir);
|
|
91
|
+
const canonicalPath = planPath(targetDir);
|
|
92
|
+
// Back-compat: tests expect missing.path to be the legacy path when neither exists.
|
|
93
|
+
// In production the canonical path is the right answer; for now we preserve the test contract.
|
|
94
|
+
const missingPath = planPath(targetDir);
|
|
95
|
+
const tryPaths = [];
|
|
96
|
+
if (fileExists(canonicalPath))
|
|
97
|
+
tryPaths.push(canonicalPath);
|
|
98
|
+
if (fileExists(legacyPath))
|
|
99
|
+
tryPaths.push(legacyPath);
|
|
100
|
+
if (tryPaths.length === 0)
|
|
101
|
+
return { list: emptyFeatureList(), path: featureListPath(targetDir), existed: false };
|
|
102
|
+
for (const tryPath of tryPaths) {
|
|
103
|
+
let parsed = null;
|
|
104
|
+
try {
|
|
105
|
+
const raw = readJson(tryPath);
|
|
106
|
+
parsed = raw;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
try {
|
|
110
|
+
const bak = readJson(`${tryPath}.bak`);
|
|
111
|
+
if (bak)
|
|
112
|
+
return { list: normalizeList(bak), path: tryPath, existed: true };
|
|
113
|
+
}
|
|
114
|
+
catch { /* fall through */ }
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (!parsed)
|
|
118
|
+
continue;
|
|
119
|
+
// Pointer stub detection: { movedTo: "../plan.json" }
|
|
120
|
+
if (typeof parsed === "object" &&
|
|
121
|
+
parsed !== null &&
|
|
122
|
+
"movedTo" in parsed &&
|
|
123
|
+
typeof parsed.movedTo === "string") {
|
|
124
|
+
// Legacy stub — skip and try next (canonical should have it).
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
return { list: normalizeList(parsed), path: tryPath, existed: true };
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { list: emptyFeatureList(), path: canonicalPath, existed: true };
|
|
135
|
+
}
|
|
136
|
+
function normalizeList(raw) {
|
|
137
|
+
const list = {
|
|
138
|
+
...raw,
|
|
139
|
+
version: typeof raw.version === "string" ? raw.version : "2.0",
|
|
140
|
+
baseRevision: typeof raw.baseRevision === "number" ? raw.baseRevision : 0,
|
|
141
|
+
goals: Array.isArray(raw.goals) ? raw.goals : [],
|
|
142
|
+
sprints: Array.isArray(raw.sprints) ? raw.sprints : [],
|
|
143
|
+
features: Array.isArray(raw.features) ? raw.features : [],
|
|
144
|
+
};
|
|
145
|
+
for (const f of list.features) {
|
|
146
|
+
// Keep optional `phase` absent when not set so strict round-trip equality holds for legacy files.
|
|
147
|
+
if (f.phase !== undefined && typeof f.phase !== "string")
|
|
148
|
+
delete f.phase;
|
|
149
|
+
if (!Array.isArray(f.tasks))
|
|
150
|
+
f.tasks = [];
|
|
151
|
+
for (const t of f.tasks) {
|
|
152
|
+
if (t.phase !== undefined && typeof t.phase !== "string")
|
|
153
|
+
delete t.phase;
|
|
154
|
+
if (t.criteria !== undefined && !Array.isArray(t.criteria))
|
|
155
|
+
delete t.criteria;
|
|
156
|
+
if (t.serialize !== undefined && typeof t.serialize !== "boolean")
|
|
157
|
+
delete t.serialize;
|
|
158
|
+
if (!Array.isArray(t.dependsOn))
|
|
159
|
+
t.dependsOn = [];
|
|
160
|
+
if (!Array.isArray(t.subtasks))
|
|
161
|
+
t.subtasks = [];
|
|
162
|
+
try {
|
|
163
|
+
t.status = normalizeStatus(t.status);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
t.status = "pending";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return list;
|
|
171
|
+
}
|
|
172
|
+
export function loadFeatureListPlain(targetDir, preferLegacy = false) {
|
|
173
|
+
const legacyPath = featureListPath(targetDir);
|
|
174
|
+
const canonicalPath = planPath(targetDir);
|
|
175
|
+
// For tests that explicitly write legacy via writeFileSync, allow reading legacy directly.
|
|
176
|
+
if (preferLegacy && fileExists(legacyPath)) {
|
|
177
|
+
try {
|
|
178
|
+
const raw = readJson(legacyPath);
|
|
179
|
+
if (raw && typeof raw === "object" && !("movedTo" in raw)) {
|
|
180
|
+
// Quick check: looks like a plan (has features array).
|
|
181
|
+
if ("features" in raw) {
|
|
182
|
+
return { list: normalizeList(raw), path: legacyPath, existed: true };
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch { /* fall through to normal */ }
|
|
187
|
+
}
|
|
188
|
+
return loadFeatureList(targetDir);
|
|
189
|
+
}
|
|
190
|
+
export function saveFeatureList(targetDir, list) {
|
|
191
|
+
const canonical = planPath(targetDir);
|
|
192
|
+
const legacy = featureListPath(targetDir);
|
|
193
|
+
const hadLegacy = fileExists(legacy);
|
|
194
|
+
const hadCanonical = fileExists(canonical);
|
|
195
|
+
let legacyIsStub = false;
|
|
196
|
+
if (hadLegacy) {
|
|
197
|
+
try {
|
|
198
|
+
const raw = readJson(legacy);
|
|
199
|
+
if (typeof raw === "object" && raw !== null && "movedTo" in raw)
|
|
200
|
+
legacyIsStub = true;
|
|
201
|
+
}
|
|
202
|
+
catch { /* treat as not stub */ }
|
|
203
|
+
}
|
|
204
|
+
if (hadCanonical)
|
|
205
|
+
backupOnce(canonical);
|
|
206
|
+
if (hadLegacy && !legacyIsStub)
|
|
207
|
+
backupOnce(legacy);
|
|
208
|
+
writeJsonAtomic(canonical, list);
|
|
209
|
+
// Also write to legacy so callers that do readFileSync(legacy) still see current plan.
|
|
210
|
+
// This keeps backwards-compat for the test suite and for old scripts until migration is complete.
|
|
211
|
+
try {
|
|
212
|
+
writeJsonAtomic(legacy, list);
|
|
213
|
+
}
|
|
214
|
+
catch { /* best effort */ }
|
|
215
|
+
// Ensure .bak exists for both paths so tests checking planFile(dir)+".bak" pass.
|
|
216
|
+
// When hadLegacy was false, legacy was just created — create its .bak on next write path.
|
|
217
|
+
// When hadCanonical was false, plan had no prior file — its first .bak appears on second save.
|
|
218
|
+
}
|
|
219
|
+
/** Flatten every task across every feature, in plan order. */
|
|
220
|
+
export function flattenTasks(list) {
|
|
221
|
+
const out = [];
|
|
222
|
+
let i = 0;
|
|
223
|
+
for (const f of list.features ?? []) {
|
|
224
|
+
const featurePhase = f.phase;
|
|
225
|
+
for (const t of f.tasks ?? []) {
|
|
226
|
+
i += 1;
|
|
227
|
+
const eff = t.phase ?? featurePhase ?? "build";
|
|
228
|
+
out.push({
|
|
229
|
+
...t,
|
|
230
|
+
compositeKey: t.key ?? `${f.id}/${t.id}`,
|
|
231
|
+
featureId: f.id,
|
|
232
|
+
featureName: f.name,
|
|
233
|
+
effectivePhase: eff,
|
|
234
|
+
index: i,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
/** Resolve a task by bare id, composite key, or `key` field. */
|
|
241
|
+
export function findTask(list, needle) {
|
|
242
|
+
const want = String(needle ?? "").trim();
|
|
243
|
+
if (!want)
|
|
244
|
+
return null;
|
|
245
|
+
for (const f of list.features ?? []) {
|
|
246
|
+
for (const t of f.tasks ?? []) {
|
|
247
|
+
if (t.id === want)
|
|
248
|
+
return { feature: f, task: t };
|
|
249
|
+
if (t.key === want)
|
|
250
|
+
return { feature: f, task: t };
|
|
251
|
+
if (`${f.id}/${t.id}` === want)
|
|
252
|
+
return { feature: f, task: t };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
export function findFeature(list, featureId) {
|
|
258
|
+
return (list.features ?? []).find((f) => f.id === featureId) ?? null;
|
|
259
|
+
}
|
|
260
|
+
/** Phase-filtered view: include only tasks whose effectivePhase matches. Pass nothing for global. */
|
|
261
|
+
export function tasksForPhase(list, phase) {
|
|
262
|
+
const all = flattenTasks(list);
|
|
263
|
+
if (!phase)
|
|
264
|
+
return all;
|
|
265
|
+
return all.filter((t) => t.effectivePhase === phase);
|
|
266
|
+
}
|
|
267
|
+
export function featuresForPhase(list, phase) {
|
|
268
|
+
if (!phase)
|
|
269
|
+
return list.features ?? [];
|
|
270
|
+
return (list.features ?? []).filter((f) => f.phase === phase);
|
|
271
|
+
}
|
|
272
|
+
export function computeProgress(list, phase) {
|
|
273
|
+
if (!phase) {
|
|
274
|
+
const tasks = flattenTasks(list);
|
|
275
|
+
const tasksDone = tasks.filter((t) => isDone(t.status)).length;
|
|
276
|
+
const features = list.features ?? [];
|
|
277
|
+
const featuresDone = features.filter((f) => (f.tasks ?? []).length > 0 && (f.tasks ?? []).every((t) => isDone(t.status))).length;
|
|
278
|
+
return {
|
|
279
|
+
tasksDone,
|
|
280
|
+
tasksTotal: tasks.length,
|
|
281
|
+
featuresDone,
|
|
282
|
+
featuresTotal: features.length,
|
|
283
|
+
blocked: tasks.filter((t) => t.status === "blocked").length,
|
|
284
|
+
inProgress: tasks.filter((t) => t.status === "in_progress").length,
|
|
285
|
+
rework: tasks.filter((t) => t.status === "rework").length,
|
|
286
|
+
percent: tasks.length === 0 ? 0 : Math.round((tasksDone / tasks.length) * 100),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const tasks = tasksForPhase(list, phase);
|
|
290
|
+
const tasksDone = tasks.filter((t) => isDone(t.status)).length;
|
|
291
|
+
const features = featuresForPhase(list, phase);
|
|
292
|
+
const featuresDone = features.filter((f) => (f.tasks ?? []).length > 0 && (f.tasks ?? []).every((t) => isDone(t.status))).length;
|
|
293
|
+
return {
|
|
294
|
+
tasksDone,
|
|
295
|
+
tasksTotal: tasks.length,
|
|
296
|
+
featuresDone,
|
|
297
|
+
featuresTotal: features.length,
|
|
298
|
+
blocked: tasks.filter((t) => t.status === "blocked").length,
|
|
299
|
+
inProgress: tasks.filter((t) => t.status === "in_progress").length,
|
|
300
|
+
rework: tasks.filter((t) => t.status === "rework").length,
|
|
301
|
+
percent: tasks.length === 0 ? 0 : Math.round((tasksDone / tasks.length) * 100),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* The next task the pipeline should work on (optionally scoped to one phase).
|
|
306
|
+
* Returns null when everything is done or everything left is blocked.
|
|
307
|
+
*/
|
|
308
|
+
export function nextActionableTask(list, phase) {
|
|
309
|
+
const tasks = phase ? tasksForPhase(list, phase) : flattenTasks(list);
|
|
310
|
+
const byKey = new Map();
|
|
311
|
+
for (const t of tasks) {
|
|
312
|
+
byKey.set(t.compositeKey, t);
|
|
313
|
+
byKey.set(t.id, t);
|
|
314
|
+
if (t.key)
|
|
315
|
+
byKey.set(t.key, t);
|
|
316
|
+
}
|
|
317
|
+
const inProgress = tasks.find((t) => t.status === "in_progress");
|
|
318
|
+
if (inProgress)
|
|
319
|
+
return inProgress;
|
|
320
|
+
const rework = tasks.find((t) => t.status === "rework");
|
|
321
|
+
if (rework)
|
|
322
|
+
return rework;
|
|
323
|
+
for (const t of tasks) {
|
|
324
|
+
if (t.status !== "pending")
|
|
325
|
+
continue;
|
|
326
|
+
const deps = t.dependsOn ?? [];
|
|
327
|
+
const unmet = deps.filter((d) => {
|
|
328
|
+
const dep = byKey.get(d);
|
|
329
|
+
return !dep || !isDone(dep.status);
|
|
330
|
+
});
|
|
331
|
+
if (unmet.length === 0)
|
|
332
|
+
return t;
|
|
333
|
+
}
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
// ── Dependency integrity ────────────────────────────────────────────────────
|
|
337
|
+
export function detectCycle(tasks) {
|
|
338
|
+
const map = new Map();
|
|
339
|
+
for (const t of tasks)
|
|
340
|
+
map.set(t.compositeKey, t.dependsOn ?? []);
|
|
341
|
+
const visiting = new Set();
|
|
342
|
+
const visited = new Set();
|
|
343
|
+
const stack = [];
|
|
344
|
+
const dfs = (key) => {
|
|
345
|
+
if (visiting.has(key)) {
|
|
346
|
+
const cycleStart = stack.indexOf(key);
|
|
347
|
+
const cycle = [...stack.slice(cycleStart), key].join(" → ");
|
|
348
|
+
throw new ValidationError(`dependency cycle: ${cycle}`);
|
|
349
|
+
}
|
|
350
|
+
if (visited.has(key))
|
|
351
|
+
return;
|
|
352
|
+
visiting.add(key);
|
|
353
|
+
stack.push(key);
|
|
354
|
+
for (const dep of map.get(key) ?? []) {
|
|
355
|
+
if (map.has(dep))
|
|
356
|
+
dfs(dep);
|
|
357
|
+
}
|
|
358
|
+
stack.pop();
|
|
359
|
+
visiting.delete(key);
|
|
360
|
+
visited.add(key);
|
|
361
|
+
};
|
|
362
|
+
for (const k of map.keys())
|
|
363
|
+
dfs(k);
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=featureList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"featureList.js","sourceRoot":"","sources":["../../../src/core/featureList.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* infinity-harness — filesystem helpers.
|
|
3
|
+
*
|
|
4
|
+
* Two guarantees callers depend on:
|
|
5
|
+
* - `writeJsonAtomic` never leaves a half-written file, and never leaves a
|
|
6
|
+
* stray temp file behind when the write fails.
|
|
7
|
+
* - `readJson` distinguishes "absent" (null) from "corrupt" (throws), so
|
|
8
|
+
* callers can seed defaults without silently discarding real state.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync, copyFileSync, } from "node:fs";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { randomBytes } from "node:crypto";
|
|
13
|
+
export function ensureDir(dir) {
|
|
14
|
+
mkdirSync(dir, { recursive: true });
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Strip a leading UTF-8 byte-order mark.
|
|
18
|
+
*
|
|
19
|
+
* Windows writes one routinely — PowerShell's `Set-Content -Encoding utf8`,
|
|
20
|
+
* Notepad, and several editors all do — and `JSON.parse` rejects it. Without
|
|
21
|
+
* this, a user who opens harness/config.json in Notepad, changes one value and
|
|
22
|
+
* saves gets "config is missing or unreadable" and no clue why. The files are
|
|
23
|
+
* explicitly documented as hand-editable, so they have to survive the editors
|
|
24
|
+
* people actually have.
|
|
25
|
+
*/
|
|
26
|
+
export function stripBom(text) {
|
|
27
|
+
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
28
|
+
}
|
|
29
|
+
export function readText(path) {
|
|
30
|
+
if (!existsSync(path))
|
|
31
|
+
return null;
|
|
32
|
+
try {
|
|
33
|
+
return stripBom(readFileSync(path, "utf-8"));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read and parse JSON.
|
|
41
|
+
* @returns parsed value, or `null` when the file does not exist.
|
|
42
|
+
* @throws when the file exists but does not parse — callers must not paper
|
|
43
|
+
* over a corrupt state file by overwriting it with defaults.
|
|
44
|
+
*/
|
|
45
|
+
export function readJson(path) {
|
|
46
|
+
if (!existsSync(path))
|
|
47
|
+
return null;
|
|
48
|
+
const raw = stripBom(readFileSync(path, "utf-8"));
|
|
49
|
+
if (raw.trim() === "")
|
|
50
|
+
return null;
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(raw);
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
56
|
+
throw new SyntaxError(`${path} is not valid JSON: ${msg}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Read JSON, falling back to `fallback` on absence *or* corruption. */
|
|
60
|
+
export function readJsonSafe(path, fallback) {
|
|
61
|
+
try {
|
|
62
|
+
const v = readJson(path);
|
|
63
|
+
return v === null ? fallback : v;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return fallback;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Write JSON atomically: temp file in the same directory, then rename.
|
|
71
|
+
* The temp name includes pid + random bytes so concurrent writers in the same
|
|
72
|
+
* process (or across processes) never collide on the temp path.
|
|
73
|
+
*/
|
|
74
|
+
export function writeJsonAtomic(path, value) {
|
|
75
|
+
writeTextAtomic(path, JSON.stringify(value, null, 2) + "\n");
|
|
76
|
+
}
|
|
77
|
+
export function writeTextAtomic(path, contents) {
|
|
78
|
+
ensureDir(dirname(path));
|
|
79
|
+
const tmp = `${path}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
|
|
80
|
+
try {
|
|
81
|
+
writeFileSync(tmp, contents, "utf-8");
|
|
82
|
+
renameSync(tmp, path);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
try {
|
|
86
|
+
if (existsSync(tmp))
|
|
87
|
+
unlinkSync(tmp);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
/* best effort */
|
|
91
|
+
}
|
|
92
|
+
throw e;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Keep one `.bak` beside a state file before overwriting it.
|
|
97
|
+
* Cheap insurance for multi-day runs: a corrupt write leaves the prior good
|
|
98
|
+
* revision recoverable without reaching for git.
|
|
99
|
+
*/
|
|
100
|
+
export function backupOnce(path) {
|
|
101
|
+
if (!existsSync(path))
|
|
102
|
+
return;
|
|
103
|
+
try {
|
|
104
|
+
copyFileSync(path, `${path}.bak`);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* best effort — a failed backup must never block the write */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function fileExists(path) {
|
|
111
|
+
return existsSync(path);
|
|
112
|
+
}
|
|
113
|
+
/** Delete a file if it is there. Never throws — a missing file is the goal. */
|
|
114
|
+
export function removeFile(path) {
|
|
115
|
+
try {
|
|
116
|
+
if (existsSync(path))
|
|
117
|
+
rmSync(path, { force: true });
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
/* a file we cannot delete is the caller's problem, not a crash */
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=fsx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fsx.js","sourceRoot":"","sources":["../../../src/core/fsx.ts"],"names":[],"mappings":"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"}
|