codeloop-mcp-server 0.1.50 → 0.1.51

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.
Files changed (47) hide show
  1. package/dist/auth/critical_floors.d.ts.map +1 -1
  2. package/dist/auth/critical_floors.js +4 -0
  3. package/dist/auth/critical_floors.js.map +1 -1
  4. package/dist/evidence/loop_state.d.ts +53 -0
  5. package/dist/evidence/loop_state.d.ts.map +1 -0
  6. package/dist/evidence/loop_state.js +147 -0
  7. package/dist/evidence/loop_state.js.map +1 -0
  8. package/dist/evidence/verify_staleness.d.ts +9 -0
  9. package/dist/evidence/verify_staleness.d.ts.map +1 -0
  10. package/dist/evidence/verify_staleness.js +180 -0
  11. package/dist/evidence/verify_staleness.js.map +1 -0
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +252 -17
  15. package/dist/index.js.map +1 -1
  16. package/dist/runners/maestro.d.ts +13 -0
  17. package/dist/runners/maestro.d.ts.map +1 -1
  18. package/dist/runners/maestro.js +37 -1
  19. package/dist/runners/maestro.js.map +1 -1
  20. package/dist/runners/modal_detector.d.ts +60 -0
  21. package/dist/runners/modal_detector.d.ts.map +1 -0
  22. package/dist/runners/modal_detector.js +160 -0
  23. package/dist/runners/modal_detector.js.map +1 -0
  24. package/dist/runners/python_tests.d.ts +26 -0
  25. package/dist/runners/python_tests.d.ts.map +1 -0
  26. package/dist/runners/python_tests.js +181 -0
  27. package/dist/runners/python_tests.js.map +1 -0
  28. package/dist/runners/rust_tests.d.ts +28 -0
  29. package/dist/runners/rust_tests.d.ts.map +1 -0
  30. package/dist/runners/rust_tests.js +76 -0
  31. package/dist/runners/rust_tests.js.map +1 -0
  32. package/dist/tools/diagnose.d.ts.map +1 -1
  33. package/dist/tools/diagnose.js +13 -0
  34. package/dist/tools/diagnose.js.map +1 -1
  35. package/dist/tools/gate_check.d.ts +2 -1
  36. package/dist/tools/gate_check.d.ts.map +1 -1
  37. package/dist/tools/gate_check.js +46 -32
  38. package/dist/tools/gate_check.js.map +1 -1
  39. package/dist/tools/is_ui_project.d.ts +23 -0
  40. package/dist/tools/is_ui_project.d.ts.map +1 -0
  41. package/dist/tools/is_ui_project.js +42 -0
  42. package/dist/tools/is_ui_project.js.map +1 -0
  43. package/dist/tools/verify.d.ts +28 -0
  44. package/dist/tools/verify.d.ts.map +1 -1
  45. package/dist/tools/verify.js +159 -7
  46. package/dist/tools/verify.js.map +1 -1
  47. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"critical_floors.d.ts","sourceRoot":"","sources":["../../src/auth/critical_floors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,EAwB1C,CAAC"}
