@event4u/agent-config 15.0.0 → 16.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +142 -348
- package/dist/agent-src/commands/roadmap/create.md +3 -2
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +50 -0
- package/dist/agent-src/rules/user-interaction.md +31 -30
- package/dist/agent-src/skills/error-handling-patterns/SKILL.md +9 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/{chunk-HZ52BRGG.js → chunk-H32DGUJH.js} +14 -89
- package/dist/cli-delegate/chunk-NUHLI2RX.js +80 -0
- package/dist/cli-delegate/{chunk-TYSY33MD.js → chunk-QJWAUMJW.js} +12 -2
- package/dist/cli-delegate/{chunk-2KKXJW25.js → chunk-S3VFXMTM.js} +1 -1
- package/dist/cli-delegate/cmd_conformance.js +831 -272
- package/dist/cli-delegate/cmd_explain.js +4 -2
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +13 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +5 -5
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1274 -571
- package/dist/install/install.mjs +1 -0
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1218 -632
- package/dist/scripts/_lib/installed_lock.js +1 -1
- package/dist/server/io/yamlIO.js +50 -0
- package/package.json +1 -1
- package/src/config/continuity-surface.json +7 -0
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/host-injection-effect.json +1 -1
- package/src/config/preamble-payload-budget.json +12 -7
- package/src/config/preamble-payload-exceptions.json +28 -0
- package/src/scripts/_dispatch.bash +3 -1
- package/src/scripts/_lib/base_ref_payload.ts +315 -0
- package/src/scripts/_lib/dropped_decision.ts +142 -0
- package/src/scripts/_lib/installed_lock.ts +1 -1
- package/src/scripts/_lib/measured_payload_ceiling.ts +424 -0
- package/src/scripts/_lib/payload_catalogue_completeness.ts +211 -0
- package/src/scripts/_lib/release_findings_ingest.ts +324 -0
- package/src/scripts/_lib/release_tag_dispatch.ts +63 -0
- package/src/scripts/_lib/review_baseline.ts +254 -0
- package/src/scripts/_lib/review_skipped_record.ts +61 -1
- package/src/scripts/_lib/standing_bound_ratchet.ts +216 -103
- package/src/scripts/_lib/turn_end_refusals.ts +51 -4
- package/src/scripts/check_beta_review_markers.ts +35 -4
- package/src/scripts/check_finding_dispositions.ts +167 -16
- package/src/scripts/check_held_object_arrivals.ts +381 -0
- package/src/scripts/check_preamble_payload_budget.ts +399 -122
- package/src/scripts/check_release_published.ts +1 -1
- package/src/scripts/check_reply_consistency.ts +86 -17
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +39 -3
- package/src/scripts/hooks/block_kernel_rule_writes.ts +12 -2
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/end_review_nudge_hook.ts +101 -21
- package/src/scripts/hooks/review_baseline_hook.ts +130 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +232 -14
- package/src/scripts/hooks_doctor.ts +4 -1
- package/src/scripts/install.ts +1 -1
- package/src/scripts/interruption_report.ts +8 -2
- package/src/scripts/janitor.ts +8 -1
- package/src/scripts/measure_turn_end_gate.ts +302 -5
- package/src/scripts/release.ts +52 -64
- package/src/scripts/release_drill.ts +193 -14
- package/src/scripts/release_env.ts +1 -1
- package/src/scripts/release_publication.ts +1 -1
- package/src/scripts/report_held_object_arrivals.ts +456 -0
- package/src/scripts/sync_agent_settings.ts +140 -18
- package/src/server/io/yamlIO.ts +48 -0
|
@@ -10,11 +10,13 @@ import {
|
|
|
10
10
|
sessionRefusalFile,
|
|
11
11
|
statePathFor,
|
|
12
12
|
validateAssumption
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-QJWAUMJW.js";
|
|
14
14
|
import {
|
|
15
15
|
readHookStdin
|
|
16
16
|
} from "./chunk-F7DQWYR5.js";
|
|
17
17
|
import {
|
|
18
|
+
DeadScopeError,
|
|
19
|
+
assertScanned,
|
|
18
20
|
reportScanned
|
|
19
21
|
} from "./chunk-I3EIN2YG.js";
|
|
20
22
|
import {
|
|
@@ -52,6 +54,9 @@ import {
|
|
|
52
54
|
prune_stale_session_states,
|
|
53
55
|
session_state_file
|
|
54
56
|
} from "./chunk-E4XIZDIB.js";
|
|
57
|
+
import {
|
|
58
|
+
artefact_roots
|
|
59
|
+
} from "./chunk-NUHLI2RX.js";
|
|
55
60
|
import {
|
|
56
61
|
detectToolPresence
|
|
57
62
|
} from "./chunk-A4QJ6ENT.js";
|
|
@@ -80,12 +85,12 @@ import {
|
|
|
80
85
|
|
|
81
86
|
// src/scripts/_cli/cmd_conformance.ts
|
|
82
87
|
var YAML = __toESM(require_dist(), 1);
|
|
83
|
-
import * as
|
|
88
|
+
import * as fs15 from "node:fs";
|
|
84
89
|
import * as os7 from "node:os";
|
|
85
|
-
import * as
|
|
90
|
+
import * as path15 from "node:path";
|
|
86
91
|
import process5 from "node:process";
|
|
87
|
-
import { spawnSync as
|
|
88
|
-
import { fileURLToPath as
|
|
92
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
93
|
+
import { fileURLToPath as fileURLToPath14, pathToFileURL as pathToFileURL14 } from "node:url";
|
|
89
94
|
|
|
90
95
|
// src/install/txlog.ts
|
|
91
96
|
import { createReadStream, createWriteStream, existsSync, readFileSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
@@ -131,10 +136,10 @@ var ROUTE_TARGET_TEMPLATES = Object.freeze({
|
|
|
131
136
|
])
|
|
132
137
|
});
|
|
133
138
|
function parseRouteTarget(target) {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
const kind = target.slice(0,
|
|
137
|
-
const id = target.slice(
|
|
139
|
+
const sep2 = target.indexOf(":");
|
|
140
|
+
if (sep2 <= 0) return null;
|
|
141
|
+
const kind = target.slice(0, sep2);
|
|
142
|
+
const id = target.slice(sep2 + 1);
|
|
138
143
|
if (!id) return null;
|
|
139
144
|
if (!Object.prototype.hasOwnProperty.call(ROUTE_TARGET_TEMPLATES, kind)) return null;
|
|
140
145
|
return { kind, id };
|
|
@@ -146,18 +151,18 @@ function routeTargetPathsPosix(target) {
|
|
|
146
151
|
}
|
|
147
152
|
|
|
148
153
|
// src/scripts/report_conformance_funnel.ts
|
|
149
|
-
import * as
|
|
154
|
+
import * as fs14 from "node:fs";
|
|
150
155
|
import * as os6 from "node:os";
|
|
151
|
-
import * as
|
|
156
|
+
import * as path14 from "node:path";
|
|
152
157
|
import process4 from "node:process";
|
|
153
|
-
import { fileURLToPath as
|
|
158
|
+
import { fileURLToPath as fileURLToPath13, pathToFileURL as pathToFileURL13 } from "node:url";
|
|
154
159
|
|
|
155
160
|
// src/scripts/conformance_scan.ts
|
|
156
161
|
import { createHash as createHash3 } from "node:crypto";
|
|
157
|
-
import
|
|
162
|
+
import fs10 from "node:fs";
|
|
158
163
|
import os2 from "node:os";
|
|
159
|
-
import
|
|
160
|
-
import { fileURLToPath as
|
|
164
|
+
import path10 from "node:path";
|
|
165
|
+
import { fileURLToPath as fileURLToPath9, pathToFileURL as pathToFileURL9 } from "node:url";
|
|
161
166
|
|
|
162
167
|
// src/scripts/_lib/transcript_entry.ts
|
|
163
168
|
function _isObject(v) {
|
|
@@ -1940,17 +1945,464 @@ var COMMAND_OBJECTS = {
|
|
|
1940
1945
|
};
|
|
1941
1946
|
|
|
1942
1947
|
// src/scripts/hooks/turn_end_gate_hook.ts
|
|
1943
|
-
import * as
|
|
1944
|
-
import * as
|
|
1945
|
-
import { fileURLToPath as
|
|
1948
|
+
import * as fs7 from "node:fs";
|
|
1949
|
+
import * as path7 from "node:path";
|
|
1950
|
+
import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL6 } from "node:url";
|
|
1946
1951
|
|
|
1947
|
-
// src/scripts/
|
|
1948
|
-
import { spawnSync } from "node:child_process";
|
|
1949
|
-
import * as crypto from "node:crypto";
|
|
1952
|
+
// src/scripts/check_reply_consistency.ts
|
|
1950
1953
|
import * as fs3 from "node:fs";
|
|
1951
|
-
import * as os from "node:os";
|
|
1952
1954
|
import * as path3 from "node:path";
|
|
1953
1955
|
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "node:url";
|
|
1956
|
+
var _HERE = fileURLToPath3(import.meta.url);
|
|
1957
|
+
var ROOT = path3.resolve(path3.dirname(_HERE), "..", "..");
|
|
1958
|
+
var QUIET = process.argv.slice(2).includes("--quiet");
|
|
1959
|
+
var OPTION_LINE_RE = /^\s*>?\s*(\d+)\.\s+\S/;
|
|
1960
|
+
var REC_LINE_RE = /(?:Recommendation|Empfehlung)\s*:\s*(\d+)\b/i;
|
|
1961
|
+
var TAG_RE = /\((?:recommended|rec|empfohlen)\)/i;
|
|
1962
|
+
var CODESPAN_RE = /`[^`\n]*`/g;
|
|
1963
|
+
var HEADING_RE = /^#{1,6}\s/;
|
|
1964
|
+
var FENCE_RE = /^\s*(```|~~~)/;
|
|
1965
|
+
var REC_ADJACENCY_WINDOW = 2;
|
|
1966
|
+
function _strip_codespans(line) {
|
|
1967
|
+
return line.replace(CODESPAN_RE, "``");
|
|
1968
|
+
}
|
|
1969
|
+
function splitAndMask(text) {
|
|
1970
|
+
return mask_fences(_splitlines(text));
|
|
1971
|
+
}
|
|
1972
|
+
function _splitlines(text) {
|
|
1973
|
+
if (text === "") return [];
|
|
1974
|
+
const parts = text.split(/\r\n|\r|\n/);
|
|
1975
|
+
if (parts.length > 0 && parts[parts.length - 1] === "") {
|
|
1976
|
+
parts.pop();
|
|
1977
|
+
}
|
|
1978
|
+
return parts;
|
|
1979
|
+
}
|
|
1980
|
+
function mask_fences(lines) {
|
|
1981
|
+
const out = [];
|
|
1982
|
+
let fence = null;
|
|
1983
|
+
for (const raw of lines) {
|
|
1984
|
+
const m = FENCE_RE.exec(raw);
|
|
1985
|
+
if (m) {
|
|
1986
|
+
const marker = m[1];
|
|
1987
|
+
if (fence === null) {
|
|
1988
|
+
fence = marker;
|
|
1989
|
+
} else if (fence === marker) {
|
|
1990
|
+
fence = null;
|
|
1991
|
+
}
|
|
1992
|
+
out.push("");
|
|
1993
|
+
continue;
|
|
1994
|
+
}
|
|
1995
|
+
out.push(fence === null ? raw : "");
|
|
1996
|
+
}
|
|
1997
|
+
return out;
|
|
1998
|
+
}
|
|
1999
|
+
function find_option_blocks(text) {
|
|
2000
|
+
const lines = mask_fences(_splitlines(text));
|
|
2001
|
+
const blocks = [];
|
|
2002
|
+
let current = null;
|
|
2003
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
2004
|
+
const m = OPTION_LINE_RE.exec(lines[idx]);
|
|
2005
|
+
if (m) {
|
|
2006
|
+
const num = parseInt(m[1], 10);
|
|
2007
|
+
if (current === null) {
|
|
2008
|
+
current = { startLine: idx + 1, endLine: idx + 1, numbers: [num] };
|
|
2009
|
+
} else {
|
|
2010
|
+
current.endLine = idx + 1;
|
|
2011
|
+
current.numbers.push(num);
|
|
2012
|
+
}
|
|
2013
|
+
} else {
|
|
2014
|
+
if (current !== null && current.numbers.length >= 2) {
|
|
2015
|
+
blocks.push(current);
|
|
2016
|
+
}
|
|
2017
|
+
current = null;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
if (current !== null && current.numbers.length >= 2) {
|
|
2021
|
+
blocks.push(current);
|
|
2022
|
+
}
|
|
2023
|
+
return blocks;
|
|
2024
|
+
}
|
|
2025
|
+
function recommendationsUnder(text, block, maskedLines) {
|
|
2026
|
+
const lines = maskedLines ?? mask_fences(_splitlines(text));
|
|
2027
|
+
const recs = [];
|
|
2028
|
+
let inspected = 0;
|
|
2029
|
+
for (let idx = block.endLine; idx < lines.length && inspected < REC_ADJACENCY_WINDOW; idx++) {
|
|
2030
|
+
const line = lines[idx];
|
|
2031
|
+
if (line.trim() === "") {
|
|
2032
|
+
continue;
|
|
2033
|
+
}
|
|
2034
|
+
if (HEADING_RE.test(line) || OPTION_LINE_RE.test(line)) {
|
|
2035
|
+
break;
|
|
2036
|
+
}
|
|
2037
|
+
inspected += 1;
|
|
2038
|
+
const m = REC_LINE_RE.exec(line);
|
|
2039
|
+
if (m) {
|
|
2040
|
+
recs.push({ line: idx + 1, num: parseInt(m[1], 10) });
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
return recs;
|
|
2044
|
+
}
|
|
2045
|
+
function check_reply(text) {
|
|
2046
|
+
const rawLines = _splitlines(text);
|
|
2047
|
+
const lines = mask_fences(rawLines);
|
|
2048
|
+
const findings = [];
|
|
2049
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
2050
|
+
const stripped = _strip_codespans(lines[idx]);
|
|
2051
|
+
if (OPTION_LINE_RE.test(stripped) && TAG_RE.test(stripped)) {
|
|
2052
|
+
findings.push({
|
|
2053
|
+
line: idx + 1,
|
|
2054
|
+
message: `inline tag on numbered option \u2014 ${rawLines[idx].trim()}`
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
const blocks = find_option_blocks(text);
|
|
2059
|
+
for (const block of blocks) {
|
|
2060
|
+
const recs = recommendationsUnder(text, block);
|
|
2061
|
+
if (recs.length === 0) {
|
|
2062
|
+
findings.push({
|
|
2063
|
+
line: block.startLine,
|
|
2064
|
+
message: "numbered options block without a Recommendation:/Empfehlung: line directly under it"
|
|
2065
|
+
});
|
|
2066
|
+
continue;
|
|
2067
|
+
}
|
|
2068
|
+
if (recs.length > 1) {
|
|
2069
|
+
findings.push({
|
|
2070
|
+
line: recs[1].line,
|
|
2071
|
+
message: `multiple recommendation lines under one options block: [${recs.map((r) => r.num).join(", ")}]`
|
|
2072
|
+
});
|
|
2073
|
+
continue;
|
|
2074
|
+
}
|
|
2075
|
+
const rec = recs[0];
|
|
2076
|
+
if (!block.numbers.includes(rec.num)) {
|
|
2077
|
+
findings.push({
|
|
2078
|
+
line: rec.line,
|
|
2079
|
+
message: `recommendation ${String(rec.num)} not present in the options block above it (options: [${block.numbers.join(", ")}])`
|
|
2080
|
+
});
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
findings.sort((a, b) => a.line - b.line);
|
|
2084
|
+
return findings;
|
|
2085
|
+
}
|
|
2086
|
+
function _isDir(p) {
|
|
2087
|
+
try {
|
|
2088
|
+
return fs3.statSync(p).isDirectory();
|
|
2089
|
+
} catch {
|
|
2090
|
+
return false;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
function _rglobMdSorted(dir) {
|
|
2094
|
+
const out = [];
|
|
2095
|
+
const walk = (current) => {
|
|
2096
|
+
let entries;
|
|
2097
|
+
try {
|
|
2098
|
+
entries = fs3.readdirSync(current, { withFileTypes: true });
|
|
2099
|
+
} catch {
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
for (const ent of entries) {
|
|
2103
|
+
const full = path3.join(current, ent.name);
|
|
2104
|
+
if (ent.isDirectory() || ent.isSymbolicLink() && _isDir(full)) {
|
|
2105
|
+
walk(full);
|
|
2106
|
+
} else if (ent.name.endsWith(".md")) {
|
|
2107
|
+
out.push(full);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
walk(dir);
|
|
2112
|
+
out.sort();
|
|
2113
|
+
return out;
|
|
2114
|
+
}
|
|
2115
|
+
function _relToPosixOrAbs(child, root) {
|
|
2116
|
+
const rel = path3.relative(root, child);
|
|
2117
|
+
if (rel === "" || rel.startsWith("..") || path3.isAbsolute(rel)) {
|
|
2118
|
+
return child.split(path3.sep).join("/");
|
|
2119
|
+
}
|
|
2120
|
+
return rel.split(path3.sep).join("/");
|
|
2121
|
+
}
|
|
2122
|
+
function cmd_scan_dir(root) {
|
|
2123
|
+
let roots;
|
|
2124
|
+
if (!_isDir(root)) {
|
|
2125
|
+
const legacy = path3.join(ROOT, ".agent-src.uncondensed");
|
|
2126
|
+
if (path3.resolve(root) === path3.resolve(legacy)) {
|
|
2127
|
+
roots = artefact_roots();
|
|
2128
|
+
if (roots.length === 0) {
|
|
2129
|
+
process.stderr.write("error: no artefact roots found (legacy or packages/*)\n");
|
|
2130
|
+
return 1;
|
|
2131
|
+
}
|
|
2132
|
+
} else {
|
|
2133
|
+
process.stderr.write(`error: not a directory: ${root}
|
|
2134
|
+
`);
|
|
2135
|
+
return 1;
|
|
2136
|
+
}
|
|
2137
|
+
} else {
|
|
2138
|
+
roots = [root];
|
|
2139
|
+
}
|
|
2140
|
+
const violations = [];
|
|
2141
|
+
let scanned = 0;
|
|
2142
|
+
for (const r of roots) {
|
|
2143
|
+
for (const md of _rglobMdSorted(r)) {
|
|
2144
|
+
scanned += 1;
|
|
2145
|
+
const text = fs3.readFileSync(md, "utf-8");
|
|
2146
|
+
const lines = _splitlines(text);
|
|
2147
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
2148
|
+
const raw = lines[idx];
|
|
2149
|
+
const stripped = _strip_codespans(raw);
|
|
2150
|
+
if (OPTION_LINE_RE.test(stripped) && TAG_RE.test(stripped)) {
|
|
2151
|
+
violations.push([md, idx + 1, raw.trim()]);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
try {
|
|
2157
|
+
assertScanned({
|
|
2158
|
+
gate: "check_reply_consistency",
|
|
2159
|
+
scanned,
|
|
2160
|
+
units: "markdown file(s)",
|
|
2161
|
+
roots: roots.map((r) => _relToPosixOrAbs(r, ROOT))
|
|
2162
|
+
});
|
|
2163
|
+
} catch (exc) {
|
|
2164
|
+
if (exc instanceof DeadScopeError) {
|
|
2165
|
+
process.stderr.write(`\u274C ${exc.message}
|
|
2166
|
+
`);
|
|
2167
|
+
return 1;
|
|
2168
|
+
}
|
|
2169
|
+
throw exc;
|
|
2170
|
+
}
|
|
2171
|
+
if (violations.length > 0) {
|
|
2172
|
+
for (const [p, line, snippet] of violations) {
|
|
2173
|
+
process.stderr.write(` \u{1F534} ${p}:${String(line)} \u2014 inline-tag \u2014 ${snippet}
|
|
2174
|
+
`);
|
|
2175
|
+
}
|
|
2176
|
+
process.stderr.write(`
|
|
2177
|
+
\u274C ${String(violations.length)} legacy-pattern violation(s)
|
|
2178
|
+
`);
|
|
2179
|
+
return 2;
|
|
2180
|
+
}
|
|
2181
|
+
if (!QUIET) {
|
|
2182
|
+
const scanned2 = roots.map((r) => _relToPosixOrAbs(r, ROOT)).join(", ");
|
|
2183
|
+
process.stdout.write(`\u2705 No legacy (recommended) tags found under ${scanned2}
|
|
2184
|
+
`);
|
|
2185
|
+
}
|
|
2186
|
+
return 0;
|
|
2187
|
+
}
|
|
2188
|
+
var USAGE = "usage: check_reply_consistency [-h] (--stdin | --file FILE | --scan-dir SCAN_DIR)\n [-v] [--quiet]\n";
|
|
2189
|
+
function _usage_error(message) {
|
|
2190
|
+
process.stderr.write(USAGE);
|
|
2191
|
+
process.stderr.write(`check_reply_consistency: error: ${message}
|
|
2192
|
+
`);
|
|
2193
|
+
process.exit(1);
|
|
2194
|
+
}
|
|
2195
|
+
function _readStdin() {
|
|
2196
|
+
try {
|
|
2197
|
+
return fs3.readFileSync(0, "utf-8");
|
|
2198
|
+
} catch {
|
|
2199
|
+
return "";
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
function parse_args(argv) {
|
|
2203
|
+
let stdin = false;
|
|
2204
|
+
let file = null;
|
|
2205
|
+
let scan_dir = null;
|
|
2206
|
+
let verbose = false;
|
|
2207
|
+
const groupSeen = [];
|
|
2208
|
+
const need = (i, name) => {
|
|
2209
|
+
const v = argv[i];
|
|
2210
|
+
if (v === void 0) {
|
|
2211
|
+
_usage_error(`argument ${name}: expected one argument`);
|
|
2212
|
+
}
|
|
2213
|
+
return v;
|
|
2214
|
+
};
|
|
2215
|
+
for (let i = 0; i < argv.length; i++) {
|
|
2216
|
+
const arg = argv[i];
|
|
2217
|
+
if (arg === "--stdin") {
|
|
2218
|
+
stdin = true;
|
|
2219
|
+
groupSeen.push("--stdin");
|
|
2220
|
+
} else if (arg === "--file") {
|
|
2221
|
+
file = need(++i, "--file");
|
|
2222
|
+
groupSeen.push("--file");
|
|
2223
|
+
} else if (arg.startsWith("--file=")) {
|
|
2224
|
+
file = arg.slice("--file=".length);
|
|
2225
|
+
groupSeen.push("--file");
|
|
2226
|
+
} else if (arg === "--scan-dir") {
|
|
2227
|
+
scan_dir = need(++i, "--scan-dir");
|
|
2228
|
+
groupSeen.push("--scan-dir");
|
|
2229
|
+
} else if (arg.startsWith("--scan-dir=")) {
|
|
2230
|
+
scan_dir = arg.slice("--scan-dir=".length);
|
|
2231
|
+
groupSeen.push("--scan-dir");
|
|
2232
|
+
} else if (arg === "-v" || arg === "--verbose") {
|
|
2233
|
+
verbose = true;
|
|
2234
|
+
} else if (arg === "--quiet") {
|
|
2235
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
2236
|
+
process.stdout.write(USAGE);
|
|
2237
|
+
process.exit(0);
|
|
2238
|
+
} else {
|
|
2239
|
+
_usage_error(`unrecognized arguments: ${arg}`);
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
if (groupSeen.length === 0) {
|
|
2243
|
+
_usage_error("one of the arguments --stdin --file --scan-dir is required");
|
|
2244
|
+
}
|
|
2245
|
+
if (groupSeen.length > 1) {
|
|
2246
|
+
_usage_error(`argument ${groupSeen[1]}: not allowed with argument ${groupSeen[0]}`);
|
|
2247
|
+
}
|
|
2248
|
+
return { stdin, file, scan_dir, verbose };
|
|
2249
|
+
}
|
|
2250
|
+
function main3(argv = process.argv.slice(2)) {
|
|
2251
|
+
const args = parse_args(argv);
|
|
2252
|
+
if (args.scan_dir !== null) {
|
|
2253
|
+
return cmd_scan_dir(args.scan_dir);
|
|
2254
|
+
}
|
|
2255
|
+
let text;
|
|
2256
|
+
try {
|
|
2257
|
+
text = args.stdin ? _readStdin() : fs3.readFileSync(args.file, "utf-8");
|
|
2258
|
+
} catch (e) {
|
|
2259
|
+
process.stderr.write(`error: ${String(e)}
|
|
2260
|
+
`);
|
|
2261
|
+
return 1;
|
|
2262
|
+
}
|
|
2263
|
+
const findings = check_reply(text);
|
|
2264
|
+
if (findings.length === 0) {
|
|
2265
|
+
if (args.verbose && !QUIET) {
|
|
2266
|
+
process.stdout.write("\u2705 reply consistent (every options block carries one adjacent recommendation)\n");
|
|
2267
|
+
}
|
|
2268
|
+
return 0;
|
|
2269
|
+
}
|
|
2270
|
+
for (const f of findings) {
|
|
2271
|
+
process.stderr.write(`\u274C line ${String(f.line)}: ${f.message}
|
|
2272
|
+
`);
|
|
2273
|
+
}
|
|
2274
|
+
return 2;
|
|
2275
|
+
}
|
|
2276
|
+
function _isCliEntry3() {
|
|
2277
|
+
if (true) {
|
|
2278
|
+
return false;
|
|
2279
|
+
}
|
|
2280
|
+
if (process.argv[1] === void 0) {
|
|
2281
|
+
return false;
|
|
2282
|
+
}
|
|
2283
|
+
const argvUrl = pathToFileURL3(path3.resolve(process.argv[1])).href;
|
|
2284
|
+
if (import.meta.url === argvUrl) {
|
|
2285
|
+
return true;
|
|
2286
|
+
}
|
|
2287
|
+
try {
|
|
2288
|
+
const here = fs3.realpathSync(fileURLToPath3(import.meta.url));
|
|
2289
|
+
const argv = fs3.realpathSync(path3.resolve(process.argv[1]));
|
|
2290
|
+
return here === argv;
|
|
2291
|
+
} catch {
|
|
2292
|
+
return false;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
var _IN_BUNDLE = true;
|
|
2296
|
+
if (!_IN_BUNDLE && (_isCliEntry3() || process.argv[1] === _HERE)) {
|
|
2297
|
+
process.exit(main3());
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
// src/scripts/_lib/dropped_decision.ts
|
|
2301
|
+
function detectDroppedDecision(assistantTurnTexts, closingFromPayload = false) {
|
|
2302
|
+
if (assistantTurnTexts.length < 2) return null;
|
|
2303
|
+
const asksIn = (text) => {
|
|
2304
|
+
const blocks = find_option_blocks(text);
|
|
2305
|
+
if (blocks.length === 0) return blocks;
|
|
2306
|
+
const masked = splitAndMask(text);
|
|
2307
|
+
return blocks.filter((b) => recommendationsUnder(text, b, masked).length > 0);
|
|
2308
|
+
};
|
|
2309
|
+
const closing = assistantTurnTexts[assistantTurnTexts.length - 1];
|
|
2310
|
+
if (asksIn(closing).length > 0) return null;
|
|
2311
|
+
for (let i = assistantTurnTexts.length - 2; i >= 0; i -= 1) {
|
|
2312
|
+
const earlier = assistantTurnTexts[i];
|
|
2313
|
+
const blocks = asksIn(earlier);
|
|
2314
|
+
if (blocks.length === 0) continue;
|
|
2315
|
+
const block = blocks[blocks.length - 1];
|
|
2316
|
+
return {
|
|
2317
|
+
detector: "pending-decision",
|
|
2318
|
+
// The option NUMBERS and the line span, never the option text: the
|
|
2319
|
+
// evidence is quoted into a refusal that reaches the transcript, and
|
|
2320
|
+
// an options block can carry anything the reply carried.
|
|
2321
|
+
evidence: `options ${block.numbers.join("/")} at lines ${block.startLine}-${block.endLine} of an earlier reply this turn (closing reply read from ${closingFromPayload ? "the stop payload" : "the transcript"})`,
|
|
2322
|
+
reason: "an earlier reply in THIS turn put numbered options to the user and no user answer followed \u2014 this closing reply carries no options block, so the decision disappeared without being answered, cancelled or superseded. A hook nudge, a reviewer result or a task notification may ADD to a turn; it may not displace a question already asked (user-interaction Iron Law 3: a decision handed to the user outlives the turn). Re-present the block and its recommendation line at the end of this reply"
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
return null;
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// src/scripts/hooks/end_review_nudge_hook.ts
|
|
2329
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
2330
|
+
import * as crypto from "node:crypto";
|
|
2331
|
+
import * as fs5 from "node:fs";
|
|
2332
|
+
import * as os from "node:os";
|
|
2333
|
+
import * as path5 from "node:path";
|
|
2334
|
+
import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL4 } from "node:url";
|
|
2335
|
+
|
|
2336
|
+
// src/scripts/_lib/review_baseline.ts
|
|
2337
|
+
import { spawnSync } from "node:child_process";
|
|
2338
|
+
import * as fs4 from "node:fs";
|
|
2339
|
+
import * as path4 from "node:path";
|
|
2340
|
+
function currentHeadSha(cwd) {
|
|
2341
|
+
try {
|
|
2342
|
+
const r = spawnSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" });
|
|
2343
|
+
if (r.status !== 0) return null;
|
|
2344
|
+
const out = (r.stdout ?? "").trim();
|
|
2345
|
+
return out === "" ? null : out;
|
|
2346
|
+
} catch {
|
|
2347
|
+
return null;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
var BASELINE_STATE_REL = path4.join("agents", "runtime", "state", "review-baseline");
|
|
2351
|
+
function baselineStateDir(workspaceRoot) {
|
|
2352
|
+
return path4.join(workspaceRoot, BASELINE_STATE_REL);
|
|
2353
|
+
}
|
|
2354
|
+
function baselineStateFile(workspaceRoot, sessionKey) {
|
|
2355
|
+
return path4.join(baselineStateDir(workspaceRoot), `${sessionKey}.json`);
|
|
2356
|
+
}
|
|
2357
|
+
function parseBaseline(raw) {
|
|
2358
|
+
let decoded;
|
|
2359
|
+
try {
|
|
2360
|
+
decoded = JSON.parse(raw);
|
|
2361
|
+
} catch {
|
|
2362
|
+
return null;
|
|
2363
|
+
}
|
|
2364
|
+
if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) return null;
|
|
2365
|
+
const o = decoded;
|
|
2366
|
+
const lines = o["baseline_lines"];
|
|
2367
|
+
if (typeof lines !== "number" || !Number.isFinite(lines) || lines < 0) return null;
|
|
2368
|
+
const written = o["written_at"];
|
|
2369
|
+
if (typeof written !== "string") return null;
|
|
2370
|
+
const head = o["head_sha"];
|
|
2371
|
+
const measure = o["measure"];
|
|
2372
|
+
return {
|
|
2373
|
+
head_sha: typeof head === "string" && head !== "" ? head : null,
|
|
2374
|
+
baseline_lines: lines,
|
|
2375
|
+
...measure === "exact" || measure === "capped_approximation" ? { measure } : {},
|
|
2376
|
+
written_at: written
|
|
2377
|
+
};
|
|
2378
|
+
}
|
|
2379
|
+
function readBaseline(workspaceRoot, sessionKey) {
|
|
2380
|
+
const file = baselineStateFile(workspaceRoot, sessionKey);
|
|
2381
|
+
let raw;
|
|
2382
|
+
try {
|
|
2383
|
+
raw = fs4.readFileSync(file, "utf-8");
|
|
2384
|
+
} catch (exc) {
|
|
2385
|
+
const code = exc?.code;
|
|
2386
|
+
return code === "ENOENT" ? "absent" : "unreadable";
|
|
2387
|
+
}
|
|
2388
|
+
return parseBaseline(raw) ?? "unreadable";
|
|
2389
|
+
}
|
|
2390
|
+
function applyBaseline(measuredLines, baseline, headSha, measuredMeasure = "exact") {
|
|
2391
|
+
if (baseline === "absent" || baseline === "unreadable") {
|
|
2392
|
+
return { applied: false, fallback: baseline, lines: measuredLines };
|
|
2393
|
+
}
|
|
2394
|
+
if (baseline.measure !== "exact" || measuredMeasure !== "exact") {
|
|
2395
|
+
return { applied: false, fallback: "mixed-measure", lines: measuredLines };
|
|
2396
|
+
}
|
|
2397
|
+
if (baseline.head_sha !== headSha()) {
|
|
2398
|
+
return { applied: false, fallback: "head-moved", lines: measuredLines };
|
|
2399
|
+
}
|
|
2400
|
+
return {
|
|
2401
|
+
applied: true,
|
|
2402
|
+
baseline,
|
|
2403
|
+
lines: Math.max(0, measuredLines - baseline.baseline_lines)
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
1954
2406
|
|
|
1955
2407
|
// src/scripts/_lib/audit_field_provenance.ts
|
|
1956
2408
|
var MAX_APPLIED_IDS = 32;
|
|
@@ -1966,6 +2418,13 @@ function appliedIds(values) {
|
|
|
1966
2418
|
}
|
|
1967
2419
|
|
|
1968
2420
|
// src/scripts/_lib/review_skipped_record.ts
|
|
2421
|
+
var BASELINE_APPLICATIONS = [
|
|
2422
|
+
"applied",
|
|
2423
|
+
"absent",
|
|
2424
|
+
"unreadable",
|
|
2425
|
+
"head_moved",
|
|
2426
|
+
"mixed_measure"
|
|
2427
|
+
];
|
|
1969
2428
|
var PRODUCER_PRIVACY_CLASS = "ids-only";
|
|
1970
2429
|
function isNonNegInt(n) {
|
|
1971
2430
|
return typeof n === "number" && Number.isFinite(n) && Number.isInteger(n) && n >= 0;
|
|
@@ -1981,6 +2440,9 @@ function buildReviewSkippedLine(input) {
|
|
|
1981
2440
|
}
|
|
1982
2441
|
if (!input.ts) errors.push("ts (ISO-8601 UTC) is required");
|
|
1983
2442
|
if (!input.id) errors.push("id (ULID, UUID, or content hash) is required");
|
|
2443
|
+
if (input.baseline !== void 0 && !BASELINE_APPLICATIONS.includes(input.baseline)) {
|
|
2444
|
+
errors.push(`baseline must be one of ${BASELINE_APPLICATIONS.join(", ")}`);
|
|
2445
|
+
}
|
|
1984
2446
|
if (errors.length) return { line: null, errors };
|
|
1985
2447
|
const line = {
|
|
1986
2448
|
schema_version: 1,
|
|
@@ -2007,7 +2469,14 @@ function buildReviewSkippedLine(input) {
|
|
|
2007
2469
|
persona: null,
|
|
2008
2470
|
input_kind: "prompt",
|
|
2009
2471
|
type: "note",
|
|
2010
|
-
review_skipped: {
|
|
2472
|
+
review_skipped: {
|
|
2473
|
+
diff_lines: input.diff_lines,
|
|
2474
|
+
mutation_measure: input.mutation_measure,
|
|
2475
|
+
// Omitted, not defaulted, when the producer recorded nothing —
|
|
2476
|
+
// audit-log-v1's absent-versus-present split is the same one
|
|
2477
|
+
// `skills_applied` uses above, for the same reason.
|
|
2478
|
+
...input.baseline === void 0 ? {} : { baseline: input.baseline }
|
|
2479
|
+
}
|
|
2011
2480
|
};
|
|
2012
2481
|
return { line, errors: [] };
|
|
2013
2482
|
}
|
|
@@ -2075,13 +2544,13 @@ function isSafeTranscriptPath(rawPath, opts = {}) {
|
|
|
2075
2544
|
const homeDir = opts.homeDir ?? os.homedir();
|
|
2076
2545
|
const maxBytes = opts.maxBytes ?? TRANSCRIPT_SCAN_MAX_BYTES;
|
|
2077
2546
|
try {
|
|
2078
|
-
const realHome =
|
|
2079
|
-
const realPath =
|
|
2080
|
-
const
|
|
2081
|
-
if (
|
|
2547
|
+
const realHome = fs5.realpathSync(homeDir);
|
|
2548
|
+
const realPath = fs5.realpathSync(rawPath);
|
|
2549
|
+
const relative3 = path5.relative(realHome, realPath);
|
|
2550
|
+
if (relative3 === "" || relative3.startsWith("..") || path5.isAbsolute(relative3)) {
|
|
2082
2551
|
return false;
|
|
2083
2552
|
}
|
|
2084
|
-
const { size } =
|
|
2553
|
+
const { size } = fs5.statSync(realPath);
|
|
2085
2554
|
return size <= maxBytes;
|
|
2086
2555
|
} catch {
|
|
2087
2556
|
return false;
|
|
@@ -2112,7 +2581,7 @@ function nonDocMutatedLines(rows) {
|
|
|
2112
2581
|
}
|
|
2113
2582
|
function gitNumstatRows(cwd) {
|
|
2114
2583
|
try {
|
|
2115
|
-
const r =
|
|
2584
|
+
const r = spawnSync2("git", ["diff", "--numstat", "HEAD"], {
|
|
2116
2585
|
cwd,
|
|
2117
2586
|
encoding: "utf8",
|
|
2118
2587
|
maxBuffer: 16 * 1024 * 1024
|
|
@@ -2125,7 +2594,7 @@ function gitNumstatRows(cwd) {
|
|
|
2125
2594
|
}
|
|
2126
2595
|
function untrackedNonDocFiles(cwd) {
|
|
2127
2596
|
try {
|
|
2128
|
-
const r =
|
|
2597
|
+
const r = spawnSync2("git", ["ls-files", "--others", "--exclude-standard"], {
|
|
2129
2598
|
cwd,
|
|
2130
2599
|
encoding: "utf8",
|
|
2131
2600
|
maxBuffer: 16 * 1024 * 1024
|
|
@@ -2140,13 +2609,13 @@ var BINARY_PROBE_BYTES = 8e3;
|
|
|
2140
2609
|
var UNTRACKED_FILE_READ_CAP_BYTES = 8 * 1024 * 1024;
|
|
2141
2610
|
function untrackedFileLineCount(cwd, relPath) {
|
|
2142
2611
|
try {
|
|
2143
|
-
const abs =
|
|
2144
|
-
const size =
|
|
2612
|
+
const abs = path5.resolve(cwd, relPath);
|
|
2613
|
+
const size = fs5.statSync(abs).size;
|
|
2145
2614
|
if (size === 0) return 0;
|
|
2146
2615
|
if (size > UNTRACKED_FILE_READ_CAP_BYTES) {
|
|
2147
2616
|
return MUTATION_LINE_THRESHOLD + 1;
|
|
2148
2617
|
}
|
|
2149
|
-
const buf =
|
|
2618
|
+
const buf = fs5.readFileSync(abs);
|
|
2150
2619
|
if (buf.length === 0) return 0;
|
|
2151
2620
|
const probe = buf.subarray(0, Math.min(buf.length, BINARY_PROBE_BYTES));
|
|
2152
2621
|
if (probe.includes(0)) return 0;
|
|
@@ -2225,30 +2694,31 @@ function scanTranscriptForReviewer(transcriptText) {
|
|
|
2225
2694
|
function buildAdvisoryLine(diffLines) {
|
|
2226
2695
|
return `this session mutated ${diffLines} lines without a neutral review; spawn a cross-model reviewer before claiming done (delegation-policy / verify-budget)`;
|
|
2227
2696
|
}
|
|
2228
|
-
function appendReviewSkippedTelemetry(workspaceRoot, diffLines, mutationMeasure) {
|
|
2697
|
+
function appendReviewSkippedTelemetry(workspaceRoot, diffLines, mutationMeasure, baseline) {
|
|
2229
2698
|
if (is_replay_mode()) return;
|
|
2230
2699
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
2231
2700
|
const { line, errors } = buildReviewSkippedLine({
|
|
2232
2701
|
diff_lines: diffLines,
|
|
2233
2702
|
mutation_measure: mutationMeasure,
|
|
2234
2703
|
ts,
|
|
2235
|
-
id: crypto.randomUUID()
|
|
2704
|
+
id: crypto.randomUUID(),
|
|
2705
|
+
baseline
|
|
2236
2706
|
});
|
|
2237
2707
|
if (errors.length || line === null) return;
|
|
2238
2708
|
try {
|
|
2239
|
-
const dir =
|
|
2240
|
-
|
|
2241
|
-
const file =
|
|
2242
|
-
|
|
2709
|
+
const dir = path5.join(workspaceRoot, "agents", "runtime", "state", "audit");
|
|
2710
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
2711
|
+
const file = path5.join(dir, `${ts.slice(0, 7)}.jsonl`);
|
|
2712
|
+
fs5.appendFileSync(file, `${JSON.stringify(line)}
|
|
2243
2713
|
`, "utf8");
|
|
2244
2714
|
} catch {
|
|
2245
2715
|
}
|
|
2246
2716
|
}
|
|
2247
2717
|
function sessionStateDir(workspaceRoot) {
|
|
2248
|
-
return
|
|
2718
|
+
return path5.join(workspaceRoot, "agents", "runtime", "state", "end-review-nudge");
|
|
2249
2719
|
}
|
|
2250
2720
|
function sessionStateFile(workspaceRoot, sessionKey) {
|
|
2251
|
-
return
|
|
2721
|
+
return path5.join(sessionStateDir(workspaceRoot), `${sessionKey}.json`);
|
|
2252
2722
|
}
|
|
2253
2723
|
function deriveSessionKey2(envelope, payload) {
|
|
2254
2724
|
const raw = str(envelope["session_id"]) || str(payload["transcript_path"] ?? payload["transcriptPath"]);
|
|
@@ -2257,7 +2727,7 @@ function deriveSessionKey2(envelope, payload) {
|
|
|
2257
2727
|
}
|
|
2258
2728
|
function hasFiredThisSession(workspaceRoot, sessionKey) {
|
|
2259
2729
|
try {
|
|
2260
|
-
return
|
|
2730
|
+
return fs5.existsSync(sessionStateFile(workspaceRoot, sessionKey));
|
|
2261
2731
|
} catch {
|
|
2262
2732
|
return false;
|
|
2263
2733
|
}
|
|
@@ -2271,26 +2741,45 @@ function markFiredThisSession(workspaceRoot, sessionKey, ts) {
|
|
|
2271
2741
|
function _read_stdin() {
|
|
2272
2742
|
return readHookStdin();
|
|
2273
2743
|
}
|
|
2274
|
-
|
|
2744
|
+
var FALLBACK_TO_TELEMETRY = {
|
|
2745
|
+
absent: "absent",
|
|
2746
|
+
unreadable: "unreadable",
|
|
2747
|
+
"head-moved": "head_moved",
|
|
2748
|
+
"mixed-measure": "mixed_measure"
|
|
2749
|
+
};
|
|
2750
|
+
function main4() {
|
|
2275
2751
|
const [envelope, payload] = unwrap(_read_stdin(), "claude");
|
|
2276
2752
|
const event = String(envelope["event"] ?? "");
|
|
2277
2753
|
if (event !== "" && event !== "stop") {
|
|
2278
2754
|
return 0;
|
|
2279
2755
|
}
|
|
2280
2756
|
const workspace_root = String(envelope["workspace_root"] ?? "").trim() || process.cwd();
|
|
2281
|
-
let
|
|
2757
|
+
let measuredLines;
|
|
2282
2758
|
let mutationMeasure;
|
|
2283
2759
|
try {
|
|
2284
2760
|
const measured = totalNonDocMutatedLinesWithMeasure(workspace_root);
|
|
2285
|
-
|
|
2761
|
+
measuredLines = measured.lines;
|
|
2286
2762
|
mutationMeasure = measured.measure;
|
|
2287
2763
|
} catch {
|
|
2288
2764
|
return 0;
|
|
2289
2765
|
}
|
|
2766
|
+
const sessionKey = deriveSessionKey2(envelope, payload);
|
|
2767
|
+
const baselineOutcome = applyBaseline(
|
|
2768
|
+
measuredLines,
|
|
2769
|
+
readBaseline(workspace_root, sessionKey),
|
|
2770
|
+
// A THUNK, not a value: on the `absent` and `unreadable` branches the
|
|
2771
|
+
// result is discarded, and those two are the steady state on every host
|
|
2772
|
+
// without this nudge and on every session before its first baseline
|
|
2773
|
+
// write. Passing the value eagerly spent an 11-14 ms `git rev-parse` on
|
|
2774
|
+
// each of those stops for nothing.
|
|
2775
|
+
() => currentHeadSha(workspace_root),
|
|
2776
|
+
mutationMeasure
|
|
2777
|
+
);
|
|
2778
|
+
const diffLines = baselineOutcome.lines;
|
|
2779
|
+
const baselineApplication = baselineOutcome.applied ? "applied" : FALLBACK_TO_TELEMETRY[baselineOutcome.fallback];
|
|
2290
2780
|
if (diffLines <= MUTATION_LINE_THRESHOLD) {
|
|
2291
2781
|
return 0;
|
|
2292
2782
|
}
|
|
2293
|
-
const sessionKey = deriveSessionKey2(envelope, payload);
|
|
2294
2783
|
if (hasFiredThisSession(workspace_root, sessionKey)) {
|
|
2295
2784
|
return 0;
|
|
2296
2785
|
}
|
|
@@ -2300,7 +2789,7 @@ function main3() {
|
|
|
2300
2789
|
let reviewerRan = false;
|
|
2301
2790
|
if (transcriptPath && isSafeTranscriptPath(transcriptPath)) {
|
|
2302
2791
|
try {
|
|
2303
|
-
reviewerRan = scanTranscriptForReviewer(
|
|
2792
|
+
reviewerRan = scanTranscriptForReviewer(fs5.readFileSync(transcriptPath, "utf-8"));
|
|
2304
2793
|
} catch {
|
|
2305
2794
|
reviewerRan = false;
|
|
2306
2795
|
}
|
|
@@ -2310,7 +2799,12 @@ function main3() {
|
|
|
2310
2799
|
}
|
|
2311
2800
|
markFiredThisSession(workspace_root, sessionKey, (/* @__PURE__ */ new Date()).toISOString());
|
|
2312
2801
|
try {
|
|
2313
|
-
appendReviewSkippedTelemetry(
|
|
2802
|
+
appendReviewSkippedTelemetry(
|
|
2803
|
+
workspace_root,
|
|
2804
|
+
diffLines,
|
|
2805
|
+
mutationMeasure,
|
|
2806
|
+
baselineApplication
|
|
2807
|
+
);
|
|
2314
2808
|
} catch {
|
|
2315
2809
|
}
|
|
2316
2810
|
process.stdout.write(
|
|
@@ -2323,34 +2817,34 @@ function main3() {
|
|
|
2323
2817
|
);
|
|
2324
2818
|
return EXIT_WARN2;
|
|
2325
2819
|
}
|
|
2326
|
-
function
|
|
2820
|
+
function _isCliEntry4() {
|
|
2327
2821
|
if (true) {
|
|
2328
2822
|
return false;
|
|
2329
2823
|
}
|
|
2330
2824
|
if (process.argv[1] === void 0) {
|
|
2331
2825
|
return false;
|
|
2332
2826
|
}
|
|
2333
|
-
const argvUrl =
|
|
2827
|
+
const argvUrl = pathToFileURL4(path5.resolve(process.argv[1])).href;
|
|
2334
2828
|
if (import.meta.url === argvUrl) {
|
|
2335
2829
|
return true;
|
|
2336
2830
|
}
|
|
2337
2831
|
try {
|
|
2338
|
-
const here =
|
|
2339
|
-
const argv =
|
|
2832
|
+
const here = fs5.realpathSync(fileURLToPath4(import.meta.url));
|
|
2833
|
+
const argv = fs5.realpathSync(path5.resolve(process.argv[1]));
|
|
2340
2834
|
return here === argv;
|
|
2341
2835
|
} catch {
|
|
2342
2836
|
return false;
|
|
2343
2837
|
}
|
|
2344
2838
|
}
|
|
2345
|
-
if (
|
|
2346
|
-
process.exit(
|
|
2839
|
+
if (_isCliEntry4()) {
|
|
2840
|
+
process.exit(main4());
|
|
2347
2841
|
}
|
|
2348
2842
|
|
|
2349
2843
|
// src/scripts/hooks/subagent_ledger_hook.ts
|
|
2350
2844
|
import * as crypto2 from "node:crypto";
|
|
2351
|
-
import * as
|
|
2352
|
-
import * as
|
|
2353
|
-
import { fileURLToPath as
|
|
2845
|
+
import * as fs6 from "node:fs";
|
|
2846
|
+
import * as path6 from "node:path";
|
|
2847
|
+
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL5 } from "node:url";
|
|
2354
2848
|
|
|
2355
2849
|
// src/scripts/_lib/subagent_response.ts
|
|
2356
2850
|
var MAX_SUMMARY_CHARS = 2e3;
|
|
@@ -2456,7 +2950,7 @@ var EXIT_ALLOW3 = 0;
|
|
|
2456
2950
|
function isObject2(v) {
|
|
2457
2951
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2458
2952
|
}
|
|
2459
|
-
var LEDGER_DIR =
|
|
2953
|
+
var LEDGER_DIR = path6.join("agents", "runtime", "state", "subagent-ledger");
|
|
2460
2954
|
var OPEN_SUBDIR = "open";
|
|
2461
2955
|
var OPEN_RECORD_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2462
2956
|
function refFor(agentId) {
|
|
@@ -2546,10 +3040,10 @@ function _jsonObjectCandidates(message) {
|
|
|
2546
3040
|
return out;
|
|
2547
3041
|
}
|
|
2548
3042
|
function openDir(root) {
|
|
2549
|
-
return
|
|
3043
|
+
return path6.join(root, LEDGER_DIR, OPEN_SUBDIR);
|
|
2550
3044
|
}
|
|
2551
3045
|
function openFile(root, ref) {
|
|
2552
|
-
return
|
|
3046
|
+
return path6.join(openDir(root), `${ref}.json`);
|
|
2553
3047
|
}
|
|
2554
3048
|
var DEPTH_BASES = /* @__PURE__ */ new Set(["observed", "asserted-parent", "assumed-root"]);
|
|
2555
3049
|
function _asOpenRecord(value, expectedRef) {
|
|
@@ -2576,7 +3070,7 @@ function readOpenRecords(root) {
|
|
|
2576
3070
|
const out = /* @__PURE__ */ new Map();
|
|
2577
3071
|
let names;
|
|
2578
3072
|
try {
|
|
2579
|
-
names =
|
|
3073
|
+
names = fs6.readdirSync(openDir(root));
|
|
2580
3074
|
} catch {
|
|
2581
3075
|
return out;
|
|
2582
3076
|
}
|
|
@@ -2584,7 +3078,7 @@ function readOpenRecords(root) {
|
|
|
2584
3078
|
if (!name.endsWith(".json")) continue;
|
|
2585
3079
|
const expectedRef = name.slice(0, -".json".length);
|
|
2586
3080
|
try {
|
|
2587
|
-
const raw =
|
|
3081
|
+
const raw = fs6.readFileSync(path6.join(openDir(root), name), "utf8");
|
|
2588
3082
|
const rec = _asOpenRecord(JSON.parse(raw), expectedRef);
|
|
2589
3083
|
if (rec) out.set(rec.ref, rec);
|
|
2590
3084
|
} catch {
|
|
@@ -2599,13 +3093,13 @@ function resolveDepth(parentRef, open) {
|
|
|
2599
3093
|
return { depth: parent.depth + 1, depth_basis: "observed" };
|
|
2600
3094
|
}
|
|
2601
3095
|
function ledgerFileFor2(root, ts) {
|
|
2602
|
-
return
|
|
3096
|
+
return path6.join(root, LEDGER_DIR, `${ts.slice(0, 7)}.jsonl`);
|
|
2603
3097
|
}
|
|
2604
3098
|
function appendLedgerLine(root, ts, line) {
|
|
2605
3099
|
if (is_replay_mode()) return;
|
|
2606
3100
|
const file = ledgerFileFor2(root, ts);
|
|
2607
|
-
|
|
2608
|
-
|
|
3101
|
+
fs6.mkdirSync(path6.dirname(file), { recursive: true });
|
|
3102
|
+
fs6.appendFileSync(file, `${JSON.stringify(line)}
|
|
2609
3103
|
`, "utf8");
|
|
2610
3104
|
}
|
|
2611
3105
|
var WALL_CLOCK_ARMS_MS = [
|
|
@@ -2619,7 +3113,7 @@ function armsExceeded(elapsedMs) {
|
|
|
2619
3113
|
return WALL_CLOCK_ARMS_MS.filter((a) => elapsedMs >= a.ms).map((a) => a.label);
|
|
2620
3114
|
}
|
|
2621
3115
|
function openRecordStats(root, nowMs = Date.now()) {
|
|
2622
|
-
const ledgerPresent =
|
|
3116
|
+
const ledgerPresent = fs6.existsSync(path6.join(root, LEDGER_DIR));
|
|
2623
3117
|
const open = readOpenRecords(root);
|
|
2624
3118
|
let maxDepth = 0;
|
|
2625
3119
|
let counted = 0;
|
|
@@ -2647,13 +3141,13 @@ function writeOpenRecord(root, rec) {
|
|
|
2647
3141
|
function removeOpenRecord(root, ref) {
|
|
2648
3142
|
if (is_replay_mode()) return;
|
|
2649
3143
|
try {
|
|
2650
|
-
|
|
3144
|
+
fs6.unlinkSync(openFile(root, ref));
|
|
2651
3145
|
} catch {
|
|
2652
3146
|
}
|
|
2653
3147
|
}
|
|
2654
3148
|
function countOpen(root) {
|
|
2655
3149
|
try {
|
|
2656
|
-
return
|
|
3150
|
+
return fs6.readdirSync(openDir(root)).filter((n) => n.endsWith(".json")).length;
|
|
2657
3151
|
} catch {
|
|
2658
3152
|
return 0;
|
|
2659
3153
|
}
|
|
@@ -2804,7 +3298,7 @@ function resolveConsumerRoot(envelope) {
|
|
|
2804
3298
|
}
|
|
2805
3299
|
return process.cwd();
|
|
2806
3300
|
}
|
|
2807
|
-
function
|
|
3301
|
+
function main5() {
|
|
2808
3302
|
const raw = readHookStdin();
|
|
2809
3303
|
let envelope = {};
|
|
2810
3304
|
try {
|
|
@@ -2814,20 +3308,20 @@ function main4() {
|
|
|
2814
3308
|
}
|
|
2815
3309
|
return processEnvelope(envelope, resolveConsumerRoot(envelope));
|
|
2816
3310
|
}
|
|
2817
|
-
function
|
|
3311
|
+
function _isCliEntry5() {
|
|
2818
3312
|
if (true) {
|
|
2819
3313
|
return false;
|
|
2820
3314
|
}
|
|
2821
3315
|
if (process.argv[1] === void 0) return false;
|
|
2822
|
-
const argvUrl =
|
|
3316
|
+
const argvUrl = pathToFileURL5(path6.resolve(process.argv[1])).href;
|
|
2823
3317
|
if (import.meta.url === argvUrl) return true;
|
|
2824
3318
|
try {
|
|
2825
|
-
return
|
|
3319
|
+
return fs6.realpathSync(fileURLToPath5(import.meta.url)) === fs6.realpathSync(path6.resolve(process.argv[1]));
|
|
2826
3320
|
} catch {
|
|
2827
3321
|
return false;
|
|
2828
3322
|
}
|
|
2829
3323
|
}
|
|
2830
|
-
if (
|
|
3324
|
+
if (_isCliEntry5()) process.exit(main5());
|
|
2831
3325
|
|
|
2832
3326
|
// src/scripts/hooks/turn_end_gate_hook.ts
|
|
2833
3327
|
var EXIT_BLOCK = 1;
|
|
@@ -2936,7 +3430,7 @@ function detectPromissory(reply) {
|
|
|
2936
3430
|
function readCiSettled(workspaceRoot, session_id) {
|
|
2937
3431
|
if (!hasStableSessionId(session_id)) return { seen: false, settled: false };
|
|
2938
3432
|
try {
|
|
2939
|
-
const raw =
|
|
3433
|
+
const raw = fs7.readFileSync(path7.join(workspaceRoot, statePathFor(session_id)), "utf-8");
|
|
2940
3434
|
const decoded = JSON.parse(raw);
|
|
2941
3435
|
if (!owns_session_state(decoded, session_id)) return { seen: false, settled: false };
|
|
2942
3436
|
if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
|
|
@@ -2972,8 +3466,8 @@ function detectCompletionClaim(reply, ci) {
|
|
|
2972
3466
|
function readLanguagePin(workspaceRoot, session_id) {
|
|
2973
3467
|
if (!hasStableSessionId(session_id)) return "und";
|
|
2974
3468
|
try {
|
|
2975
|
-
const raw =
|
|
2976
|
-
|
|
3469
|
+
const raw = fs7.readFileSync(
|
|
3470
|
+
path7.join(workspaceRoot, statePathFor2(session_id)),
|
|
2977
3471
|
"utf-8"
|
|
2978
3472
|
);
|
|
2979
3473
|
const decoded = JSON.parse(raw);
|
|
@@ -3024,12 +3518,37 @@ function detectUnverifiedEdit(toolCalls) {
|
|
|
3024
3518
|
reason: "this turn changed a file and then ran no verification command \u2014 no test, type-check, lint or build call follows the last edit (verify-before-complete: a claim without a fresh run is unverified)"
|
|
3025
3519
|
};
|
|
3026
3520
|
}
|
|
3521
|
+
var _SOURCE_RE = /\.(?:ts|tsx|js|jsx|mjs|cjs|vue|svelte|php|py|rb|go|rs|java|kt|swift|cs|scala|ex|exs)$/i;
|
|
3522
|
+
var _TEST_PATH_RE = /(?:^|\/)(?:tests?|specs?|__tests__|__specs__|e2e|cypress|playwright|features)\//i;
|
|
3523
|
+
var _TEST_FILE_RE = /(?:\.(?:test|spec)\.[a-z]+$)|(?:_test\.[a-z]+$)|(?:(?:^|\/)test_[^/]+$)|(?:Test\.php$)|(?:Spec\.php$)|(?:\.feature$)/i;
|
|
3524
|
+
function _isTestPath(p) {
|
|
3525
|
+
return _TEST_PATH_RE.test(p) || _TEST_FILE_RE.test(p);
|
|
3526
|
+
}
|
|
3527
|
+
function _isProductionSource(p) {
|
|
3528
|
+
return _SOURCE_RE.test(p) && !_isTestPath(p);
|
|
3529
|
+
}
|
|
3530
|
+
function detectUntestedChange(reply, toolCalls) {
|
|
3531
|
+
const edited = toolCalls.filter((c) => _EDIT_TOOLS.has(c.name) && c.path !== void 0).map((c) => c.path);
|
|
3532
|
+
const source = edited.filter(_isProductionSource);
|
|
3533
|
+
if (source.length === 0) return null;
|
|
3534
|
+
if (edited.some(_isTestPath)) return null;
|
|
3535
|
+
const prose = visibleProse(reply);
|
|
3536
|
+
const m = _COMPLETION_RE.exec(prose);
|
|
3537
|
+
if (!m) return null;
|
|
3538
|
+
if (_NEGATED_CLAIM_RE.test(_lineAround(prose, m.index))) return null;
|
|
3539
|
+
const shown = source.slice(0, 3).join(", ");
|
|
3540
|
+
return {
|
|
3541
|
+
detector: "untested",
|
|
3542
|
+
evidence: shown + (source.length > 3 ? ` (+${String(source.length - 3)} more)` : ""),
|
|
3543
|
+
reason: "a completion claim over production code this turn changed, with NO test file touched anywhere in the turn \u2014 running a linter is not evidence the change works. Write the test that would have caught the failure, and enumerate the states the change actually has (every view, every CRUD path, the empty and error states) rather than the happy one. If a test genuinely does not belong here, say which file carries the coverage instead"
|
|
3544
|
+
};
|
|
3545
|
+
}
|
|
3027
3546
|
function sessionStateFile2(workspaceRoot, sessionKey) {
|
|
3028
3547
|
return sessionRefusalFile(workspaceRoot, sessionKey);
|
|
3029
3548
|
}
|
|
3030
3549
|
function alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal) {
|
|
3031
3550
|
try {
|
|
3032
|
-
const raw =
|
|
3551
|
+
const raw = fs7.readFileSync(sessionStateFile2(workspaceRoot, sessionKey), "utf-8");
|
|
3033
3552
|
const decoded = JSON.parse(raw);
|
|
3034
3553
|
if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
|
|
3035
3554
|
return decoded["refused_turn"] === turnOrdinal;
|
|
@@ -3038,12 +3557,12 @@ function alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal) {
|
|
|
3038
3557
|
}
|
|
3039
3558
|
return false;
|
|
3040
3559
|
}
|
|
3041
|
-
function markRefusedTurn(workspaceRoot, sessionKey, turnOrdinal, detectors) {
|
|
3560
|
+
function markRefusedTurn(workspaceRoot, sessionKey, turnOrdinal, detectors, promptId) {
|
|
3042
3561
|
if (is_replay_mode()) return;
|
|
3043
3562
|
const file = sessionStateFile2(workspaceRoot, sessionKey);
|
|
3044
3563
|
let prev = null;
|
|
3045
3564
|
try {
|
|
3046
|
-
prev = parseRecord(
|
|
3565
|
+
prev = parseRecord(fs7.readFileSync(file, "utf-8"));
|
|
3047
3566
|
} catch {
|
|
3048
3567
|
}
|
|
3049
3568
|
let version;
|
|
@@ -3059,27 +3578,34 @@ function markRefusedTurn(workspaceRoot, sessionKey, turnOrdinal, detectors) {
|
|
|
3059
3578
|
detectors,
|
|
3060
3579
|
turnOrdinal,
|
|
3061
3580
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3062
|
-
version
|
|
3581
|
+
version,
|
|
3582
|
+
promptId
|
|
3063
3583
|
})
|
|
3064
3584
|
);
|
|
3065
3585
|
} catch {
|
|
3066
3586
|
}
|
|
3067
3587
|
}
|
|
3068
3588
|
function readTranscriptTail(transcriptPath, opts = {}) {
|
|
3069
|
-
const empty = {
|
|
3589
|
+
const empty = {
|
|
3590
|
+
lastAssistant: "",
|
|
3591
|
+
turnOrdinal: 0,
|
|
3592
|
+
toolCalls: [],
|
|
3593
|
+
assistantTurnTexts: []
|
|
3594
|
+
};
|
|
3070
3595
|
if (!transcriptPath || !isSafeTranscriptPath(transcriptPath, opts)) return empty;
|
|
3071
3596
|
let lines;
|
|
3072
3597
|
try {
|
|
3073
|
-
if (opts.maxBytes !== void 0 &&
|
|
3598
|
+
if (opts.maxBytes !== void 0 && fs7.statSync(transcriptPath).size > opts.maxBytes) {
|
|
3074
3599
|
return empty;
|
|
3075
3600
|
}
|
|
3076
|
-
lines =
|
|
3601
|
+
lines = fs7.readFileSync(transcriptPath, "utf-8").split("\n");
|
|
3077
3602
|
} catch {
|
|
3078
3603
|
return empty;
|
|
3079
3604
|
}
|
|
3080
3605
|
let lastAssistant = "";
|
|
3081
3606
|
let turnOrdinal = 0;
|
|
3082
3607
|
let toolCalls = [];
|
|
3608
|
+
let assistantTurnTexts = [];
|
|
3083
3609
|
for (const rawLine of lines) {
|
|
3084
3610
|
const line = rawLine.trim();
|
|
3085
3611
|
if (!line) continue;
|
|
@@ -3098,20 +3624,27 @@ function readTranscriptTail(transcriptPath, opts = {}) {
|
|
|
3098
3624
|
if (typeof msg !== "object" || msg === null || Array.isArray(msg)) continue;
|
|
3099
3625
|
const content = msg["content"];
|
|
3100
3626
|
if (role === "assistant") {
|
|
3101
|
-
toolCalls.push(...
|
|
3627
|
+
toolCalls.push(...extractToolCalls(content));
|
|
3102
3628
|
}
|
|
3103
3629
|
const text = _messageText(content);
|
|
3104
3630
|
if (text === null) continue;
|
|
3105
3631
|
if (role === "assistant") {
|
|
3106
3632
|
lastAssistant = text;
|
|
3633
|
+
assistantTurnTexts.push(text);
|
|
3107
3634
|
} else if (!isSyntheticPrompt(text)) {
|
|
3108
3635
|
turnOrdinal += 1;
|
|
3109
3636
|
toolCalls = [];
|
|
3637
|
+
assistantTurnTexts = [];
|
|
3110
3638
|
}
|
|
3111
3639
|
}
|
|
3112
|
-
return {
|
|
3640
|
+
return {
|
|
3641
|
+
lastAssistant: lastAssistant.trim(),
|
|
3642
|
+
turnOrdinal,
|
|
3643
|
+
toolCalls,
|
|
3644
|
+
assistantTurnTexts
|
|
3645
|
+
};
|
|
3113
3646
|
}
|
|
3114
|
-
function
|
|
3647
|
+
function extractToolCalls(content) {
|
|
3115
3648
|
if (!Array.isArray(content)) return [];
|
|
3116
3649
|
const out = [];
|
|
3117
3650
|
for (const blk of content) {
|
|
@@ -3149,7 +3682,7 @@ function _messageText(content) {
|
|
|
3149
3682
|
function str3(v) {
|
|
3150
3683
|
return typeof v === "string" ? v : "";
|
|
3151
3684
|
}
|
|
3152
|
-
function
|
|
3685
|
+
function main6() {
|
|
3153
3686
|
let envelope;
|
|
3154
3687
|
let payload;
|
|
3155
3688
|
try {
|
|
@@ -3167,10 +3700,19 @@ function main5() {
|
|
|
3167
3700
|
const transcriptPath = str3(
|
|
3168
3701
|
payload["transcript_path"] ?? payload["transcriptPath"]
|
|
3169
3702
|
);
|
|
3170
|
-
const { lastAssistant, turnOrdinal, toolCalls } = readTranscriptTail(
|
|
3171
|
-
|
|
3172
|
-
|
|
3703
|
+
const { lastAssistant, turnOrdinal, toolCalls, assistantTurnTexts } = readTranscriptTail(
|
|
3704
|
+
transcriptPath,
|
|
3705
|
+
{ maxBytes: TRANSCRIPT_READ_MAX_BYTES }
|
|
3706
|
+
);
|
|
3173
3707
|
if (!lastAssistant) return EXIT_ALLOW4;
|
|
3708
|
+
const payloadClosing = str3(
|
|
3709
|
+
payload["last_assistant_message"] ?? payload["lastAssistantMessage"]
|
|
3710
|
+
);
|
|
3711
|
+
const closingFromPayload = payloadClosing !== "" && !assistantTurnTexts.includes(payloadClosing);
|
|
3712
|
+
const turnTexts = closingFromPayload ? [...assistantTurnTexts, payloadClosing] : assistantTurnTexts;
|
|
3713
|
+
const promptId = str3(
|
|
3714
|
+
payload["prompt_id"] ?? payload["promptId"]
|
|
3715
|
+
);
|
|
3174
3716
|
const rawSessionId = str3(envelope["session_id"]) || "";
|
|
3175
3717
|
const sessionKey = deriveSessionKey(rawSessionId || "unknown-session");
|
|
3176
3718
|
if (alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal)) return EXIT_ALLOW4;
|
|
@@ -3192,7 +3734,23 @@ function main5() {
|
|
|
3192
3734
|
// gating is where the comment above says gating belongs — inside the
|
|
3193
3735
|
// detector: no CI observed, or a settled read, or no completion claim
|
|
3194
3736
|
// ⇒ no finding.
|
|
3195
|
-
dispatchOpen ? null : detectCompletionClaim(lastAssistant, readCiSettled(workspaceRoot, rawSessionId))
|
|
3737
|
+
dispatchOpen ? null : detectCompletionClaim(lastAssistant, readCiSettled(workspaceRoot, rawSessionId)),
|
|
3738
|
+
// Detector E is UNCONDITIONAL, and that is the opposite choice from A
|
|
3739
|
+
// and D one line above rather than an oversight. An open dispatch
|
|
3740
|
+
// excuses a promissory closing because the work is genuinely still
|
|
3741
|
+
// running; it excuses nothing about a question the user was already
|
|
3742
|
+
// asked. Worse: a dispatch — or the `end-review-nudge` stop concern
|
|
3743
|
+
// that produced the measured failure — IS the continuation that drops
|
|
3744
|
+
// the decision, so narrowing E the same way would silence it in exactly
|
|
3745
|
+
// the case it exists for.
|
|
3746
|
+
detectDroppedDecision(turnTexts, closingFromPayload),
|
|
3747
|
+
// Detector F takes the OTHER side of that same question, and the two
|
|
3748
|
+
// sitting adjacent is the clearest place to say why. E fires on a
|
|
3749
|
+
// question already asked, which an open dispatch cannot excuse. F fires
|
|
3750
|
+
// on a completion CLAIM, and a turn waiting on a subagent has not
|
|
3751
|
+
// finished — so its closing is not the claim F is about. Same slot,
|
|
3752
|
+
// opposite trigger, opposite treatment of `dispatchOpen`.
|
|
3753
|
+
dispatchOpen ? null : detectUntestedChange(lastAssistant, toolCalls)
|
|
3196
3754
|
]) {
|
|
3197
3755
|
if (f) findings.push(f);
|
|
3198
3756
|
}
|
|
@@ -3201,7 +3759,8 @@ function main5() {
|
|
|
3201
3759
|
workspaceRoot,
|
|
3202
3760
|
sessionKey,
|
|
3203
3761
|
turnOrdinal,
|
|
3204
|
-
findings.map((f) => f.detector)
|
|
3762
|
+
findings.map((f) => f.detector),
|
|
3763
|
+
promptId
|
|
3205
3764
|
);
|
|
3206
3765
|
const lines = findings.map(
|
|
3207
3766
|
(f) => ` \xB7 ${f.detector}: ${f.reason}
|
|
@@ -3210,43 +3769,43 @@ function main5() {
|
|
|
3210
3769
|
process.stderr.write(
|
|
3211
3770
|
`turn-end-gate: REFUSED \u2014 this turn is not finished.
|
|
3212
3771
|
${lines.join("\n")}
|
|
3213
|
-
Do the promised work now, correct the language, run the verification the edit needs,
|
|
3772
|
+
Do the promised work now, correct the language, run the verification the edit needs, read the CI verdict before claiming it, or re-present the options block this turn dropped \u2014 then end the turn.
|
|
3214
3773
|
This turn will not be refused a second time.
|
|
3215
3774
|
`
|
|
3216
3775
|
);
|
|
3217
3776
|
return EXIT_BLOCK;
|
|
3218
3777
|
}
|
|
3219
|
-
function
|
|
3778
|
+
function _isCliEntry6() {
|
|
3220
3779
|
if (true) {
|
|
3221
3780
|
return false;
|
|
3222
3781
|
}
|
|
3223
3782
|
if (process.argv[1] === void 0) {
|
|
3224
3783
|
return false;
|
|
3225
3784
|
}
|
|
3226
|
-
const argvUrl =
|
|
3785
|
+
const argvUrl = pathToFileURL6(path7.resolve(process.argv[1])).href;
|
|
3227
3786
|
if (import.meta.url === argvUrl) {
|
|
3228
3787
|
return true;
|
|
3229
3788
|
}
|
|
3230
3789
|
try {
|
|
3231
|
-
const here =
|
|
3232
|
-
const argv =
|
|
3790
|
+
const here = fs7.realpathSync(fileURLToPath6(import.meta.url));
|
|
3791
|
+
const argv = fs7.realpathSync(path7.resolve(process.argv[1]));
|
|
3233
3792
|
return here === argv;
|
|
3234
3793
|
} catch {
|
|
3235
3794
|
return false;
|
|
3236
3795
|
}
|
|
3237
3796
|
}
|
|
3238
|
-
if (
|
|
3239
|
-
process.exit(
|
|
3797
|
+
if (_isCliEntry6()) {
|
|
3798
|
+
process.exit(main6());
|
|
3240
3799
|
}
|
|
3241
3800
|
|
|
3242
3801
|
// src/scripts/hooks/evidence_independence.ts
|
|
3243
3802
|
import crypto3 from "node:crypto";
|
|
3244
|
-
import
|
|
3245
|
-
import
|
|
3246
|
-
import { fileURLToPath as
|
|
3803
|
+
import fs8 from "node:fs";
|
|
3804
|
+
import path8 from "node:path";
|
|
3805
|
+
import { fileURLToPath as fileURLToPath7, pathToFileURL as pathToFileURL7 } from "node:url";
|
|
3247
3806
|
var EXIT_ALLOW5 = 0;
|
|
3248
3807
|
var EXIT_BLOCK2 = 1;
|
|
3249
|
-
var STATE_FILE3 =
|
|
3808
|
+
var STATE_FILE3 = path8.join("agents", "state", "evidence-dispatch.json");
|
|
3250
3809
|
function _isObject5(v) {
|
|
3251
3810
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3252
3811
|
}
|
|
@@ -3314,8 +3873,8 @@ function extractDispatch(envelope) {
|
|
|
3314
3873
|
}
|
|
3315
3874
|
function _ledgerStamp(consumer_root) {
|
|
3316
3875
|
try {
|
|
3317
|
-
const raw =
|
|
3318
|
-
|
|
3876
|
+
const raw = fs8.readFileSync(
|
|
3877
|
+
path8.join(consumer_root, "agents", "state", "git-authorization.json"),
|
|
3319
3878
|
"utf8"
|
|
3320
3879
|
);
|
|
3321
3880
|
const d = JSON.parse(raw);
|
|
@@ -3326,7 +3885,7 @@ function _ledgerStamp(consumer_root) {
|
|
|
3326
3885
|
}
|
|
3327
3886
|
function _load(target) {
|
|
3328
3887
|
try {
|
|
3329
|
-
const decoded = JSON.parse(
|
|
3888
|
+
const decoded = JSON.parse(fs8.readFileSync(target, "utf8"));
|
|
3330
3889
|
if (_isObject5(decoded) && Array.isArray(decoded["evaluations"])) {
|
|
3331
3890
|
return decoded;
|
|
3332
3891
|
}
|
|
@@ -3384,7 +3943,7 @@ function run3(stdin_text, options) {
|
|
|
3384
3943
|
return EXIT_ALLOW5;
|
|
3385
3944
|
}
|
|
3386
3945
|
}
|
|
3387
|
-
const target =
|
|
3946
|
+
const target = path8.join(options.consumer_root, STATE_FILE3);
|
|
3388
3947
|
const state = _load(target);
|
|
3389
3948
|
const session_id = typeof envelope["session_id"] === "string" ? envelope["session_id"] : "";
|
|
3390
3949
|
const turnMarker = `${session_id}:${_ledgerStamp(options.consumer_root)}`;
|
|
@@ -3422,7 +3981,7 @@ function _errText3(err) {
|
|
|
3422
3981
|
const t = err instanceof Error ? err.message : String(err);
|
|
3423
3982
|
return t.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
3424
3983
|
}
|
|
3425
|
-
function
|
|
3984
|
+
function main7(argv) {
|
|
3426
3985
|
const args = argv ?? process.argv.slice(2);
|
|
3427
3986
|
let consumer_root = process.cwd();
|
|
3428
3987
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -3436,33 +3995,33 @@ function main6(argv) {
|
|
|
3436
3995
|
}
|
|
3437
3996
|
return run3(readHookStdin(), { consumer_root });
|
|
3438
3997
|
}
|
|
3439
|
-
function
|
|
3998
|
+
function _isCliEntry7() {
|
|
3440
3999
|
if (true) {
|
|
3441
4000
|
return false;
|
|
3442
4001
|
}
|
|
3443
4002
|
if (process.argv[1] === void 0) {
|
|
3444
4003
|
return false;
|
|
3445
4004
|
}
|
|
3446
|
-
const argvUrl =
|
|
4005
|
+
const argvUrl = pathToFileURL7(path8.resolve(process.argv[1])).href;
|
|
3447
4006
|
if (import.meta.url === argvUrl) {
|
|
3448
4007
|
return true;
|
|
3449
4008
|
}
|
|
3450
4009
|
try {
|
|
3451
|
-
const here =
|
|
3452
|
-
const argv =
|
|
4010
|
+
const here = fs8.realpathSync(fileURLToPath7(import.meta.url));
|
|
4011
|
+
const argv = fs8.realpathSync(path8.resolve(process.argv[1]));
|
|
3453
4012
|
return here === argv;
|
|
3454
4013
|
} catch {
|
|
3455
4014
|
return false;
|
|
3456
4015
|
}
|
|
3457
4016
|
}
|
|
3458
|
-
if (
|
|
3459
|
-
process.exit(
|
|
4017
|
+
if (_isCliEntry7()) {
|
|
4018
|
+
process.exit(main7());
|
|
3460
4019
|
}
|
|
3461
4020
|
|
|
3462
4021
|
// src/scripts/hooks/delegation_nudge_hook.ts
|
|
3463
|
-
import
|
|
3464
|
-
import
|
|
3465
|
-
import { fileURLToPath as
|
|
4022
|
+
import fs9 from "node:fs";
|
|
4023
|
+
import path9 from "node:path";
|
|
4024
|
+
import { fileURLToPath as fileURLToPath8, pathToFileURL as pathToFileURL8 } from "node:url";
|
|
3466
4025
|
|
|
3467
4026
|
// src/scripts/_lib/auto_dispatch.ts
|
|
3468
4027
|
var SIZE_FLOOR = 1;
|
|
@@ -3917,7 +4476,7 @@ function _workspaceRoot(envelope) {
|
|
|
3917
4476
|
}
|
|
3918
4477
|
return process.cwd();
|
|
3919
4478
|
}
|
|
3920
|
-
function
|
|
4479
|
+
function main8() {
|
|
3921
4480
|
let envelope = {};
|
|
3922
4481
|
try {
|
|
3923
4482
|
const raw = readHookStdin();
|
|
@@ -3956,31 +4515,31 @@ function main7() {
|
|
|
3956
4515
|
);
|
|
3957
4516
|
return EXIT_WARN3;
|
|
3958
4517
|
}
|
|
3959
|
-
function
|
|
4518
|
+
function _isCliEntry8() {
|
|
3960
4519
|
if (true) {
|
|
3961
4520
|
return false;
|
|
3962
4521
|
}
|
|
3963
4522
|
if (process.argv[1] === void 0) {
|
|
3964
4523
|
return false;
|
|
3965
4524
|
}
|
|
3966
|
-
const argvUrl =
|
|
4525
|
+
const argvUrl = pathToFileURL8(path9.resolve(process.argv[1])).href;
|
|
3967
4526
|
if (import.meta.url === argvUrl) {
|
|
3968
4527
|
return true;
|
|
3969
4528
|
}
|
|
3970
4529
|
try {
|
|
3971
|
-
const here =
|
|
3972
|
-
const argv =
|
|
4530
|
+
const here = fs9.realpathSync(fileURLToPath8(import.meta.url));
|
|
4531
|
+
const argv = fs9.realpathSync(path9.resolve(process.argv[1]));
|
|
3973
4532
|
return here === argv;
|
|
3974
4533
|
} catch {
|
|
3975
4534
|
return false;
|
|
3976
4535
|
}
|
|
3977
4536
|
}
|
|
3978
|
-
if (
|
|
3979
|
-
process.exit(
|
|
4537
|
+
if (_isCliEntry8()) {
|
|
4538
|
+
process.exit(main8());
|
|
3980
4539
|
}
|
|
3981
4540
|
|
|
3982
4541
|
// src/scripts/conformance_scan.ts
|
|
3983
|
-
var REPO_ROOT =
|
|
4542
|
+
var REPO_ROOT = path10.resolve(path10.dirname(fileURLToPath9(import.meta.url)), "..", "..");
|
|
3984
4543
|
var OBSERVED_BAND = { low: 9.1, high: 39.2 };
|
|
3985
4544
|
var BAND_MIN_TURNS = 1978;
|
|
3986
4545
|
var CARRIER_CHANGES = [
|
|
@@ -4346,7 +4905,7 @@ function scanSession(sessionId, lines) {
|
|
|
4346
4905
|
closeWindow();
|
|
4347
4906
|
return report;
|
|
4348
4907
|
}
|
|
4349
|
-
var DEFAULT_RATE_SERIES =
|
|
4908
|
+
var DEFAULT_RATE_SERIES = path10.join(
|
|
4350
4909
|
REPO_ROOT,
|
|
4351
4910
|
"agents",
|
|
4352
4911
|
"runtime",
|
|
@@ -4355,7 +4914,7 @@ var DEFAULT_RATE_SERIES = path8.join(
|
|
|
4355
4914
|
);
|
|
4356
4915
|
function defaultStore(projectDir) {
|
|
4357
4916
|
const slug = projectStoreSlug(projectDir);
|
|
4358
|
-
return
|
|
4917
|
+
return path10.join(process.env["HOME"] ?? "", ".claude", "projects", slug);
|
|
4359
4918
|
}
|
|
4360
4919
|
function _tokens(chars) {
|
|
4361
4920
|
return Math.round(chars / 4);
|
|
@@ -4366,13 +4925,13 @@ function measureDelivered(projectRulesDir, globalRulesDir) {
|
|
|
4366
4925
|
return {
|
|
4367
4926
|
project: {
|
|
4368
4927
|
dir: projectRulesDir,
|
|
4369
|
-
present:
|
|
4928
|
+
present: fs10.existsSync(projectRulesDir),
|
|
4370
4929
|
files: p.files,
|
|
4371
4930
|
tokens: _tokens(p.chars)
|
|
4372
4931
|
},
|
|
4373
4932
|
global: {
|
|
4374
4933
|
dir: globalRulesDir,
|
|
4375
|
-
present:
|
|
4934
|
+
present: fs10.existsSync(globalRulesDir),
|
|
4376
4935
|
files: g.files,
|
|
4377
4936
|
tokens: _tokens(g.chars)
|
|
4378
4937
|
},
|
|
@@ -4380,13 +4939,13 @@ function measureDelivered(projectRulesDir, globalRulesDir) {
|
|
|
4380
4939
|
};
|
|
4381
4940
|
}
|
|
4382
4941
|
function storeKey(store) {
|
|
4383
|
-
return createHash3("sha256").update(
|
|
4942
|
+
return createHash3("sha256").update(path10.resolve(store)).digest("hex").slice(0, 12);
|
|
4384
4943
|
}
|
|
4385
4944
|
function scanStore(store, limit) {
|
|
4386
|
-
const files =
|
|
4945
|
+
const files = fs10.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, m: fs10.statSync(path10.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.f);
|
|
4387
4946
|
const per_session = [];
|
|
4388
4947
|
for (const f of files) {
|
|
4389
|
-
const lines =
|
|
4948
|
+
const lines = fs10.readFileSync(path10.join(store, f), "utf8").split("\n");
|
|
4390
4949
|
const r = scanSession(f.replace(/\.jsonl$/, "").slice(0, 8), lines);
|
|
4391
4950
|
if (r.assistant_turns > 0) {
|
|
4392
4951
|
per_session.push(r);
|
|
@@ -4414,10 +4973,10 @@ function scanStore(store, limit) {
|
|
|
4414
4973
|
const task_completeness_rate_pct = completeness_windows === 0 ? 0 : Number((100 * task_completeness / completeness_windows).toFixed(1));
|
|
4415
4974
|
const era = spansCarrierChange(per_session.map((s) => s.first_at));
|
|
4416
4975
|
const delivered = measureDelivered(
|
|
4417
|
-
|
|
4418
|
-
|
|
4976
|
+
path10.join(REPO_ROOT, ".claude", "rules"),
|
|
4977
|
+
path10.join(process.env["HOME"] ?? os2.homedir(), ".claude", "rules")
|
|
4419
4978
|
);
|
|
4420
|
-
const ownStore =
|
|
4979
|
+
const ownStore = path10.resolve(store) === path10.resolve(defaultStore(REPO_ROOT));
|
|
4421
4980
|
return {
|
|
4422
4981
|
scanned_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4423
4982
|
store,
|
|
@@ -4447,8 +5006,8 @@ function scanStore(store, limit) {
|
|
|
4447
5006
|
};
|
|
4448
5007
|
}
|
|
4449
5008
|
function recordRate(file, scanned_at, rate) {
|
|
4450
|
-
|
|
4451
|
-
|
|
5009
|
+
fs10.mkdirSync(path10.dirname(file), { recursive: true });
|
|
5010
|
+
fs10.appendFileSync(file, `${JSON.stringify({ measured_at: scanned_at, ...rate })}
|
|
4452
5011
|
`, "utf8");
|
|
4453
5012
|
}
|
|
4454
5013
|
function render(report) {
|
|
@@ -4547,7 +5106,7 @@ function render(report) {
|
|
|
4547
5106
|
lines.push(" checkbox batching \u2014 measured NOWHERE. ~5 low-severity findings.");
|
|
4548
5107
|
return lines.join("\n");
|
|
4549
5108
|
}
|
|
4550
|
-
function
|
|
5109
|
+
function main9(argv) {
|
|
4551
5110
|
const args = argv ?? process.argv.slice(2);
|
|
4552
5111
|
let store = null;
|
|
4553
5112
|
let limit = 30;
|
|
@@ -4579,14 +5138,14 @@ function main8(argv) {
|
|
|
4579
5138
|
return 2;
|
|
4580
5139
|
}
|
|
4581
5140
|
const resolved = store ?? defaultStore(process.cwd());
|
|
4582
|
-
if (!
|
|
5141
|
+
if (!fs10.existsSync(resolved)) {
|
|
4583
5142
|
process.stderr.write(`conformance:behavior: no transcript store at ${resolved}
|
|
4584
5143
|
`);
|
|
4585
5144
|
return 1;
|
|
4586
5145
|
}
|
|
4587
5146
|
const report = scanStore(resolved, Number.isFinite(limit) && limit > 0 ? limit : 30);
|
|
4588
5147
|
if (out) {
|
|
4589
|
-
|
|
5148
|
+
fs10.writeFileSync(out, `${JSON.stringify(report, null, 2)}
|
|
4590
5149
|
`, "utf8");
|
|
4591
5150
|
}
|
|
4592
5151
|
if (record) {
|
|
@@ -4594,7 +5153,7 @@ function main8(argv) {
|
|
|
4594
5153
|
}
|
|
4595
5154
|
if (why !== null) {
|
|
4596
5155
|
const id = why;
|
|
4597
|
-
|
|
5156
|
+
fs10.writeSync(
|
|
4598
5157
|
1,
|
|
4599
5158
|
json ? `${JSON.stringify(
|
|
4600
5159
|
{
|
|
@@ -4614,48 +5173,48 @@ function main8(argv) {
|
|
|
4614
5173
|
);
|
|
4615
5174
|
return 0;
|
|
4616
5175
|
}
|
|
4617
|
-
|
|
5176
|
+
fs10.writeSync(1, json ? `${JSON.stringify(report, null, 2)}
|
|
4618
5177
|
` : `${render(report)}
|
|
4619
5178
|
`);
|
|
4620
5179
|
return 0;
|
|
4621
5180
|
}
|
|
4622
|
-
function
|
|
5181
|
+
function _isCliEntry9() {
|
|
4623
5182
|
if (true) {
|
|
4624
5183
|
return false;
|
|
4625
5184
|
}
|
|
4626
5185
|
if (process.argv[1] === void 0) {
|
|
4627
5186
|
return false;
|
|
4628
5187
|
}
|
|
4629
|
-
const argvUrl =
|
|
5188
|
+
const argvUrl = pathToFileURL9(path10.resolve(process.argv[1])).href;
|
|
4630
5189
|
if (import.meta.url === argvUrl) {
|
|
4631
5190
|
return true;
|
|
4632
5191
|
}
|
|
4633
5192
|
try {
|
|
4634
|
-
const here =
|
|
4635
|
-
const argv =
|
|
5193
|
+
const here = fs10.realpathSync(fileURLToPath9(import.meta.url));
|
|
5194
|
+
const argv = fs10.realpathSync(path10.resolve(process.argv[1]));
|
|
4636
5195
|
return here === argv;
|
|
4637
5196
|
} catch {
|
|
4638
5197
|
return false;
|
|
4639
5198
|
}
|
|
4640
5199
|
}
|
|
4641
|
-
if (
|
|
4642
|
-
process.exit(
|
|
5200
|
+
if (_isCliEntry9()) {
|
|
5201
|
+
process.exit(main9());
|
|
4643
5202
|
}
|
|
4644
5203
|
|
|
4645
5204
|
// src/scripts/report_carrier_divergence.ts
|
|
4646
|
-
import * as
|
|
5205
|
+
import * as fs11 from "node:fs";
|
|
4647
5206
|
import * as os3 from "node:os";
|
|
4648
|
-
import * as
|
|
5207
|
+
import * as path11 from "node:path";
|
|
4649
5208
|
import process2 from "node:process";
|
|
4650
|
-
import { fileURLToPath as
|
|
4651
|
-
var
|
|
4652
|
-
var REPO_ROOT2 =
|
|
4653
|
-
var PROJECT_RULES =
|
|
4654
|
-
var PROJECTION_SOURCE =
|
|
4655
|
-
var GLOBAL_RULES =
|
|
5209
|
+
import { fileURLToPath as fileURLToPath10, pathToFileURL as pathToFileURL10 } from "node:url";
|
|
5210
|
+
var _HERE2 = fileURLToPath10(import.meta.url);
|
|
5211
|
+
var REPO_ROOT2 = path11.resolve(path11.dirname(_HERE2), "..", "..");
|
|
5212
|
+
var PROJECT_RULES = path11.join(".claude", "rules");
|
|
5213
|
+
var PROJECTION_SOURCE = path11.join("dist", "agent-src", "rules");
|
|
5214
|
+
var GLOBAL_RULES = path11.join(".claude", "rules");
|
|
4656
5215
|
function _listRules(dir) {
|
|
4657
5216
|
try {
|
|
4658
|
-
return
|
|
5217
|
+
return fs11.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
4659
5218
|
} catch {
|
|
4660
5219
|
return [];
|
|
4661
5220
|
}
|
|
@@ -4663,7 +5222,7 @@ function _listRules(dir) {
|
|
|
4663
5222
|
function _ruleType(file) {
|
|
4664
5223
|
let text;
|
|
4665
5224
|
try {
|
|
4666
|
-
text =
|
|
5225
|
+
text = fs11.readFileSync(file, "utf-8");
|
|
4667
5226
|
} catch {
|
|
4668
5227
|
return "";
|
|
4669
5228
|
}
|
|
@@ -4674,8 +5233,8 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
|
|
|
4674
5233
|
const out = {
|
|
4675
5234
|
projectDir,
|
|
4676
5235
|
globalDir,
|
|
4677
|
-
globalPresent:
|
|
4678
|
-
projectPresent:
|
|
5236
|
+
globalPresent: fs11.existsSync(globalDir),
|
|
5237
|
+
projectPresent: fs11.existsSync(projectDir),
|
|
4679
5238
|
shared: 0,
|
|
4680
5239
|
identical: [],
|
|
4681
5240
|
provenanceOnly: [],
|
|
@@ -4698,7 +5257,7 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
|
|
|
4698
5257
|
}
|
|
4699
5258
|
if (!inP && inG) {
|
|
4700
5259
|
out.globalOnly.push(rule);
|
|
4701
|
-
if (sourceDir !== void 0 && _ruleType(
|
|
5260
|
+
if (sourceDir !== void 0 && _ruleType(path11.join(sourceDir, rule)) === "manual") {
|
|
4702
5261
|
out.manualOnlyGlobal.push(rule);
|
|
4703
5262
|
}
|
|
4704
5263
|
continue;
|
|
@@ -4708,8 +5267,8 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
|
|
|
4708
5267
|
let projectCopy;
|
|
4709
5268
|
let globalCopy;
|
|
4710
5269
|
try {
|
|
4711
|
-
projectCopy =
|
|
4712
|
-
globalCopy =
|
|
5270
|
+
projectCopy = fs11.readFileSync(path11.join(projectDir, rule));
|
|
5271
|
+
globalCopy = fs11.readFileSync(path11.join(globalDir, rule));
|
|
4713
5272
|
verdict = comparePair(projectCopy, globalCopy);
|
|
4714
5273
|
} catch {
|
|
4715
5274
|
out.shared -= 1;
|
|
@@ -4854,11 +5413,11 @@ function render2(d) {
|
|
|
4854
5413
|
}
|
|
4855
5414
|
return lines.join("\n");
|
|
4856
5415
|
}
|
|
4857
|
-
function
|
|
5416
|
+
function main10(argv) {
|
|
4858
5417
|
const args = argv ?? process2.argv.slice(2);
|
|
4859
|
-
let projectDir =
|
|
4860
|
-
let globalDir =
|
|
4861
|
-
let sourceDir =
|
|
5418
|
+
let projectDir = path11.join(REPO_ROOT2, PROJECT_RULES);
|
|
5419
|
+
let globalDir = path11.join(os3.homedir(), GLOBAL_RULES);
|
|
5420
|
+
let sourceDir = path11.join(REPO_ROOT2, PROJECTION_SOURCE);
|
|
4862
5421
|
const value = (i) => {
|
|
4863
5422
|
const v = args[i + 1];
|
|
4864
5423
|
return v === void 0 || v.startsWith("-") ? null : v;
|
|
@@ -4872,7 +5431,7 @@ function main9(argv) {
|
|
|
4872
5431
|
`);
|
|
4873
5432
|
return 1;
|
|
4874
5433
|
}
|
|
4875
|
-
const resolved =
|
|
5434
|
+
const resolved = path11.resolve(v);
|
|
4876
5435
|
if (a === "--project") projectDir = resolved;
|
|
4877
5436
|
else if (a === "--global") globalDir = resolved;
|
|
4878
5437
|
else sourceDir = resolved;
|
|
@@ -4892,26 +5451,26 @@ function main9(argv) {
|
|
|
4892
5451
|
`);
|
|
4893
5452
|
return 0;
|
|
4894
5453
|
}
|
|
4895
|
-
function
|
|
5454
|
+
function _isCliEntry10() {
|
|
4896
5455
|
if (process2.argv[1] === void 0) return false;
|
|
4897
|
-
if (import.meta.url ===
|
|
5456
|
+
if (import.meta.url === pathToFileURL10(path11.resolve(process2.argv[1])).href) return true;
|
|
4898
5457
|
try {
|
|
4899
|
-
return
|
|
5458
|
+
return fs11.realpathSync(_HERE2) === fs11.realpathSync(path11.resolve(process2.argv[1]));
|
|
4900
5459
|
} catch {
|
|
4901
5460
|
return false;
|
|
4902
5461
|
}
|
|
4903
5462
|
}
|
|
4904
|
-
if (
|
|
4905
|
-
process2.exit(
|
|
5463
|
+
if (_isCliEntry10()) {
|
|
5464
|
+
process2.exit(main10());
|
|
4906
5465
|
}
|
|
4907
5466
|
|
|
4908
5467
|
// src/scripts/report_skill_activation.ts
|
|
4909
|
-
import * as
|
|
5468
|
+
import * as fs12 from "node:fs";
|
|
4910
5469
|
import * as os4 from "node:os";
|
|
4911
|
-
import * as
|
|
4912
|
-
import { fileURLToPath as
|
|
4913
|
-
var
|
|
4914
|
-
var REPO_ROOT3 =
|
|
5470
|
+
import * as path12 from "node:path";
|
|
5471
|
+
import { fileURLToPath as fileURLToPath11, pathToFileURL as pathToFileURL11 } from "node:url";
|
|
5472
|
+
var _HERE3 = fileURLToPath11(import.meta.url);
|
|
5473
|
+
var REPO_ROOT3 = path12.resolve(path12.dirname(_HERE3), "..", "..");
|
|
4915
5474
|
var SKILLS_ROOT = "src/skills";
|
|
4916
5475
|
var TRIGGER_KEYS = ["triggers:", "trigger_description:", "file_pattern:", "path_prefix:"];
|
|
4917
5476
|
var DETERMINISTIC_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?(MUST|NEVER|ALWAYS)\b/m;
|
|
@@ -4922,25 +5481,25 @@ function censusSkills(root) {
|
|
|
4922
5481
|
withTriggerCorpus: [],
|
|
4923
5482
|
withDeterministicObligation: []
|
|
4924
5483
|
};
|
|
4925
|
-
if (!
|
|
5484
|
+
if (!fs12.existsSync(root)) {
|
|
4926
5485
|
return out;
|
|
4927
5486
|
}
|
|
4928
|
-
for (const entry of
|
|
5487
|
+
for (const entry of fs12.readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4929
5488
|
if (!entry.isDirectory()) {
|
|
4930
5489
|
continue;
|
|
4931
5490
|
}
|
|
4932
|
-
const file =
|
|
4933
|
-
if (!
|
|
5491
|
+
const file = path12.join(root, entry.name, "SKILL.md");
|
|
5492
|
+
if (!fs12.existsSync(file)) {
|
|
4934
5493
|
continue;
|
|
4935
5494
|
}
|
|
4936
5495
|
out.total += 1;
|
|
4937
|
-
const text =
|
|
5496
|
+
const text = fs12.readFileSync(file, "utf8");
|
|
4938
5497
|
const fmEnd = text.startsWith("---") ? text.indexOf("\n---", 3) : -1;
|
|
4939
5498
|
const fm = fmEnd === -1 ? "" : text.slice(0, fmEnd);
|
|
4940
5499
|
if (TRIGGER_KEYS.some((k) => new RegExp(`^${k}`, "m").test(fm))) {
|
|
4941
5500
|
out.withTriggerKey.push(entry.name);
|
|
4942
5501
|
}
|
|
4943
|
-
if (
|
|
5502
|
+
if (fs12.existsSync(path12.join(root, entry.name, "evals", "triggers.json"))) {
|
|
4944
5503
|
out.withTriggerCorpus.push(entry.name);
|
|
4945
5504
|
}
|
|
4946
5505
|
const body = fmEnd === -1 ? text : text.slice(fmEnd + 4);
|
|
@@ -4951,17 +5510,17 @@ function censusSkills(root) {
|
|
|
4951
5510
|
return out;
|
|
4952
5511
|
}
|
|
4953
5512
|
function defaultStore2(cwd) {
|
|
4954
|
-
return
|
|
5513
|
+
return path12.join(os4.homedir(), ".claude", "projects", projectStoreSlug(cwd));
|
|
4955
5514
|
}
|
|
4956
5515
|
function measureUsage(store, limit) {
|
|
4957
5516
|
const rep = { store, sessions: 0, assistantTurns: 0, invocations: 0, bySkill: {} };
|
|
4958
|
-
if (!
|
|
5517
|
+
if (!fs12.existsSync(store)) {
|
|
4959
5518
|
return rep;
|
|
4960
5519
|
}
|
|
4961
|
-
const files =
|
|
5520
|
+
const files = fs12.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => path12.join(store, f)).sort((a, b) => fs12.statSync(b).mtimeMs - fs12.statSync(a).mtimeMs).slice(0, limit);
|
|
4962
5521
|
rep.sessions = files.length;
|
|
4963
5522
|
for (const file of files) {
|
|
4964
|
-
for (const line of
|
|
5523
|
+
for (const line of fs12.readFileSync(file, "utf8").split("\n")) {
|
|
4965
5524
|
if (!line.trim()) {
|
|
4966
5525
|
continue;
|
|
4967
5526
|
}
|
|
@@ -5034,15 +5593,15 @@ function buildRecord(census, usage, today) {
|
|
|
5034
5593
|
assistant_turns: usage.reduce((a, u) => a + u.assistantTurns, 0),
|
|
5035
5594
|
invocations: usage.reduce((a, u) => a + u.invocations, 0),
|
|
5036
5595
|
distinct_skills_invoked: distinct.size,
|
|
5037
|
-
stores: usage.map((u) =>
|
|
5596
|
+
stores: usage.map((u) => path12.basename(u.store))
|
|
5038
5597
|
};
|
|
5039
5598
|
}
|
|
5040
5599
|
function readRecord(repoRoot) {
|
|
5041
|
-
const file =
|
|
5042
|
-
if (!
|
|
5600
|
+
const file = path12.join(repoRoot, RECORD_PATH);
|
|
5601
|
+
if (!fs12.existsSync(file)) {
|
|
5043
5602
|
return null;
|
|
5044
5603
|
}
|
|
5045
|
-
return JSON.parse(
|
|
5604
|
+
return JSON.parse(fs12.readFileSync(file, "utf8"));
|
|
5046
5605
|
}
|
|
5047
5606
|
function render3(census, usage, published = null) {
|
|
5048
5607
|
const lines = [];
|
|
@@ -5077,7 +5636,7 @@ function render3(census, usage, published = null) {
|
|
|
5077
5636
|
}
|
|
5078
5637
|
totalInv += u.invocations;
|
|
5079
5638
|
lines.push(
|
|
5080
|
-
` ${
|
|
5639
|
+
` ${path12.basename(u.store).slice(-38).padEnd(38)} sessions=${String(u.sessions).padStart(3)} asst=${String(u.assistantTurns).padStart(6)} Skill calls=${String(u.invocations).padStart(3)}`
|
|
5081
5640
|
);
|
|
5082
5641
|
}
|
|
5083
5642
|
lines.push("");
|
|
@@ -5147,8 +5706,8 @@ function render3(census, usage, published = null) {
|
|
|
5147
5706
|
lines.push(" catalogue once per session, then count descriptions against bare names.");
|
|
5148
5707
|
return lines.join("\n");
|
|
5149
5708
|
}
|
|
5150
|
-
var
|
|
5151
|
-
function
|
|
5709
|
+
var USAGE2 = "usage: report_skill_activation [--root DIR] [--limit N] [--store PATH]... [--emit] [--allow-empty-store]\n";
|
|
5710
|
+
function main11(argv) {
|
|
5152
5711
|
let limit = 30;
|
|
5153
5712
|
let emit = false;
|
|
5154
5713
|
let allowEmpty = false;
|
|
@@ -5170,7 +5729,7 @@ function main10(argv) {
|
|
|
5170
5729
|
} else if (a === "--allow-empty-store") {
|
|
5171
5730
|
allowEmpty = true;
|
|
5172
5731
|
} else if (a === "--help" || a === "-h") {
|
|
5173
|
-
process.stdout.write(
|
|
5732
|
+
process.stdout.write(USAGE2);
|
|
5174
5733
|
return 0;
|
|
5175
5734
|
}
|
|
5176
5735
|
}
|
|
@@ -5181,7 +5740,7 @@ function main10(argv) {
|
|
|
5181
5740
|
if (stores.length === 0) {
|
|
5182
5741
|
stores.push(defaultStore2(REPO_ROOT3));
|
|
5183
5742
|
}
|
|
5184
|
-
const census = censusSkills(
|
|
5743
|
+
const census = censusSkills(path12.join(root, SKILLS_ROOT));
|
|
5185
5744
|
const usage = stores.map((s) => measureUsage(s, limit));
|
|
5186
5745
|
process.stdout.write(`${render3(census, usage, readRecord(root))}
|
|
5187
5746
|
`);
|
|
@@ -5202,27 +5761,27 @@ function main10(argv) {
|
|
|
5202
5761
|
return 1;
|
|
5203
5762
|
}
|
|
5204
5763
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5205
|
-
const file =
|
|
5206
|
-
|
|
5207
|
-
|
|
5764
|
+
const file = path12.join(root, RECORD_PATH);
|
|
5765
|
+
fs12.mkdirSync(path12.dirname(file), { recursive: true });
|
|
5766
|
+
fs12.writeFileSync(file, `${JSON.stringify(buildRecord(census, usage, today), null, 2)}
|
|
5208
5767
|
`);
|
|
5209
5768
|
process.stdout.write(`
|
|
5210
5769
|
wrote ${RECORD_PATH} (measured ${today})
|
|
5211
5770
|
`);
|
|
5212
5771
|
return 0;
|
|
5213
5772
|
}
|
|
5214
|
-
if (process.argv[1] !== void 0 && import.meta.url ===
|
|
5215
|
-
process.exit(
|
|
5773
|
+
if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL11(path12.resolve(process.argv[1])).href) {
|
|
5774
|
+
process.exit(main11(process.argv.slice(2)));
|
|
5216
5775
|
}
|
|
5217
5776
|
|
|
5218
5777
|
// src/scripts/report_skill_obligation_violations.ts
|
|
5219
|
-
import * as
|
|
5778
|
+
import * as fs13 from "node:fs";
|
|
5220
5779
|
import * as os5 from "node:os";
|
|
5221
|
-
import * as
|
|
5780
|
+
import * as path13 from "node:path";
|
|
5222
5781
|
import process3 from "node:process";
|
|
5223
|
-
import { fileURLToPath as
|
|
5224
|
-
var
|
|
5225
|
-
var REPO_ROOT4 =
|
|
5782
|
+
import { fileURLToPath as fileURLToPath12, pathToFileURL as pathToFileURL12 } from "node:url";
|
|
5783
|
+
var _HERE4 = fileURLToPath12(import.meta.url);
|
|
5784
|
+
var REPO_ROOT4 = path13.resolve(path13.dirname(_HERE4), "..", "..");
|
|
5226
5785
|
var OBLIGATION_LINE_RE = new RegExp(`${DETERMINISTIC_RE.source}.*`, "gm");
|
|
5227
5786
|
var PATH_RE = /`([A-Za-z0-9_./-]*\/[A-Za-z0-9_.-]+\.[a-z]{2,6})`/g;
|
|
5228
5787
|
var CMD_RE = /`([a-z][a-z0-9-]*(?: [a-z][a-z0-9:._-]*){1,3})`/g;
|
|
@@ -5260,7 +5819,7 @@ function _artefacts(line) {
|
|
|
5260
5819
|
return out;
|
|
5261
5820
|
}
|
|
5262
5821
|
function extractObligations(repoRoot) {
|
|
5263
|
-
const skillsRoot =
|
|
5822
|
+
const skillsRoot = path13.join(repoRoot, SKILLS_ROOT);
|
|
5264
5823
|
const census = censusSkills(skillsRoot);
|
|
5265
5824
|
const out = {
|
|
5266
5825
|
skills: census.withDeterministicObligation,
|
|
@@ -5274,7 +5833,7 @@ function extractObligations(repoRoot) {
|
|
|
5274
5833
|
for (const skill of census.withDeterministicObligation) {
|
|
5275
5834
|
let text;
|
|
5276
5835
|
try {
|
|
5277
|
-
text =
|
|
5836
|
+
text = fs13.readFileSync(path13.join(skillsRoot, skill, "SKILL.md"), "utf-8");
|
|
5278
5837
|
} catch {
|
|
5279
5838
|
continue;
|
|
5280
5839
|
}
|
|
@@ -5390,7 +5949,7 @@ function scanSessionForViolations(session, lines, forbidden, parsed) {
|
|
|
5390
5949
|
return flags;
|
|
5391
5950
|
}
|
|
5392
5951
|
function defaultStore3(cwd) {
|
|
5393
|
-
return
|
|
5952
|
+
return path13.join(os5.homedir(), ".claude", "projects", projectStoreSlug(cwd));
|
|
5394
5953
|
}
|
|
5395
5954
|
function scanStore2(repoRoot, store, limit) {
|
|
5396
5955
|
const census = extractObligations(repoRoot);
|
|
@@ -5403,14 +5962,14 @@ function scanStore2(repoRoot, store, limit) {
|
|
|
5403
5962
|
};
|
|
5404
5963
|
let files;
|
|
5405
5964
|
try {
|
|
5406
|
-
files =
|
|
5965
|
+
files = fs13.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ p: path13.join(store, f), m: fs13.statSync(path13.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.p);
|
|
5407
5966
|
} catch {
|
|
5408
5967
|
return report;
|
|
5409
5968
|
}
|
|
5410
5969
|
report.sessions = files.length;
|
|
5411
5970
|
for (const file of files) {
|
|
5412
|
-
const lines =
|
|
5413
|
-
const id =
|
|
5971
|
+
const lines = fs13.readFileSync(file, "utf-8").split("\n");
|
|
5972
|
+
const id = path13.basename(file).replace(/\.jsonl$/, "").slice(0, 8);
|
|
5414
5973
|
const entries = _parseSession(lines);
|
|
5415
5974
|
if (loadedSkillsFrom(entries).size > 0) report.sessionsWithASkill += 1;
|
|
5416
5975
|
report.flags.push(...scanSessionForViolations(id, lines, census.forbidden, entries));
|
|
@@ -5480,7 +6039,7 @@ function render4(r) {
|
|
|
5480
6039
|
lines.push(" ships as detection-only, and this one says so.");
|
|
5481
6040
|
return lines.join("\n");
|
|
5482
6041
|
}
|
|
5483
|
-
function
|
|
6042
|
+
function main12(argv) {
|
|
5484
6043
|
const args = argv ?? process3.argv.slice(2);
|
|
5485
6044
|
let limit = 30;
|
|
5486
6045
|
let json = false;
|
|
@@ -5533,35 +6092,35 @@ function main11(argv) {
|
|
|
5533
6092
|
}
|
|
5534
6093
|
return 0;
|
|
5535
6094
|
}
|
|
5536
|
-
function
|
|
6095
|
+
function _isCliEntry11() {
|
|
5537
6096
|
if (process3.argv[1] === void 0) return false;
|
|
5538
|
-
if (import.meta.url ===
|
|
6097
|
+
if (import.meta.url === pathToFileURL12(path13.resolve(process3.argv[1])).href) return true;
|
|
5539
6098
|
try {
|
|
5540
|
-
return
|
|
6099
|
+
return fs13.realpathSync(_HERE4) === fs13.realpathSync(path13.resolve(process3.argv[1]));
|
|
5541
6100
|
} catch {
|
|
5542
6101
|
return false;
|
|
5543
6102
|
}
|
|
5544
6103
|
}
|
|
5545
|
-
if (
|
|
5546
|
-
process3.exit(
|
|
6104
|
+
if (_isCliEntry11()) {
|
|
6105
|
+
process3.exit(main12());
|
|
5547
6106
|
}
|
|
5548
6107
|
|
|
5549
6108
|
// src/scripts/report_conformance_funnel.ts
|
|
5550
|
-
var
|
|
5551
|
-
var REPO_ROOT5 =
|
|
6109
|
+
var _HERE5 = fileURLToPath13(import.meta.url);
|
|
6110
|
+
var REPO_ROOT5 = path14.resolve(path14.dirname(_HERE5), "..", "..");
|
|
5552
6111
|
function buildFunnel(opts = {}) {
|
|
5553
6112
|
const repoRoot = opts.repoRoot ?? REPO_ROOT5;
|
|
5554
|
-
const projectRulesDir = opts.projectRulesDir ??
|
|
5555
|
-
const globalRulesDir = opts.globalRulesDir ??
|
|
5556
|
-
const sourceDir = opts.sourceDir ??
|
|
6113
|
+
const projectRulesDir = opts.projectRulesDir ?? path14.join(repoRoot, PROJECT_RULES);
|
|
6114
|
+
const globalRulesDir = opts.globalRulesDir ?? path14.join(os6.homedir(), GLOBAL_RULES);
|
|
6115
|
+
const sourceDir = opts.sourceDir ?? path14.join(repoRoot, PROJECTION_SOURCE);
|
|
5557
6116
|
const store = opts.store ?? defaultStore(repoRoot);
|
|
5558
6117
|
const limit = opts.limit ?? 30;
|
|
5559
6118
|
return {
|
|
5560
6119
|
delivered: measureDelivered(projectRulesDir, globalRulesDir),
|
|
5561
6120
|
divergence: compareCarriers(projectRulesDir, globalRulesDir, sourceDir),
|
|
5562
|
-
skills: censusSkills(
|
|
6121
|
+
skills: censusSkills(path14.join(repoRoot, SKILLS_ROOT)),
|
|
5563
6122
|
usage: measureUsage(store, limit),
|
|
5564
|
-
storePresent:
|
|
6123
|
+
storePresent: fs14.existsSync(store),
|
|
5565
6124
|
sk2: scanStore2(repoRoot, store, limit)
|
|
5566
6125
|
};
|
|
5567
6126
|
}
|
|
@@ -5686,7 +6245,7 @@ function render5(r) {
|
|
|
5686
6245
|
lines.push("workflow without a measured false-positive rate first (locked policy).");
|
|
5687
6246
|
return lines.join("\n");
|
|
5688
6247
|
}
|
|
5689
|
-
function
|
|
6248
|
+
function main13(argv) {
|
|
5690
6249
|
const args = argv ?? process4.argv.slice(2);
|
|
5691
6250
|
const opts = {};
|
|
5692
6251
|
const value = (i) => {
|
|
@@ -5702,7 +6261,7 @@ function main12(argv) {
|
|
|
5702
6261
|
`);
|
|
5703
6262
|
return 1;
|
|
5704
6263
|
}
|
|
5705
|
-
const resolved =
|
|
6264
|
+
const resolved = path14.resolve(v);
|
|
5706
6265
|
if (a === "--store") opts.store = resolved;
|
|
5707
6266
|
else if (a === "--project") opts.projectRulesDir = resolved;
|
|
5708
6267
|
else if (a === "--global") opts.globalRulesDir = resolved;
|
|
@@ -5732,21 +6291,21 @@ function main12(argv) {
|
|
|
5732
6291
|
`);
|
|
5733
6292
|
return 0;
|
|
5734
6293
|
}
|
|
5735
|
-
function
|
|
6294
|
+
function _isCliEntry12() {
|
|
5736
6295
|
if (process4.argv[1] === void 0) return false;
|
|
5737
|
-
if (import.meta.url ===
|
|
6296
|
+
if (import.meta.url === pathToFileURL13(path14.resolve(process4.argv[1])).href) return true;
|
|
5738
6297
|
try {
|
|
5739
|
-
return
|
|
6298
|
+
return fs14.realpathSync(_HERE5) === fs14.realpathSync(path14.resolve(process4.argv[1]));
|
|
5740
6299
|
} catch {
|
|
5741
6300
|
return false;
|
|
5742
6301
|
}
|
|
5743
6302
|
}
|
|
5744
|
-
if (
|
|
5745
|
-
process4.exit(
|
|
6303
|
+
if (_isCliEntry12()) {
|
|
6304
|
+
process4.exit(main13());
|
|
5746
6305
|
}
|
|
5747
6306
|
|
|
5748
6307
|
// src/scripts/_cli/cmd_conformance.ts
|
|
5749
|
-
var
|
|
6308
|
+
var _HERE6 = fileURLToPath14(import.meta.url);
|
|
5750
6309
|
var PACKAGE_ROOT = resolvePackageRoot(import.meta.url);
|
|
5751
6310
|
var CONFORMANCE_CHECK_IDS = [
|
|
5752
6311
|
"txlog-clean",
|
|
@@ -5769,17 +6328,17 @@ function conformanceLogPath() {
|
|
|
5769
6328
|
if (override && override.trim().length > 0) {
|
|
5770
6329
|
return override;
|
|
5771
6330
|
}
|
|
5772
|
-
return
|
|
6331
|
+
return path15.join(os7.homedir(), ".event4u", "agent-config", "conformance-log.jsonl");
|
|
5773
6332
|
}
|
|
5774
6333
|
function installLogPath() {
|
|
5775
6334
|
const override = process5.env["AGENT_CONFIG_INSTALL_LOG"];
|
|
5776
6335
|
if (override && override.trim().length > 0) {
|
|
5777
6336
|
return override;
|
|
5778
6337
|
}
|
|
5779
|
-
return
|
|
6338
|
+
return path15.join(os7.homedir(), ".event4u", "agent-config", "install-log.jsonl");
|
|
5780
6339
|
}
|
|
5781
6340
|
function _check_txlog_clean(logPath = installLogPath()) {
|
|
5782
|
-
if (!
|
|
6341
|
+
if (!fs15.existsSync(logPath)) {
|
|
5783
6342
|
return {
|
|
5784
6343
|
id: "txlog-clean",
|
|
5785
6344
|
status: "ok",
|
|
@@ -5813,11 +6372,11 @@ function _check_txlog_clean(logPath = installLogPath()) {
|
|
|
5813
6372
|
};
|
|
5814
6373
|
}
|
|
5815
6374
|
function routeTargetPaths(target) {
|
|
5816
|
-
return routeTargetPathsPosix(target).map((rel) =>
|
|
6375
|
+
return routeTargetPathsPosix(target).map((rel) => path15.join(...rel.split("/")));
|
|
5817
6376
|
}
|
|
5818
6377
|
function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
5819
|
-
const routerPath =
|
|
5820
|
-
if (!
|
|
6378
|
+
const routerPath = path15.join(packageRoot, "dist", "router.json");
|
|
6379
|
+
if (!fs15.existsSync(routerPath)) {
|
|
5821
6380
|
return {
|
|
5822
6381
|
id: "router-pointers",
|
|
5823
6382
|
status: "fail",
|
|
@@ -5827,7 +6386,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5827
6386
|
}
|
|
5828
6387
|
let router;
|
|
5829
6388
|
try {
|
|
5830
|
-
router = JSON.parse(
|
|
6389
|
+
router = JSON.parse(fs15.readFileSync(routerPath, "utf8"));
|
|
5831
6390
|
} catch (exc) {
|
|
5832
6391
|
return {
|
|
5833
6392
|
id: "router-pointers",
|
|
@@ -5836,13 +6395,13 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5836
6395
|
remedy: "reinstall the package \u2014 dist/router.json is corrupt"
|
|
5837
6396
|
};
|
|
5838
6397
|
}
|
|
5839
|
-
const rulesDir =
|
|
6398
|
+
const rulesDir = path15.join(packageRoot, "dist", "agent-src", "rules");
|
|
5840
6399
|
const broken = [];
|
|
5841
6400
|
let ids = 0;
|
|
5842
6401
|
let targets = 0;
|
|
5843
6402
|
for (const kernelId of router["kernel"] ?? []) {
|
|
5844
6403
|
ids += 1;
|
|
5845
|
-
if (!
|
|
6404
|
+
if (!fs15.existsSync(path15.join(rulesDir, `${kernelId}.md`))) {
|
|
5846
6405
|
broken.push(`kernel:${kernelId} \u2192 rules/${kernelId}.md missing`);
|
|
5847
6406
|
}
|
|
5848
6407
|
}
|
|
@@ -5850,7 +6409,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5850
6409
|
for (const entry of router[tier] ?? []) {
|
|
5851
6410
|
const ruleId = String(entry["id"] ?? "");
|
|
5852
6411
|
ids += 1;
|
|
5853
|
-
if (!
|
|
6412
|
+
if (!fs15.existsSync(path15.join(rulesDir, `${ruleId}.md`))) {
|
|
5854
6413
|
broken.push(`${tier}:${ruleId} \u2192 rules/${ruleId}.md missing`);
|
|
5855
6414
|
}
|
|
5856
6415
|
for (const target of entry["routes_to"] ?? []) {
|
|
@@ -5860,7 +6419,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5860
6419
|
broken.push(`${ruleId} \u2192 unparseable routes_to ${JSON.stringify(target)}`);
|
|
5861
6420
|
continue;
|
|
5862
6421
|
}
|
|
5863
|
-
if (!candidates.some((rel) =>
|
|
6422
|
+
if (!candidates.some((rel) => fs15.existsSync(path15.join(packageRoot, rel)))) {
|
|
5864
6423
|
broken.push(`${ruleId} \u2192 ${target} (${candidates[0]} missing)`);
|
|
5865
6424
|
}
|
|
5866
6425
|
}
|
|
@@ -5892,9 +6451,9 @@ function detectSmokePlatform(projectRoot) {
|
|
|
5892
6451
|
return "claude";
|
|
5893
6452
|
}
|
|
5894
6453
|
function dispatcherRunner(packageRoot) {
|
|
5895
|
-
const localTsx =
|
|
5896
|
-
const script =
|
|
5897
|
-
if (
|
|
6454
|
+
const localTsx = path15.join(packageRoot, "node_modules", ".bin", "tsx");
|
|
6455
|
+
const script = path15.join(packageRoot, "src", "scripts", "hooks", "dispatch_hook.ts");
|
|
6456
|
+
if (fs15.existsSync(localTsx)) {
|
|
5898
6457
|
return [localTsx, script];
|
|
5899
6458
|
}
|
|
5900
6459
|
return ["npx", "tsx", script];
|
|
@@ -5911,7 +6470,7 @@ function _check_hook_dispatcher(projectRoot, packageRoot = PACKAGE_ROOT, opts =
|
|
|
5911
6470
|
...event === "stop" ? { stop_reason: "conformance-smoke" } : { source: "startup" }
|
|
5912
6471
|
});
|
|
5913
6472
|
const argv = [...runner, "--platform", platform, "--event", event];
|
|
5914
|
-
const proc =
|
|
6473
|
+
const proc = spawnSync3(argv[0], argv.slice(1), {
|
|
5915
6474
|
input: payload,
|
|
5916
6475
|
cwd: projectRoot,
|
|
5917
6476
|
encoding: "utf8",
|
|
@@ -5944,12 +6503,12 @@ function _check_hook_dispatcher(projectRoot, packageRoot = PACKAGE_ROOT, opts =
|
|
|
5944
6503
|
};
|
|
5945
6504
|
}
|
|
5946
6505
|
function readSettingsMode(projectRoot) {
|
|
5947
|
-
const settingsPath =
|
|
5948
|
-
if (!
|
|
6506
|
+
const settingsPath = path15.join(projectRoot, ".agent-settings.yml");
|
|
6507
|
+
if (!fs15.existsSync(settingsPath)) {
|
|
5949
6508
|
return "eager-all";
|
|
5950
6509
|
}
|
|
5951
6510
|
try {
|
|
5952
|
-
const parsed = YAML.parse(
|
|
6511
|
+
const parsed = YAML.parse(fs15.readFileSync(settingsPath, "utf8"));
|
|
5953
6512
|
const lean = parsed?.["lean_projection"] ?? null;
|
|
5954
6513
|
const mode = lean?.["mode"];
|
|
5955
6514
|
return typeof mode === "string" && mode.length > 0 ? mode : "eager-all";
|
|
@@ -5960,7 +6519,7 @@ function readSettingsMode(projectRoot) {
|
|
|
5960
6519
|
function kernelIdsFromRouter(packageRoot) {
|
|
5961
6520
|
try {
|
|
5962
6521
|
const router = JSON.parse(
|
|
5963
|
-
|
|
6522
|
+
fs15.readFileSync(path15.join(packageRoot, "dist", "router.json"), "utf8")
|
|
5964
6523
|
);
|
|
5965
6524
|
return new Set((router["kernel"] ?? []).map(String));
|
|
5966
6525
|
} catch {
|
|
@@ -5977,8 +6536,8 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
5977
6536
|
remedy: "set lean_projection.mode to 'eager-all' or 'thin' in .agent-settings.yml"
|
|
5978
6537
|
};
|
|
5979
6538
|
}
|
|
5980
|
-
const rulesDir = projectedRulesDir ??
|
|
5981
|
-
if (!
|
|
6539
|
+
const rulesDir = projectedRulesDir ?? path15.join(projectRoot, ".augment", "rules");
|
|
6540
|
+
if (!fs15.existsSync(rulesDir)) {
|
|
5982
6541
|
return {
|
|
5983
6542
|
id: "lean-projection",
|
|
5984
6543
|
status: "skipped",
|
|
@@ -5987,7 +6546,7 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
5987
6546
|
};
|
|
5988
6547
|
}
|
|
5989
6548
|
const kernel = kernelIdsFromRouter(packageRoot);
|
|
5990
|
-
const files =
|
|
6549
|
+
const files = fs15.readdirSync(rulesDir).filter((f) => f.endsWith(".md") && f !== "INDEX.md").sort();
|
|
5991
6550
|
const nonKernel = files.filter((f) => !kernel.has(f.replace(/\.md$/, "")));
|
|
5992
6551
|
if (nonKernel.length === 0) {
|
|
5993
6552
|
return {
|
|
@@ -6001,7 +6560,7 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
6001
6560
|
for (const f of nonKernel) {
|
|
6002
6561
|
let text = "";
|
|
6003
6562
|
try {
|
|
6004
|
-
text =
|
|
6563
|
+
text = fs15.readFileSync(path15.join(rulesDir, f), "utf8");
|
|
6005
6564
|
} catch {
|
|
6006
6565
|
continue;
|
|
6007
6566
|
}
|
|
@@ -6032,8 +6591,8 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
6032
6591
|
function _check_host_manifest(projectRoot) {
|
|
6033
6592
|
const detected = Object.entries(detectToolPresence(projectRoot)).filter(([, v]) => v === true).map(([k]) => k);
|
|
6034
6593
|
const detectedNote = detected.length > 0 ? `detected surfaces: ${detected.join(", ")}` : "no surfaces detected";
|
|
6035
|
-
const settingsPath =
|
|
6036
|
-
if (!
|
|
6594
|
+
const settingsPath = path15.join(projectRoot, ".agent-settings.yml");
|
|
6595
|
+
if (!fs15.existsSync(settingsPath)) {
|
|
6037
6596
|
return {
|
|
6038
6597
|
id: "host-manifest",
|
|
6039
6598
|
status: "ok",
|
|
@@ -6043,7 +6602,7 @@ function _check_host_manifest(projectRoot) {
|
|
|
6043
6602
|
}
|
|
6044
6603
|
let parsed = null;
|
|
6045
6604
|
try {
|
|
6046
|
-
parsed = YAML.parse(
|
|
6605
|
+
parsed = YAML.parse(fs15.readFileSync(settingsPath, "utf8"));
|
|
6047
6606
|
} catch (exc) {
|
|
6048
6607
|
return {
|
|
6049
6608
|
id: "host-manifest",
|
|
@@ -6104,14 +6663,14 @@ function appendConformanceReport(checks, projectRoot, logPath = conformanceLogPa
|
|
|
6104
6663
|
} catch {
|
|
6105
6664
|
}
|
|
6106
6665
|
}
|
|
6107
|
-
var
|
|
6666
|
+
var USAGE3 = "usage: agent-config conformance [-h] [--project PROJECT] [--json]\n [--skip-dispatcher] [--funnel]\n";
|
|
6108
6667
|
function _parse(argv) {
|
|
6109
6668
|
const opts = { project: null, json: false, skip_dispatcher: false, funnel: false };
|
|
6110
6669
|
let i = 0;
|
|
6111
6670
|
while (i < argv.length) {
|
|
6112
6671
|
const a = argv[i];
|
|
6113
6672
|
if (a === "-h" || a === "--help") {
|
|
6114
|
-
process5.stdout.write(
|
|
6673
|
+
process5.stdout.write(USAGE3);
|
|
6115
6674
|
throw new ArgparseExit(0);
|
|
6116
6675
|
}
|
|
6117
6676
|
if (a === "--json") {
|
|
@@ -6125,7 +6684,7 @@ function _parse(argv) {
|
|
|
6125
6684
|
opts.project = a.slice(a.indexOf("=") + 1);
|
|
6126
6685
|
} else {
|
|
6127
6686
|
if (i + 1 >= argv.length) {
|
|
6128
|
-
process5.stderr.write(
|
|
6687
|
+
process5.stderr.write(USAGE3);
|
|
6129
6688
|
process5.stderr.write("agent-config conformance: error: --project expects a value\n");
|
|
6130
6689
|
throw new ArgparseExit(2);
|
|
6131
6690
|
}
|
|
@@ -6133,7 +6692,7 @@ function _parse(argv) {
|
|
|
6133
6692
|
i += 1;
|
|
6134
6693
|
}
|
|
6135
6694
|
} else {
|
|
6136
|
-
process5.stderr.write(
|
|
6695
|
+
process5.stderr.write(USAGE3);
|
|
6137
6696
|
process5.stderr.write(`agent-config conformance: error: unrecognized argument ${a}
|
|
6138
6697
|
`);
|
|
6139
6698
|
throw new ArgparseExit(2);
|
|
@@ -6142,7 +6701,7 @@ function _parse(argv) {
|
|
|
6142
6701
|
}
|
|
6143
6702
|
return opts;
|
|
6144
6703
|
}
|
|
6145
|
-
function
|
|
6704
|
+
function main14(argv = null) {
|
|
6146
6705
|
const opts = _parse(argv !== null ? Array.from(argv) : process5.argv.slice(2));
|
|
6147
6706
|
if (opts.funnel) {
|
|
6148
6707
|
print(render5(buildFunnel()));
|
|
@@ -6213,25 +6772,25 @@ function main13(argv = null) {
|
|
|
6213
6772
|
}
|
|
6214
6773
|
return failed ? 1 : 0;
|
|
6215
6774
|
}
|
|
6216
|
-
function
|
|
6775
|
+
function _isCliEntry13() {
|
|
6217
6776
|
if (process5.argv[1] === void 0) {
|
|
6218
6777
|
return false;
|
|
6219
6778
|
}
|
|
6220
|
-
const argvUrl =
|
|
6779
|
+
const argvUrl = pathToFileURL14(path15.resolve(process5.argv[1])).href;
|
|
6221
6780
|
if (import.meta.url === argvUrl) {
|
|
6222
6781
|
return true;
|
|
6223
6782
|
}
|
|
6224
6783
|
try {
|
|
6225
|
-
const here =
|
|
6226
|
-
const argv =
|
|
6784
|
+
const here = fs15.realpathSync(fileURLToPath14(import.meta.url));
|
|
6785
|
+
const argv = fs15.realpathSync(path15.resolve(process5.argv[1]));
|
|
6227
6786
|
return here === argv;
|
|
6228
6787
|
} catch {
|
|
6229
6788
|
return false;
|
|
6230
6789
|
}
|
|
6231
6790
|
}
|
|
6232
|
-
if (
|
|
6791
|
+
if (_isCliEntry13() || process5.argv[1] === _HERE6) {
|
|
6233
6792
|
try {
|
|
6234
|
-
process5.exitCode =
|
|
6793
|
+
process5.exitCode = main14(process5.argv.slice(2));
|
|
6235
6794
|
} catch (e) {
|
|
6236
6795
|
if (e instanceof ArgparseExit) {
|
|
6237
6796
|
process5.exitCode = e.code;
|
|
@@ -6254,7 +6813,7 @@ export {
|
|
|
6254
6813
|
conformanceLogPath,
|
|
6255
6814
|
detectSmokePlatform,
|
|
6256
6815
|
installLogPath,
|
|
6257
|
-
|
|
6816
|
+
main14 as main,
|
|
6258
6817
|
routeTargetPaths,
|
|
6259
6818
|
runConformanceChecks
|
|
6260
6819
|
};
|