1
+ {"version":3,"file":"critical_floors.d.ts","sourceRoot":"","sources":["../../src/auth/critical_floors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,WAAW,aAAa;IAC5B,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,EA6B1C,CAAC"}
@@ -60,5 +60,9 @@ export const CRITICAL_FLOORS = [
60
60
  min_version: "0.1.50",
61
61
  reason: "WPF / desktop final-mile fixes — pre-0.1.50 builds returned `(undefined, undefined)` when codeloop_interact was called with `text` / `role` / `automation_id` selectors instead of raw x/y on Windows desktop targets (every WPF tab/list-item click missed because the runner had no UIA tree walker for selectors), accepted weak cross-run design_compare matches and scored unrelated screens at 0% (e.g. `10-led-bom_*` paired with `led-design-bom-add-component`), wrote screenshot artifacts under the user's HOME folder when project_dir was omitted on Cursor-launched MCP servers, returned empty arrays from codeloop_discover_screens for desktop projects (designs/desktop/*.png never surfaced), classified MSB3027 / MSB3021 file-locked build errors as `issue_unclassified` (agents looped on the same locked-EXE forever), lacked `codeloop doctor --prune-artifacts` to clean up old corrupt-PNG runs, and surfaced bare 'App window not found' errors with no candidates / next_step diagnostic when the priority ladder exhausted",
62
62
  },
63
+ {
64
+ min_version: "0.1.51",
65
+ reason: "Full auto-loop + modal handling — pre-0.1.51 builds let the agent silently skip codeloop_verify between edits (no post-edit hooks, no staleness directive in tool responses), only ran ONE platform stack on multi-stack monorepos (.NET backend + React frontend / Django + Next.js / Tauri all silently skipped half the codebase), had no Python or Rust verify runners, didn't fire withInitHint on visual_review / design_compare / interaction_replay / generate_dev_report (so fresh workspaces could complete a full visual cycle without ever calling codeloop_init_project), spun the auto-fix loop forever with no server-side iteration cap (no escalate at 15 gate / 8 diagnose attempts), didn't auto-run Maestro flows in verify, and IGNORED MODALS during recording — every Save / Confirm-delete / EULA / browser beforeunload prompt was clicked-through-or-skipped, blocking the rest of the user_journey arc and silently dragging gate confidence down. 0.1.51 closes all of these and adds codeloop_capture_all_screens + codeloop_handle_modal so the loop is finally hands-off.",
66
+ },
63
67
  ];
64
68
  //# sourceMappingURL=critical_floors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"critical_floors.js","sourceRoot":"","sources":["../../src/auth/critical_floors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EACJ,ufAAuf;KAC1f;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EAAE,4hBAA4hB;KACriB;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EAAE,yvBAAyvB;KAClwB;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EACJ,kxBAAkxB;KACrxB;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EACJ,0/BAA0/B;KAC7/B;CACF,CAAC"}
1
+ {"version":3,"file":"critical_floors.js","sourceRoot":"","sources":["../../src/auth/critical_floors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EACJ,ufAAuf;KAC1f;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EAAE,4hBAA4hB;KACriB;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EAAE,yvBAAyvB;KAClwB;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EACJ,kxBAAkxB;KACrxB;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EACJ,0/BAA0/B;KAC7/B;IACD;QACE,WAAW,EAAE,QAAQ;QACrB,MAAM,EACJ,0iCAA0iC;KAC7iC;CACF,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 0.1.51 H7 — Server-side iteration counter for the auto-fix loop.
3
+ *
4
+ * Pre-H7 the codeloop loop was AT-MOST a soft suggestion: the agent
5
+ * was told "max 15 gate attempts / 8 diagnose attempts" via the
6
+ * post-call directive but nothing in the server actually counted, so
7
+ * a buggy or pathological loop could spin forever burning tokens.
8
+ *
9
+ * The hybrid storage:
10
+ * - Source of truth: `~/.codeloop/loop-state.json` (per-project map).
11
+ * - Per-run snapshot: `<artifacts>/runs/<run_id>/loop_state.json`.
12
+ *
13
+ * The source-of-truth lives in HOME so it survives `rm -rf
14
+ * artifacts/` (a common dev habit) and so the counter genuinely
15
+ * reflects the user's loop, not the most-recent run that happened
16
+ * to win the race. The per-run snapshot is purely an audit trail —
17
+ * downstream tools (gate_check, dev_report) read it from the run
18
+ * dir to attribute counts to a specific run without having to walk
19
+ * back through HOME state.
20
+ *
21
+ * Caps:
22
+ * - gate_attempts: 15 (escalate)
23
+ * - diagnose_attempts: 8 (escalate)
24
+ */
25
+ export interface LoopStateSnapshot {
26
+ project_dir: string;
27
+ gate_attempts: number;
28
+ diagnose_attempts: number;
29
+ last_gate_at?: string;
30
+ last_diagnose_at?: string;
31
+ escalated_reason?: string;
32
+ }
33
+ export declare const GATE_ATTEMPT_CAP = 15;
34
+ export declare const DIAGNOSE_ATTEMPT_CAP = 8;
35
+ export declare function readLoopState(projectDir: string): LoopStateSnapshot;
36
+ export declare function incrementGateAttempts(projectDir: string): LoopStateSnapshot;
37
+ export declare function incrementDiagnoseAttempts(projectDir: string): LoopStateSnapshot;
38
+ /**
39
+ * Reset both counters for a project. Called by `codeloop_doctor
40
+ * --reset-loop-state` and automatically when gate_check returns
41
+ * `ready_for_review`. The reset is idempotent.
42
+ */
43
+ export declare function resetLoopState(projectDir: string): void;
44
+ /**
45
+ * Write a per-run snapshot of the latest counters into the run
46
+ * directory. Best-effort — a missing/locked dir is not fatal because
47
+ * the source-of-truth state in HOME is the contract.
48
+ */
49
+ export declare function snapshotLoopStateToRun(runDir: string, state: LoopStateSnapshot): void;
50
+ export declare function shouldEscalateGate(state: LoopStateSnapshot): boolean;
51
+ export declare function shouldEscalateDiagnose(state: LoopStateSnapshot): boolean;
52
+ export declare function escalationReason(state: LoopStateSnapshot): string | null;
53
+ //# sourceMappingURL=loop_state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop_state.d.ts","sourceRoot":"","sources":["../../src/evidence/loop_state.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AASD,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,oBAAoB,IAAI,CAAC;AA+CtC,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAUnE;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAiB3E;AAED,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAiB/E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CASvD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,iBAAiB,GACvB,IAAI,CAWN;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAEpE;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAExE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAsBxE"}
@@ -0,0 +1,147 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
+ import { homedir } from "os";
3
+ import { join, resolve } from "path";
4
+ export const GATE_ATTEMPT_CAP = 15;
5
+ export const DIAGNOSE_ATTEMPT_CAP = 8;
6
+ function homeStatePath() {
7
+ // We expose a private override env var for tests so we don't have
8
+ // to monkey-patch homedir() — see __tests__/loop_state.test.ts.
9
+ if (process.env.CODELOOP_LOOP_STATE_DIR) {
10
+ return join(process.env.CODELOOP_LOOP_STATE_DIR, "loop-state.json");
11
+ }
12
+ return join(homedir(), ".codeloop", "loop-state.json");
13
+ }
14
+ function projectKey(projectDir) {
15
+ // Resolve to an absolute, normalised path. We deliberately do NOT
16
+ // case-fold on Windows — even though the FS is case-insensitive
17
+ // by default, NTFS can be case-sensitive (e.g. via fsutil) and
18
+ // mismatching case across two cmd sessions on the same project
19
+ // is exotic enough that we'd rather treat them as one project
20
+ // when they share a path string. resolve() is enough here.
21
+ return resolve(projectDir);
22
+ }
23
+ function readAll() {
24
+ const path = homeStatePath();
25
+ if (!existsSync(path)) {
26
+ return { schema_version: 1, projects: {} };
27
+ }
28
+ try {
29
+ const raw = readFileSync(path, "utf-8");
30
+ const parsed = JSON.parse(raw);
31
+ if (parsed && typeof parsed === "object" && parsed.projects) {
32
+ return {
33
+ schema_version: 1,
34
+ projects: parsed.projects,
35
+ };
36
+ }
37
+ }
38
+ catch {
39
+ /* fall through — corrupt files reset, see below */
40
+ }
41
+ return { schema_version: 1, projects: {} };
42
+ }
43
+ function writeAll(state) {
44
+ const path = homeStatePath();
45
+ mkdirSync(join(path, ".."), { recursive: true });
46
+ writeFileSync(path, JSON.stringify(state, null, 2), "utf-8");
47
+ }
48
+ export function readLoopState(projectDir) {
49
+ const all = readAll();
50
+ const key = projectKey(projectDir);
51
+ return (all.projects[key] ?? {
52
+ project_dir: key,
53
+ gate_attempts: 0,
54
+ diagnose_attempts: 0,
55
+ });
56
+ }
57
+ export function incrementGateAttempts(projectDir) {
58
+ const all = readAll();
59
+ const key = projectKey(projectDir);
60
+ const current = all.projects[key] ?? {
61
+ project_dir: key,
62
+ gate_attempts: 0,
63
+ diagnose_attempts: 0,
64
+ };
65
+ const updated = {
66
+ ...current,
67
+ project_dir: key,
68
+ gate_attempts: current.gate_attempts + 1,
69
+ last_gate_at: new Date().toISOString(),
70
+ };
71
+ all.projects[key] = updated;
72
+ writeAll(all);
73
+ return updated;
74
+ }
75
+ export function incrementDiagnoseAttempts(projectDir) {
76
+ const all = readAll();
77
+ const key = projectKey(projectDir);
78
+ const current = all.projects[key] ?? {
79
+ project_dir: key,
80
+ gate_attempts: 0,
81
+ diagnose_attempts: 0,
82
+ };
83
+ const updated = {
84
+ ...current,
85
+ project_dir: key,
86
+ diagnose_attempts: current.diagnose_attempts + 1,
87
+ last_diagnose_at: new Date().toISOString(),
88
+ };
89
+ all.projects[key] = updated;
90
+ writeAll(all);
91
+ return updated;
92
+ }
93
+ /**
94
+ * Reset both counters for a project. Called by `codeloop_doctor
95
+ * --reset-loop-state` and automatically when gate_check returns
96
+ * `ready_for_review`. The reset is idempotent.
97
+ */
98
+ export function resetLoopState(projectDir) {
99
+ const all = readAll();
100
+ const key = projectKey(projectDir);
101
+ all.projects[key] = {
102
+ project_dir: key,
103
+ gate_attempts: 0,
104
+ diagnose_attempts: 0,
105
+ };
106
+ writeAll(all);
107
+ }
108
+ /**
109
+ * Write a per-run snapshot of the latest counters into the run
110
+ * directory. Best-effort — a missing/locked dir is not fatal because
111
+ * the source-of-truth state in HOME is the contract.
112
+ */
113
+ export function snapshotLoopStateToRun(runDir, state) {
114
+ try {
115
+ mkdirSync(runDir, { recursive: true });
116
+ writeFileSync(join(runDir, "loop_state.json"), JSON.stringify(state, null, 2), "utf-8");
117
+ }
118
+ catch {
119
+ /* best-effort */
120
+ }
121
+ }
122
+ export function shouldEscalateGate(state) {
123
+ return state.gate_attempts >= GATE_ATTEMPT_CAP;
124
+ }
125
+ export function shouldEscalateDiagnose(state) {
126
+ return state.diagnose_attempts >= DIAGNOSE_ATTEMPT_CAP;
127
+ }
128
+ export function escalationReason(state) {
129
+ if (shouldEscalateGate(state)) {
130
+ return (`gate_attempts=${state.gate_attempts} reached the 0.1.51 H7 cap of ` +
131
+ `${GATE_ATTEMPT_CAP}. Stop the auto-fix loop. Surface every failing ` +
132
+ `gate to the user with the failing reasons and the next manual ` +
133
+ `step they should take. Do NOT call codeloop_verify or ` +
134
+ `codeloop_gate_check again until the user explicitly resets the ` +
135
+ `loop counter (codeloop_doctor --reset-loop-state) or makes a ` +
136
+ `targeted fix and asks you to retry.`);
137
+ }
138
+ if (shouldEscalateDiagnose(state)) {
139
+ return (`diagnose_attempts=${state.diagnose_attempts} reached the 0.1.51 ` +
140
+ `H7 cap of ${DIAGNOSE_ATTEMPT_CAP}. The same class of failure has ` +
141
+ `survived ${state.diagnose_attempts} fix attempts. Stop calling ` +
142
+ `codeloop_diagnose and escalate to the user with the failing ` +
143
+ `repair task and what you have already tried.`);
144
+ }
145
+ return null;
146
+ }
147
+ //# sourceMappingURL=loop_state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop_state.js","sourceRoot":"","sources":["../../src/evidence/loop_state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface StalenessResult {
2
+ is_stale: boolean;
3
+ last_verify_at: string | null;
4
+ newest_source_path: string | null;
5
+ newest_source_mtime: string | null;
6
+ }
7
+ export declare function checkVerifyStaleness(cwd: string): StalenessResult;
8
+ export declare function buildStalenessDirective(r: StalenessResult): string;
9
+ //# sourceMappingURL=verify_staleness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify_staleness.d.ts","sourceRoot":"","sources":["../../src/evidence/verify_staleness.ts"],"names":[],"mappings":"AAwEA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAuFjE;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAgBlE"}
@@ -0,0 +1,180 @@
1
+ import { existsSync, statSync, readdirSync } from "fs";
2
+ import { join } from "path";
3
+ import { loadRunIndex } from "./run_lineage.js";
4
+ import { getArtifactsBaseDir } from "./artifacts.js";
5
+ /**
6
+ * 0.1.51 H2 — Verify-staleness reminder.
7
+ *
8
+ * Inject a HARD directive into MCP responses (via `withInitHint`)
9
+ * whenever ANY source file in the project has been modified more
10
+ * recently than the latest `codeloop_verify` run. Pre-H2 the
11
+ * loop relied entirely on the agent's discipline: the rule was
12
+ * "you MUST call codeloop_verify after every code change", but
13
+ * a long agent session would drift, and the only way to catch
14
+ * the drift was to wait for a downstream tool (gate_check) to
15
+ * blow up. With H2 the staleness signal lives at the response-
16
+ * envelope level — every CodeLoop tool response says "your
17
+ * source is newer than the last verify, run codeloop_verify
18
+ * BEFORE doing anything else".
19
+ *
20
+ * Heuristics:
21
+ * - "Source files" = anything in cwd other than the skip list
22
+ * (node_modules, artifacts, .git, .vs, dist/build/out, .codeloop).
23
+ * - "Last verify" = the newest `finished_at` in run_index.json.
24
+ * - We DON'T scan the entire tree — the depth-2 scan from the
25
+ * hot-path heuristics in detectPlatform is cheap enough to fire
26
+ * on every tool response without a perceptible delay; deeper
27
+ * stale changes will still trigger because workspace-root
28
+ * files are almost always touched alongside any meaningful edit.
29
+ */
30
+ const SKIP_DIRS = new Set([
31
+ "node_modules",
32
+ ".git",
33
+ ".vs",
34
+ ".idea",
35
+ ".vscode",
36
+ ".codeloop",
37
+ "artifacts",
38
+ "dist",
39
+ "build",
40
+ "out",
41
+ "bin",
42
+ "obj",
43
+ "packages",
44
+ "Pods",
45
+ ".next",
46
+ ".turbo",
47
+ ".cache",
48
+ ".gradle",
49
+ "DerivedData",
50
+ "__pycache__",
51
+ "target", // Rust target/ — common, large, never the source of a "stale" signal
52
+ ".venv",
53
+ "venv",
54
+ ]);
55
+ // Files we skip even when their mtime is newer than the verify run —
56
+ // they're updated by tooling outside the agent's control and aren't
57
+ // "source code" in the staleness sense (`package-lock.json` bumped
58
+ // by a CI runner, `.DS_Store`, etc.).
59
+ const SKIP_FILE_PATTERNS = [
60
+ /^\.DS_Store$/,
61
+ /^Thumbs\.db$/,
62
+ /^package-lock\.json$/,
63
+ /^pnpm-lock\.yaml$/,
64
+ /^yarn\.lock$/,
65
+ /^poetry\.lock$/,
66
+ /^Cargo\.lock$/,
67
+ /\.log$/,
68
+ ];
69
+ export function checkVerifyStaleness(cwd) {
70
+ let lastVerifyMs = null;
71
+ let lastVerifyAt = null;
72
+ try {
73
+ const base = getArtifactsBaseDir(cwd);
74
+ const idx = loadRunIndex(base);
75
+ for (const e of idx.entries) {
76
+ // Prefer finished_at; fall back to started_at when finished
77
+ // was never written (interrupted runs).
78
+ const ts = e.finished_at ?? e.started_at;
79
+ if (!ts)
80
+ continue;
81
+ const ms = Date.parse(ts);
82
+ if (Number.isNaN(ms))
83
+ continue;
84
+ if (lastVerifyMs === null || ms > lastVerifyMs) {
85
+ lastVerifyMs = ms;
86
+ lastVerifyAt = ts;
87
+ }
88
+ }
89
+ }
90
+ catch {
91
+ /* fall through with null */
92
+ }
93
+ if (lastVerifyMs === null) {
94
+ // Never verified. We don't fire the staleness directive in this
95
+ // case because the init-hint already covers the "no run yet"
96
+ // path — firing both would be noisy and redundant.
97
+ return {
98
+ is_stale: false,
99
+ last_verify_at: null,
100
+ newest_source_path: null,
101
+ newest_source_mtime: null,
102
+ };
103
+ }
104
+ // Walk depth=2 from cwd. The H2 plan explicitly accepts a coarse
105
+ // signal — depth-2 catches >95% of edits since IDE-level edits
106
+ // virtually always change at least one file at workspace-root or
107
+ // first-level package.
108
+ let newest = null;
109
+ function visit(dir, depth) {
110
+ if (depth > 2)
111
+ return;
112
+ let entries;
113
+ try {
114
+ entries = readdirSync(dir, { withFileTypes: true });
115
+ }
116
+ catch {
117
+ return;
118
+ }
119
+ for (const ent of entries) {
120
+ const name = ent.name;
121
+ if (name.startsWith(".") && SKIP_DIRS.has(name))
122
+ continue;
123
+ if (SKIP_DIRS.has(name))
124
+ continue;
125
+ const p = join(dir, name);
126
+ if (ent.isDirectory()) {
127
+ visit(p, depth + 1);
128
+ }
129
+ else if (ent.isFile()) {
130
+ if (SKIP_FILE_PATTERNS.some((re) => re.test(name)))
131
+ continue;
132
+ try {
133
+ const ms = statSync(p).mtimeMs;
134
+ if (!newest || ms > newest.mtime) {
135
+ newest = { path: p, mtime: ms };
136
+ }
137
+ }
138
+ catch {
139
+ /* skip unreadable */
140
+ }
141
+ }
142
+ }
143
+ }
144
+ if (existsSync(cwd))
145
+ visit(cwd, 0);
146
+ if (!newest) {
147
+ return {
148
+ is_stale: false,
149
+ last_verify_at: lastVerifyAt,
150
+ newest_source_path: null,
151
+ newest_source_mtime: null,
152
+ };
153
+ }
154
+ // TS narrowing: `newest` is non-null in this branch.
155
+ const n = newest;
156
+ const isStale = n.mtime > lastVerifyMs;
157
+ return {
158
+ is_stale: isStale,
159
+ last_verify_at: lastVerifyAt,
160
+ newest_source_path: isStale ? n.path : null,
161
+ newest_source_mtime: isStale ? new Date(n.mtime).toISOString() : null,
162
+ };
163
+ }
164
+ export function buildStalenessDirective(r) {
165
+ if (!r.is_stale)
166
+ return "";
167
+ return (`[CodeLoop H2] HARD DIRECTIVE — your project source is NEWER than the ` +
168
+ `last codeloop_verify run.\n\n` +
169
+ ` Newest source file: ${r.newest_source_path ?? "(unknown)"}\n` +
170
+ ` Newest source mtime: ${r.newest_source_mtime ?? "(unknown)"}\n` +
171
+ ` Last verify finished: ${r.last_verify_at ?? "(unknown)"}\n\n` +
172
+ `Before continuing with ANY other tool, call codeloop_verify against the ` +
173
+ `current project. Do NOT call codeloop_gate_check, codeloop_visual_review, ` +
174
+ `codeloop_design_compare, codeloop_interaction_replay, codeloop_capture_screenshot, ` +
175
+ `codeloop_capture_all_screens, codeloop_generate_dev_report, ` +
176
+ `codeloop_section_status, codeloop_release_readiness, or codeloop_handle_modal ` +
177
+ `until codeloop_verify has run against this newer code. Skipping verify will ` +
178
+ `surface a green gate against stale evidence and you will ship a regression.`);
179
+ }
180
+ //# sourceMappingURL=verify_staleness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify_staleness.js","sourceRoot":"","sources":["../../src/evidence/verify_staleness.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export declare function markVerifiedNow(cwd: string): void;
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAqfA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjD"}