@codacy/verity-cli 0.29.4-experimental.f2e812c → 0.30.0-experimental.43e7755
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +133 -0
- package/bin/verity.js +1283 -595
- package/data/skills/verity-setup/SKILL.md +21 -8
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -6974,10 +6974,10 @@ var require_resolve_block_map = __commonJS({
|
|
|
6974
6974
|
let offset = bm.offset;
|
|
6975
6975
|
let commentEnd = null;
|
|
6976
6976
|
for (const collItem of bm.items) {
|
|
6977
|
-
const { start, key, sep, value } = collItem;
|
|
6977
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
6978
6978
|
const keyProps = resolveProps.resolveProps(start, {
|
|
6979
6979
|
indicator: "explicit-key-ind",
|
|
6980
|
-
next: key ??
|
|
6980
|
+
next: key ?? sep2?.[0],
|
|
6981
6981
|
offset,
|
|
6982
6982
|
onError,
|
|
6983
6983
|
parentIndent: bm.indent,
|
|
@@ -6991,7 +6991,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
6991
6991
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
6992
6992
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
6993
6993
|
}
|
|
6994
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
6994
|
+
if (!keyProps.anchor && !keyProps.tag && !sep2) {
|
|
6995
6995
|
commentEnd = keyProps.end;
|
|
6996
6996
|
if (keyProps.comment) {
|
|
6997
6997
|
if (map.comment)
|
|
@@ -7015,7 +7015,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
7015
7015
|
ctx.atKey = false;
|
|
7016
7016
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
7017
7017
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
7018
|
-
const valueProps = resolveProps.resolveProps(
|
|
7018
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
7019
7019
|
indicator: "map-value-ind",
|
|
7020
7020
|
next: value,
|
|
7021
7021
|
offset: keyNode.range[2],
|
|
@@ -7031,7 +7031,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
7031
7031
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
7032
7032
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
7033
7033
|
}
|
|
7034
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
7034
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep2, null, valueProps, onError);
|
|
7035
7035
|
if (ctx.schema.compat)
|
|
7036
7036
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
7037
7037
|
offset = valueNode.range[2];
|
|
@@ -7122,7 +7122,7 @@ var require_resolve_end = __commonJS({
|
|
|
7122
7122
|
let comment = "";
|
|
7123
7123
|
if (end) {
|
|
7124
7124
|
let hasSpace = false;
|
|
7125
|
-
let
|
|
7125
|
+
let sep2 = "";
|
|
7126
7126
|
for (const token of end) {
|
|
7127
7127
|
const { source, type } = token;
|
|
7128
7128
|
switch (type) {
|
|
@@ -7136,13 +7136,13 @@ var require_resolve_end = __commonJS({
|
|
|
7136
7136
|
if (!comment)
|
|
7137
7137
|
comment = cb;
|
|
7138
7138
|
else
|
|
7139
|
-
comment +=
|
|
7140
|
-
|
|
7139
|
+
comment += sep2 + cb;
|
|
7140
|
+
sep2 = "";
|
|
7141
7141
|
break;
|
|
7142
7142
|
}
|
|
7143
7143
|
case "newline":
|
|
7144
7144
|
if (comment)
|
|
7145
|
-
|
|
7145
|
+
sep2 += source;
|
|
7146
7146
|
hasSpace = true;
|
|
7147
7147
|
break;
|
|
7148
7148
|
default:
|
|
@@ -7185,18 +7185,18 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7185
7185
|
let offset = fc.offset + fc.start.source.length;
|
|
7186
7186
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
7187
7187
|
const collItem = fc.items[i];
|
|
7188
|
-
const { start, key, sep, value } = collItem;
|
|
7188
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
7189
7189
|
const props = resolveProps.resolveProps(start, {
|
|
7190
7190
|
flow: fcName,
|
|
7191
7191
|
indicator: "explicit-key-ind",
|
|
7192
|
-
next: key ??
|
|
7192
|
+
next: key ?? sep2?.[0],
|
|
7193
7193
|
offset,
|
|
7194
7194
|
onError,
|
|
7195
7195
|
parentIndent: fc.indent,
|
|
7196
7196
|
startOnNewline: false
|
|
7197
7197
|
});
|
|
7198
7198
|
if (!props.found) {
|
|
7199
|
-
if (!props.anchor && !props.tag && !
|
|
7199
|
+
if (!props.anchor && !props.tag && !sep2 && !value) {
|
|
7200
7200
|
if (i === 0 && props.comma)
|
|
7201
7201
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
7202
7202
|
else if (i < fc.items.length - 1)
|
|
@@ -7250,8 +7250,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7250
7250
|
}
|
|
7251
7251
|
}
|
|
7252
7252
|
}
|
|
7253
|
-
if (!isMap && !
|
|
7254
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
7253
|
+
if (!isMap && !sep2 && !props.found) {
|
|
7254
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
|
|
7255
7255
|
coll.items.push(valueNode);
|
|
7256
7256
|
offset = valueNode.range[2];
|
|
7257
7257
|
if (isBlock(value))
|
|
@@ -7263,7 +7263,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7263
7263
|
if (isBlock(key))
|
|
7264
7264
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
7265
7265
|
ctx.atKey = false;
|
|
7266
|
-
const valueProps = resolveProps.resolveProps(
|
|
7266
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
7267
7267
|
flow: fcName,
|
|
7268
7268
|
indicator: "map-value-ind",
|
|
7269
7269
|
next: value,
|
|
@@ -7274,8 +7274,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7274
7274
|
});
|
|
7275
7275
|
if (valueProps.found) {
|
|
7276
7276
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
7277
|
-
if (
|
|
7278
|
-
for (const st of
|
|
7277
|
+
if (sep2)
|
|
7278
|
+
for (const st of sep2) {
|
|
7279
7279
|
if (st === valueProps.found)
|
|
7280
7280
|
break;
|
|
7281
7281
|
if (st.type === "newline") {
|
|
@@ -7292,7 +7292,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7292
7292
|
else
|
|
7293
7293
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
7294
7294
|
}
|
|
7295
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
7295
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep2, null, valueProps, onError) : null;
|
|
7296
7296
|
if (valueNode) {
|
|
7297
7297
|
if (isBlock(value))
|
|
7298
7298
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -7472,7 +7472,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
7472
7472
|
chompStart = i + 1;
|
|
7473
7473
|
}
|
|
7474
7474
|
let value = "";
|
|
7475
|
-
let
|
|
7475
|
+
let sep2 = "";
|
|
7476
7476
|
let prevMoreIndented = false;
|
|
7477
7477
|
for (let i = 0; i < contentStart; ++i)
|
|
7478
7478
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -7489,24 +7489,24 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
7489
7489
|
indent = "";
|
|
7490
7490
|
}
|
|
7491
7491
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
7492
|
-
value +=
|
|
7493
|
-
|
|
7492
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
7493
|
+
sep2 = "\n";
|
|
7494
7494
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
7495
|
-
if (
|
|
7496
|
-
|
|
7497
|
-
else if (!prevMoreIndented &&
|
|
7498
|
-
|
|
7499
|
-
value +=
|
|
7500
|
-
|
|
7495
|
+
if (sep2 === " ")
|
|
7496
|
+
sep2 = "\n";
|
|
7497
|
+
else if (!prevMoreIndented && sep2 === "\n")
|
|
7498
|
+
sep2 = "\n\n";
|
|
7499
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
7500
|
+
sep2 = "\n";
|
|
7501
7501
|
prevMoreIndented = true;
|
|
7502
7502
|
} else if (content === "") {
|
|
7503
|
-
if (
|
|
7503
|
+
if (sep2 === "\n")
|
|
7504
7504
|
value += "\n";
|
|
7505
7505
|
else
|
|
7506
|
-
|
|
7506
|
+
sep2 = "\n";
|
|
7507
7507
|
} else {
|
|
7508
|
-
value +=
|
|
7509
|
-
|
|
7508
|
+
value += sep2 + content;
|
|
7509
|
+
sep2 = " ";
|
|
7510
7510
|
prevMoreIndented = false;
|
|
7511
7511
|
}
|
|
7512
7512
|
}
|
|
@@ -7684,25 +7684,25 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
7684
7684
|
if (!match)
|
|
7685
7685
|
return source;
|
|
7686
7686
|
let res = match[1];
|
|
7687
|
-
let
|
|
7687
|
+
let sep2 = " ";
|
|
7688
7688
|
let pos = first.lastIndex;
|
|
7689
7689
|
line.lastIndex = pos;
|
|
7690
7690
|
while (match = line.exec(source)) {
|
|
7691
7691
|
if (match[1] === "") {
|
|
7692
|
-
if (
|
|
7693
|
-
res +=
|
|
7692
|
+
if (sep2 === "\n")
|
|
7693
|
+
res += sep2;
|
|
7694
7694
|
else
|
|
7695
|
-
|
|
7695
|
+
sep2 = "\n";
|
|
7696
7696
|
} else {
|
|
7697
|
-
res +=
|
|
7698
|
-
|
|
7697
|
+
res += sep2 + match[1];
|
|
7698
|
+
sep2 = " ";
|
|
7699
7699
|
}
|
|
7700
7700
|
pos = line.lastIndex;
|
|
7701
7701
|
}
|
|
7702
7702
|
const last = /[ \t]*(.*)/sy;
|
|
7703
7703
|
last.lastIndex = pos;
|
|
7704
7704
|
match = last.exec(source);
|
|
7705
|
-
return res +
|
|
7705
|
+
return res + sep2 + (match?.[1] ?? "");
|
|
7706
7706
|
}
|
|
7707
7707
|
function doubleQuotedValue(source, onError) {
|
|
7708
7708
|
let res = "";
|
|
@@ -8509,14 +8509,14 @@ var require_cst_stringify = __commonJS({
|
|
|
8509
8509
|
}
|
|
8510
8510
|
}
|
|
8511
8511
|
}
|
|
8512
|
-
function stringifyItem({ start, key, sep, value }) {
|
|
8512
|
+
function stringifyItem({ start, key, sep: sep2, value }) {
|
|
8513
8513
|
let res = "";
|
|
8514
8514
|
for (const st of start)
|
|
8515
8515
|
res += st.source;
|
|
8516
8516
|
if (key)
|
|
8517
8517
|
res += stringifyToken(key);
|
|
8518
|
-
if (
|
|
8519
|
-
for (const st of
|
|
8518
|
+
if (sep2)
|
|
8519
|
+
for (const st of sep2)
|
|
8520
8520
|
res += st.source;
|
|
8521
8521
|
if (value)
|
|
8522
8522
|
res += stringifyToken(value);
|
|
@@ -9659,18 +9659,18 @@ var require_parser = __commonJS({
|
|
|
9659
9659
|
if (this.type === "map-value-ind") {
|
|
9660
9660
|
const prev = getPrevProps(this.peek(2));
|
|
9661
9661
|
const start = getFirstKeyStartProps(prev);
|
|
9662
|
-
let
|
|
9662
|
+
let sep2;
|
|
9663
9663
|
if (scalar.end) {
|
|
9664
|
-
|
|
9665
|
-
|
|
9664
|
+
sep2 = scalar.end;
|
|
9665
|
+
sep2.push(this.sourceToken);
|
|
9666
9666
|
delete scalar.end;
|
|
9667
9667
|
} else
|
|
9668
|
-
|
|
9668
|
+
sep2 = [this.sourceToken];
|
|
9669
9669
|
const map = {
|
|
9670
9670
|
type: "block-map",
|
|
9671
9671
|
offset: scalar.offset,
|
|
9672
9672
|
indent: scalar.indent,
|
|
9673
|
-
items: [{ start, key: scalar, sep }]
|
|
9673
|
+
items: [{ start, key: scalar, sep: sep2 }]
|
|
9674
9674
|
};
|
|
9675
9675
|
this.onKeyLine = true;
|
|
9676
9676
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -9822,15 +9822,15 @@ var require_parser = __commonJS({
|
|
|
9822
9822
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
9823
9823
|
const start2 = getFirstKeyStartProps(it.start);
|
|
9824
9824
|
const key = it.key;
|
|
9825
|
-
const
|
|
9826
|
-
|
|
9825
|
+
const sep2 = it.sep;
|
|
9826
|
+
sep2.push(this.sourceToken);
|
|
9827
9827
|
delete it.key;
|
|
9828
9828
|
delete it.sep;
|
|
9829
9829
|
this.stack.push({
|
|
9830
9830
|
type: "block-map",
|
|
9831
9831
|
offset: this.offset,
|
|
9832
9832
|
indent: this.indent,
|
|
9833
|
-
items: [{ start: start2, key, sep }]
|
|
9833
|
+
items: [{ start: start2, key, sep: sep2 }]
|
|
9834
9834
|
});
|
|
9835
9835
|
} else if (start.length > 0) {
|
|
9836
9836
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -10024,13 +10024,13 @@ var require_parser = __commonJS({
|
|
|
10024
10024
|
const prev = getPrevProps(parent);
|
|
10025
10025
|
const start = getFirstKeyStartProps(prev);
|
|
10026
10026
|
fixFlowSeqItems(fc);
|
|
10027
|
-
const
|
|
10028
|
-
|
|
10027
|
+
const sep2 = fc.end.splice(1, fc.end.length);
|
|
10028
|
+
sep2.push(this.sourceToken);
|
|
10029
10029
|
const map = {
|
|
10030
10030
|
type: "block-map",
|
|
10031
10031
|
offset: fc.offset,
|
|
10032
10032
|
indent: fc.indent,
|
|
10033
|
-
items: [{ start, key: fc, sep }]
|
|
10033
|
+
items: [{ start, key: fc, sep: sep2 }]
|
|
10034
10034
|
};
|
|
10035
10035
|
this.onKeyLine = true;
|
|
10036
10036
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -10342,6 +10342,10 @@ function repoRoot() {
|
|
|
10342
10342
|
}
|
|
10343
10343
|
return _repoRoot;
|
|
10344
10344
|
}
|
|
10345
|
+
function _resetRepoRoot() {
|
|
10346
|
+
_repoRoot = null;
|
|
10347
|
+
_mainRoot = void 0;
|
|
10348
|
+
}
|
|
10345
10349
|
var _mainRoot;
|
|
10346
10350
|
function mainWorktreeRoot() {
|
|
10347
10351
|
if (_mainRoot !== void 0) return _mainRoot;
|
|
@@ -10502,6 +10506,7 @@ var GITHUB_APP_INSTALL_URL = `https://github.com/apps/${GITHUB_APP_SLUG}/install
|
|
|
10502
10506
|
function githubAppInstallUrl(accountId) {
|
|
10503
10507
|
return accountId != null ? `https://github.com/apps/${GITHUB_APP_SLUG}/installations/new/permissions?target_id=${accountId}` : GITHUB_APP_INSTALL_URL;
|
|
10504
10508
|
}
|
|
10509
|
+
var ADVISORY_EPISODE_FILE = `${VERITY_DIR}/.advisory-episode`;
|
|
10505
10510
|
|
|
10506
10511
|
// src/lib/output.ts
|
|
10507
10512
|
var RED = "\x1B[0;31m";
|
|
@@ -10775,7 +10780,7 @@ async function foldLegacyLocalCredential(remoteArg) {
|
|
|
10775
10780
|
const existing = await readGlobalCredential(remote);
|
|
10776
10781
|
const merged = {
|
|
10777
10782
|
token: existing?.token ?? local.token,
|
|
10778
|
-
serviceUrl: existing?.serviceUrl
|
|
10783
|
+
serviceUrl: existing?.serviceUrl,
|
|
10779
10784
|
userId: existing?.userId ?? local.userId,
|
|
10780
10785
|
email: existing?.email ?? local.email
|
|
10781
10786
|
};
|
|
@@ -10869,9 +10874,6 @@ function getChangedFiles() {
|
|
|
10869
10874
|
const filtered = Array.from(sets).filter((f) => !isVerityOwnedPath(f));
|
|
10870
10875
|
return { files: filtered, hasRecentCommitFiles };
|
|
10871
10876
|
}
|
|
10872
|
-
function getStagedFiles() {
|
|
10873
|
-
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !isVerityOwnedPath(f));
|
|
10874
|
-
}
|
|
10875
10877
|
function getDirtyFiles() {
|
|
10876
10878
|
const set = /* @__PURE__ */ new Set();
|
|
10877
10879
|
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
@@ -10892,33 +10894,6 @@ function showContentAtRef(ref, repoRelPath) {
|
|
|
10892
10894
|
return null;
|
|
10893
10895
|
}
|
|
10894
10896
|
}
|
|
10895
|
-
function getPushRangeFiles() {
|
|
10896
|
-
const diff = (range) => splitLines(execGit(`git diff --name-only ${range}`)).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10897
|
-
const resolvers = [
|
|
10898
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{push}") ? "@{push}..HEAD" : null,
|
|
10899
|
-
() => execGit("git rev-parse --abbrev-ref --symbolic-full-name @{upstream}") ? "@{upstream}..HEAD" : null,
|
|
10900
|
-
() => {
|
|
10901
|
-
const branch = execGit("git rev-parse --abbrev-ref HEAD");
|
|
10902
|
-
return branch && branch !== "HEAD" && execGit(`git rev-parse --verify -q origin/${branch}`) ? `origin/${branch}..HEAD` : null;
|
|
10903
|
-
}
|
|
10904
|
-
];
|
|
10905
|
-
for (const resolve2 of resolvers) {
|
|
10906
|
-
const range = resolve2();
|
|
10907
|
-
if (range) return { files: diff(range), range };
|
|
10908
|
-
}
|
|
10909
|
-
const baseline = readBaselineSha();
|
|
10910
|
-
if (baseline) {
|
|
10911
|
-
const files = diff(`${baseline}..HEAD`);
|
|
10912
|
-
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
10913
|
-
}
|
|
10914
|
-
const last = diff("HEAD~1..HEAD");
|
|
10915
|
-
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
10916
|
-
}
|
|
10917
|
-
function getPushRangeMessages() {
|
|
10918
|
-
const { range } = getPushRangeFiles();
|
|
10919
|
-
if (!range) return "";
|
|
10920
|
-
return execGit(`git log ${range} --format=%B%x00`).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
10921
|
-
}
|
|
10922
10897
|
function filterAnalyzable(files) {
|
|
10923
10898
|
return files.filter((f) => {
|
|
10924
10899
|
const ext = (0, import_node_path3.extname)(f).slice(1);
|
|
@@ -10951,7 +10926,7 @@ function getCurrentBranch() {
|
|
|
10951
10926
|
function commitResolves(sha) {
|
|
10952
10927
|
if (!sha) return false;
|
|
10953
10928
|
try {
|
|
10954
|
-
(0, import_node_child_process3.
|
|
10929
|
+
(0, import_node_child_process3.execFileSync)("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
10955
10930
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10956
10931
|
});
|
|
10957
10932
|
return true;
|
|
@@ -10962,8 +10937,9 @@ function commitResolves(sha) {
|
|
|
10962
10937
|
function commitsSincePaths(sha, paths) {
|
|
10963
10938
|
if (!sha) return null;
|
|
10964
10939
|
try {
|
|
10965
|
-
const
|
|
10966
|
-
|
|
10940
|
+
const args = ["rev-list", "--count", `${sha}..HEAD`];
|
|
10941
|
+
if (paths.length > 0) args.push("--", ...paths.slice(0, 50));
|
|
10942
|
+
const out = (0, import_node_child_process3.execFileSync)("git", args, {
|
|
10967
10943
|
encoding: "utf-8",
|
|
10968
10944
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10969
10945
|
}).trim();
|
|
@@ -11245,7 +11221,8 @@ async function serviceUrlFromVerityMd() {
|
|
|
11245
11221
|
}
|
|
11246
11222
|
return null;
|
|
11247
11223
|
}
|
|
11248
|
-
async function resolveServiceUrlDetailed(flagUrl) {
|
|
11224
|
+
async function resolveServiceUrlDetailed(flagUrl, opts = {}) {
|
|
11225
|
+
const allowVerityMd = opts.allowVerityMd ?? true;
|
|
11249
11226
|
if (flagUrl) {
|
|
11250
11227
|
return { ok: true, data: { url: flagUrl, source: "flag" } };
|
|
11251
11228
|
}
|
|
@@ -11257,9 +11234,11 @@ async function resolveServiceUrlDetailed(flagUrl) {
|
|
|
11257
11234
|
if (credsUrl) {
|
|
11258
11235
|
return { ok: true, data: { url: credsUrl, source: "credentials" } };
|
|
11259
11236
|
}
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11237
|
+
if (allowVerityMd) {
|
|
11238
|
+
const mdUrl = await serviceUrlFromVerityMd();
|
|
11239
|
+
if (mdUrl) {
|
|
11240
|
+
return { ok: true, data: { url: mdUrl, source: "verity_md" } };
|
|
11241
|
+
}
|
|
11263
11242
|
}
|
|
11264
11243
|
return {
|
|
11265
11244
|
ok: false,
|
|
@@ -11267,7 +11246,7 @@ async function resolveServiceUrlDetailed(flagUrl) {
|
|
|
11267
11246
|
};
|
|
11268
11247
|
}
|
|
11269
11248
|
async function resolveServiceUrlForAuth(flagUrl) {
|
|
11270
|
-
const strict = await resolveServiceUrlDetailed(flagUrl);
|
|
11249
|
+
const strict = await resolveServiceUrlDetailed(flagUrl, { allowVerityMd: false });
|
|
11271
11250
|
if (strict.ok) return strict.data;
|
|
11272
11251
|
return { url: DEFAULT_SERVICE_URL, source: "default" };
|
|
11273
11252
|
}
|
|
@@ -11405,7 +11384,7 @@ var readline = __toESM(require("node:readline/promises"));
|
|
|
11405
11384
|
var import_node_os = require("node:os");
|
|
11406
11385
|
|
|
11407
11386
|
// src/lib/provider-auth.ts
|
|
11408
|
-
var sleep = (ms) => new Promise((
|
|
11387
|
+
var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
11409
11388
|
var form = (fields) => new URLSearchParams(fields).toString();
|
|
11410
11389
|
async function githubAccountId(owner) {
|
|
11411
11390
|
try {
|
|
@@ -13161,9 +13140,38 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
13161
13140
|
|
|
13162
13141
|
// src/lib/memory-sync.ts
|
|
13163
13142
|
var import_promises8 = require("node:fs/promises");
|
|
13143
|
+
var import_node_fs9 = require("node:fs");
|
|
13144
|
+
var import_node_path10 = require("node:path");
|
|
13145
|
+
var import_node_crypto3 = require("node:crypto");
|
|
13146
|
+
|
|
13147
|
+
// src/lib/safe-path.ts
|
|
13164
13148
|
var import_node_fs8 = require("node:fs");
|
|
13165
13149
|
var import_node_path9 = require("node:path");
|
|
13166
|
-
|
|
13150
|
+
function resolveInside(baseDir, candidate) {
|
|
13151
|
+
if (typeof candidate !== "string" || candidate.length === 0) return null;
|
|
13152
|
+
if ((0, import_node_path9.isAbsolute)(candidate)) return null;
|
|
13153
|
+
const baseAbs = (0, import_node_path9.resolve)(baseDir);
|
|
13154
|
+
const full = (0, import_node_path9.resolve)(baseAbs, candidate);
|
|
13155
|
+
const baseSep = baseAbs.endsWith(import_node_path9.sep) ? baseAbs : baseAbs + import_node_path9.sep;
|
|
13156
|
+
if (full !== baseAbs && !full.startsWith(baseSep)) return null;
|
|
13157
|
+
try {
|
|
13158
|
+
if ((0, import_node_fs8.existsSync)(baseAbs)) {
|
|
13159
|
+
const realBase = (0, import_node_fs8.realpathSync)(baseAbs);
|
|
13160
|
+
const realBaseSep = realBase.endsWith(import_node_path9.sep) ? realBase : realBase + import_node_path9.sep;
|
|
13161
|
+
let probe = full;
|
|
13162
|
+
while (!(0, import_node_fs8.existsSync)(probe)) {
|
|
13163
|
+
const parent = (0, import_node_path9.dirname)(probe);
|
|
13164
|
+
if (parent === probe) break;
|
|
13165
|
+
probe = parent;
|
|
13166
|
+
}
|
|
13167
|
+
const realProbe = (0, import_node_fs8.realpathSync)(probe);
|
|
13168
|
+
if (realProbe !== realBase && !realProbe.startsWith(realBaseSep)) return null;
|
|
13169
|
+
}
|
|
13170
|
+
} catch {
|
|
13171
|
+
return null;
|
|
13172
|
+
}
|
|
13173
|
+
return full;
|
|
13174
|
+
}
|
|
13167
13175
|
|
|
13168
13176
|
// src/lib/glob-match.ts
|
|
13169
13177
|
function globToRegex(glob) {
|
|
@@ -13233,32 +13241,32 @@ var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
|
13233
13241
|
async function ensureMemoryDir() {
|
|
13234
13242
|
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
13235
13243
|
for (const domain of DOMAINS2) {
|
|
13236
|
-
await (0, import_promises8.mkdir)((0,
|
|
13244
|
+
await (0, import_promises8.mkdir)((0, import_node_path10.join)(memoryDir2(), domain), { recursive: true });
|
|
13237
13245
|
}
|
|
13238
|
-
if (!(0,
|
|
13239
|
-
await (0, import_promises8.writeFile)((0,
|
|
13246
|
+
if (!(0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
13247
|
+
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
13240
13248
|
}
|
|
13241
|
-
if (!(0,
|
|
13242
|
-
await (0, import_promises8.writeFile)((0,
|
|
13249
|
+
if (!(0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "index.md"))) {
|
|
13250
|
+
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
13243
13251
|
}
|
|
13244
|
-
if (!(0,
|
|
13245
|
-
await (0, import_promises8.writeFile)((0,
|
|
13252
|
+
if (!(0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md"))) {
|
|
13253
|
+
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
13246
13254
|
}
|
|
13247
13255
|
}
|
|
13248
13256
|
async function buildManifest() {
|
|
13249
|
-
if (!(0,
|
|
13257
|
+
if (!(0, import_node_fs9.existsSync)(memoryDir2())) {
|
|
13250
13258
|
return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
|
|
13251
13259
|
}
|
|
13252
13260
|
const nodes = [];
|
|
13253
13261
|
for (const domain of DOMAINS2) {
|
|
13254
|
-
const domainDir = (0,
|
|
13255
|
-
if (!(0,
|
|
13262
|
+
const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
|
|
13263
|
+
if (!(0, import_node_fs9.existsSync)(domainDir)) continue;
|
|
13256
13264
|
try {
|
|
13257
13265
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
13258
13266
|
for (const file of files) {
|
|
13259
13267
|
if (!file.endsWith(".md")) continue;
|
|
13260
13268
|
const filePath = `${domain}/${file}`;
|
|
13261
|
-
const fullPath = (0,
|
|
13269
|
+
const fullPath = (0, import_node_path10.join)(memoryDir2(), filePath);
|
|
13262
13270
|
try {
|
|
13263
13271
|
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
13264
13272
|
const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
@@ -13271,13 +13279,13 @@ async function buildManifest() {
|
|
|
13271
13279
|
}
|
|
13272
13280
|
let indexHash = null;
|
|
13273
13281
|
try {
|
|
13274
|
-
const indexContent = await (0, import_promises8.readFile)((0,
|
|
13282
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "index.md"), "utf-8");
|
|
13275
13283
|
indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
13276
13284
|
} catch {
|
|
13277
13285
|
}
|
|
13278
13286
|
let logLength = 0;
|
|
13279
13287
|
try {
|
|
13280
|
-
const logContent = await (0, import_promises8.readFile)((0,
|
|
13288
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8");
|
|
13281
13289
|
logLength = logContent.split("\n").length;
|
|
13282
13290
|
} catch {
|
|
13283
13291
|
}
|
|
@@ -13288,15 +13296,15 @@ function hashContent(content) {
|
|
|
13288
13296
|
}
|
|
13289
13297
|
async function readOnDiskNodes() {
|
|
13290
13298
|
const out = /* @__PURE__ */ new Map();
|
|
13291
|
-
if (!(0,
|
|
13299
|
+
if (!(0, import_node_fs9.existsSync)(memoryDir2())) return out;
|
|
13292
13300
|
for (const domain of DOMAINS2) {
|
|
13293
|
-
const domainDir = (0,
|
|
13294
|
-
if (!(0,
|
|
13301
|
+
const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
|
|
13302
|
+
if (!(0, import_node_fs9.existsSync)(domainDir)) continue;
|
|
13295
13303
|
try {
|
|
13296
13304
|
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
13297
13305
|
if (!file.endsWith(".md")) continue;
|
|
13298
13306
|
try {
|
|
13299
|
-
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0,
|
|
13307
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8")));
|
|
13300
13308
|
} catch {
|
|
13301
13309
|
}
|
|
13302
13310
|
}
|
|
@@ -13342,8 +13350,8 @@ async function computeEditedNodeUploads() {
|
|
|
13342
13350
|
const uploads = [];
|
|
13343
13351
|
for (const [path, prevHash] of prev) {
|
|
13344
13352
|
if (prevHash == null) continue;
|
|
13345
|
-
const full = (0,
|
|
13346
|
-
if (!(0,
|
|
13353
|
+
const full = (0, import_node_path10.join)(memoryDir2(), path);
|
|
13354
|
+
if (!(0, import_node_fs9.existsSync)(full)) continue;
|
|
13347
13355
|
let content;
|
|
13348
13356
|
try {
|
|
13349
13357
|
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
@@ -13379,16 +13387,19 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
13379
13387
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
13380
13388
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
13381
13389
|
try {
|
|
13382
|
-
const existing = (0,
|
|
13383
|
-
await (0, import_promises8.writeFile)((0,
|
|
13390
|
+
const existing = (0, import_node_fs9.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
|
|
13391
|
+
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
13384
13392
|
} catch {
|
|
13385
13393
|
}
|
|
13386
13394
|
await recordSyncedNodePaths();
|
|
13387
13395
|
return count;
|
|
13388
13396
|
}
|
|
13389
13397
|
async function applyOneWrite(write, treePaths) {
|
|
13390
|
-
const fullPath = (
|
|
13398
|
+
const fullPath = resolveInside(memoryDir2(), write.path);
|
|
13391
13399
|
const notes = [];
|
|
13400
|
+
if (!fullPath) {
|
|
13401
|
+
return { written: false, notes: [`${String(write.path)}: rejected \u2014 path escapes the memory directory`] };
|
|
13402
|
+
}
|
|
13392
13403
|
let content = write.content;
|
|
13393
13404
|
if (treePaths && treePaths.length > 0) {
|
|
13394
13405
|
const grounded = groundFileGlobs(content, treePaths);
|
|
@@ -13397,7 +13408,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
13397
13408
|
notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
|
|
13398
13409
|
}
|
|
13399
13410
|
}
|
|
13400
|
-
if ((0,
|
|
13411
|
+
if ((0, import_node_fs9.existsSync)(fullPath)) {
|
|
13401
13412
|
let existing = "";
|
|
13402
13413
|
try {
|
|
13403
13414
|
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
@@ -13409,7 +13420,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
13409
13420
|
return { written: false, notes };
|
|
13410
13421
|
}
|
|
13411
13422
|
}
|
|
13412
|
-
await (0, import_promises8.mkdir)((0,
|
|
13423
|
+
await (0, import_promises8.mkdir)((0, import_node_path10.dirname)(fullPath), { recursive: true });
|
|
13413
13424
|
await (0, import_promises8.writeFile)(fullPath, content);
|
|
13414
13425
|
return { written: true, notes };
|
|
13415
13426
|
}
|
|
@@ -13450,8 +13461,8 @@ async function regenerateIndex() {
|
|
|
13450
13461
|
];
|
|
13451
13462
|
let totalNodes = 0;
|
|
13452
13463
|
for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
|
|
13453
|
-
const domainDir = (0,
|
|
13454
|
-
if (!(0,
|
|
13464
|
+
const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
|
|
13465
|
+
if (!(0, import_node_fs9.existsSync)(domainDir)) continue;
|
|
13455
13466
|
try {
|
|
13456
13467
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
13457
13468
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
@@ -13461,7 +13472,7 @@ async function regenerateIndex() {
|
|
|
13461
13472
|
for (const file of mdFiles.sort()) {
|
|
13462
13473
|
const slug = file.replace(/\.md$/, "");
|
|
13463
13474
|
try {
|
|
13464
|
-
const content = await (0, import_promises8.readFile)((0,
|
|
13475
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path10.join)(domainDir, file), "utf-8");
|
|
13465
13476
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
13466
13477
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
13467
13478
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -13485,7 +13496,7 @@ async function regenerateIndex() {
|
|
|
13485
13496
|
lines.push("No nodes yet. Run an analysis to start building the knowledge graph.");
|
|
13486
13497
|
}
|
|
13487
13498
|
const next = lines.join("\n") + "\n";
|
|
13488
|
-
const indexPath = (0,
|
|
13499
|
+
const indexPath = (0, import_node_path10.join)(memoryDir2(), "index.md");
|
|
13489
13500
|
let existing = null;
|
|
13490
13501
|
try {
|
|
13491
13502
|
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
@@ -13567,9 +13578,9 @@ function hasLegacyMemoryBlock(text) {
|
|
|
13567
13578
|
return findMarker(text, LEGACY_MD_START) !== -1;
|
|
13568
13579
|
}
|
|
13569
13580
|
async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
13570
|
-
const claudeMdPath = (0,
|
|
13581
|
+
const claudeMdPath = (0, import_node_path10.join)(cwd, "CLAUDE.md");
|
|
13571
13582
|
let existing = "";
|
|
13572
|
-
if ((0,
|
|
13583
|
+
if ((0, import_node_fs9.existsSync)(claudeMdPath)) {
|
|
13573
13584
|
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
13574
13585
|
}
|
|
13575
13586
|
let startTag = CLAUDE_MD_START;
|
|
@@ -13699,9 +13710,9 @@ Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
|
13699
13710
|
`;
|
|
13700
13711
|
|
|
13701
13712
|
// src/lib/dossier-session.ts
|
|
13702
|
-
var
|
|
13713
|
+
var import_node_fs14 = require("node:fs");
|
|
13703
13714
|
var import_node_crypto7 = require("node:crypto");
|
|
13704
|
-
var
|
|
13715
|
+
var import_node_path13 = require("node:path");
|
|
13705
13716
|
|
|
13706
13717
|
// src/lib/skip-detection.ts
|
|
13707
13718
|
function isBareAckPrompt(prompt) {
|
|
@@ -13865,8 +13876,8 @@ var DEFAULT_MEMORY_BUDGET_BYTES = 4096;
|
|
|
13865
13876
|
|
|
13866
13877
|
// src/lib/dossier/log.ts
|
|
13867
13878
|
var import_node_crypto4 = require("node:crypto");
|
|
13868
|
-
var
|
|
13869
|
-
var
|
|
13879
|
+
var import_node_fs10 = require("node:fs");
|
|
13880
|
+
var import_node_path11 = require("node:path");
|
|
13870
13881
|
var CRC_TABLE = (() => {
|
|
13871
13882
|
const t = new Int32Array(256);
|
|
13872
13883
|
for (let i = 0; i < 256; i++) {
|
|
@@ -13885,13 +13896,13 @@ function crc32(s) {
|
|
|
13885
13896
|
function openDossier(identity) {
|
|
13886
13897
|
try {
|
|
13887
13898
|
const dir = dossierDir(identity);
|
|
13888
|
-
(0,
|
|
13899
|
+
(0, import_node_fs10.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
13889
13900
|
return {
|
|
13890
13901
|
dir,
|
|
13891
13902
|
identity,
|
|
13892
|
-
eventsPath: (0,
|
|
13893
|
-
foldPath: (0,
|
|
13894
|
-
rotatedDir: (0,
|
|
13903
|
+
eventsPath: (0, import_node_path11.join)(dir, "events.jsonl"),
|
|
13904
|
+
foldPath: (0, import_node_path11.join)(dir, "fold.json"),
|
|
13905
|
+
rotatedDir: (0, import_node_path11.join)(dir, "rotated")
|
|
13895
13906
|
};
|
|
13896
13907
|
} catch {
|
|
13897
13908
|
return null;
|
|
@@ -13953,7 +13964,7 @@ function appendEvent(d, ev) {
|
|
|
13953
13964
|
at: ev.at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
13954
13965
|
...ev
|
|
13955
13966
|
});
|
|
13956
|
-
(0,
|
|
13967
|
+
(0, import_node_fs10.appendFileSync)(d.eventsPath, line, { mode: 384 });
|
|
13957
13968
|
return true;
|
|
13958
13969
|
} catch {
|
|
13959
13970
|
return false;
|
|
@@ -13961,14 +13972,14 @@ function appendEvent(d, ev) {
|
|
|
13961
13972
|
}
|
|
13962
13973
|
function rotateIfNeeded2(d) {
|
|
13963
13974
|
try {
|
|
13964
|
-
if (!(0,
|
|
13965
|
-
if ((0,
|
|
13966
|
-
(0,
|
|
13967
|
-
(0,
|
|
13968
|
-
const kept = (0,
|
|
13975
|
+
if (!(0, import_node_fs10.existsSync)(d.eventsPath)) return;
|
|
13976
|
+
if ((0, import_node_fs10.statSync)(d.eventsPath).size < ROTATE_BYTES) return;
|
|
13977
|
+
(0, import_node_fs10.mkdirSync)(d.rotatedDir, { recursive: true, mode: 448 });
|
|
13978
|
+
(0, import_node_fs10.renameSync)(d.eventsPath, (0, import_node_path11.join)(d.rotatedDir, `events.${Date.now()}.jsonl`));
|
|
13979
|
+
const kept = (0, import_node_fs10.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
13969
13980
|
for (const stale of kept.slice(0, Math.max(0, kept.length - ROTATE_KEEP))) {
|
|
13970
13981
|
try {
|
|
13971
|
-
(0,
|
|
13982
|
+
(0, import_node_fs10.renameSync)((0, import_node_path11.join)(d.rotatedDir, stale), (0, import_node_path11.join)(d.rotatedDir, `${stale}.pruned`));
|
|
13972
13983
|
} catch {
|
|
13973
13984
|
}
|
|
13974
13985
|
}
|
|
@@ -13978,8 +13989,8 @@ function rotateIfNeeded2(d) {
|
|
|
13978
13989
|
|
|
13979
13990
|
// src/lib/dossier/fold-dossier.ts
|
|
13980
13991
|
var import_node_crypto5 = require("node:crypto");
|
|
13981
|
-
var
|
|
13982
|
-
var
|
|
13992
|
+
var import_node_fs11 = require("node:fs");
|
|
13993
|
+
var import_node_path12 = require("node:path");
|
|
13983
13994
|
var EMPTY_CAPABILITIES = () => ({
|
|
13984
13995
|
human_reachable: { value: "unknown", tier: "unknown" },
|
|
13985
13996
|
authorship_observability: { value: "unknown", tier: "unknown" },
|
|
@@ -14030,12 +14041,12 @@ function foldDossier(d, opts = {}) {
|
|
|
14030
14041
|
}
|
|
14031
14042
|
};
|
|
14032
14043
|
try {
|
|
14033
|
-
if ((0,
|
|
14034
|
-
const files = (0,
|
|
14044
|
+
if ((0, import_node_fs11.existsSync)(d.rotatedDir)) {
|
|
14045
|
+
const files = (0, import_node_fs11.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
14035
14046
|
state.meta.rotations = files.length;
|
|
14036
14047
|
for (const f of files) {
|
|
14037
14048
|
try {
|
|
14038
|
-
ingest((0,
|
|
14049
|
+
ingest((0, import_node_fs11.readFileSync)((0, import_node_path12.join)(d.rotatedDir, f), "utf8"));
|
|
14039
14050
|
} catch {
|
|
14040
14051
|
state.meta.dropped_lines++;
|
|
14041
14052
|
}
|
|
@@ -14044,9 +14055,9 @@ function foldDossier(d, opts = {}) {
|
|
|
14044
14055
|
} catch {
|
|
14045
14056
|
}
|
|
14046
14057
|
try {
|
|
14047
|
-
if ((0,
|
|
14048
|
-
state.meta.upto_offset = (0,
|
|
14049
|
-
ingest((0,
|
|
14058
|
+
if ((0, import_node_fs11.existsSync)(d.eventsPath)) {
|
|
14059
|
+
state.meta.upto_offset = (0, import_node_fs11.statSync)(d.eventsPath).size;
|
|
14060
|
+
ingest((0, import_node_fs11.readFileSync)(d.eventsPath, "utf8"));
|
|
14050
14061
|
}
|
|
14051
14062
|
} catch {
|
|
14052
14063
|
}
|
|
@@ -14316,7 +14327,7 @@ function applyBounds(state, input) {
|
|
|
14316
14327
|
}
|
|
14317
14328
|
|
|
14318
14329
|
// src/lib/dossier/cache.ts
|
|
14319
|
-
var
|
|
14330
|
+
var import_node_fs12 = require("node:fs");
|
|
14320
14331
|
function compactState(s) {
|
|
14321
14332
|
const ms = (iso) => Date.parse(iso) || 0;
|
|
14322
14333
|
return {
|
|
@@ -14445,20 +14456,20 @@ function encodeState(s) {
|
|
|
14445
14456
|
function writeFoldCache(d, state) {
|
|
14446
14457
|
try {
|
|
14447
14458
|
const tmp = `${d.foldPath}.${process.pid}.tmp`;
|
|
14448
|
-
(0,
|
|
14449
|
-
(0,
|
|
14459
|
+
(0, import_node_fs12.writeFileSync)(tmp, encodeState(state), { mode: 384 });
|
|
14460
|
+
(0, import_node_fs12.renameSync)(tmp, d.foldPath);
|
|
14450
14461
|
} catch {
|
|
14451
14462
|
}
|
|
14452
14463
|
}
|
|
14453
14464
|
function readFoldCache(d) {
|
|
14454
14465
|
try {
|
|
14455
|
-
if (!(0,
|
|
14456
|
-
const raw = JSON.parse((0,
|
|
14466
|
+
if (!(0, import_node_fs12.existsSync)(d.foldPath)) return null;
|
|
14467
|
+
const raw = JSON.parse((0, import_node_fs12.readFileSync)(d.foldPath, "utf8"));
|
|
14457
14468
|
if (raw?.v !== 1) return null;
|
|
14458
14469
|
const cached2 = expandState(raw);
|
|
14459
14470
|
if (!cached2?.meta) return null;
|
|
14460
|
-
const size = (0,
|
|
14461
|
-
const rotations = (0,
|
|
14471
|
+
const size = (0, import_node_fs12.existsSync)(d.eventsPath) ? (0, import_node_fs12.statSync)(d.eventsPath).size : 0;
|
|
14472
|
+
const rotations = (0, import_node_fs12.existsSync)(d.rotatedDir) ? (0, import_node_fs12.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).length : 0;
|
|
14462
14473
|
if (cached2.meta.upto_offset !== size || cached2.meta.rotations !== rotations) return null;
|
|
14463
14474
|
return cached2;
|
|
14464
14475
|
} catch {
|
|
@@ -14509,13 +14520,13 @@ function assessContinuity(i) {
|
|
|
14509
14520
|
|
|
14510
14521
|
// src/lib/dossier/reanchor.ts
|
|
14511
14522
|
var import_node_crypto6 = require("node:crypto");
|
|
14512
|
-
var
|
|
14523
|
+
var import_node_fs13 = require("node:fs");
|
|
14513
14524
|
function lineSha(text) {
|
|
14514
14525
|
return (0, import_node_crypto6.createHash)("sha256").update(text.trim()).digest("hex").slice(0, HASH_WIDTH);
|
|
14515
14526
|
}
|
|
14516
14527
|
function fileHash(path) {
|
|
14517
14528
|
try {
|
|
14518
|
-
return (0, import_node_crypto6.createHash)("sha256").update((0,
|
|
14529
|
+
return (0, import_node_crypto6.createHash)("sha256").update((0, import_node_fs13.readFileSync)(path)).digest("hex").slice(0, HASH_WIDTH);
|
|
14519
14530
|
} catch {
|
|
14520
14531
|
return null;
|
|
14521
14532
|
}
|
|
@@ -14887,20 +14898,20 @@ function foreignAuthoredPaths(identity, opts = {}) {
|
|
|
14887
14898
|
let sessions = 0;
|
|
14888
14899
|
try {
|
|
14889
14900
|
const dir = treeDir(identity);
|
|
14890
|
-
if (!(0,
|
|
14891
|
-
for (const entry of (0,
|
|
14901
|
+
if (!(0, import_node_fs14.existsSync)(dir)) return { paths: [], sessions: 0 };
|
|
14902
|
+
for (const entry of (0, import_node_fs14.readdirSync)(dir, { withFileTypes: true })) {
|
|
14892
14903
|
if (!entry.isDirectory()) continue;
|
|
14893
14904
|
if (entry.name === identity.sessionKey) continue;
|
|
14894
|
-
const log = (0,
|
|
14905
|
+
const log = (0, import_node_path13.join)(dir, entry.name, "events.jsonl");
|
|
14895
14906
|
try {
|
|
14896
|
-
if (!(0,
|
|
14897
|
-
if (now - (0,
|
|
14907
|
+
if (!(0, import_node_fs14.existsSync)(log)) continue;
|
|
14908
|
+
if (now - (0, import_node_fs14.statSync)(log).mtimeMs > windowMs) continue;
|
|
14898
14909
|
const sib = {
|
|
14899
|
-
dir: (0,
|
|
14910
|
+
dir: (0, import_node_path13.join)(dir, entry.name),
|
|
14900
14911
|
identity,
|
|
14901
14912
|
eventsPath: log,
|
|
14902
|
-
foldPath: (0,
|
|
14903
|
-
rotatedDir: (0,
|
|
14913
|
+
foldPath: (0, import_node_path13.join)(dir, entry.name, "fold.json"),
|
|
14914
|
+
rotatedDir: (0, import_node_path13.join)(dir, entry.name, "rotated")
|
|
14904
14915
|
};
|
|
14905
14916
|
const state = readFoldCache(sib) ?? foldDossier(sib);
|
|
14906
14917
|
sessions++;
|
|
@@ -14928,25 +14939,25 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
|
|
|
14928
14939
|
let removed = 0;
|
|
14929
14940
|
try {
|
|
14930
14941
|
const mine = dossierDir(identity);
|
|
14931
|
-
const userDir = (0,
|
|
14932
|
-
if (!(0,
|
|
14942
|
+
const userDir = (0, import_node_path13.dirname)((0, import_node_path13.dirname)(mine));
|
|
14943
|
+
if (!(0, import_node_fs14.existsSync)(userDir)) return 0;
|
|
14933
14944
|
const cutoff = Date.now() - maxAgeMs;
|
|
14934
|
-
for (const tree of (0,
|
|
14945
|
+
for (const tree of (0, import_node_fs14.readdirSync)(userDir, { withFileTypes: true })) {
|
|
14935
14946
|
if (!tree.isDirectory()) continue;
|
|
14936
|
-
const treePath = (0,
|
|
14947
|
+
const treePath = (0, import_node_path13.join)(userDir, tree.name);
|
|
14937
14948
|
let live = 0;
|
|
14938
|
-
for (const entry of (0,
|
|
14949
|
+
for (const entry of (0, import_node_fs14.readdirSync)(treePath, { withFileTypes: true })) {
|
|
14939
14950
|
if (!entry.isDirectory()) continue;
|
|
14940
|
-
const dir = (0,
|
|
14951
|
+
const dir = (0, import_node_path13.join)(treePath, entry.name);
|
|
14941
14952
|
if (dir === mine) {
|
|
14942
14953
|
live++;
|
|
14943
14954
|
continue;
|
|
14944
14955
|
}
|
|
14945
14956
|
try {
|
|
14946
|
-
const log = (0,
|
|
14947
|
-
const at = (0,
|
|
14957
|
+
const log = (0, import_node_path13.join)(dir, "events.jsonl");
|
|
14958
|
+
const at = (0, import_node_fs14.existsSync)(log) ? (0, import_node_fs14.statSync)(log).mtimeMs : (0, import_node_fs14.statSync)(dir).mtimeMs;
|
|
14948
14959
|
if (at < cutoff) {
|
|
14949
|
-
(0,
|
|
14960
|
+
(0, import_node_fs14.rmSync)(dir, { recursive: true, force: true });
|
|
14950
14961
|
removed++;
|
|
14951
14962
|
} else {
|
|
14952
14963
|
live++;
|
|
@@ -14956,7 +14967,7 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
|
|
|
14956
14967
|
}
|
|
14957
14968
|
if (live === 0) {
|
|
14958
14969
|
try {
|
|
14959
|
-
(0,
|
|
14970
|
+
(0, import_node_fs14.rmSync)(treePath, { recursive: false, force: false });
|
|
14960
14971
|
} catch {
|
|
14961
14972
|
}
|
|
14962
14973
|
}
|
|
@@ -14973,8 +14984,8 @@ function sessionDossier(token, sessionId) {
|
|
|
14973
14984
|
}
|
|
14974
14985
|
function hasActiveGoal(d) {
|
|
14975
14986
|
try {
|
|
14976
|
-
if (!(0,
|
|
14977
|
-
return (0,
|
|
14987
|
+
if (!(0, import_node_fs14.existsSync)(d.eventsPath)) return false;
|
|
14988
|
+
return (0, import_node_fs14.readFileSync)(d.eventsPath, "utf8").includes('"k":"goal"');
|
|
14978
14989
|
} catch {
|
|
14979
14990
|
return false;
|
|
14980
14991
|
}
|
|
@@ -14998,7 +15009,7 @@ function recordTurn(d, t) {
|
|
|
14998
15009
|
for (const a of t.authored) {
|
|
14999
15010
|
const origin = a.owner === "subagent" ? "subagent" : "edit_tool";
|
|
15000
15011
|
const prior = t.known?.authored?.get(a.p);
|
|
15001
|
-
const hash = fileHash((0,
|
|
15012
|
+
const hash = fileHash((0, import_node_path13.join)(root, a.p));
|
|
15002
15013
|
const hunks = Math.max(0, a.h - (prior?.hunks ?? 0));
|
|
15003
15014
|
const adds = Math.max(0, a.a - (prior?.adds ?? 0));
|
|
15004
15015
|
const dels = Math.max(0, a.d - (prior?.dels ?? 0));
|
|
@@ -15031,7 +15042,7 @@ function recordTurn(d, t) {
|
|
|
15031
15042
|
}
|
|
15032
15043
|
const seenDivergence = t.known?.divergence ?? /* @__PURE__ */ new Set();
|
|
15033
15044
|
for (const u of t.unobserved) {
|
|
15034
|
-
const hash = fileHash((0,
|
|
15045
|
+
const hash = fileHash((0, import_node_path13.join)(root, u.p));
|
|
15035
15046
|
if (seenDivergence.has(divergenceKey(u.p, hash))) continue;
|
|
15036
15047
|
appendEvent(d, { k: "divergence", kind: "external_mutation", path: u.p, hash });
|
|
15037
15048
|
}
|
|
@@ -15060,12 +15071,14 @@ function recordVerdict(d, v) {
|
|
|
15060
15071
|
if (summary) appendEvent(d, { k: "goal_delivered", summary });
|
|
15061
15072
|
}
|
|
15062
15073
|
const lines = /* @__PURE__ */ new Map();
|
|
15074
|
+
const sent = new Set(v.sentPaths);
|
|
15063
15075
|
for (const f of v.findings) {
|
|
15064
15076
|
if (!f.file || typeof f.line !== "number" || !f.pattern_id) continue;
|
|
15077
|
+
if (!sent.has(f.file)) continue;
|
|
15065
15078
|
if (!lines.has(f.file)) {
|
|
15066
15079
|
try {
|
|
15067
|
-
const abs = (0,
|
|
15068
|
-
lines.set(f.file, (0,
|
|
15080
|
+
const abs = (0, import_node_path13.join)(root, f.file);
|
|
15081
|
+
lines.set(f.file, (0, import_node_fs14.existsSync)(abs) ? (0, import_node_fs14.readFileSync)(abs, "utf8").split("\n") : null);
|
|
15069
15082
|
} catch {
|
|
15070
15083
|
lines.set(f.file, null);
|
|
15071
15084
|
}
|
|
@@ -15165,8 +15178,8 @@ function recallMemory(d, identity, opts) {
|
|
|
15165
15178
|
budgetBytes: opts.budgetBytes,
|
|
15166
15179
|
readFileLines: (file) => {
|
|
15167
15180
|
try {
|
|
15168
|
-
const abs = (0,
|
|
15169
|
-
return (0,
|
|
15181
|
+
const abs = (0, import_node_path13.join)(root, file);
|
|
15182
|
+
return (0, import_node_fs14.existsSync)(abs) ? (0, import_node_fs14.readFileSync)(abs, "utf8").split("\n") : null;
|
|
15170
15183
|
} catch {
|
|
15171
15184
|
return null;
|
|
15172
15185
|
}
|
|
@@ -15304,22 +15317,22 @@ async function fireClassify(prompt, sessionId, globals) {
|
|
|
15304
15317
|
}
|
|
15305
15318
|
|
|
15306
15319
|
// src/commands/lifecycle.ts
|
|
15307
|
-
var
|
|
15308
|
-
var
|
|
15320
|
+
var import_node_fs18 = require("node:fs");
|
|
15321
|
+
var import_node_path17 = require("node:path");
|
|
15309
15322
|
|
|
15310
15323
|
// src/lib/baseline.ts
|
|
15311
|
-
var
|
|
15312
|
-
var
|
|
15324
|
+
var import_node_fs17 = require("node:fs");
|
|
15325
|
+
var import_node_path16 = require("node:path");
|
|
15313
15326
|
var import_node_crypto9 = require("node:crypto");
|
|
15314
15327
|
|
|
15315
15328
|
// src/lib/snapshot.ts
|
|
15316
|
-
var
|
|
15317
|
-
var
|
|
15329
|
+
var import_node_fs16 = require("node:fs");
|
|
15330
|
+
var import_node_path15 = require("node:path");
|
|
15318
15331
|
var import_node_child_process6 = require("node:child_process");
|
|
15319
15332
|
|
|
15320
15333
|
// src/lib/files.ts
|
|
15321
|
-
var
|
|
15322
|
-
var
|
|
15334
|
+
var import_node_fs15 = require("node:fs");
|
|
15335
|
+
var import_node_path14 = require("node:path");
|
|
15323
15336
|
var LANG_MAP = {
|
|
15324
15337
|
// Analyzable (static analysis + Gemini)
|
|
15325
15338
|
ts: "typescript",
|
|
@@ -15387,7 +15400,7 @@ var LANG_MAP = {
|
|
|
15387
15400
|
mk: "make"
|
|
15388
15401
|
};
|
|
15389
15402
|
function detectLanguage(filepath) {
|
|
15390
|
-
const ext = (0,
|
|
15403
|
+
const ext = (0, import_node_path14.extname)(filepath).slice(1);
|
|
15391
15404
|
return LANG_MAP[ext] ?? ext;
|
|
15392
15405
|
}
|
|
15393
15406
|
function sortByMtime(files) {
|
|
@@ -15395,7 +15408,7 @@ function sortByMtime(files) {
|
|
|
15395
15408
|
const resolved = resolveFile(f);
|
|
15396
15409
|
if (!resolved) return null;
|
|
15397
15410
|
try {
|
|
15398
|
-
const stat3 = (0,
|
|
15411
|
+
const stat3 = (0, import_node_fs15.statSync)(resolved);
|
|
15399
15412
|
return { path: f, resolved, mtime: stat3.mtimeMs };
|
|
15400
15413
|
} catch {
|
|
15401
15414
|
return null;
|
|
@@ -15428,7 +15441,7 @@ function collectCodeDelta(files, opts) {
|
|
|
15428
15441
|
}
|
|
15429
15442
|
let size;
|
|
15430
15443
|
try {
|
|
15431
|
-
size = (0,
|
|
15444
|
+
size = (0, import_node_fs15.statSync)(resolved).size;
|
|
15432
15445
|
} catch {
|
|
15433
15446
|
exclude(filepath, "not-stattable");
|
|
15434
15447
|
continue;
|
|
@@ -15445,7 +15458,7 @@ function collectCodeDelta(files, opts) {
|
|
|
15445
15458
|
}
|
|
15446
15459
|
let content;
|
|
15447
15460
|
try {
|
|
15448
|
-
content = (0,
|
|
15461
|
+
content = (0, import_node_fs15.readFileSync)(resolved, "utf-8");
|
|
15449
15462
|
} catch {
|
|
15450
15463
|
exclude(filepath, "not-readable");
|
|
15451
15464
|
continue;
|
|
@@ -15484,15 +15497,16 @@ function collectCodeDelta(files, opts) {
|
|
|
15484
15497
|
|
|
15485
15498
|
// src/lib/snapshot.ts
|
|
15486
15499
|
function generateSnapshotDiffs(files) {
|
|
15487
|
-
if (!(0,
|
|
15500
|
+
if (!(0, import_node_fs16.existsSync)(SNAPSHOT_DIR)) {
|
|
15488
15501
|
return { diffs: [], has_snapshots: false };
|
|
15489
15502
|
}
|
|
15490
15503
|
const diffs = [];
|
|
15491
15504
|
for (const file of files) {
|
|
15492
|
-
|
|
15505
|
+
if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
|
|
15506
|
+
const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
|
|
15493
15507
|
const language = file.language ?? detectLanguage(file.path);
|
|
15494
|
-
if ((0,
|
|
15495
|
-
const oldContent = (0,
|
|
15508
|
+
if ((0, import_node_fs16.existsSync)(snapshotPath)) {
|
|
15509
|
+
const oldContent = (0, import_node_fs16.readFileSync)(snapshotPath, "utf-8");
|
|
15496
15510
|
if (oldContent === file.content) continue;
|
|
15497
15511
|
const diff = computeDiff(oldContent, file.content, file.path);
|
|
15498
15512
|
if (diff) {
|
|
@@ -15513,23 +15527,51 @@ ${addedLines}`,
|
|
|
15513
15527
|
}
|
|
15514
15528
|
return { diffs, has_snapshots: true };
|
|
15515
15529
|
}
|
|
15530
|
+
function ensureSnapshotGitignored() {
|
|
15531
|
+
let content = "";
|
|
15532
|
+
try {
|
|
15533
|
+
content = (0, import_node_fs16.readFileSync)(".gitignore", "utf-8");
|
|
15534
|
+
} catch {
|
|
15535
|
+
}
|
|
15536
|
+
let ignored = null;
|
|
15537
|
+
try {
|
|
15538
|
+
(0, import_node_child_process6.execSync)("git check-ignore -q -- .verity/.snapshot/__probe__", { stdio: "pipe" });
|
|
15539
|
+
ignored = true;
|
|
15540
|
+
} catch (err) {
|
|
15541
|
+
ignored = err.status === 1 ? false : null;
|
|
15542
|
+
}
|
|
15543
|
+
if (ignored === true) return "covered";
|
|
15544
|
+
if (ignored === null) {
|
|
15545
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
15546
|
+
const covering = [".verity/.snapshot/", ".verity/.snapshot", ".verity/", ".verity", ".verity/*"];
|
|
15547
|
+
if (lines.some((l) => covering.includes(l))) return "covered";
|
|
15548
|
+
}
|
|
15549
|
+
try {
|
|
15550
|
+
const block = "# Verity \u2014 snapshots of analyzed files (machine state, never commit)\n.verity/.snapshot/\n";
|
|
15551
|
+
(0, import_node_fs16.writeFileSync)(".gitignore", content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block);
|
|
15552
|
+
return "added";
|
|
15553
|
+
} catch {
|
|
15554
|
+
return "failed";
|
|
15555
|
+
}
|
|
15556
|
+
}
|
|
15516
15557
|
function saveSnapshots(files) {
|
|
15517
15558
|
const snapshotPaths = /* @__PURE__ */ new Set();
|
|
15518
15559
|
for (const file of files) {
|
|
15519
|
-
|
|
15560
|
+
if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
|
|
15561
|
+
const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
|
|
15520
15562
|
snapshotPaths.add(snapshotPath);
|
|
15521
|
-
(0,
|
|
15522
|
-
(0,
|
|
15563
|
+
(0, import_node_fs16.mkdirSync)((0, import_node_path15.dirname)(snapshotPath), { recursive: true });
|
|
15564
|
+
(0, import_node_fs16.writeFileSync)(snapshotPath, file.content);
|
|
15523
15565
|
}
|
|
15524
15566
|
cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
|
|
15525
15567
|
}
|
|
15526
15568
|
function computeDiff(oldContent, newContent, filePath) {
|
|
15527
|
-
const tmpOld = (0,
|
|
15528
|
-
const tmpNew = (0,
|
|
15569
|
+
const tmpOld = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-old.tmp");
|
|
15570
|
+
const tmpNew = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-new.tmp");
|
|
15529
15571
|
try {
|
|
15530
|
-
(0,
|
|
15531
|
-
(0,
|
|
15532
|
-
(0,
|
|
15572
|
+
(0, import_node_fs16.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
|
|
15573
|
+
(0, import_node_fs16.writeFileSync)(tmpOld, oldContent);
|
|
15574
|
+
(0, import_node_fs16.writeFileSync)(tmpNew, newContent);
|
|
15533
15575
|
const result = (0, import_node_child_process6.execSync)(
|
|
15534
15576
|
`git diff --no-index --unified=10 -- "${tmpOld}" "${tmpNew}"`,
|
|
15535
15577
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -15543,32 +15585,32 @@ function computeDiff(oldContent, newContent, filePath) {
|
|
|
15543
15585
|
return null;
|
|
15544
15586
|
} finally {
|
|
15545
15587
|
try {
|
|
15546
|
-
(0,
|
|
15588
|
+
(0, import_node_fs16.unlinkSync)(tmpOld);
|
|
15547
15589
|
} catch {
|
|
15548
15590
|
}
|
|
15549
15591
|
try {
|
|
15550
|
-
(0,
|
|
15592
|
+
(0, import_node_fs16.unlinkSync)(tmpNew);
|
|
15551
15593
|
} catch {
|
|
15552
15594
|
}
|
|
15553
15595
|
}
|
|
15554
15596
|
}
|
|
15555
15597
|
function cleanStaleSnapshots(dir, keepSet) {
|
|
15556
|
-
if (!(0,
|
|
15598
|
+
if (!(0, import_node_fs16.existsSync)(dir)) return;
|
|
15557
15599
|
try {
|
|
15558
|
-
const entries = (0,
|
|
15600
|
+
const entries = (0, import_node_fs16.readdirSync)(dir, { withFileTypes: true });
|
|
15559
15601
|
for (const entry of entries) {
|
|
15560
|
-
if (entry.name.
|
|
15561
|
-
const fullPath = (0,
|
|
15602
|
+
if (dir === SNAPSHOT_DIR && (entry.name === ".diff-old.tmp" || entry.name === ".diff-new.tmp")) continue;
|
|
15603
|
+
const fullPath = (0, import_node_path15.join)(dir, entry.name);
|
|
15562
15604
|
if (entry.isDirectory()) {
|
|
15563
15605
|
cleanStaleSnapshots(fullPath, keepSet);
|
|
15564
15606
|
try {
|
|
15565
|
-
const remaining = (0,
|
|
15566
|
-
if (remaining.length === 0) (0,
|
|
15607
|
+
const remaining = (0, import_node_fs16.readdirSync)(fullPath);
|
|
15608
|
+
if (remaining.length === 0) (0, import_node_fs16.rmdirSync)(fullPath);
|
|
15567
15609
|
} catch {
|
|
15568
15610
|
}
|
|
15569
15611
|
} else if (!keepSet.has(fullPath)) {
|
|
15570
15612
|
try {
|
|
15571
|
-
(0,
|
|
15613
|
+
(0, import_node_fs16.unlinkSync)(fullPath);
|
|
15572
15614
|
} catch {
|
|
15573
15615
|
}
|
|
15574
15616
|
}
|
|
@@ -15587,20 +15629,20 @@ function sessionKey(sessionId) {
|
|
|
15587
15629
|
return (0, import_node_crypto9.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
15588
15630
|
}
|
|
15589
15631
|
function sessionDir(key) {
|
|
15590
|
-
return (0,
|
|
15632
|
+
return (0, import_node_path16.join)(projectPath(BASELINE_DIR), key);
|
|
15591
15633
|
}
|
|
15592
15634
|
function manifestPath(dir) {
|
|
15593
|
-
return (0,
|
|
15635
|
+
return (0, import_node_path16.join)(dir, "manifest.json");
|
|
15594
15636
|
}
|
|
15595
15637
|
function mirrorPath(dir, repoRelPath) {
|
|
15596
|
-
return (0,
|
|
15638
|
+
return (0, import_node_path16.join)(dir, "files", repoRelPath);
|
|
15597
15639
|
}
|
|
15598
15640
|
var CARRY_FILE = `${BASELINE_DIR}/.carry`;
|
|
15599
15641
|
var CARRY_WINDOW_MS = 12e4;
|
|
15600
15642
|
function writeCarry(sessionId, headSha) {
|
|
15601
15643
|
try {
|
|
15602
|
-
(0,
|
|
15603
|
-
(0,
|
|
15644
|
+
(0, import_node_fs17.mkdirSync)(projectPath(BASELINE_DIR), { recursive: true });
|
|
15645
|
+
(0, import_node_fs17.writeFileSync)(
|
|
15604
15646
|
projectPath(CARRY_FILE),
|
|
15605
15647
|
JSON.stringify({ from_key: sessionKey(sessionId), head_sha: headSha, ts: Date.now() })
|
|
15606
15648
|
);
|
|
@@ -15610,10 +15652,10 @@ function writeCarry(sessionId, headSha) {
|
|
|
15610
15652
|
function claimCarry(newKey) {
|
|
15611
15653
|
const carryPath = projectPath(CARRY_FILE);
|
|
15612
15654
|
try {
|
|
15613
|
-
if (!(0,
|
|
15614
|
-
const carry = JSON.parse((0,
|
|
15655
|
+
if (!(0, import_node_fs17.existsSync)(carryPath)) return null;
|
|
15656
|
+
const carry = JSON.parse((0, import_node_fs17.readFileSync)(carryPath, "utf-8"));
|
|
15615
15657
|
try {
|
|
15616
|
-
(0,
|
|
15658
|
+
(0, import_node_fs17.rmSync)(carryPath, { force: true });
|
|
15617
15659
|
} catch {
|
|
15618
15660
|
}
|
|
15619
15661
|
if (!carry?.from_key || typeof carry.ts !== "number") return null;
|
|
@@ -15624,11 +15666,11 @@ function claimCarry(newKey) {
|
|
|
15624
15666
|
if (!prior) return null;
|
|
15625
15667
|
const toDir = sessionDir(newKey);
|
|
15626
15668
|
try {
|
|
15627
|
-
(0,
|
|
15669
|
+
(0, import_node_fs17.rmSync)(toDir, { recursive: true, force: true });
|
|
15628
15670
|
} catch {
|
|
15629
15671
|
}
|
|
15630
|
-
(0,
|
|
15631
|
-
(0,
|
|
15672
|
+
(0, import_node_fs17.renameSync)(fromDir, toDir);
|
|
15673
|
+
(0, import_node_fs17.writeFileSync)(manifestPath(toDir), JSON.stringify({ ...prior, session_id: newKey }) + "\n");
|
|
15632
15674
|
return readManifest(toDir);
|
|
15633
15675
|
} catch {
|
|
15634
15676
|
return null;
|
|
@@ -15652,21 +15694,21 @@ function captureBaseline(opts = {}) {
|
|
|
15652
15694
|
const head_sha = getCurrentCommit();
|
|
15653
15695
|
const dirty = getDirtyFiles();
|
|
15654
15696
|
try {
|
|
15655
|
-
(0,
|
|
15697
|
+
(0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
|
|
15656
15698
|
} catch {
|
|
15657
15699
|
}
|
|
15658
|
-
const filesDir = (0,
|
|
15700
|
+
const filesDir = (0, import_node_path16.join)(dir, "files");
|
|
15659
15701
|
const mirrored = [];
|
|
15660
15702
|
try {
|
|
15661
|
-
(0,
|
|
15703
|
+
(0, import_node_fs17.mkdirSync)(filesDir, { recursive: true });
|
|
15662
15704
|
for (const p of dirty) {
|
|
15663
15705
|
if (p.includes("..")) continue;
|
|
15664
15706
|
const content = safeReadForMirror(projectPath(p));
|
|
15665
15707
|
if (content === null) continue;
|
|
15666
15708
|
const dest = mirrorPath(dir, p);
|
|
15667
15709
|
try {
|
|
15668
|
-
(0,
|
|
15669
|
-
(0,
|
|
15710
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
15711
|
+
(0, import_node_fs17.writeFileSync)(dest, content);
|
|
15670
15712
|
mirrored.push(p);
|
|
15671
15713
|
} catch {
|
|
15672
15714
|
}
|
|
@@ -15681,8 +15723,8 @@ function captureBaseline(opts = {}) {
|
|
|
15681
15723
|
version: BASELINE_VERSION
|
|
15682
15724
|
};
|
|
15683
15725
|
try {
|
|
15684
|
-
(0,
|
|
15685
|
-
(0,
|
|
15726
|
+
(0, import_node_fs17.mkdirSync)(dir, { recursive: true });
|
|
15727
|
+
(0, import_node_fs17.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
|
|
15686
15728
|
} catch {
|
|
15687
15729
|
}
|
|
15688
15730
|
pruneOldBaselines();
|
|
@@ -15693,9 +15735,9 @@ function readBaseline(sessionId) {
|
|
|
15693
15735
|
}
|
|
15694
15736
|
function readManifest(dir) {
|
|
15695
15737
|
const mp = manifestPath(dir);
|
|
15696
|
-
if (!(0,
|
|
15738
|
+
if (!(0, import_node_fs17.existsSync)(mp)) return null;
|
|
15697
15739
|
try {
|
|
15698
|
-
const parsed = JSON.parse((0,
|
|
15740
|
+
const parsed = JSON.parse((0, import_node_fs17.readFileSync)(mp, "utf-8"));
|
|
15699
15741
|
if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
|
|
15700
15742
|
return null;
|
|
15701
15743
|
}
|
|
@@ -15726,9 +15768,9 @@ function preImage(repoRelPath, baseline) {
|
|
|
15726
15768
|
function resolvePreImage(repoRelPath, baseline) {
|
|
15727
15769
|
if (baseline.dirty_paths.includes(repoRelPath)) {
|
|
15728
15770
|
const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
|
|
15729
|
-
if ((0,
|
|
15771
|
+
if ((0, import_node_fs17.existsSync)(mp)) {
|
|
15730
15772
|
try {
|
|
15731
|
-
return { content: (0,
|
|
15773
|
+
return { content: (0, import_node_fs17.readFileSync)(mp, "utf-8"), existed: true };
|
|
15732
15774
|
} catch {
|
|
15733
15775
|
}
|
|
15734
15776
|
}
|
|
@@ -15773,8 +15815,8 @@ function absorbIntoBaseline(paths, sessionId) {
|
|
|
15773
15815
|
const content = safeReadForMirror(projectPath(p));
|
|
15774
15816
|
if (content === null) continue;
|
|
15775
15817
|
const dest = mirrorPath(dir, p);
|
|
15776
|
-
(0,
|
|
15777
|
-
(0,
|
|
15818
|
+
(0, import_node_fs17.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
15819
|
+
(0, import_node_fs17.writeFileSync)(dest, content);
|
|
15778
15820
|
dirty.add(p);
|
|
15779
15821
|
adopted++;
|
|
15780
15822
|
} catch {
|
|
@@ -15783,7 +15825,7 @@ function absorbIntoBaseline(paths, sessionId) {
|
|
|
15783
15825
|
if (adopted === 0) return 0;
|
|
15784
15826
|
try {
|
|
15785
15827
|
const updated = { ...baseline, dirty_paths: [...dirty] };
|
|
15786
|
-
(0,
|
|
15828
|
+
(0, import_node_fs17.writeFileSync)(manifestPath(dir), JSON.stringify(updated));
|
|
15787
15829
|
preImageCache.delete(baseline);
|
|
15788
15830
|
} catch {
|
|
15789
15831
|
return 0;
|
|
@@ -15794,7 +15836,7 @@ function changedSinceBaseline(repoRelPath, baseline) {
|
|
|
15794
15836
|
const pre = preImage(repoRelPath, baseline);
|
|
15795
15837
|
let current;
|
|
15796
15838
|
try {
|
|
15797
|
-
current = (0,
|
|
15839
|
+
current = (0, import_node_fs17.readFileSync)(projectPath(repoRelPath), "utf-8");
|
|
15798
15840
|
} catch {
|
|
15799
15841
|
return pre.existed;
|
|
15800
15842
|
}
|
|
@@ -15803,8 +15845,8 @@ function changedSinceBaseline(repoRelPath, baseline) {
|
|
|
15803
15845
|
}
|
|
15804
15846
|
function safeReadForMirror(absPath) {
|
|
15805
15847
|
try {
|
|
15806
|
-
if ((0,
|
|
15807
|
-
const buf = (0,
|
|
15848
|
+
if ((0, import_node_fs17.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
|
|
15849
|
+
const buf = (0, import_node_fs17.readFileSync)(absPath);
|
|
15808
15850
|
if (buf.includes(0)) return null;
|
|
15809
15851
|
return buf.toString("utf-8");
|
|
15810
15852
|
} catch {
|
|
@@ -15815,18 +15857,18 @@ function pruneOldBaselines() {
|
|
|
15815
15857
|
const root = projectPath(BASELINE_DIR);
|
|
15816
15858
|
let entries;
|
|
15817
15859
|
try {
|
|
15818
|
-
entries = (0,
|
|
15860
|
+
entries = (0, import_node_fs17.readdirSync)(root);
|
|
15819
15861
|
} catch {
|
|
15820
15862
|
return;
|
|
15821
15863
|
}
|
|
15822
15864
|
const now = Date.now();
|
|
15823
15865
|
for (const name of entries) {
|
|
15824
|
-
const dir = (0,
|
|
15866
|
+
const dir = (0, import_node_path16.join)(root, name);
|
|
15825
15867
|
const manifest = readManifest(dir);
|
|
15826
15868
|
if (!manifest) {
|
|
15827
15869
|
try {
|
|
15828
|
-
if (now - (0,
|
|
15829
|
-
(0,
|
|
15870
|
+
if (now - (0, import_node_fs17.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
|
|
15871
|
+
(0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
|
|
15830
15872
|
}
|
|
15831
15873
|
} catch {
|
|
15832
15874
|
}
|
|
@@ -15834,7 +15876,7 @@ function pruneOldBaselines() {
|
|
|
15834
15876
|
}
|
|
15835
15877
|
if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
|
|
15836
15878
|
try {
|
|
15837
|
-
(0,
|
|
15879
|
+
(0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
|
|
15838
15880
|
} catch {
|
|
15839
15881
|
}
|
|
15840
15882
|
}
|
|
@@ -16007,8 +16049,8 @@ function buildCompactionContext(session) {
|
|
|
16007
16049
|
commitsSince: commitsSincePaths(watermark, (state.authored ?? []).map((a) => a.path)),
|
|
16008
16050
|
readFileLines: (file) => {
|
|
16009
16051
|
try {
|
|
16010
|
-
const abs = (0,
|
|
16011
|
-
return (0,
|
|
16052
|
+
const abs = (0, import_node_path17.join)(root, file);
|
|
16053
|
+
return (0, import_node_fs18.existsSync)(abs) ? (0, import_node_fs18.readFileSync)(abs, "utf8").split("\n") : null;
|
|
16012
16054
|
} catch {
|
|
16013
16055
|
return null;
|
|
16014
16056
|
}
|
|
@@ -16044,17 +16086,17 @@ async function readHookStdin() {
|
|
|
16044
16086
|
try {
|
|
16045
16087
|
if (process.stdin.isTTY) return {};
|
|
16046
16088
|
const chunks = [];
|
|
16047
|
-
const timeout = new Promise((
|
|
16048
|
-
const read = new Promise((
|
|
16089
|
+
const timeout = new Promise((resolve4) => setTimeout(() => resolve4({}), 500));
|
|
16090
|
+
const read = new Promise((resolve4) => {
|
|
16049
16091
|
process.stdin.on("data", (c) => chunks.push(c));
|
|
16050
16092
|
process.stdin.on("end", () => {
|
|
16051
16093
|
try {
|
|
16052
|
-
|
|
16094
|
+
resolve4(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
|
|
16053
16095
|
} catch {
|
|
16054
|
-
|
|
16096
|
+
resolve4({});
|
|
16055
16097
|
}
|
|
16056
16098
|
});
|
|
16057
|
-
process.stdin.on("error", () =>
|
|
16099
|
+
process.stdin.on("error", () => resolve4({}));
|
|
16058
16100
|
process.stdin.resume();
|
|
16059
16101
|
});
|
|
16060
16102
|
return await Promise.race([read, timeout]);
|
|
@@ -16572,6 +16614,7 @@ function createRun(opts, globals) {
|
|
|
16572
16614
|
phaseReached: "",
|
|
16573
16615
|
phasesCompleted: [],
|
|
16574
16616
|
skipReason: null,
|
|
16617
|
+
treeFrame: null,
|
|
16575
16618
|
turnId: "",
|
|
16576
16619
|
// The value `resolveReachability` itself returns when every rung declines.
|
|
16577
16620
|
reachability: { human_reachable: "unknown", human_reachable_source: "inferred", rung: "none" },
|
|
@@ -16640,7 +16683,7 @@ function createRun(opts, globals) {
|
|
|
16640
16683
|
}
|
|
16641
16684
|
|
|
16642
16685
|
// src/lib/stderr-log.ts
|
|
16643
|
-
var
|
|
16686
|
+
var import_node_fs19 = require("node:fs");
|
|
16644
16687
|
var TOKEN_RE2 = /verity_[0-9a-f]{16,}/g;
|
|
16645
16688
|
var ANSI_RE = /\u001b\[[0-?]*[ -/]*[@-~]/g;
|
|
16646
16689
|
function scrub(s) {
|
|
@@ -16653,9 +16696,9 @@ function append(text) {
|
|
|
16653
16696
|
try {
|
|
16654
16697
|
const dir = projectPath(DEBUG_LOG_DIR);
|
|
16655
16698
|
const file = projectPath(STDERR_LOG_FILE);
|
|
16656
|
-
(0,
|
|
16699
|
+
(0, import_node_fs19.mkdirSync)(dir, { recursive: true });
|
|
16657
16700
|
rotateIfNeeded(file);
|
|
16658
|
-
(0,
|
|
16701
|
+
(0, import_node_fs19.appendFileSync)(file, text);
|
|
16659
16702
|
} catch {
|
|
16660
16703
|
}
|
|
16661
16704
|
}
|
|
@@ -16711,13 +16754,17 @@ function formatRunEvidence(run, startedAt) {
|
|
|
16711
16754
|
`;
|
|
16712
16755
|
out += row("turn", `${run.turnId || "(unminted)"}${run.sessionId ? ` \xB7 session ${run.sessionId}` : ""}`);
|
|
16713
16756
|
out += row("reached", `${run.phaseReached || "(none)"}${run.skipReason ? ` \xB7 SKIPPED: ${run.skipReason}` : ""} \xB7 ${ms}ms`);
|
|
16757
|
+
if (run.treeFrame) {
|
|
16758
|
+
const f = run.treeFrame;
|
|
16759
|
+
out += row("tree", f.worktreeRoot ? `${f.worktreeRoot}${f.isLinkedWorktree ? " \xB7 linked worktree" : ""}${f.branch ? ` \xB7 branch ${f.branch}` : " \xB7 detached"}` : `(unresolved: ${f.refusal ?? "unknown"})`);
|
|
16760
|
+
}
|
|
16714
16761
|
out += row("changed", `${run.changedUniverse.length} from git \xB7 analyzable ${run.analyzable.length} \xB7 reviewable ${run.reviewable.length} \xB7 security ${run.securityFiles.length} \xB7 forReview ${run.allForReview.length}`);
|
|
16715
16762
|
const done = (phase) => run.phasesCompleted.includes(phase);
|
|
16716
16763
|
const ifDone = (phase, value) => done(phase) ? value : "?";
|
|
16717
16764
|
const md = run.modeDecision;
|
|
16718
16765
|
if (md) {
|
|
16719
16766
|
const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
|
|
16720
|
-
out += row("mode", `${md.resolved} \xB7 ${how} \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
|
|
16767
|
+
out += row("mode", `${md.resolved} \xB7 ${how}` + (md.flip ? ` (flipped to plan: no delta at ${md.flip})` : "") + ` \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
|
|
16721
16768
|
} else {
|
|
16722
16769
|
out += row("mode", `? (this run stopped in ${run.phaseReached || "no phase"}, before the mode was decided)`);
|
|
16723
16770
|
}
|
|
@@ -16807,6 +16854,301 @@ function installRunEvidence(run) {
|
|
|
16807
16854
|
});
|
|
16808
16855
|
}
|
|
16809
16856
|
|
|
16857
|
+
// src/lib/git-frame.ts
|
|
16858
|
+
var import_node_child_process7 = require("node:child_process");
|
|
16859
|
+
var import_node_fs20 = require("node:fs");
|
|
16860
|
+
var import_node_os3 = require("node:os");
|
|
16861
|
+
var import_node_path18 = require("node:path");
|
|
16862
|
+
var import_node_path19 = require("node:path");
|
|
16863
|
+
var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
|
|
16864
|
+
var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
|
|
16865
|
+
var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
|
|
16866
|
+
var PUSH_HEAD = `git${GIT_GLOBAL_OPTS}\\s+push\\b`;
|
|
16867
|
+
var GH_PR_HEAD = `gh${GIT_GLOBAL_OPTS}\\s+pr\\s+create\\b`;
|
|
16868
|
+
var COMMIT_RE = new RegExp(`(?:^|[\\s;&|(])${COMMIT_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${COMMIT_HEAD}`);
|
|
16869
|
+
var PUSH_RE = new RegExp(`(?:^|[\\s;&|(])${PUSH_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${PUSH_HEAD}`);
|
|
16870
|
+
var GH_PR_RE = new RegExp(`(?:^|[\\s;&|(])${GH_PR_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${GH_PR_HEAD}`);
|
|
16871
|
+
function splitSegments(command) {
|
|
16872
|
+
return (command ?? "").split(/&&|\|\||;|\n/);
|
|
16873
|
+
}
|
|
16874
|
+
function findMomentSegment(command, on) {
|
|
16875
|
+
const segments = splitSegments(command);
|
|
16876
|
+
let commitIdx = -1;
|
|
16877
|
+
let pushIdx = -1;
|
|
16878
|
+
for (let i = 0; i < segments.length; i++) {
|
|
16879
|
+
const seg = segments[i];
|
|
16880
|
+
if (/--dry-run\b/.test(seg)) continue;
|
|
16881
|
+
if (commitIdx === -1 && COMMIT_RE.test(seg)) commitIdx = i;
|
|
16882
|
+
if (pushIdx === -1 && (PUSH_RE.test(seg) || GH_PR_RE.test(seg))) pushIdx = i;
|
|
16883
|
+
}
|
|
16884
|
+
if (commitIdx !== -1 && on.includes("commit")) return { moment: "pre-commit", segmentIndex: commitIdx };
|
|
16885
|
+
if (pushIdx !== -1 && on.includes("push")) return { moment: "pre-push", segmentIndex: pushIdx };
|
|
16886
|
+
return null;
|
|
16887
|
+
}
|
|
16888
|
+
function classifyCommand(command, on) {
|
|
16889
|
+
return findMomentSegment(command, on)?.moment ?? null;
|
|
16890
|
+
}
|
|
16891
|
+
function unquote(token) {
|
|
16892
|
+
if (token.length >= 2) {
|
|
16893
|
+
const first = token[0];
|
|
16894
|
+
if ((first === "'" || first === '"') && token.endsWith(first)) return token.slice(1, -1);
|
|
16895
|
+
}
|
|
16896
|
+
return token;
|
|
16897
|
+
}
|
|
16898
|
+
var SHELL_DYNAMIC = /[$`\\]/;
|
|
16899
|
+
function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
16900
|
+
const segments = splitSegments(command);
|
|
16901
|
+
let dir = baseDir;
|
|
16902
|
+
let named = false;
|
|
16903
|
+
for (let i = 0; i < segmentIndex; i++) {
|
|
16904
|
+
const m = segments[i].match(new RegExp(`^\\s*cd(?:\\s+(${VALUE_TOKEN}))?\\s*$`));
|
|
16905
|
+
if (!m) continue;
|
|
16906
|
+
named = true;
|
|
16907
|
+
if (m[1] === void 0) {
|
|
16908
|
+
dir = (0, import_node_os3.homedir)();
|
|
16909
|
+
continue;
|
|
16910
|
+
}
|
|
16911
|
+
const raw = unquote(m[1]);
|
|
16912
|
+
if (SHELL_DYNAMIC.test(raw) || raw === "-") {
|
|
16913
|
+
return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
|
|
16914
|
+
}
|
|
16915
|
+
const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
|
|
16916
|
+
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
16917
|
+
}
|
|
16918
|
+
const seg = segments[segmentIndex];
|
|
16919
|
+
const overrideMatch = /--(?:git-dir|work-tree)(?:=|\s)|\bGIT_(?:DIR|WORK_TREE|INDEX_FILE)=/.exec(seg);
|
|
16920
|
+
if (overrideMatch) {
|
|
16921
|
+
return { dir: null, named: true, unresolvable: `git-dir/work-tree/index override in command: ${overrideMatch[0].trim()}` };
|
|
16922
|
+
}
|
|
16923
|
+
const gitMatch = seg.match(new RegExp(`(?:^|[\\s;&|(])(?:[^\\s;&|()'"]*\\/)?git(${GIT_GLOBAL_OPTS})\\s`));
|
|
16924
|
+
if (gitMatch) {
|
|
16925
|
+
const optsRegion = gitMatch[1] ?? "";
|
|
16926
|
+
const cRe = new RegExp(`-C\\s+(${VALUE_TOKEN})`, "g");
|
|
16927
|
+
let cm;
|
|
16928
|
+
while ((cm = cRe.exec(optsRegion)) !== null) {
|
|
16929
|
+
named = true;
|
|
16930
|
+
const raw = unquote(cm[1]);
|
|
16931
|
+
if (SHELL_DYNAMIC.test(raw)) {
|
|
16932
|
+
return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
|
|
16933
|
+
}
|
|
16934
|
+
const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
|
|
16935
|
+
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
16936
|
+
}
|
|
16937
|
+
}
|
|
16938
|
+
return { dir, named, unresolvable: null };
|
|
16939
|
+
}
|
|
16940
|
+
var PUSH_VALUE_FLAGS = /* @__PURE__ */ new Set(["-o", "--push-option", "--receive-pack", "--exec"]);
|
|
16941
|
+
function parsePushTarget(segment) {
|
|
16942
|
+
const none = { remote: null, srcRef: null, dstRef: null, isDelete: false };
|
|
16943
|
+
const m = PUSH_RE.exec(segment);
|
|
16944
|
+
if (!m) return none;
|
|
16945
|
+
const rest = segment.slice(m.index + m[0].length);
|
|
16946
|
+
const tokens = (rest.match(new RegExp(`'[^']*'|"[^"]*"|\\S+`, "g")) ?? []).map(unquote);
|
|
16947
|
+
let remote = null;
|
|
16948
|
+
let refspec = null;
|
|
16949
|
+
let isDelete = false;
|
|
16950
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
16951
|
+
const t = tokens[i];
|
|
16952
|
+
if (t === "-d" || t === "--delete") {
|
|
16953
|
+
isDelete = true;
|
|
16954
|
+
continue;
|
|
16955
|
+
}
|
|
16956
|
+
if (t.startsWith("--repo=")) {
|
|
16957
|
+
remote = t.slice("--repo=".length);
|
|
16958
|
+
continue;
|
|
16959
|
+
}
|
|
16960
|
+
if (t === "--repo") {
|
|
16961
|
+
remote = tokens[++i] ?? null;
|
|
16962
|
+
continue;
|
|
16963
|
+
}
|
|
16964
|
+
if (PUSH_VALUE_FLAGS.has(t)) {
|
|
16965
|
+
i++;
|
|
16966
|
+
continue;
|
|
16967
|
+
}
|
|
16968
|
+
if (t.startsWith("-")) continue;
|
|
16969
|
+
if (SHELL_DYNAMIC.test(t)) return none;
|
|
16970
|
+
if (remote === null) {
|
|
16971
|
+
remote = t;
|
|
16972
|
+
continue;
|
|
16973
|
+
}
|
|
16974
|
+
if (refspec === null) {
|
|
16975
|
+
refspec = t;
|
|
16976
|
+
continue;
|
|
16977
|
+
}
|
|
16978
|
+
break;
|
|
16979
|
+
}
|
|
16980
|
+
if (refspec === null) return { remote, srcRef: null, dstRef: null, isDelete };
|
|
16981
|
+
const spec = refspec.startsWith("+") ? refspec.slice(1) : refspec;
|
|
16982
|
+
const colon = spec.indexOf(":");
|
|
16983
|
+
if (colon === -1) return { remote, srcRef: spec, dstRef: null, isDelete };
|
|
16984
|
+
const src = spec.slice(0, colon);
|
|
16985
|
+
const dst = spec.slice(colon + 1);
|
|
16986
|
+
if (src === "") return { remote, srcRef: null, dstRef: dst || null, isDelete: true };
|
|
16987
|
+
return { remote, srcRef: src, dstRef: dst || null, isDelete };
|
|
16988
|
+
}
|
|
16989
|
+
function gitAt(dir, args) {
|
|
16990
|
+
try {
|
|
16991
|
+
return (0, import_node_child_process7.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
16992
|
+
} catch {
|
|
16993
|
+
return "";
|
|
16994
|
+
}
|
|
16995
|
+
}
|
|
16996
|
+
function realpathOr(p) {
|
|
16997
|
+
try {
|
|
16998
|
+
return import_node_fs20.realpathSync.native(p);
|
|
16999
|
+
} catch {
|
|
17000
|
+
return (0, import_node_path18.resolve)(p);
|
|
17001
|
+
}
|
|
17002
|
+
}
|
|
17003
|
+
function resolveFrame(input) {
|
|
17004
|
+
const found = findMomentSegment(input.command, input.on);
|
|
17005
|
+
const hookDirUsable = !!input.hookCwd && (0, import_node_fs20.existsSync)(input.hookCwd);
|
|
17006
|
+
const baseDir = hookDirUsable ? input.hookCwd : process.cwd();
|
|
17007
|
+
let anchor = hookDirUsable ? "hook-cwd" : "process-cwd";
|
|
17008
|
+
const refuse = (refusal) => ({
|
|
17009
|
+
moment: found?.moment ?? null,
|
|
17010
|
+
frame: { worktreeRoot: null, gitDir: null, commonDir: null, isLinkedWorktree: false, branch: null, anchor, refusal }
|
|
17011
|
+
});
|
|
17012
|
+
let dir = baseDir;
|
|
17013
|
+
if (found) {
|
|
17014
|
+
const segments = splitSegments(input.command);
|
|
17015
|
+
const kindRes = found.moment === "pre-commit" ? [COMMIT_RE] : [PUSH_RE, GH_PR_RE];
|
|
17016
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
17017
|
+
for (let i = 0; i < segments.length; i++) {
|
|
17018
|
+
if (/--dry-run\b/.test(segments[i])) continue;
|
|
17019
|
+
if (!kindRes.some((re) => re.test(segments[i]))) continue;
|
|
17020
|
+
const target = extractCommandTarget(input.command, i, baseDir);
|
|
17021
|
+
if (target.named) anchor = "command-target";
|
|
17022
|
+
if (target.unresolvable) return refuse(`target:${target.unresolvable}`);
|
|
17023
|
+
dirs.add(target.dir ?? baseDir);
|
|
17024
|
+
}
|
|
17025
|
+
if (dirs.size > 1) return refuse(`target:multiple ${found.moment} targets in one command`);
|
|
17026
|
+
const targetDir = dirs.size === 1 ? [...dirs][0] : baseDir;
|
|
17027
|
+
if (targetDir !== baseDir) {
|
|
17028
|
+
if (!(0, import_node_fs20.existsSync)(targetDir)) return refuse(`target:directory does not exist: ${targetDir}`);
|
|
17029
|
+
dir = targetDir;
|
|
17030
|
+
}
|
|
17031
|
+
}
|
|
17032
|
+
const toplevel = gitAt(dir, ["rev-parse", "--show-toplevel"]);
|
|
17033
|
+
if (!toplevel) {
|
|
17034
|
+
return refuse(anchor === "command-target" ? `target:not a git repository: ${dir}` : `anchor:not a git repository: ${dir}`);
|
|
17035
|
+
}
|
|
17036
|
+
const gitDirRaw = gitAt(dir, ["rev-parse", "--absolute-git-dir"]);
|
|
17037
|
+
const commonRaw = gitAt(dir, ["rev-parse", "--git-common-dir"]);
|
|
17038
|
+
const gitDir = gitDirRaw ? realpathOr(gitDirRaw) : null;
|
|
17039
|
+
const commonDir = commonRaw ? realpathOr((0, import_node_path18.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path18.resolve)(dir, commonRaw)) : null;
|
|
17040
|
+
const branchRaw = gitAt(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
17041
|
+
return {
|
|
17042
|
+
moment: found?.moment ?? null,
|
|
17043
|
+
frame: {
|
|
17044
|
+
worktreeRoot: realpathOr(toplevel),
|
|
17045
|
+
gitDir,
|
|
17046
|
+
commonDir,
|
|
17047
|
+
// The one honest definition: a linked worktree's own git dir differs from
|
|
17048
|
+
// the shared one. No path convention involved — see the header.
|
|
17049
|
+
isLinkedWorktree: !!gitDir && !!commonDir && gitDir !== commonDir,
|
|
17050
|
+
branch: !branchRaw || branchRaw === "HEAD" ? null : branchRaw,
|
|
17051
|
+
anchor,
|
|
17052
|
+
refusal: null
|
|
17053
|
+
}
|
|
17054
|
+
};
|
|
17055
|
+
}
|
|
17056
|
+
function frameGit(frame, args) {
|
|
17057
|
+
if (!frame.worktreeRoot) return "";
|
|
17058
|
+
return gitAt(frame.worktreeRoot, args);
|
|
17059
|
+
}
|
|
17060
|
+
function refResolves(frame, ref) {
|
|
17061
|
+
return frameGit(frame, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]) !== "";
|
|
17062
|
+
}
|
|
17063
|
+
var SHA_RE2 = /^[0-9a-f]{40}$/;
|
|
17064
|
+
function baselineShaAt(frame) {
|
|
17065
|
+
if (!frame.worktreeRoot) return null;
|
|
17066
|
+
try {
|
|
17067
|
+
const sha = (0, import_node_fs20.readFileSync)((0, import_node_path19.join)(frame.worktreeRoot, BASELINE_SHA_FILE), "utf-8").trim();
|
|
17068
|
+
if (!SHA_RE2.test(sha)) return null;
|
|
17069
|
+
return refResolves(frame, sha) ? sha : null;
|
|
17070
|
+
} catch {
|
|
17071
|
+
return null;
|
|
17072
|
+
}
|
|
17073
|
+
}
|
|
17074
|
+
function stagedRange() {
|
|
17075
|
+
return { kind: "staged", base: "HEAD", head: "INDEX", via: "index" };
|
|
17076
|
+
}
|
|
17077
|
+
function resolvePushRange(frame, command, on) {
|
|
17078
|
+
const nothing = (via) => ({ kind: "nothing", base: null, head: "HEAD", via });
|
|
17079
|
+
if (!frame.worktreeRoot) return nothing("refused");
|
|
17080
|
+
const found = findMomentSegment(command, on);
|
|
17081
|
+
const segment = found ? splitSegments(command)[found.segmentIndex] : "";
|
|
17082
|
+
const target = parsePushTarget(segment);
|
|
17083
|
+
if (target.isDelete) return nothing("deletion");
|
|
17084
|
+
const head = target.srcRef ?? "HEAD";
|
|
17085
|
+
if (!refResolves(frame, head)) return nothing(`src-unresolvable:${head}`);
|
|
17086
|
+
const srcName = target.srcRef;
|
|
17087
|
+
const branchForRemote = srcName ?? frame.branch;
|
|
17088
|
+
const candidates = [];
|
|
17089
|
+
if (target.remote && (target.dstRef ?? srcName)) {
|
|
17090
|
+
const dstName = (target.dstRef ?? srcName).replace(/^refs\/heads\//, "");
|
|
17091
|
+
candidates.push({ ref: `refs/remotes/${target.remote}/${dstName}`, via: `refspec:${target.remote}/${dstName}` });
|
|
17092
|
+
}
|
|
17093
|
+
if (target.remote && !srcName && !target.dstRef && frame.branch) {
|
|
17094
|
+
candidates.push({ ref: `refs/remotes/${target.remote}/${frame.branch}`, via: `remote:${target.remote}/${frame.branch}` });
|
|
17095
|
+
}
|
|
17096
|
+
candidates.push({ ref: srcName ? `${srcName}@{push}` : "@{push}", via: "@{push}" });
|
|
17097
|
+
candidates.push({ ref: srcName ? `${srcName}@{upstream}` : "@{upstream}", via: "@{upstream}" });
|
|
17098
|
+
if (branchForRemote) {
|
|
17099
|
+
candidates.push({
|
|
17100
|
+
ref: `refs/remotes/origin/${branchForRemote.replace(/^refs\/heads\//, "")}`,
|
|
17101
|
+
via: `origin/${branchForRemote.replace(/^refs\/heads\//, "")}`
|
|
17102
|
+
});
|
|
17103
|
+
}
|
|
17104
|
+
for (const c of candidates) {
|
|
17105
|
+
if (!refResolves(frame, c.ref)) continue;
|
|
17106
|
+
const mergeBase = frameGit(frame, ["merge-base", c.ref, head]);
|
|
17107
|
+
if (SHA_RE2.test(mergeBase)) return { kind: "push", base: mergeBase, head, via: c.via };
|
|
17108
|
+
}
|
|
17109
|
+
const baseline = baselineShaAt(frame);
|
|
17110
|
+
if (baseline) return { kind: "baseline", base: baseline, head, via: "review-baseline" };
|
|
17111
|
+
if (refResolves(frame, `${head}~1`)) return { kind: "last-commit", base: `${head}~1`, head, via: `${head}~1` };
|
|
17112
|
+
return nothing("no-parent");
|
|
17113
|
+
}
|
|
17114
|
+
function rangeFiles(frame, range) {
|
|
17115
|
+
let out;
|
|
17116
|
+
switch (range.kind) {
|
|
17117
|
+
case "staged":
|
|
17118
|
+
out = frameGit(frame, ["diff", "--cached", "--name-only"]);
|
|
17119
|
+
break;
|
|
17120
|
+
case "push":
|
|
17121
|
+
case "baseline":
|
|
17122
|
+
case "last-commit":
|
|
17123
|
+
out = frameGit(frame, ["diff", "--name-only", range.base, range.head === "INDEX" ? "HEAD" : range.head]);
|
|
17124
|
+
break;
|
|
17125
|
+
case "nothing":
|
|
17126
|
+
return [];
|
|
17127
|
+
}
|
|
17128
|
+
return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
|
|
17129
|
+
}
|
|
17130
|
+
function rangeMessages(frame, range) {
|
|
17131
|
+
if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
|
|
17132
|
+
return frameGit(frame, ["log", `${range.base}..${range.head === "INDEX" ? "HEAD" : range.head}`, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
17133
|
+
}
|
|
17134
|
+
function frameTelemetry(frame, range, divergence) {
|
|
17135
|
+
const t = {
|
|
17136
|
+
anchor: frame.anchor,
|
|
17137
|
+
linked_worktree: frame.isLinkedWorktree,
|
|
17138
|
+
range_via: range?.via ?? null,
|
|
17139
|
+
refusal: frame.refusal
|
|
17140
|
+
};
|
|
17141
|
+
if (divergence) {
|
|
17142
|
+
t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr(frame.worktreeRoot) !== realpathOr(divergence.actualRoot);
|
|
17143
|
+
const a = [...divergence.actualFiles].sort().join("\n");
|
|
17144
|
+
const b = [...divergence.frameFiles].sort().join("\n");
|
|
17145
|
+
t.files_differ = a !== b;
|
|
17146
|
+
t.frame_file_count = divergence.frameFiles.length;
|
|
17147
|
+
t.actual_file_count = divergence.actualFiles.length;
|
|
17148
|
+
}
|
|
17149
|
+
return t;
|
|
17150
|
+
}
|
|
17151
|
+
|
|
16810
17152
|
// src/lib/reachability.ts
|
|
16811
17153
|
function resolveReachability(input = {}) {
|
|
16812
17154
|
const env = input.env ?? {};
|
|
@@ -16835,7 +17177,7 @@ function truthy(v) {
|
|
|
16835
17177
|
}
|
|
16836
17178
|
|
|
16837
17179
|
// src/lib/transcript.ts
|
|
16838
|
-
var
|
|
17180
|
+
var import_node_fs21 = require("node:fs");
|
|
16839
17181
|
var MAX_READ_BYTES = 256 * 1024;
|
|
16840
17182
|
var SMALL_FILE_BYTES = 64 * 1024;
|
|
16841
17183
|
var MAX_FILES_LIST = 20;
|
|
@@ -16859,7 +17201,7 @@ async function extractActionSummary(transcriptPath) {
|
|
|
16859
17201
|
function readTurnLines(transcriptPath) {
|
|
16860
17202
|
let size;
|
|
16861
17203
|
try {
|
|
16862
|
-
size = (0,
|
|
17204
|
+
size = (0, import_node_fs21.statSync)(transcriptPath).size;
|
|
16863
17205
|
} catch {
|
|
16864
17206
|
return null;
|
|
16865
17207
|
}
|
|
@@ -16867,7 +17209,7 @@ function readTurnLines(transcriptPath) {
|
|
|
16867
17209
|
let raw;
|
|
16868
17210
|
let windowed = false;
|
|
16869
17211
|
if (size <= SMALL_FILE_BYTES) {
|
|
16870
|
-
raw = (0,
|
|
17212
|
+
raw = (0, import_node_fs21.readFileSync)(transcriptPath, "utf-8");
|
|
16871
17213
|
} else {
|
|
16872
17214
|
windowed = true;
|
|
16873
17215
|
const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
|
|
@@ -17047,11 +17389,11 @@ function sanitizeCommand(rawCmd) {
|
|
|
17047
17389
|
let cmd = rawCmd.split("\n")[0];
|
|
17048
17390
|
let cut = -1;
|
|
17049
17391
|
let marker = "";
|
|
17050
|
-
for (const
|
|
17051
|
-
const idx = cmd.indexOf(
|
|
17392
|
+
for (const sep2 of [" | ", " > ", " >> ", " 2>", " && ", " ; "]) {
|
|
17393
|
+
const idx = cmd.indexOf(sep2);
|
|
17052
17394
|
if (idx > 0 && (cut === -1 || idx < cut)) {
|
|
17053
17395
|
cut = idx;
|
|
17054
|
-
marker =
|
|
17396
|
+
marker = sep2.trim();
|
|
17055
17397
|
}
|
|
17056
17398
|
}
|
|
17057
17399
|
if (cut > -1) cmd = cmd.slice(0, cut);
|
|
@@ -17075,28 +17417,28 @@ async function readStopHookStdin() {
|
|
|
17075
17417
|
try {
|
|
17076
17418
|
if (process.stdin.isTTY) return empty;
|
|
17077
17419
|
const chunks = [];
|
|
17078
|
-
const timeout = new Promise((
|
|
17079
|
-
const read = new Promise((
|
|
17420
|
+
const timeout = new Promise((resolve4) => setTimeout(() => resolve4(empty), 500));
|
|
17421
|
+
const read = new Promise((resolve4) => {
|
|
17080
17422
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
17081
17423
|
process.stdin.on("end", () => {
|
|
17082
17424
|
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
17083
17425
|
if (!raw) {
|
|
17084
|
-
|
|
17426
|
+
resolve4(empty);
|
|
17085
17427
|
return;
|
|
17086
17428
|
}
|
|
17087
17429
|
try {
|
|
17088
17430
|
const data = JSON.parse(raw);
|
|
17089
|
-
|
|
17431
|
+
resolve4({
|
|
17090
17432
|
assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
|
|
17091
17433
|
stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null,
|
|
17092
17434
|
transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null,
|
|
17093
17435
|
sessionId: typeof data.session_id === "string" ? data.session_id : null
|
|
17094
17436
|
});
|
|
17095
17437
|
} catch {
|
|
17096
|
-
|
|
17438
|
+
resolve4(empty);
|
|
17097
17439
|
}
|
|
17098
17440
|
});
|
|
17099
|
-
process.stdin.on("error", () =>
|
|
17441
|
+
process.stdin.on("error", () => resolve4(empty));
|
|
17100
17442
|
process.stdin.resume();
|
|
17101
17443
|
});
|
|
17102
17444
|
return await Promise.race([read, timeout]);
|
|
@@ -17110,6 +17452,7 @@ async function bootstrap(run) {
|
|
|
17110
17452
|
process.chdir(repoRoot());
|
|
17111
17453
|
} catch {
|
|
17112
17454
|
}
|
|
17455
|
+
run.treeFrame = resolveFrame({ command: "", on: [], hookCwd: null }).frame;
|
|
17113
17456
|
const turnId = `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
17114
17457
|
let reachability = resolveReachability({
|
|
17115
17458
|
autonomousFlag: process.env.VERITY_AUTONOMOUS === "1" || opts.mode === "autonomous",
|
|
@@ -17146,6 +17489,39 @@ async function bootstrap(run) {
|
|
|
17146
17489
|
Object.assign(run, { actionSummary, assistantResponse, baseline, baselineSessionId, reachability, sessionId, stopReason, tokenResult, transcriptPath, turnId });
|
|
17147
17490
|
}
|
|
17148
17491
|
|
|
17492
|
+
// src/lib/self-scope.ts
|
|
17493
|
+
var LEGACY_GATE_SKILLS = /* @__PURE__ */ new Set([
|
|
17494
|
+
"gate-setup",
|
|
17495
|
+
"gate-analyze",
|
|
17496
|
+
"gate-review",
|
|
17497
|
+
"gate-status",
|
|
17498
|
+
"gate-feedback",
|
|
17499
|
+
"gate-insights",
|
|
17500
|
+
"gate-learn",
|
|
17501
|
+
"gate-memory",
|
|
17502
|
+
"gate-reflect"
|
|
17503
|
+
]);
|
|
17504
|
+
function isVerityOwned(path) {
|
|
17505
|
+
const segments = path.replace(/\\/g, "/").split("/");
|
|
17506
|
+
for (let i = 0; i < segments.length; i++) {
|
|
17507
|
+
const seg = segments[i];
|
|
17508
|
+
if (seg === ".verity" || seg === ".codacy") return true;
|
|
17509
|
+
if (i === segments.length - 1 && (seg === "VERITY.md" || seg === "GATE.md")) return true;
|
|
17510
|
+
if (seg === ".claude" && segments[i + 1] === "skills" && typeof segments[i + 2] === "string") {
|
|
17511
|
+
const skill = segments[i + 2];
|
|
17512
|
+
if (skill.startsWith("verity-") || LEGACY_GATE_SKILLS.has(skill)) return true;
|
|
17513
|
+
}
|
|
17514
|
+
if (seg === ".claude" && segments[i + 1] === "settings.json") return true;
|
|
17515
|
+
}
|
|
17516
|
+
return false;
|
|
17517
|
+
}
|
|
17518
|
+
function partitionVerityOwned(paths) {
|
|
17519
|
+
const kept = [];
|
|
17520
|
+
const owned = [];
|
|
17521
|
+
for (const p of paths) (isVerityOwned(p) ? owned : kept).push(p);
|
|
17522
|
+
return { kept, owned };
|
|
17523
|
+
}
|
|
17524
|
+
|
|
17149
17525
|
// src/lib/channel.ts
|
|
17150
17526
|
var MAX_AGENT_CONTEXT_CHARS = 1500;
|
|
17151
17527
|
var MAX_AGENT_ITEMS = 5;
|
|
@@ -17155,6 +17531,29 @@ function renderItem(label2, text, patternId, file, line) {
|
|
|
17155
17531
|
const id = patternId ? ` [${patternId}]` : "";
|
|
17156
17532
|
return `- ${label2}${text}${where}${id}`;
|
|
17157
17533
|
}
|
|
17534
|
+
function channelInputFrom(response, intentRepeat = 0, priorPendingFingerprints = []) {
|
|
17535
|
+
const metadata = response.metadata ?? {};
|
|
17536
|
+
const intent = response.intent_alignment ?? {};
|
|
17537
|
+
return {
|
|
17538
|
+
intentRepeat,
|
|
17539
|
+
priorPendingFingerprints,
|
|
17540
|
+
gateDecision: String(response.gate_decision ?? ""),
|
|
17541
|
+
findings: response.findings ?? [],
|
|
17542
|
+
pendingItems: response.pending_items ?? [],
|
|
17543
|
+
reviewStatus: metadata.review_status,
|
|
17544
|
+
coverage: metadata.coverage,
|
|
17545
|
+
intentVerdict: intent.verdict,
|
|
17546
|
+
intentGaps: intent.gaps
|
|
17547
|
+
};
|
|
17548
|
+
}
|
|
17549
|
+
function classifyChannelContent(input) {
|
|
17550
|
+
const refusal = input.reviewStatus === "not_reviewed" || input.reviewStatus === "no_authorship_evidence";
|
|
17551
|
+
const intentFlag = input.intentVerdict === "misaligned" || input.intentVerdict === "partial";
|
|
17552
|
+
const advisory = (input.findings ?? []).some((f) => f.scope !== "pre-existing") || (input.pendingItems ?? []).some(
|
|
17553
|
+
(p) => p.pattern_id !== "intent-misalignment" && !!(p.description ?? p.title ?? p.reason)
|
|
17554
|
+
);
|
|
17555
|
+
return { refusal, intentFlag, advisory };
|
|
17556
|
+
}
|
|
17158
17557
|
function buildAgentContext(input) {
|
|
17159
17558
|
const lines = [];
|
|
17160
17559
|
if (input.reviewStatus === "not_reviewed") {
|
|
@@ -17240,7 +17639,7 @@ function channelSilence(input) {
|
|
|
17240
17639
|
// src/lib/cli-version.ts
|
|
17241
17640
|
function cliVersion() {
|
|
17242
17641
|
try {
|
|
17243
|
-
return true ? "0.
|
|
17642
|
+
return true ? "0.30.0-experimental.43e7755" : "dev";
|
|
17244
17643
|
} catch {
|
|
17245
17644
|
return "dev";
|
|
17246
17645
|
}
|
|
@@ -17280,8 +17679,8 @@ async function sendSkipBeacon(ctx, reason) {
|
|
|
17280
17679
|
}
|
|
17281
17680
|
|
|
17282
17681
|
// src/lib/static-analysis.ts
|
|
17283
|
-
var
|
|
17284
|
-
var
|
|
17682
|
+
var import_node_child_process8 = require("node:child_process");
|
|
17683
|
+
var import_node_fs22 = require("node:fs");
|
|
17285
17684
|
var SEVERITY_ORDER = {
|
|
17286
17685
|
Error: 0,
|
|
17287
17686
|
Critical: 0,
|
|
@@ -17293,7 +17692,7 @@ var SEVERITY_ORDER = {
|
|
|
17293
17692
|
};
|
|
17294
17693
|
function isCodacyAvailable() {
|
|
17295
17694
|
try {
|
|
17296
|
-
(0,
|
|
17695
|
+
(0, import_node_child_process8.execSync)("which codacy-analysis", { stdio: "pipe" });
|
|
17297
17696
|
return true;
|
|
17298
17697
|
} catch {
|
|
17299
17698
|
return false;
|
|
@@ -17329,13 +17728,13 @@ function runCodacyAnalysis(files) {
|
|
|
17329
17728
|
if (files.length === 0) return empty;
|
|
17330
17729
|
const existingFiles = files.filter((f) => {
|
|
17331
17730
|
try {
|
|
17332
|
-
return (0,
|
|
17731
|
+
return (0, import_node_fs22.existsSync)(f);
|
|
17333
17732
|
} catch {
|
|
17334
17733
|
return false;
|
|
17335
17734
|
}
|
|
17336
17735
|
});
|
|
17337
17736
|
if (existingFiles.length === 0) return empty;
|
|
17338
|
-
const proc = (0,
|
|
17737
|
+
const proc = (0, import_node_child_process8.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
|
|
17339
17738
|
encoding: "utf-8",
|
|
17340
17739
|
maxBuffer: 10 * 1024 * 1024
|
|
17341
17740
|
});
|
|
@@ -17562,9 +17961,10 @@ async function scope(run) {
|
|
|
17562
17961
|
const { assistantResponse } = run;
|
|
17563
17962
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
17564
17963
|
run.changedUniverse = allChanged;
|
|
17565
|
-
const
|
|
17566
|
-
const
|
|
17567
|
-
const
|
|
17964
|
+
const { kept: external } = partitionVerityOwned(allChanged);
|
|
17965
|
+
const analyzable = filterAnalyzable(external);
|
|
17966
|
+
const reviewable = filterReviewable(external);
|
|
17967
|
+
const securityFiles = filterSecurity(external);
|
|
17568
17968
|
const noFilesChanged = analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0;
|
|
17569
17969
|
if (noFilesChanged && !assistantResponse) {
|
|
17570
17970
|
await passAndExit(run, "No analyzable files changed", "no-analyzable-files");
|
|
@@ -17574,8 +17974,8 @@ async function scope(run) {
|
|
|
17574
17974
|
}
|
|
17575
17975
|
|
|
17576
17976
|
// src/lib/specs.ts
|
|
17577
|
-
var
|
|
17578
|
-
var
|
|
17977
|
+
var import_node_fs23 = require("node:fs");
|
|
17978
|
+
var import_node_path20 = require("node:path");
|
|
17579
17979
|
var SPEC_CANDIDATES = [
|
|
17580
17980
|
"CLAUDE.md",
|
|
17581
17981
|
"AGENTS.md",
|
|
@@ -17606,16 +18006,16 @@ function discoverSpecs(consulted = []) {
|
|
|
17606
18006
|
const totalCap = relevant ? MAX_TOTAL_SPEC_BYTES : UNCONSULTED_TOTAL_BYTES;
|
|
17607
18007
|
if (totalBytes >= totalCap) return false;
|
|
17608
18008
|
if (seen.has(specPath)) return true;
|
|
17609
|
-
if (!(0,
|
|
18009
|
+
if (!(0, import_node_fs23.existsSync)(specPath)) return true;
|
|
17610
18010
|
seen.add(specPath);
|
|
17611
18011
|
const remaining = totalCap - totalBytes;
|
|
17612
18012
|
const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
|
|
17613
18013
|
const readBytes = Math.min(fileCap, remaining);
|
|
17614
18014
|
try {
|
|
17615
18015
|
const buf = Buffer.alloc(readBytes);
|
|
17616
|
-
const fd = (0,
|
|
17617
|
-
const bytesRead = (0,
|
|
17618
|
-
(0,
|
|
18016
|
+
const fd = (0, import_node_fs23.openSync)(specPath, "r");
|
|
18017
|
+
const bytesRead = (0, import_node_fs23.readSync)(fd, buf, 0, readBytes, 0);
|
|
18018
|
+
(0, import_node_fs23.closeSync)(fd);
|
|
17619
18019
|
const content = buf.slice(0, bytesRead).toString("utf-8");
|
|
17620
18020
|
if (!content) return true;
|
|
17621
18021
|
result.push({ path: specPath, content });
|
|
@@ -17631,7 +18031,7 @@ function discoverSpecs(consulted = []) {
|
|
|
17631
18031
|
if (!addSpec(candidate)) break;
|
|
17632
18032
|
}
|
|
17633
18033
|
for (const dir of ["spec", "docs"]) {
|
|
17634
|
-
if (!(0,
|
|
18034
|
+
if (!(0, import_node_fs23.existsSync)(dir)) continue;
|
|
17635
18035
|
try {
|
|
17636
18036
|
const mdFiles = findMdFiles(dir, 2).sort();
|
|
17637
18037
|
for (const mdFile of mdFiles) {
|
|
@@ -17646,9 +18046,9 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
17646
18046
|
if (depth >= maxDepth) return [];
|
|
17647
18047
|
const result = [];
|
|
17648
18048
|
try {
|
|
17649
|
-
const entries = (0,
|
|
18049
|
+
const entries = (0, import_node_fs23.readdirSync)(dir, { withFileTypes: true });
|
|
17650
18050
|
for (const entry of entries) {
|
|
17651
|
-
const fullPath = (0,
|
|
18051
|
+
const fullPath = (0, import_node_path20.join)(dir, entry.name);
|
|
17652
18052
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
17653
18053
|
result.push(fullPath);
|
|
17654
18054
|
} else if (entry.isDirectory() && depth < maxDepth - 1) {
|
|
@@ -17660,19 +18060,19 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
17660
18060
|
return result;
|
|
17661
18061
|
}
|
|
17662
18062
|
function discoverPlans() {
|
|
17663
|
-
const homePlansDir = (0,
|
|
18063
|
+
const homePlansDir = (0, import_node_path20.join)(process.env.HOME ?? "", ".claude", "plans");
|
|
17664
18064
|
const localPlansDir = ".claude/plans";
|
|
17665
18065
|
const candidates = [];
|
|
17666
18066
|
const seen = /* @__PURE__ */ new Set();
|
|
17667
18067
|
for (const plansDir of [localPlansDir, homePlansDir]) {
|
|
17668
|
-
if (!(0,
|
|
18068
|
+
if (!(0, import_node_fs23.existsSync)(plansDir)) continue;
|
|
17669
18069
|
try {
|
|
17670
|
-
for (const f of (0,
|
|
18070
|
+
for (const f of (0, import_node_fs23.readdirSync)(plansDir)) {
|
|
17671
18071
|
if (!f.endsWith(".md") || seen.has(f)) continue;
|
|
17672
18072
|
seen.add(f);
|
|
17673
|
-
const fullPath = (0,
|
|
18073
|
+
const fullPath = (0, import_node_path20.join)(plansDir, f);
|
|
17674
18074
|
try {
|
|
17675
|
-
const stat3 = (0,
|
|
18075
|
+
const stat3 = (0, import_node_fs23.statSync)(fullPath);
|
|
17676
18076
|
candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
|
|
17677
18077
|
} catch {
|
|
17678
18078
|
}
|
|
@@ -17685,7 +18085,7 @@ function discoverPlans() {
|
|
|
17685
18085
|
for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
|
|
17686
18086
|
if (entry.size > MAX_PLAN_FILE_BYTES) continue;
|
|
17687
18087
|
try {
|
|
17688
|
-
const content = (0,
|
|
18088
|
+
const content = (0, import_node_fs23.readFileSync)(entry.path, "utf-8");
|
|
17689
18089
|
result.push({ name: entry.name, content });
|
|
17690
18090
|
} catch {
|
|
17691
18091
|
}
|
|
@@ -17960,7 +18360,7 @@ async function mode(run) {
|
|
|
17960
18360
|
}
|
|
17961
18361
|
|
|
17962
18362
|
// src/lib/debounce.ts
|
|
17963
|
-
var
|
|
18363
|
+
var import_node_fs24 = require("node:fs");
|
|
17964
18364
|
var import_node_crypto10 = require("node:crypto");
|
|
17965
18365
|
function scopedFile(base, sessionId) {
|
|
17966
18366
|
if (!sessionId) return base;
|
|
@@ -17968,9 +18368,9 @@ function scopedFile(base, sessionId) {
|
|
|
17968
18368
|
}
|
|
17969
18369
|
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
17970
18370
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
17971
|
-
if (!(0,
|
|
18371
|
+
if (!(0, import_node_fs24.existsSync)(file)) return null;
|
|
17972
18372
|
try {
|
|
17973
|
-
const lastTs = parseInt((0,
|
|
18373
|
+
const lastTs = parseInt((0, import_node_fs24.readFileSync)(file, "utf-8").trim(), 10);
|
|
17974
18374
|
const nowTs = Math.floor(Date.now() / 1e3);
|
|
17975
18375
|
const elapsed = nowTs - lastTs;
|
|
17976
18376
|
if (elapsed < debounceSeconds) {
|
|
@@ -17983,10 +18383,10 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
|
17983
18383
|
function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
17984
18384
|
if (bypassForRecentCommits) return null;
|
|
17985
18385
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
17986
|
-
if (!(0,
|
|
18386
|
+
if (!(0, import_node_fs24.existsSync)(file)) return null;
|
|
17987
18387
|
let debounceTime;
|
|
17988
18388
|
try {
|
|
17989
|
-
debounceTime = (0,
|
|
18389
|
+
debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
|
|
17990
18390
|
} catch {
|
|
17991
18391
|
return null;
|
|
17992
18392
|
}
|
|
@@ -17994,7 +18394,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
|
17994
18394
|
const resolved = resolveFile(f);
|
|
17995
18395
|
if (!resolved) continue;
|
|
17996
18396
|
try {
|
|
17997
|
-
const stat3 = (0,
|
|
18397
|
+
const stat3 = (0, import_node_fs24.statSync)(resolved);
|
|
17998
18398
|
if (stat3.mtimeMs > debounceTime) {
|
|
17999
18399
|
return null;
|
|
18000
18400
|
}
|
|
@@ -18010,8 +18410,8 @@ function computeContentHash(files) {
|
|
|
18010
18410
|
for (const f of sorted) {
|
|
18011
18411
|
const resolved = resolveFile(f) ?? f;
|
|
18012
18412
|
try {
|
|
18013
|
-
if ((0,
|
|
18014
|
-
hash.update((0,
|
|
18413
|
+
if ((0, import_node_fs24.existsSync)(resolved)) {
|
|
18414
|
+
hash.update((0, import_node_fs24.readFileSync)(resolved));
|
|
18015
18415
|
}
|
|
18016
18416
|
} catch {
|
|
18017
18417
|
}
|
|
@@ -18021,9 +18421,9 @@ function computeContentHash(files) {
|
|
|
18021
18421
|
function checkContentHash(files, sessionId) {
|
|
18022
18422
|
const hash = computeContentHash(files);
|
|
18023
18423
|
const file = scopedFile(HASH_FILE, sessionId);
|
|
18024
|
-
if ((0,
|
|
18424
|
+
if ((0, import_node_fs24.existsSync)(file)) {
|
|
18025
18425
|
try {
|
|
18026
|
-
const storedHash = (0,
|
|
18426
|
+
const storedHash = (0, import_node_fs24.readFileSync)(file, "utf-8").trim();
|
|
18027
18427
|
if (hash === storedHash) {
|
|
18028
18428
|
return { skip: "No source changes since last analysis", hash };
|
|
18029
18429
|
}
|
|
@@ -18033,50 +18433,74 @@ function checkContentHash(files, sessionId) {
|
|
|
18033
18433
|
return { skip: null, hash };
|
|
18034
18434
|
}
|
|
18035
18435
|
function recordAnalysisStart(sessionId) {
|
|
18036
|
-
(0,
|
|
18037
|
-
(0,
|
|
18436
|
+
(0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18437
|
+
(0, import_node_fs24.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
|
|
18038
18438
|
}
|
|
18039
18439
|
function recordPassHash(hash, sessionId) {
|
|
18040
|
-
(0,
|
|
18440
|
+
(0, import_node_fs24.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
|
|
18041
18441
|
}
|
|
18042
18442
|
function narrowToRecent(files, sessionId) {
|
|
18043
18443
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
18044
|
-
if (!(0,
|
|
18444
|
+
if (!(0, import_node_fs24.existsSync)(file)) return files;
|
|
18045
18445
|
let debounceTime;
|
|
18046
18446
|
try {
|
|
18047
|
-
debounceTime = (0,
|
|
18447
|
+
debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
|
|
18048
18448
|
} catch {
|
|
18049
18449
|
return files;
|
|
18050
18450
|
}
|
|
18051
18451
|
const recent = files.filter((f) => {
|
|
18052
18452
|
try {
|
|
18053
|
-
return (0,
|
|
18453
|
+
return (0, import_node_fs24.existsSync)(f) && (0, import_node_fs24.statSync)(f).mtimeMs > debounceTime;
|
|
18054
18454
|
} catch {
|
|
18055
18455
|
return false;
|
|
18056
18456
|
}
|
|
18057
18457
|
});
|
|
18058
18458
|
return recent.length > 0 ? recent : files;
|
|
18059
18459
|
}
|
|
18060
|
-
function
|
|
18061
|
-
|
|
18460
|
+
function readIteration(currentCommit, _contentHash) {
|
|
18461
|
+
return Math.max(1, readBlockState(currentCommit).attempts);
|
|
18462
|
+
}
|
|
18463
|
+
var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
|
|
18464
|
+
function readBlockState(currentCommit, opts) {
|
|
18465
|
+
if (opts?.newUserPrompt) return NO_BLOCKS;
|
|
18466
|
+
if (!(0, import_node_fs24.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
|
|
18062
18467
|
try {
|
|
18063
|
-
const stored = (0,
|
|
18064
|
-
const
|
|
18065
|
-
|
|
18066
|
-
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
if (isNaN(iter)) return { iteration: 1, fingerprint: null };
|
|
18070
|
-
if (storedCommit !== currentCommit) return { iteration: 1, fingerprint: null };
|
|
18071
|
-
if (storedTimestamp > 0) {
|
|
18072
|
-
const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
|
|
18073
|
-
if (elapsed > 600) return { iteration: 1, fingerprint: null };
|
|
18074
|
-
}
|
|
18075
|
-
return { iteration: iter, fingerprint };
|
|
18468
|
+
const stored = (0, import_node_fs24.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
18469
|
+
const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
|
|
18470
|
+
if (!parsed) return NO_BLOCKS;
|
|
18471
|
+
if (parsed.commit !== currentCommit) return NO_BLOCKS;
|
|
18472
|
+
if (parsed.ts > 0 && Math.floor(Date.now() / 1e3) - parsed.ts > 600) return NO_BLOCKS;
|
|
18473
|
+
return { attempts: parsed.attempts, blocks: parsed.blocks, fingerprint: parsed.fingerprint };
|
|
18076
18474
|
} catch {
|
|
18077
|
-
return
|
|
18475
|
+
return NO_BLOCKS;
|
|
18078
18476
|
}
|
|
18079
18477
|
}
|
|
18478
|
+
function parseJsonState(raw) {
|
|
18479
|
+
const o = JSON.parse(raw);
|
|
18480
|
+
const attempts = typeof o.attempts === "number" ? o.attempts : NaN;
|
|
18481
|
+
if (isNaN(attempts)) return null;
|
|
18482
|
+
return {
|
|
18483
|
+
attempts,
|
|
18484
|
+
blocks: typeof o.blocks === "number" ? o.blocks : attempts,
|
|
18485
|
+
fingerprint: typeof o.fingerprint === "string" && o.fingerprint ? o.fingerprint : null,
|
|
18486
|
+
commit: typeof o.commit === "string" ? o.commit : "",
|
|
18487
|
+
ts: typeof o.ts === "number" ? o.ts : 0
|
|
18488
|
+
};
|
|
18489
|
+
}
|
|
18490
|
+
function parseLegacyState(raw) {
|
|
18491
|
+
const parts = raw.split(":");
|
|
18492
|
+
const n = parseInt(parts[0], 10);
|
|
18493
|
+
if (isNaN(n)) return null;
|
|
18494
|
+
return {
|
|
18495
|
+
attempts: n,
|
|
18496
|
+
// The old file has no separate block count; the old counter is the closest
|
|
18497
|
+
// honest answer, and it errs toward releasing sooner rather than later.
|
|
18498
|
+
blocks: n,
|
|
18499
|
+
fingerprint: parts.slice(3).join(":") || null,
|
|
18500
|
+
commit: parts[1] ?? "",
|
|
18501
|
+
ts: parseInt(parts[2] ?? "0", 10)
|
|
18502
|
+
};
|
|
18503
|
+
}
|
|
18080
18504
|
function findingsFingerprint(findings) {
|
|
18081
18505
|
const keys = findings.map((f) => `${String(f.pattern_id ?? "?")}|${String(f.file ?? "?")}`).filter((k) => k !== "?|?");
|
|
18082
18506
|
return [...new Set(keys)].sort().join(",");
|
|
@@ -18086,16 +18510,27 @@ function isSameProblem(previous, current) {
|
|
|
18086
18510
|
const prev = new Set(previous.split(","));
|
|
18087
18511
|
return current.split(",").some((k) => prev.has(k));
|
|
18088
18512
|
}
|
|
18089
|
-
function
|
|
18090
|
-
(0,
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18513
|
+
function writeBlockState(commit, state) {
|
|
18514
|
+
(0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18515
|
+
(0, import_node_fs24.writeFileSync)(
|
|
18516
|
+
ITERATION_FILE,
|
|
18517
|
+
JSON.stringify({
|
|
18518
|
+
v: 2,
|
|
18519
|
+
attempts: state.attempts,
|
|
18520
|
+
blocks: state.blocks,
|
|
18521
|
+
commit,
|
|
18522
|
+
ts: Math.floor(Date.now() / 1e3),
|
|
18523
|
+
fingerprint: state.fingerprint ?? void 0
|
|
18524
|
+
})
|
|
18525
|
+
);
|
|
18526
|
+
}
|
|
18527
|
+
function resetBlockState(commit) {
|
|
18528
|
+
writeBlockState(commit, { attempts: 0, blocks: 0, fingerprint: null });
|
|
18094
18529
|
}
|
|
18095
18530
|
|
|
18096
18531
|
// src/lib/fold.ts
|
|
18097
|
-
var
|
|
18098
|
-
var
|
|
18532
|
+
var import_node_fs25 = require("node:fs");
|
|
18533
|
+
var import_node_path21 = require("node:path");
|
|
18099
18534
|
var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
18100
18535
|
"user",
|
|
18101
18536
|
"assistant",
|
|
@@ -18133,7 +18568,7 @@ var COMMAND_CLASSES = [
|
|
|
18133
18568
|
[/\btsc\b|\bmypy\b|\btypecheck\b/, "typecheck"],
|
|
18134
18569
|
[/^git\s/, "git"]
|
|
18135
18570
|
];
|
|
18136
|
-
function
|
|
18571
|
+
function classifyCommand2(cmd) {
|
|
18137
18572
|
for (const [re, cls] of COMMAND_CLASSES) if (re.test(cmd)) return cls;
|
|
18138
18573
|
return "other";
|
|
18139
18574
|
}
|
|
@@ -18232,7 +18667,7 @@ function candidateRoots(repoRoot2) {
|
|
|
18232
18667
|
const norm = repoRoot2.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
18233
18668
|
const out = [norm];
|
|
18234
18669
|
try {
|
|
18235
|
-
const real =
|
|
18670
|
+
const real = import_node_fs25.realpathSync.native(norm).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
18236
18671
|
if (real !== norm) out.push(real);
|
|
18237
18672
|
} catch {
|
|
18238
18673
|
}
|
|
@@ -18282,8 +18717,10 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18282
18717
|
subagentSkipped: 0,
|
|
18283
18718
|
compactions: 0,
|
|
18284
18719
|
complete: false
|
|
18285
|
-
}
|
|
18720
|
+
},
|
|
18721
|
+
planApproval: { approvals: 0, activeSinceLastPrompt: false }
|
|
18286
18722
|
};
|
|
18723
|
+
const flow = { seq: 0, lastPrompt: -1, lastApproval: -1, approvals: 0 };
|
|
18287
18724
|
const byPath = /* @__PURE__ */ new Map();
|
|
18288
18725
|
const commandStats = /* @__PURE__ */ new Map();
|
|
18289
18726
|
const pendingByToolUse = /* @__PURE__ */ new Map();
|
|
@@ -18309,36 +18746,40 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18309
18746
|
if (type === "system" && record.subtype === "compact_boundary") {
|
|
18310
18747
|
result.coverage.compactions++;
|
|
18311
18748
|
}
|
|
18312
|
-
if (type === "user" && hasUserText(record))
|
|
18313
|
-
|
|
18749
|
+
if (type === "user" && hasUserText(record)) {
|
|
18750
|
+
result.coverage.userMessages++;
|
|
18751
|
+
if (owner === "agent") flow.lastPrompt = flow.seq;
|
|
18752
|
+
}
|
|
18753
|
+
if (owner === "agent") flow.seq++;
|
|
18754
|
+
collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, opts.repoRoot, result.coverage, flow);
|
|
18314
18755
|
}
|
|
18315
18756
|
};
|
|
18316
18757
|
try {
|
|
18317
|
-
if (!(0,
|
|
18318
|
-
ingest((0,
|
|
18758
|
+
if (!(0, import_node_fs25.existsSync)(transcriptPath)) return result;
|
|
18759
|
+
ingest((0, import_node_fs25.readFileSync)(transcriptPath, "utf8"), "agent");
|
|
18319
18760
|
result.coverage.complete = true;
|
|
18320
18761
|
} catch {
|
|
18321
18762
|
return result;
|
|
18322
18763
|
}
|
|
18323
18764
|
try {
|
|
18324
|
-
const sidecarDir = (0,
|
|
18325
|
-
(0,
|
|
18326
|
-
(0,
|
|
18765
|
+
const sidecarDir = (0, import_node_path21.join)(
|
|
18766
|
+
(0, import_node_path21.dirname)(transcriptPath),
|
|
18767
|
+
(0, import_node_path21.basename)(transcriptPath).replace(/\.jsonl$/, ""),
|
|
18327
18768
|
"subagents"
|
|
18328
18769
|
);
|
|
18329
|
-
if ((0,
|
|
18770
|
+
if ((0, import_node_fs25.existsSync)(sidecarDir)) {
|
|
18330
18771
|
const maxFiles = opts.maxSidecars ?? 200;
|
|
18331
18772
|
const maxBytes = opts.maxSidecarBytes ?? 16 * 1024 * 1024;
|
|
18332
18773
|
const found = [];
|
|
18333
18774
|
const walk = (d, depth) => {
|
|
18334
18775
|
if (depth > 4) return;
|
|
18335
|
-
for (const e of (0,
|
|
18336
|
-
const p = (0,
|
|
18776
|
+
for (const e of (0, import_node_fs25.readdirSync)(d, { withFileTypes: true })) {
|
|
18777
|
+
const p = (0, import_node_path21.join)(d, e.name);
|
|
18337
18778
|
if (e.isDirectory()) {
|
|
18338
18779
|
walk(p, depth + 1);
|
|
18339
18780
|
} else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
|
|
18340
18781
|
try {
|
|
18341
|
-
const st = (0,
|
|
18782
|
+
const st = (0, import_node_fs25.statSync)(p);
|
|
18342
18783
|
found.push({ path: p, size: st.size, mtimeMs: st.mtimeMs });
|
|
18343
18784
|
} catch {
|
|
18344
18785
|
result.coverage.malformed++;
|
|
@@ -18355,7 +18796,7 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18355
18796
|
continue;
|
|
18356
18797
|
}
|
|
18357
18798
|
try {
|
|
18358
|
-
ingest((0,
|
|
18799
|
+
ingest((0, import_node_fs25.readFileSync)(f.path, "utf8"), "subagent");
|
|
18359
18800
|
bytes += f.size;
|
|
18360
18801
|
result.coverage.subagentFiles++;
|
|
18361
18802
|
} catch {
|
|
@@ -18382,11 +18823,15 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18382
18823
|
if (!p || authoredPaths.has(p)) continue;
|
|
18383
18824
|
result.unobserved.push({ p, cause: classifyUnobserved(raw) });
|
|
18384
18825
|
}
|
|
18826
|
+
result.planApproval = {
|
|
18827
|
+
approvals: flow.approvals,
|
|
18828
|
+
activeSinceLastPrompt: flow.lastApproval >= 0 && flow.lastApproval > flow.lastPrompt
|
|
18829
|
+
};
|
|
18385
18830
|
return result;
|
|
18386
18831
|
}
|
|
18387
18832
|
function classifyUnobserved(path) {
|
|
18388
18833
|
try {
|
|
18389
|
-
const st = (0,
|
|
18834
|
+
const st = (0, import_node_fs25.statSync)(path);
|
|
18390
18835
|
if (!st.isFile()) return "unreadable";
|
|
18391
18836
|
} catch {
|
|
18392
18837
|
return "unreadable";
|
|
@@ -18394,7 +18839,7 @@ function classifyUnobserved(path) {
|
|
|
18394
18839
|
if (/\.(png|jpg|jpeg|gif|pdf|zip|woff2?|ico|mp4)$/i.test(path)) return "binary";
|
|
18395
18840
|
return "no_edit_record";
|
|
18396
18841
|
}
|
|
18397
|
-
function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, repoRoot2, tally) {
|
|
18842
|
+
function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, repoRoot2, tally, flow) {
|
|
18398
18843
|
const message = record.message;
|
|
18399
18844
|
const content = message?.content ?? record.content;
|
|
18400
18845
|
const blocks = Array.isArray(content) ? content : content && typeof content === "object" ? [content] : [];
|
|
@@ -18452,7 +18897,7 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
|
|
|
18452
18897
|
if (name === "Bash") {
|
|
18453
18898
|
const cmd = typeof input.command === "string" ? input.command : "";
|
|
18454
18899
|
if (cmd) {
|
|
18455
|
-
const cls =
|
|
18900
|
+
const cls = classifyCommand2(cmd);
|
|
18456
18901
|
const prev = commandStats.get(cls) ?? { last_status: null, runs: 0, head: "" };
|
|
18457
18902
|
commandStats.set(cls, {
|
|
18458
18903
|
// UNKNOWN until this command's OWN result arrives. Inheriting the
|
|
@@ -18478,6 +18923,13 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
|
|
|
18478
18923
|
}
|
|
18479
18924
|
}
|
|
18480
18925
|
const toolName = id ? pendingToolName.get(id) : void 0;
|
|
18926
|
+
if (toolName === "ExitPlanMode" && flow && block.is_error !== true) {
|
|
18927
|
+
const body = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => typeof c.text === "string" ? c.text : "").join(" ") : "";
|
|
18928
|
+
if (/approved your plan/i.test(body)) {
|
|
18929
|
+
flow.lastApproval = flow.seq;
|
|
18930
|
+
flow.approvals += 1;
|
|
18931
|
+
}
|
|
18932
|
+
}
|
|
18481
18933
|
if (toolName) {
|
|
18482
18934
|
pendingToolName.delete(id);
|
|
18483
18935
|
const prevTool = toolStats.get(toolName);
|
|
@@ -18535,8 +18987,13 @@ function checkConservation(changedFiles, result, repoRoot2) {
|
|
|
18535
18987
|
// src/commands/analyze/phases/06-evidence.ts
|
|
18536
18988
|
async function evidence(run) {
|
|
18537
18989
|
const { opts } = run;
|
|
18538
|
-
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath } = run;
|
|
18990
|
+
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run;
|
|
18539
18991
|
let { analysisMode, earlyFold } = run;
|
|
18992
|
+
const recordFlip = (stage) => {
|
|
18993
|
+
if (run.modeDecision) run.modeDecision = { ...run.modeDecision, resolved: "plan", flip: stage };
|
|
18994
|
+
logEvent("mode_flipped", { stage, to: "plan" });
|
|
18995
|
+
};
|
|
18996
|
+
const planWorthy = !!assistantResponse && !turnAuthoredCode;
|
|
18540
18997
|
let staticResults = {
|
|
18541
18998
|
tool: "@codacy/analysis-cli",
|
|
18542
18999
|
findings: [],
|
|
@@ -18556,8 +19013,9 @@ async function evidence(run) {
|
|
|
18556
19013
|
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
18557
19014
|
const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
|
|
18558
19015
|
if (debounceSkip) {
|
|
18559
|
-
if (
|
|
19016
|
+
if (planWorthy) {
|
|
18560
19017
|
analysisMode = "plan";
|
|
19018
|
+
recordFlip("debounce");
|
|
18561
19019
|
} else {
|
|
18562
19020
|
await passAndExit(run, debounceSkip, "debounce");
|
|
18563
19021
|
}
|
|
@@ -18566,8 +19024,9 @@ async function evidence(run) {
|
|
|
18566
19024
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
18567
19025
|
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
|
|
18568
19026
|
if (mtimeSkip) {
|
|
18569
|
-
if (
|
|
19027
|
+
if (planWorthy) {
|
|
18570
19028
|
analysisMode = "plan";
|
|
19029
|
+
recordFlip("mtime");
|
|
18571
19030
|
} else {
|
|
18572
19031
|
await passAndExit(run, mtimeSkip, "no-delta-since-last-review");
|
|
18573
19032
|
}
|
|
@@ -18578,8 +19037,9 @@ async function evidence(run) {
|
|
|
18578
19037
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
18579
19038
|
const hashResult = checkContentHash(allCheckable, baselineSessionId);
|
|
18580
19039
|
if (hashResult.skip) {
|
|
18581
|
-
if (
|
|
19040
|
+
if (planWorthy) {
|
|
18582
19041
|
analysisMode = "plan";
|
|
19042
|
+
recordFlip("content-hash");
|
|
18583
19043
|
} else {
|
|
18584
19044
|
await passAndExit(run, hashResult.skip, "no-delta-since-last-review");
|
|
18585
19045
|
}
|
|
@@ -18641,8 +19101,9 @@ async function evidence(run) {
|
|
|
18641
19101
|
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
18642
19102
|
});
|
|
18643
19103
|
if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
|
|
18644
|
-
if (
|
|
19104
|
+
if (planWorthy) {
|
|
18645
19105
|
analysisMode = "plan";
|
|
19106
|
+
recordFlip("empty-after-scoping");
|
|
18646
19107
|
} else {
|
|
18647
19108
|
await passAndExit(
|
|
18648
19109
|
run,
|
|
@@ -18662,32 +19123,32 @@ async function evidence(run) {
|
|
|
18662
19123
|
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
18663
19124
|
}
|
|
18664
19125
|
currentCommit = getCurrentCommit();
|
|
18665
|
-
iteration =
|
|
19126
|
+
iteration = readIteration(currentCommit);
|
|
18666
19127
|
}
|
|
18667
19128
|
}
|
|
18668
19129
|
if (analysisMode === "plan") {
|
|
18669
19130
|
recordAnalysisStart();
|
|
18670
19131
|
currentCommit = getCurrentCommit();
|
|
18671
|
-
iteration =
|
|
19132
|
+
iteration = readIteration(currentCommit);
|
|
18672
19133
|
}
|
|
18673
19134
|
Object.assign(run, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
|
|
18674
19135
|
}
|
|
18675
19136
|
|
|
18676
19137
|
// src/lib/cache-cleanup.ts
|
|
18677
|
-
var
|
|
18678
|
-
var
|
|
19138
|
+
var import_node_fs26 = require("node:fs");
|
|
19139
|
+
var import_node_path22 = require("node:path");
|
|
18679
19140
|
var CACHE_TTL_DAYS = 7;
|
|
18680
19141
|
function pruneStaleCache() {
|
|
18681
19142
|
try {
|
|
18682
19143
|
const dir = projectPath(CACHE_DIR);
|
|
18683
19144
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
18684
|
-
for (const entry of (0,
|
|
19145
|
+
for (const entry of (0, import_node_fs26.readdirSync)(dir)) {
|
|
18685
19146
|
if (!entry.startsWith("pending-")) continue;
|
|
18686
|
-
const path = (0,
|
|
19147
|
+
const path = (0, import_node_path22.join)(dir, entry);
|
|
18687
19148
|
try {
|
|
18688
|
-
const stat3 = (0,
|
|
19149
|
+
const stat3 = (0, import_node_fs26.statSync)(path);
|
|
18689
19150
|
if (stat3.mtimeMs < cutoff) {
|
|
18690
|
-
(0,
|
|
19151
|
+
(0, import_node_fs26.unlinkSync)(path);
|
|
18691
19152
|
logEvent("cache_entry_pruned", {
|
|
18692
19153
|
path: entry,
|
|
18693
19154
|
age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
|
|
@@ -18701,7 +19162,7 @@ function pruneStaleCache() {
|
|
|
18701
19162
|
}
|
|
18702
19163
|
|
|
18703
19164
|
// src/lib/context-files.ts
|
|
18704
|
-
var
|
|
19165
|
+
var import_node_fs27 = require("node:fs");
|
|
18705
19166
|
var MAX_CONTEXT_FILES = 10;
|
|
18706
19167
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
18707
19168
|
var MAX_CONTEXT_TOTAL_BYTES = 51200;
|
|
@@ -18716,8 +19177,13 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
18716
19177
|
logEvent("context_file_skipped", { path: filePath, reason: "verity_owned" });
|
|
18717
19178
|
continue;
|
|
18718
19179
|
}
|
|
19180
|
+
const safePath = resolveInside(process.cwd(), filePath);
|
|
19181
|
+
if (!safePath) {
|
|
19182
|
+
logEvent("context_file_skipped", { path: filePath, reason: "outside_repo" });
|
|
19183
|
+
continue;
|
|
19184
|
+
}
|
|
18719
19185
|
try {
|
|
18720
|
-
const content = (0,
|
|
19186
|
+
const content = (0, import_node_fs27.readFileSync)(safePath, "utf8");
|
|
18721
19187
|
const bytes = Buffer.byteLength(content);
|
|
18722
19188
|
if (bytes > MAX_CONTEXT_FILE_BYTES) {
|
|
18723
19189
|
logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
|
|
@@ -18764,7 +19230,8 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
18764
19230
|
// src/commands/analyze/phases/07-context-files.ts
|
|
18765
19231
|
async function contextFiles(run) {
|
|
18766
19232
|
const { codeDelta, contextFilePaths } = run;
|
|
18767
|
-
const
|
|
19233
|
+
const { kept: externalContext } = partitionVerityOwned(contextFilePaths ?? []);
|
|
19234
|
+
const contextFiles2 = gatherContextFiles(externalContext, codeDelta.files);
|
|
18768
19235
|
for (const f of codeDelta.files) {
|
|
18769
19236
|
f.role = "delta";
|
|
18770
19237
|
}
|
|
@@ -18778,8 +19245,8 @@ async function contextFiles(run) {
|
|
|
18778
19245
|
|
|
18779
19246
|
// src/lib/seed-runner.ts
|
|
18780
19247
|
var import_promises11 = require("node:fs/promises");
|
|
18781
|
-
var
|
|
18782
|
-
var
|
|
19248
|
+
var import_node_fs28 = require("node:fs");
|
|
19249
|
+
var import_node_path23 = require("node:path");
|
|
18783
19250
|
var import_yaml2 = __toESM(require_dist());
|
|
18784
19251
|
|
|
18785
19252
|
// src/lib/seed.ts
|
|
@@ -19018,7 +19485,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
|
19018
19485
|
return fm;
|
|
19019
19486
|
}
|
|
19020
19487
|
async function runSeed(opts) {
|
|
19021
|
-
if (!(0,
|
|
19488
|
+
if (!(0, import_node_fs28.existsSync)(STANDARD_FILE)) {
|
|
19022
19489
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
19023
19490
|
}
|
|
19024
19491
|
let standardDoc;
|
|
@@ -19030,7 +19497,7 @@ async function runSeed(opts) {
|
|
|
19030
19497
|
}
|
|
19031
19498
|
const knowledgeSpec = standardDoc.knowledge_spec ?? {};
|
|
19032
19499
|
let readmeContent;
|
|
19033
|
-
if ((0,
|
|
19500
|
+
if ((0, import_node_fs28.existsSync)("README.md")) {
|
|
19034
19501
|
try {
|
|
19035
19502
|
readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
|
|
19036
19503
|
} catch {
|
|
@@ -19038,7 +19505,7 @@ async function runSeed(opts) {
|
|
|
19038
19505
|
}
|
|
19039
19506
|
let claudeMdContent;
|
|
19040
19507
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
19041
|
-
if ((0,
|
|
19508
|
+
if ((0, import_node_fs28.existsSync)(p)) {
|
|
19042
19509
|
try {
|
|
19043
19510
|
claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
|
|
19044
19511
|
break;
|
|
@@ -19061,8 +19528,8 @@ async function runSeed(opts) {
|
|
|
19061
19528
|
if (candidates.length === 0) {
|
|
19062
19529
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
19063
19530
|
}
|
|
19064
|
-
const overviewPath = (0,
|
|
19065
|
-
if ((0,
|
|
19531
|
+
const overviewPath = (0, import_node_path23.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
19532
|
+
if ((0, import_node_fs28.existsSync)(overviewPath) && !opts.force) {
|
|
19066
19533
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates };
|
|
19067
19534
|
}
|
|
19068
19535
|
if (opts.dryRun) {
|
|
@@ -19097,9 +19564,14 @@ async function runSeed(opts) {
|
|
|
19097
19564
|
}
|
|
19098
19565
|
const nodeId = res.data.node_id;
|
|
19099
19566
|
const filePathRel = res.data.file_path;
|
|
19100
|
-
const targetPath = (
|
|
19567
|
+
const targetPath = resolveInside(MEMORY_DIR, filePathRel);
|
|
19568
|
+
if (!targetPath) {
|
|
19569
|
+
opts.onFailed?.(c, `Server returned an out-of-bounds file_path (${String(filePathRel)}); refusing to write outside the memory directory.`);
|
|
19570
|
+
failed++;
|
|
19571
|
+
continue;
|
|
19572
|
+
}
|
|
19101
19573
|
try {
|
|
19102
|
-
await (0, import_promises11.mkdir)((0,
|
|
19574
|
+
await (0, import_promises11.mkdir)((0, import_node_path23.dirname)(targetPath), { recursive: true });
|
|
19103
19575
|
await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
19104
19576
|
created++;
|
|
19105
19577
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
@@ -19112,8 +19584,8 @@ async function runSeed(opts) {
|
|
|
19112
19584
|
}
|
|
19113
19585
|
|
|
19114
19586
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
19115
|
-
var
|
|
19116
|
-
var
|
|
19587
|
+
var import_node_fs29 = require("node:fs");
|
|
19588
|
+
var import_node_path24 = require("node:path");
|
|
19117
19589
|
async function memoryManifest(run) {
|
|
19118
19590
|
const { globals } = run;
|
|
19119
19591
|
const { serviceUrl, token } = run;
|
|
@@ -19123,9 +19595,9 @@ async function memoryManifest(run) {
|
|
|
19123
19595
|
let autoSeedNotice = null;
|
|
19124
19596
|
try {
|
|
19125
19597
|
await ensureMemoryDir();
|
|
19126
|
-
const seedMarker = (0,
|
|
19127
|
-
const hasStandard = (0,
|
|
19128
|
-
const alreadyTried = (0,
|
|
19598
|
+
const seedMarker = (0, import_node_path24.join)(VERITY_DIR, ".seeded");
|
|
19599
|
+
const hasStandard = (0, import_node_fs29.existsSync)(STANDARD_FILE);
|
|
19600
|
+
const alreadyTried = (0, import_node_fs29.existsSync)(seedMarker);
|
|
19129
19601
|
if (hasStandard && !alreadyTried) {
|
|
19130
19602
|
const preManifest = await buildManifest();
|
|
19131
19603
|
if (preManifest.nodes.length === 0) {
|
|
@@ -19138,7 +19610,7 @@ async function memoryManifest(run) {
|
|
|
19138
19610
|
dryRun: false
|
|
19139
19611
|
});
|
|
19140
19612
|
if (seedResult.created > 0) {
|
|
19141
|
-
(0,
|
|
19613
|
+
(0, import_node_fs29.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
|
|
19142
19614
|
`);
|
|
19143
19615
|
autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
|
|
19144
19616
|
logEvent("auto_seed_ran", {
|
|
@@ -19146,7 +19618,7 @@ async function memoryManifest(run) {
|
|
|
19146
19618
|
failed: seedResult.failed
|
|
19147
19619
|
});
|
|
19148
19620
|
} else if (seedResult.skipped === "already_seeded") {
|
|
19149
|
-
(0,
|
|
19621
|
+
(0, import_node_fs29.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
|
|
19150
19622
|
`);
|
|
19151
19623
|
} else {
|
|
19152
19624
|
logEvent("auto_seed_noop", {
|
|
@@ -19241,7 +19713,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
|
|
|
19241
19713
|
}
|
|
19242
19714
|
|
|
19243
19715
|
// src/commands/analyze/phases/10-working-memory.ts
|
|
19244
|
-
var
|
|
19716
|
+
var import_node_path25 = require("node:path");
|
|
19245
19717
|
async function workingMemory(run) {
|
|
19246
19718
|
const { opts } = run;
|
|
19247
19719
|
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run;
|
|
@@ -19253,7 +19725,7 @@ async function workingMemory(run) {
|
|
|
19253
19725
|
const priorState = foldForMarks(memorySession.d);
|
|
19254
19726
|
incrementReport = computeIncrement(
|
|
19255
19727
|
allForReview,
|
|
19256
|
-
(p) => fileHash((0,
|
|
19728
|
+
(p) => fileHash((0, import_node_path25.join)(repoRoot(), p)),
|
|
19257
19729
|
priorState.authored_all.map((a) => ({
|
|
19258
19730
|
path: a.path,
|
|
19259
19731
|
hash_at_last_verdict: a.hash_at_last_verdict
|
|
@@ -19334,6 +19806,54 @@ async function workingMemory(run) {
|
|
|
19334
19806
|
Object.assign(run, { incrementReport, memory, memorySession, reachability });
|
|
19335
19807
|
}
|
|
19336
19808
|
|
|
19809
|
+
// src/lib/note-budget.ts
|
|
19810
|
+
var import_node_fs30 = require("node:fs");
|
|
19811
|
+
var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
|
|
19812
|
+
var EPISODE_STALE_SECONDS = 30 * 60;
|
|
19813
|
+
var FRESH = { delivered: 0, tasksCompleted: 0, ts: 0 };
|
|
19814
|
+
function resolveEpisode(prev, signals) {
|
|
19815
|
+
if (!prev) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19816
|
+
if (signals.humanSpoke) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19817
|
+
if (signals.rawFail) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19818
|
+
if (prev.tasksCompleted !== signals.tasksCompleted) {
|
|
19819
|
+
return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19820
|
+
}
|
|
19821
|
+
if (prev.ts > 0 && signals.now - prev.ts > EPISODE_STALE_SECONDS) {
|
|
19822
|
+
return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19823
|
+
}
|
|
19824
|
+
return prev;
|
|
19825
|
+
}
|
|
19826
|
+
function advisoryBudgetSpent(episode, rawDecision) {
|
|
19827
|
+
const budget = ADVISORY_BUDGET[rawDecision] ?? ADVISORY_BUDGET.WARN;
|
|
19828
|
+
return episode.delivered >= budget;
|
|
19829
|
+
}
|
|
19830
|
+
function readAdvisoryEpisode(sessionId) {
|
|
19831
|
+
const file = scopedFile(ADVISORY_EPISODE_FILE, sessionId);
|
|
19832
|
+
if (!(0, import_node_fs30.existsSync)(file)) return null;
|
|
19833
|
+
try {
|
|
19834
|
+
const o = JSON.parse((0, import_node_fs30.readFileSync)(file, "utf-8")) ?? {};
|
|
19835
|
+
const delivered = typeof o.delivered === "number" ? o.delivered : NaN;
|
|
19836
|
+
if (isNaN(delivered)) return null;
|
|
19837
|
+
return {
|
|
19838
|
+
delivered,
|
|
19839
|
+
tasksCompleted: typeof o.tasksCompleted === "number" ? o.tasksCompleted : 0,
|
|
19840
|
+
ts: typeof o.ts === "number" ? o.ts : 0
|
|
19841
|
+
};
|
|
19842
|
+
} catch {
|
|
19843
|
+
return null;
|
|
19844
|
+
}
|
|
19845
|
+
}
|
|
19846
|
+
function writeAdvisoryEpisode(episode, sessionId) {
|
|
19847
|
+
try {
|
|
19848
|
+
(0, import_node_fs30.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
19849
|
+
(0, import_node_fs30.writeFileSync)(
|
|
19850
|
+
scopedFile(ADVISORY_EPISODE_FILE, sessionId),
|
|
19851
|
+
JSON.stringify({ v: 1, ...episode })
|
|
19852
|
+
);
|
|
19853
|
+
} catch {
|
|
19854
|
+
}
|
|
19855
|
+
}
|
|
19856
|
+
|
|
19337
19857
|
// src/lib/run-mode.ts
|
|
19338
19858
|
function parseAutonomousEnv(raw) {
|
|
19339
19859
|
if (raw === void 0) return void 0;
|
|
@@ -19356,7 +19876,7 @@ function isExplicitlyAutonomous(env = process.env) {
|
|
|
19356
19876
|
}
|
|
19357
19877
|
|
|
19358
19878
|
// src/lib/task-context.ts
|
|
19359
|
-
var
|
|
19879
|
+
var import_node_child_process9 = require("node:child_process");
|
|
19360
19880
|
var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
|
|
19361
19881
|
var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
|
|
19362
19882
|
function parseLinkedIssue(sources) {
|
|
@@ -19372,7 +19892,7 @@ function parseLinkedIssue(sources) {
|
|
|
19372
19892
|
}
|
|
19373
19893
|
function safeExec(cmd, timeout) {
|
|
19374
19894
|
try {
|
|
19375
|
-
return (0,
|
|
19895
|
+
return (0, import_node_child_process9.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
|
|
19376
19896
|
} catch {
|
|
19377
19897
|
return "";
|
|
19378
19898
|
}
|
|
@@ -19427,7 +19947,13 @@ async function buildRequest(run) {
|
|
|
19427
19947
|
excluded_by_reason: excludedByReason,
|
|
19428
19948
|
// was the transcript itself truncated? The 256 KB window means "this turn"
|
|
19429
19949
|
// can quietly mean "the last 256 KB of it".
|
|
19430
|
-
transcript_windowed: actionSummary?.transcript_windowed ?? null
|
|
19950
|
+
transcript_windowed: actionSummary?.transcript_windowed ?? null,
|
|
19951
|
+
// The advisory budget's fleet counter-metric (note-budget.ts): deliveries in
|
|
19952
|
+
// the episode as of the PREVIOUS turn — this runs before phase 13 updates
|
|
19953
|
+
// the state, so the number is one turn lagged by construction. The
|
|
19954
|
+
// degenerate win for the budget is a dead channel that looks like clean
|
|
19955
|
+
// code; this is what makes "did delivery rate collapse" a query.
|
|
19956
|
+
advisory_delivered_prior: readAdvisoryEpisode(run.baselineSessionId)?.delivered ?? 0
|
|
19431
19957
|
};
|
|
19432
19958
|
const requestBody = {
|
|
19433
19959
|
coverage_telemetry: coverageTelemetry,
|
|
@@ -19566,7 +20092,8 @@ async function buildRequest(run) {
|
|
|
19566
20092
|
}
|
|
19567
20093
|
const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
|
|
19568
20094
|
const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
|
|
19569
|
-
const
|
|
20095
|
+
const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
|
|
20096
|
+
const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse || !!w4Task || planApprovalActive;
|
|
19570
20097
|
if (hasIntent) {
|
|
19571
20098
|
const intentContext = {};
|
|
19572
20099
|
if (conversation && conversation.prompts.length > 0) {
|
|
@@ -19598,6 +20125,10 @@ async function buildRequest(run) {
|
|
|
19598
20125
|
intentContext.user_prompt = w4Task.goal;
|
|
19599
20126
|
logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
|
|
19600
20127
|
}
|
|
20128
|
+
if (planApprovalActive) {
|
|
20129
|
+
intentContext.plan_approved = true;
|
|
20130
|
+
logEvent("plan_approval_carried", { approvals: foldResult.planApproval.approvals });
|
|
20131
|
+
}
|
|
19601
20132
|
if (assistantResponse) {
|
|
19602
20133
|
const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
|
|
19603
20134
|
intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
|
|
@@ -19617,14 +20148,14 @@ async function buildRequest(run) {
|
|
|
19617
20148
|
}
|
|
19618
20149
|
|
|
19619
20150
|
// src/lib/offline.ts
|
|
19620
|
-
var
|
|
20151
|
+
var import_node_fs31 = require("node:fs");
|
|
19621
20152
|
var import_node_crypto11 = require("node:crypto");
|
|
19622
20153
|
function cacheRequest(body) {
|
|
19623
20154
|
try {
|
|
19624
|
-
(0,
|
|
20155
|
+
(0, import_node_fs31.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
19625
20156
|
const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
|
|
19626
20157
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
19627
|
-
(0,
|
|
20158
|
+
(0, import_node_fs31.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
|
|
19628
20159
|
} catch {
|
|
19629
20160
|
}
|
|
19630
20161
|
}
|
|
@@ -19743,10 +20274,10 @@ async function transmit(run) {
|
|
|
19743
20274
|
}
|
|
19744
20275
|
|
|
19745
20276
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
19746
|
-
var
|
|
19747
|
-
var
|
|
20277
|
+
var import_node_fs32 = require("node:fs");
|
|
20278
|
+
var import_node_path26 = require("node:path");
|
|
19748
20279
|
async function reconcile(run) {
|
|
19749
|
-
const { actionSummary, allChanged, analyzable, baseline, codeDelta, contentHash, conversation, decision, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
|
|
20280
|
+
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
|
|
19750
20281
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
19751
20282
|
let openElsewhere = [];
|
|
19752
20283
|
if (memorySession) {
|
|
@@ -19754,7 +20285,7 @@ async function reconcile(run) {
|
|
|
19754
20285
|
const st = foldDossier(memorySession.d);
|
|
19755
20286
|
openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
|
|
19756
20287
|
try {
|
|
19757
|
-
const src = (0,
|
|
20288
|
+
const src = (0, import_node_fs32.readFileSync)((0, import_node_path26.join)(repoRoot(), file), "utf8").split("\n");
|
|
19758
20289
|
const at = src[line - 1];
|
|
19759
20290
|
return at === void 0 ? null : lineSha(at);
|
|
19760
20291
|
} catch {
|
|
@@ -19764,6 +20295,7 @@ async function reconcile(run) {
|
|
|
19764
20295
|
} catch {
|
|
19765
20296
|
}
|
|
19766
20297
|
}
|
|
20298
|
+
const { kept: externalChanged, owned: verityOwned } = partitionVerityOwned(allChanged);
|
|
19767
20299
|
const reviewCoverage = {
|
|
19768
20300
|
reviewed: sentPaths,
|
|
19769
20301
|
// Declared drops from the stages that DO report themselves today. The other
|
|
@@ -19805,11 +20337,20 @@ async function reconcile(run) {
|
|
|
19805
20337
|
stage: "baseline-scoping",
|
|
19806
20338
|
kind: "policy"
|
|
19807
20339
|
})),
|
|
20340
|
+
// ⚠ VERITY'S OWN FILES, named as such — not laundered into the
|
|
20341
|
+
// extension bucket below, where "we do not review our own installer's
|
|
20342
|
+
// dirt" would read as "a changed README". See self-scope.ts.
|
|
20343
|
+
...verityOwned.map((path) => ({
|
|
20344
|
+
path,
|
|
20345
|
+
reason: "verity-owned",
|
|
20346
|
+
stage: "self-scope",
|
|
20347
|
+
kind: "policy"
|
|
20348
|
+
})),
|
|
19808
20349
|
// The extension allowlist. POLICY: a changed README was never going to be
|
|
19809
20350
|
// reviewed, and calling that a coverage gap would downgrade nearly every
|
|
19810
20351
|
// PASS to WARN until WARN meant nothing. Recorded so the ledger balances and
|
|
19811
20352
|
// so "what did Verity ignore entirely" is answerable.
|
|
19812
|
-
...
|
|
20353
|
+
...externalChanged.filter((p) => !analyzable.includes(p) && !reviewable.includes(p) && !securityFiles.includes(p)).map((path) => ({
|
|
19813
20354
|
path,
|
|
19814
20355
|
reason: "not-a-reviewed-file-type",
|
|
19815
20356
|
stage: "extension-allowlist",
|
|
@@ -19846,6 +20387,29 @@ async function reconcile(run) {
|
|
|
19846
20387
|
decision
|
|
19847
20388
|
});
|
|
19848
20389
|
}
|
|
20390
|
+
const episodeSignals = {
|
|
20391
|
+
humanSpoke: (conversation?.prompts?.length ?? 0) > 0,
|
|
20392
|
+
rawFail: decision === "FAIL",
|
|
20393
|
+
tasksCompleted: (foldResult?.tasks ?? []).filter((t) => t.status === "completed").length,
|
|
20394
|
+
now: Math.floor(Date.now() / 1e3)
|
|
20395
|
+
};
|
|
20396
|
+
let episode = resolveEpisode(readAdvisoryEpisode(baselineSessionId), episodeSignals);
|
|
20397
|
+
const contentClass = classifyChannelContent(channelInputFrom(response));
|
|
20398
|
+
const wouldCarryAdvisory = contentClass.advisory || openElsewhere.length > 0;
|
|
20399
|
+
if (decision !== "FAIL" && !silenced && wouldCarryAdvisory && !contentClass.refusal && !contentClass.intentFlag && advisoryBudgetSpent(episode, decision)) {
|
|
20400
|
+
silenced = "note-budget";
|
|
20401
|
+
logEvent("channel_silenced", {
|
|
20402
|
+
reason: silenced,
|
|
20403
|
+
run_id: response.run_id ?? turnId,
|
|
20404
|
+
decision,
|
|
20405
|
+
episode_delivered: episode.delivered
|
|
20406
|
+
});
|
|
20407
|
+
}
|
|
20408
|
+
const deliveringAdvisory = decision !== "FAIL" && !silenced && wouldCarryAdvisory;
|
|
20409
|
+
writeAdvisoryEpisode(
|
|
20410
|
+
{ ...episode, delivered: episode.delivered + (deliveringAdvisory ? 1 : 0), ts: episodeSignals.now },
|
|
20411
|
+
baselineSessionId
|
|
20412
|
+
);
|
|
19849
20413
|
let intentRepeatCount = 0;
|
|
19850
20414
|
const priorPendingFingerprints = memorySession ? (() => {
|
|
19851
20415
|
try {
|
|
@@ -19861,6 +20425,11 @@ async function reconcile(run) {
|
|
|
19861
20425
|
decision,
|
|
19862
20426
|
branch: getCurrentBranch(),
|
|
19863
20427
|
watermarkSha: watermarkIsPartial ? null : watermarkHash,
|
|
20428
|
+
// The byte witness — the same "only honest definition of reviewed" the
|
|
20429
|
+
// coverage column uses. A finding on a path outside this set records no
|
|
20430
|
+
// statement (plan-mode prose anchored to unsent files must not become
|
|
20431
|
+
// "STILL OPEN … the tree is not clean").
|
|
20432
|
+
sentPaths,
|
|
19864
20433
|
findings: response.findings?.map((f) => ({
|
|
19865
20434
|
file: f.file,
|
|
19866
20435
|
line: f.line,
|
|
@@ -19927,6 +20496,39 @@ ${YELLOW2}${note}${NC2}
|
|
|
19927
20496
|
return exit(0);
|
|
19928
20497
|
}
|
|
19929
20498
|
|
|
20499
|
+
// src/lib/may-block.ts
|
|
20500
|
+
var HARD_BLOCK_CEILING = 5;
|
|
20501
|
+
function mayBlock(input) {
|
|
20502
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20503
|
+
if (input.reviewedFileCount === 0 && input.staticFindingCount === 0) {
|
|
20504
|
+
return { block: false, release: "no-code-reviewed" };
|
|
20505
|
+
}
|
|
20506
|
+
if (input.cycleCutFired) {
|
|
20507
|
+
return { block: false, release: "nothing-moved" };
|
|
20508
|
+
}
|
|
20509
|
+
if (input.attempts > input.maxIterations) {
|
|
20510
|
+
return { block: false, release: "same-problem-cap" };
|
|
20511
|
+
}
|
|
20512
|
+
if (input.blocks > ceiling) {
|
|
20513
|
+
return { block: false, release: "block-ceiling" };
|
|
20514
|
+
}
|
|
20515
|
+
return { block: true, release: null };
|
|
20516
|
+
}
|
|
20517
|
+
function describeRelease(release, input) {
|
|
20518
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20519
|
+
const open = input.findingCount > 0 ? `${input.findingCount} finding(s) remain OPEN and were NOT fixed. Human review required before deploying.` : "Human review required before deploying.";
|
|
20520
|
+
switch (release) {
|
|
20521
|
+
case "no-code-reviewed":
|
|
20522
|
+
return `Verity: WARN \u2014 NOT BLOCKING: no code was reviewed on this turn, so there is nothing here to fix. Reported as advice instead. ${open}`;
|
|
20523
|
+
case "nothing-moved":
|
|
20524
|
+
return `Verity: WARN \u2014 NOT BLOCKING: nothing has changed since the last verdict, so re-raising it cannot move anything forward. ${open}`;
|
|
20525
|
+
case "same-problem-cap":
|
|
20526
|
+
return `Verity: WARN \u2014 self-healing limit (${input.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${open}`;
|
|
20527
|
+
case "block-ceiling":
|
|
20528
|
+
return `Verity: WARN \u2014 ${ceiling} consecutive blocking verdicts reached; releasing the block so this cannot loop. ${open}`;
|
|
20529
|
+
}
|
|
20530
|
+
}
|
|
20531
|
+
|
|
19930
20532
|
// src/lib/remediation-guard.ts
|
|
19931
20533
|
var TOOL_CONFIG_PATTERNS = [
|
|
19932
20534
|
/(^|\/)\.codacy\//,
|
|
@@ -19969,19 +20571,7 @@ function screenRemediation(fix, findingFile) {
|
|
|
19969
20571
|
|
|
19970
20572
|
// src/commands/analyze/phases/14-render.ts
|
|
19971
20573
|
function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
|
|
19972
|
-
|
|
19973
|
-
const intent = response.intent_alignment ?? {};
|
|
19974
|
-
return buildAgentContext({
|
|
19975
|
-
intentRepeat,
|
|
19976
|
-
priorPendingFingerprints,
|
|
19977
|
-
gateDecision: String(response.gate_decision ?? ""),
|
|
19978
|
-
findings: response.findings ?? [],
|
|
19979
|
-
pendingItems: response.pending_items ?? [],
|
|
19980
|
-
reviewStatus: metadata.review_status,
|
|
19981
|
-
coverage: metadata.coverage,
|
|
19982
|
-
intentVerdict: intent.verdict,
|
|
19983
|
-
intentGaps: intent.gaps
|
|
19984
|
-
});
|
|
20574
|
+
return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
|
|
19985
20575
|
}
|
|
19986
20576
|
async function render(run) {
|
|
19987
20577
|
const { opts, globals } = run;
|
|
@@ -20075,38 +20665,63 @@ async function render(run) {
|
|
|
20075
20665
|
reverify_by: response.reverify_by
|
|
20076
20666
|
});
|
|
20077
20667
|
const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
|
|
20078
|
-
let
|
|
20668
|
+
let release = null;
|
|
20079
20669
|
let effectiveDecision = decision;
|
|
20080
20670
|
if (decision === "FAIL") {
|
|
20081
|
-
const
|
|
20671
|
+
const findings = response.findings ?? [];
|
|
20672
|
+
const blocking = findings.filter((f) => {
|
|
20082
20673
|
const sev = String(f.severity ?? "").toLowerCase();
|
|
20083
20674
|
return sev === "critical" || sev === "high";
|
|
20084
20675
|
});
|
|
20085
20676
|
const fingerprint = findingsFingerprint(blocking);
|
|
20086
|
-
const prior =
|
|
20677
|
+
const prior = readBlockState(currentCommit, {
|
|
20678
|
+
newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
|
|
20679
|
+
});
|
|
20087
20680
|
const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
|
|
20088
|
-
const nextIteration = sameProblem ? prior.iteration + 1 : 1;
|
|
20089
20681
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
20090
|
-
|
|
20091
|
-
|
|
20092
|
-
|
|
20093
|
-
|
|
20682
|
+
const attempts = sameProblem ? prior.attempts + 1 : 1;
|
|
20683
|
+
const blocks = prior.blocks + 1;
|
|
20684
|
+
const decisionNow = mayBlock({
|
|
20685
|
+
reviewedFileCount: codeDelta.files.length,
|
|
20686
|
+
staticFindingCount: run.staticResults?.findings?.length ?? 0,
|
|
20687
|
+
cycleCutFired: silenced !== null,
|
|
20688
|
+
attempts,
|
|
20689
|
+
blocks,
|
|
20690
|
+
maxIterations
|
|
20691
|
+
});
|
|
20692
|
+
if (decisionNow.block) {
|
|
20693
|
+
writeBlockState(currentCommit, { attempts, blocks, fingerprint });
|
|
20694
|
+
iteration = attempts;
|
|
20695
|
+
} else {
|
|
20696
|
+
release = decisionNow.release;
|
|
20094
20697
|
effectiveDecision = "WARN";
|
|
20095
|
-
logEvent("
|
|
20698
|
+
logEvent("block_released", {
|
|
20699
|
+
reason: release,
|
|
20700
|
+
attempts,
|
|
20701
|
+
blocks,
|
|
20702
|
+
reviewed_files: codeDelta.files.length,
|
|
20703
|
+
cycle_cut: silenced,
|
|
20704
|
+
fingerprint
|
|
20705
|
+
});
|
|
20096
20706
|
}
|
|
20097
20707
|
}
|
|
20098
|
-
if (
|
|
20708
|
+
if (release) {
|
|
20099
20709
|
const findings = response.findings ?? [];
|
|
20100
20710
|
const lines = findings.slice(0, 5).map((f) => ` [${String(f.severity ?? "?").toUpperCase()}] ${String(f.title ?? f.message ?? "")} (${String(f.file ?? "?")}:${String(f.line ?? "?")})`);
|
|
20711
|
+
const summary = describeRelease(release, {
|
|
20712
|
+
findingCount: findings.length,
|
|
20713
|
+
maxIterations: parseInt(opts.maxIterations, 10)
|
|
20714
|
+
});
|
|
20101
20715
|
emitVerdict({
|
|
20102
20716
|
proposed: "WARN",
|
|
20103
20717
|
changed: run.changedUniverse,
|
|
20104
20718
|
coverage: reviewCoverage,
|
|
20105
|
-
userSummary:
|
|
20106
|
-
${lines.join("\n")}
|
|
20719
|
+
userSummary: lines.length > 0 ? `${summary}
|
|
20720
|
+
${lines.join("\n")}` : summary,
|
|
20107
20721
|
agentContext: null,
|
|
20108
20722
|
silenced: true
|
|
20109
20723
|
});
|
|
20724
|
+
return;
|
|
20110
20725
|
}
|
|
20111
20726
|
switch (effectiveDecision) {
|
|
20112
20727
|
case "FAIL": {
|
|
@@ -20205,7 +20820,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20205
20820
|
break;
|
|
20206
20821
|
}
|
|
20207
20822
|
case "PASS": {
|
|
20208
|
-
|
|
20823
|
+
resetBlockState(currentCommit);
|
|
20209
20824
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20210
20825
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20211
20826
|
let userSummary = response.user_summary ?? "Verity: PASS";
|
|
@@ -20226,6 +20841,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20226
20841
|
break;
|
|
20227
20842
|
}
|
|
20228
20843
|
case "WARN": {
|
|
20844
|
+
if (decision !== "FAIL") resetBlockState(currentCommit);
|
|
20229
20845
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20230
20846
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20231
20847
|
let userSummary = response.user_summary ?? "Verity: WARN";
|
|
@@ -20324,7 +20940,7 @@ async function runAnalyze(opts, globals) {
|
|
|
20324
20940
|
}
|
|
20325
20941
|
|
|
20326
20942
|
// src/commands/baseline.ts
|
|
20327
|
-
var
|
|
20943
|
+
var import_node_fs33 = require("node:fs");
|
|
20328
20944
|
function registerBaselineCommands(program2) {
|
|
20329
20945
|
const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
|
|
20330
20946
|
baseline.command("capture").description("Snapshot the working tree at task start (used by SessionStart hook)").option("--session-id <id>", "Session id (overrides any value from stdin)").option("--source <source>", "Lifecycle hint: startup|resume|clear|compact").action(async (opts) => {
|
|
@@ -20333,7 +20949,7 @@ function registerBaselineCommands(program2) {
|
|
|
20333
20949
|
process.chdir(repoRoot());
|
|
20334
20950
|
} catch {
|
|
20335
20951
|
}
|
|
20336
|
-
if (!(0,
|
|
20952
|
+
if (!(0, import_node_fs33.existsSync)(VERITY_DIR)) {
|
|
20337
20953
|
process.exit(0);
|
|
20338
20954
|
}
|
|
20339
20955
|
let sessionId = opts.sessionId;
|
|
@@ -20373,7 +20989,7 @@ async function readStdin() {
|
|
|
20373
20989
|
}
|
|
20374
20990
|
|
|
20375
20991
|
// src/commands/review.ts
|
|
20376
|
-
var
|
|
20992
|
+
var import_node_fs34 = require("node:fs");
|
|
20377
20993
|
function registerReviewCommand(program2) {
|
|
20378
20994
|
program2.command("review").description("Run on-demand Verity analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
20379
20995
|
const globals = program2.opts();
|
|
@@ -20392,7 +21008,7 @@ async function runReview(opts, globals) {
|
|
|
20392
21008
|
const securityFiles = filterSecurity(allFiles);
|
|
20393
21009
|
let staticResults;
|
|
20394
21010
|
if (isCodacyAvailable()) {
|
|
20395
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
21011
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs34.existsSync)(f) || resolveFile(f) !== null);
|
|
20396
21012
|
staticResults = runCodacyAnalysis(scannable);
|
|
20397
21013
|
} else {
|
|
20398
21014
|
staticResults = {
|
|
@@ -20418,10 +21034,10 @@ async function runReview(opts, globals) {
|
|
|
20418
21034
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
20419
21035
|
specs = [];
|
|
20420
21036
|
for (const p of specPaths) {
|
|
20421
|
-
if (!(0,
|
|
21037
|
+
if (!(0, import_node_fs34.existsSync)(p)) continue;
|
|
20422
21038
|
try {
|
|
20423
|
-
const { readFileSync:
|
|
20424
|
-
const content =
|
|
21039
|
+
const { readFileSync: readFileSync20 } = await import("node:fs");
|
|
21040
|
+
const content = readFileSync20(p, "utf-8");
|
|
20425
21041
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
20426
21042
|
} catch {
|
|
20427
21043
|
}
|
|
@@ -20478,15 +21094,15 @@ async function runReview(opts, globals) {
|
|
|
20478
21094
|
}
|
|
20479
21095
|
|
|
20480
21096
|
// src/commands/guard.ts
|
|
20481
|
-
var
|
|
20482
|
-
var
|
|
21097
|
+
var import_node_fs35 = require("node:fs");
|
|
21098
|
+
var import_node_path27 = require("node:path");
|
|
20483
21099
|
var GUARD_BLOCK_CAP = 2;
|
|
20484
|
-
var GUARD_ITER_FILE = (0,
|
|
21100
|
+
var GUARD_ITER_FILE = (0, import_node_path27.join)(VERITY_DIR, ".guard-iteration");
|
|
20485
21101
|
function readPreToolUseStdin() {
|
|
20486
21102
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
20487
|
-
return new Promise((
|
|
21103
|
+
return new Promise((resolve4) => {
|
|
20488
21104
|
try {
|
|
20489
|
-
if (process.stdin.isTTY) return
|
|
21105
|
+
if (process.stdin.isTTY) return resolve4(empty);
|
|
20490
21106
|
const chunks = [];
|
|
20491
21107
|
let timer;
|
|
20492
21108
|
let settled = false;
|
|
@@ -20499,7 +21115,7 @@ function readPreToolUseStdin() {
|
|
|
20499
21115
|
process.stdin.removeListener("end", onEnd);
|
|
20500
21116
|
process.stdin.removeListener("error", onError);
|
|
20501
21117
|
process.stdin.pause();
|
|
20502
|
-
|
|
21118
|
+
resolve4(value);
|
|
20503
21119
|
};
|
|
20504
21120
|
const onEnd = () => {
|
|
20505
21121
|
try {
|
|
@@ -20520,32 +21136,13 @@ function readPreToolUseStdin() {
|
|
|
20520
21136
|
process.stdin.on("error", onError);
|
|
20521
21137
|
process.stdin.resume();
|
|
20522
21138
|
} catch {
|
|
20523
|
-
|
|
21139
|
+
resolve4(empty);
|
|
20524
21140
|
}
|
|
20525
21141
|
});
|
|
20526
21142
|
}
|
|
20527
|
-
function buildCommandRe(head) {
|
|
20528
|
-
return new RegExp(`(?:^|[\\s;&|(])${head}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${head}`);
|
|
20529
|
-
}
|
|
20530
|
-
var GIT_GLOBAL_OPTS = "(?:\\s+(?:-[Cc]\\s+\\S+|--?[\\w-]+(?:=\\S+)?))*";
|
|
20531
|
-
var COMMIT_RE = buildCommandRe(`git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`);
|
|
20532
|
-
var PUSH_RE = buildCommandRe(`git${GIT_GLOBAL_OPTS}\\s+push\\b`);
|
|
20533
|
-
var GH_PR_RE = buildCommandRe(`gh${GIT_GLOBAL_OPTS}\\s+pr\\s+create\\b`);
|
|
20534
|
-
function classifyCommand2(command, on) {
|
|
20535
|
-
let commit = false;
|
|
20536
|
-
let push = false;
|
|
20537
|
-
for (const seg of (command ?? "").split(/&&|\|\||;|\n/)) {
|
|
20538
|
-
if (/--dry-run\b/.test(seg)) continue;
|
|
20539
|
-
if (COMMIT_RE.test(seg)) commit = true;
|
|
20540
|
-
if (PUSH_RE.test(seg) || GH_PR_RE.test(seg)) push = true;
|
|
20541
|
-
}
|
|
20542
|
-
if (commit && on.includes("commit")) return "pre-commit";
|
|
20543
|
-
if (push && on.includes("push")) return "pre-push";
|
|
20544
|
-
return null;
|
|
20545
|
-
}
|
|
20546
21143
|
function readIterMap() {
|
|
20547
21144
|
try {
|
|
20548
|
-
const raw = JSON.parse((0,
|
|
21145
|
+
const raw = JSON.parse((0, import_node_fs35.readFileSync)(GUARD_ITER_FILE, "utf-8"));
|
|
20549
21146
|
if (raw && typeof raw === "object") {
|
|
20550
21147
|
if (typeof raw.moment === "string" && typeof raw.count === "number") {
|
|
20551
21148
|
return { [raw.moment]: raw.count };
|
|
@@ -20565,10 +21162,10 @@ function readIter(moment) {
|
|
|
20565
21162
|
}
|
|
20566
21163
|
function writeIter(moment, count) {
|
|
20567
21164
|
try {
|
|
20568
|
-
(0,
|
|
21165
|
+
(0, import_node_fs35.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
20569
21166
|
const map = readIterMap();
|
|
20570
21167
|
map[moment] = count;
|
|
20571
|
-
(0,
|
|
21168
|
+
(0, import_node_fs35.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
20572
21169
|
} catch {
|
|
20573
21170
|
}
|
|
20574
21171
|
}
|
|
@@ -20578,10 +21175,10 @@ function resetIter(moment) {
|
|
|
20578
21175
|
if (!(moment in map)) return;
|
|
20579
21176
|
delete map[moment];
|
|
20580
21177
|
if (Object.keys(map).length === 0) {
|
|
20581
|
-
if ((0,
|
|
21178
|
+
if ((0, import_node_fs35.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs35.unlinkSync)(GUARD_ITER_FILE);
|
|
20582
21179
|
} else {
|
|
20583
|
-
(0,
|
|
20584
|
-
(0,
|
|
21180
|
+
(0, import_node_fs35.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
21181
|
+
(0, import_node_fs35.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
20585
21182
|
}
|
|
20586
21183
|
} catch {
|
|
20587
21184
|
}
|
|
@@ -20596,8 +21193,14 @@ function registerGuardCommand(program2) {
|
|
|
20596
21193
|
}
|
|
20597
21194
|
});
|
|
20598
21195
|
}
|
|
20599
|
-
function
|
|
20600
|
-
return moment === "pre-commit" ?
|
|
21196
|
+
function resolveMomentRange(moment, frame, command, on) {
|
|
21197
|
+
return moment === "pre-commit" ? stagedRange() : resolvePushRange(frame, command, on);
|
|
21198
|
+
}
|
|
21199
|
+
function describeRange(range) {
|
|
21200
|
+
if (range.kind === "staged") return "staged";
|
|
21201
|
+
if (range.kind === "nothing" || !range.base) return null;
|
|
21202
|
+
const base = /^[0-9a-f]{40}$/.test(range.base) ? range.base.slice(0, 7) : range.base;
|
|
21203
|
+
return `${base}..${range.head} via ${range.via}`;
|
|
20601
21204
|
}
|
|
20602
21205
|
function matchFlagValue(command, flags) {
|
|
20603
21206
|
const re = new RegExp(`(?<![\\w-])(?:${flags})(?:=|\\s+)('((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)"|([^\\s'"-][^\\s]*))`);
|
|
@@ -20632,26 +21235,24 @@ function isSubstantiveIntent(text) {
|
|
|
20632
21235
|
if (SHELL_PLUMBING.test(t)) return false;
|
|
20633
21236
|
return true;
|
|
20634
21237
|
}
|
|
20635
|
-
function extractStatedIntent(moment, command) {
|
|
20636
|
-
const text = moment === "pre-commit" ? parseCommitMessage(command) : parsePrIntent(command) ??
|
|
21238
|
+
function extractStatedIntent(moment, command, pushedMessages = null) {
|
|
21239
|
+
const text = moment === "pre-commit" ? parseCommitMessage(command) : parsePrIntent(command) ?? pushedMessages;
|
|
20637
21240
|
return isSubstantiveIntent(text) ? text : null;
|
|
20638
21241
|
}
|
|
20639
21242
|
function hasBlockingFinding(response) {
|
|
20640
21243
|
const findings = response.findings ?? [];
|
|
20641
21244
|
return findings.some((f) => f.scope !== "pre-existing" && ["critical", "high"].includes((f.severity ?? "").toLowerCase()));
|
|
20642
21245
|
}
|
|
20643
|
-
function buildGuardRequest(moment, files, iter, sessionId,
|
|
21246
|
+
function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedIntent, coverageTelemetry) {
|
|
20644
21247
|
const analyzable = filterAnalyzable(files);
|
|
20645
21248
|
const securityFiles = filterSecurity(files);
|
|
20646
21249
|
let staticResults;
|
|
20647
21250
|
if (isCodacyAvailable()) {
|
|
20648
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0,
|
|
21251
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs35.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
20649
21252
|
staticResults = runCodacyAnalysis(scannable);
|
|
20650
21253
|
} else {
|
|
20651
21254
|
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
20652
21255
|
}
|
|
20653
|
-
const codeDelta = collectCodeDelta(files);
|
|
20654
|
-
if (codeDelta.total_files === 0) return null;
|
|
20655
21256
|
const trigger = moment === "pre-commit" ? "hook:pre-commit" : "hook:pre-push";
|
|
20656
21257
|
const requestBody = {
|
|
20657
21258
|
static_results: staticResults,
|
|
@@ -20668,9 +21269,9 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
20668
21269
|
iteration: iter + 1
|
|
20669
21270
|
}
|
|
20670
21271
|
};
|
|
21272
|
+
if (coverageTelemetry) requestBody.coverage_telemetry = coverageTelemetry;
|
|
20671
21273
|
const specs = discoverSpecs();
|
|
20672
21274
|
const plans = discoverPlans();
|
|
20673
|
-
const statedIntent = extractStatedIntent(moment, command);
|
|
20674
21275
|
if (specs.length > 0 || plans.length > 0 || statedIntent) {
|
|
20675
21276
|
const intentContext = {};
|
|
20676
21277
|
if (statedIntent) intentContext.user_prompt = statedIntent;
|
|
@@ -20680,6 +21281,39 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
20680
21281
|
}
|
|
20681
21282
|
return requestBody;
|
|
20682
21283
|
}
|
|
21284
|
+
function buildGuardCoverage(files, codeDelta, frame, frameRange) {
|
|
21285
|
+
const byReason = {};
|
|
21286
|
+
for (const e of codeDelta.excluded) byReason[e.reason] = (byReason[e.reason] ?? 0) + 1;
|
|
21287
|
+
return {
|
|
21288
|
+
changed_all: files.length,
|
|
21289
|
+
analyzable: filterAnalyzable(files).length,
|
|
21290
|
+
reviewable: filterReviewable(files).length,
|
|
21291
|
+
security: filterSecurity(files).length,
|
|
21292
|
+
for_review: files.length,
|
|
21293
|
+
sent: codeDelta.total_files,
|
|
21294
|
+
capped_out: codeDelta.truncated?.dropped ?? 0,
|
|
21295
|
+
excluded: codeDelta.excluded.length,
|
|
21296
|
+
excluded_by_reason: byReason,
|
|
21297
|
+
transcript_windowed: null,
|
|
21298
|
+
guard_frame: frameTelemetry(frame, frameRange)
|
|
21299
|
+
};
|
|
21300
|
+
}
|
|
21301
|
+
function coverageSummary(c) {
|
|
21302
|
+
const range = c.range ? ` @ ${c.range}` : "";
|
|
21303
|
+
return `reviewed ${c.sent.length} file(s)${range}`;
|
|
21304
|
+
}
|
|
21305
|
+
function coverageBlock(c) {
|
|
21306
|
+
const lines = [];
|
|
21307
|
+
const tree = c.root ? `${c.root}${c.linked ? " (linked worktree)" : ""}${c.branch ? ` \xB7 branch ${c.branch}` : ""}` : "(no tree resolved)";
|
|
21308
|
+
lines.push(`Reviewed (${c.moment}): ${c.sent.length} file(s)${c.range ? ` @ ${c.range}` : ""}`);
|
|
21309
|
+
lines.push(` Tree: ${tree}`);
|
|
21310
|
+
for (const f of c.sent) lines.push(` - ${f}`);
|
|
21311
|
+
if (c.excluded.length > 0) {
|
|
21312
|
+
lines.push(` Excluded (${c.excluded.length}):`);
|
|
21313
|
+
for (const e of c.excluded) lines.push(` - ${e.path} (${e.reason})`);
|
|
21314
|
+
}
|
|
21315
|
+
return lines.join("\n");
|
|
21316
|
+
}
|
|
20683
21317
|
function emitAllowNotice(userMsg, agentMsg) {
|
|
20684
21318
|
process.stdout.write(JSON.stringify({
|
|
20685
21319
|
systemMessage: userMsg,
|
|
@@ -20690,15 +21324,24 @@ function emitAllowNotice(userMsg, agentMsg) {
|
|
|
20690
21324
|
async function runGuard(opts, globals) {
|
|
20691
21325
|
const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
|
|
20692
21326
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
20693
|
-
|
|
20694
|
-
try {
|
|
20695
|
-
process.chdir(cwd);
|
|
20696
|
-
} catch {
|
|
20697
|
-
}
|
|
20698
|
-
}
|
|
20699
|
-
const moment = classifyCommand2(command, on);
|
|
21327
|
+
const moment = classifyCommand(command, on);
|
|
20700
21328
|
if (!moment) process.exit(0);
|
|
20701
21329
|
const verb = moment === "pre-commit" ? "commit" : "push";
|
|
21330
|
+
const { frame } = resolveFrame({ command, on, hookCwd: cwd });
|
|
21331
|
+
if (frame.refusal || !frame.worktreeRoot) {
|
|
21332
|
+
logEvent("guard_frame", { moment, ...frameTelemetry(frame, null) });
|
|
21333
|
+
if ((frame.refusal ?? "").startsWith("anchor:")) process.exit(0);
|
|
21334
|
+
emitAllowNotice(
|
|
21335
|
+
`\u26A0 Verity ${moment}: could not resolve the tree this ${verb} targets \u2014 ${verb}ed WITHOUT review`,
|
|
21336
|
+
`Verity ${moment}: the target tree could not be resolved (${frame.refusal}); the ${verb} was allowed WITHOUT a Verity review.`
|
|
21337
|
+
);
|
|
21338
|
+
}
|
|
21339
|
+
try {
|
|
21340
|
+
process.chdir(frame.worktreeRoot);
|
|
21341
|
+
} catch {
|
|
21342
|
+
process.exit(0);
|
|
21343
|
+
}
|
|
21344
|
+
_resetRepoRoot();
|
|
20702
21345
|
const iter = readIter(moment);
|
|
20703
21346
|
if (iter >= GUARD_BLOCK_CAP) {
|
|
20704
21347
|
resetIter(moment);
|
|
@@ -20707,13 +21350,39 @@ async function runGuard(opts, globals) {
|
|
|
20707
21350
|
`Verity ${moment}: review-cycle cap (${GUARD_BLOCK_CAP}) reached; the ${verb} was allowed without a further block.`
|
|
20708
21351
|
);
|
|
20709
21352
|
}
|
|
20710
|
-
const
|
|
21353
|
+
const range = resolveMomentRange(moment, frame, command, on);
|
|
21354
|
+
const files = rangeFiles(frame, range);
|
|
20711
21355
|
if (files.length === 0) process.exit(0);
|
|
20712
21356
|
const tokenResult = await resolveToken(globals.token);
|
|
20713
21357
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
20714
21358
|
if (!tokenResult.ok || !urlResult.ok) process.exit(0);
|
|
20715
|
-
|
|
20716
|
-
|
|
21359
|
+
logEvent("guard_frame", { moment, ...frameTelemetry(frame, range) });
|
|
21360
|
+
const codeDelta = collectCodeDelta(files);
|
|
21361
|
+
if (codeDelta.total_files === 0) process.exit(0);
|
|
21362
|
+
const statedIntent = extractStatedIntent(
|
|
21363
|
+
moment,
|
|
21364
|
+
command,
|
|
21365
|
+
moment === "pre-push" ? rangeMessages(frame, range) || null : null
|
|
21366
|
+
);
|
|
21367
|
+
const requestBody = buildGuardRequest(
|
|
21368
|
+
moment,
|
|
21369
|
+
files,
|
|
21370
|
+
codeDelta,
|
|
21371
|
+
iter,
|
|
21372
|
+
sessionId,
|
|
21373
|
+
statedIntent,
|
|
21374
|
+
buildGuardCoverage(files, codeDelta, frame, range)
|
|
21375
|
+
);
|
|
21376
|
+
const coverage = {
|
|
21377
|
+
moment,
|
|
21378
|
+
root: frame.worktreeRoot,
|
|
21379
|
+
branch: frame.branch,
|
|
21380
|
+
linked: frame.isLinkedWorktree,
|
|
21381
|
+
range: describeRange(range),
|
|
21382
|
+
sent: codeDelta.files.map((f) => f.path),
|
|
21383
|
+
excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason }))
|
|
21384
|
+
};
|
|
21385
|
+
logToFileOnly(coverageBlock(coverage));
|
|
20717
21386
|
const result = await analyzeRequest({
|
|
20718
21387
|
serviceUrl: urlResult.data,
|
|
20719
21388
|
token: tokenResult.data.token,
|
|
@@ -20734,31 +21403,39 @@ async function runGuard(opts, globals) {
|
|
|
20734
21403
|
const decision = response.gate_decision ?? "(unrecognised)";
|
|
20735
21404
|
const viewUrl = response.view_url ?? "";
|
|
20736
21405
|
const link = viewUrl ? ` \u2014 ${viewUrl}` : "";
|
|
21406
|
+
const covLine = coverageSummary(coverage);
|
|
21407
|
+
const covDetail = coverageBlock(coverage);
|
|
20737
21408
|
if (decision === "FAIL" && hasBlockingFinding(response)) {
|
|
20738
21409
|
writeIter(moment, iter + 1);
|
|
20739
|
-
writeBlockMessage(moment, response);
|
|
21410
|
+
writeBlockMessage(moment, response, covDetail);
|
|
20740
21411
|
process.exit(2);
|
|
20741
21412
|
}
|
|
20742
21413
|
resetIter(moment);
|
|
20743
21414
|
if (decision === "FAIL") {
|
|
20744
21415
|
const narrative = response.assessment?.narrative ?? "";
|
|
20745
21416
|
emitAllowNotice(
|
|
20746
|
-
`\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding${link}`,
|
|
20747
|
-
`Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
|
|
21417
|
+
`\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
|
|
21418
|
+
`Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
|
|
21419
|
+
${covDetail}${viewUrl ? `
|
|
21420
|
+
Report: ${viewUrl}` : ""}`
|
|
20748
21421
|
);
|
|
20749
21422
|
}
|
|
20750
21423
|
if (decision === "WARN") {
|
|
20751
21424
|
emitAllowNotice(
|
|
20752
|
-
`\u26A0 Verity ${moment}: WARN \u2014 proceeding${link}`,
|
|
20753
|
-
`Verity ${moment} review: WARN (proceeding)
|
|
21425
|
+
`\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
|
|
21426
|
+
`Verity ${moment} review: WARN (proceeding).
|
|
21427
|
+
${covDetail}${viewUrl ? `
|
|
21428
|
+
Report: ${viewUrl}` : ""}`
|
|
20754
21429
|
);
|
|
20755
21430
|
}
|
|
20756
21431
|
emitAllowNotice(
|
|
20757
|
-
`\u2713 Verity ${moment}: PASS${link}`,
|
|
20758
|
-
`Verity ${moment} review: PASS
|
|
21432
|
+
`\u2713 Verity ${moment}: PASS (${covLine})${link}`,
|
|
21433
|
+
`Verity ${moment} review: PASS.
|
|
21434
|
+
${covDetail}${viewUrl ? `
|
|
21435
|
+
Report: ${viewUrl}` : ""}`
|
|
20759
21436
|
);
|
|
20760
21437
|
}
|
|
20761
|
-
function writeBlockMessage(moment, response) {
|
|
21438
|
+
function writeBlockMessage(moment, response, covDetail) {
|
|
20762
21439
|
const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
|
|
20763
21440
|
const verb = moment === "pre-commit" ? "commit" : "push";
|
|
20764
21441
|
const assessment = response.assessment;
|
|
@@ -20788,6 +21465,9 @@ function writeBlockMessage(moment, response) {
|
|
|
20788
21465
|
`);
|
|
20789
21466
|
}
|
|
20790
21467
|
}
|
|
21468
|
+
process.stderr.write(`${DIM}${covDetail}${NC}
|
|
21469
|
+
|
|
21470
|
+
`);
|
|
20791
21471
|
if (viewUrl) process.stderr.write(`${CYAN}Full report: ${viewUrl}${NC}
|
|
20792
21472
|
|
|
20793
21473
|
`);
|
|
@@ -20796,16 +21476,16 @@ function writeBlockMessage(moment, response) {
|
|
|
20796
21476
|
}
|
|
20797
21477
|
|
|
20798
21478
|
// src/commands/init.ts
|
|
20799
|
-
var
|
|
21479
|
+
var import_node_fs37 = require("node:fs");
|
|
20800
21480
|
var import_promises13 = require("node:fs/promises");
|
|
20801
|
-
var
|
|
20802
|
-
var
|
|
21481
|
+
var import_node_path29 = require("node:path");
|
|
21482
|
+
var import_node_child_process11 = require("node:child_process");
|
|
20803
21483
|
var readline2 = __toESM(require("node:readline/promises"));
|
|
20804
21484
|
|
|
20805
21485
|
// src/commands/migrate.ts
|
|
20806
|
-
var
|
|
20807
|
-
var
|
|
20808
|
-
var
|
|
21486
|
+
var import_node_fs36 = require("node:fs");
|
|
21487
|
+
var import_node_path28 = require("node:path");
|
|
21488
|
+
var import_node_child_process10 = require("node:child_process");
|
|
20809
21489
|
|
|
20810
21490
|
// src/lib/telemetry.ts
|
|
20811
21491
|
var import_promises12 = require("node:fs/promises");
|
|
@@ -20900,11 +21580,11 @@ async function uninstallTelemetry() {
|
|
|
20900
21580
|
// src/commands/migrate.ts
|
|
20901
21581
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
20902
21582
|
function defaultNpmRemover(pkg) {
|
|
20903
|
-
(0,
|
|
21583
|
+
(0, import_node_child_process10.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
20904
21584
|
}
|
|
20905
21585
|
function isGitTracked(cwd, relPath) {
|
|
20906
21586
|
try {
|
|
20907
|
-
(0,
|
|
21587
|
+
(0, import_node_child_process10.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
20908
21588
|
return true;
|
|
20909
21589
|
} catch {
|
|
20910
21590
|
return false;
|
|
@@ -20912,7 +21592,7 @@ function isGitTracked(cwd, relPath) {
|
|
|
20912
21592
|
}
|
|
20913
21593
|
function isGitRepo(cwd) {
|
|
20914
21594
|
try {
|
|
20915
|
-
(0,
|
|
21595
|
+
(0, import_node_child_process10.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
20916
21596
|
return true;
|
|
20917
21597
|
} catch {
|
|
20918
21598
|
return false;
|
|
@@ -20933,12 +21613,12 @@ async function runMigration(opts = {}) {
|
|
|
20933
21613
|
return { actions, migrated: actions.length > 0 };
|
|
20934
21614
|
}
|
|
20935
21615
|
function migrateProjectDir(root, actions) {
|
|
20936
|
-
const gateDir = (0,
|
|
20937
|
-
const verityDir = (0,
|
|
20938
|
-
if ((0,
|
|
21616
|
+
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
21617
|
+
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
21618
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && !(0, import_node_fs36.existsSync)(verityDir)) {
|
|
20939
21619
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
20940
21620
|
}
|
|
20941
|
-
if ((0,
|
|
21621
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && (0, import_node_fs36.existsSync)(verityDir)) {
|
|
20942
21622
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
20943
21623
|
}
|
|
20944
21624
|
return false;
|
|
@@ -20952,20 +21632,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
20952
21632
|
);
|
|
20953
21633
|
}
|
|
20954
21634
|
try {
|
|
20955
|
-
(0,
|
|
21635
|
+
(0, import_node_child_process10.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
20956
21636
|
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
20957
21637
|
moved = true;
|
|
20958
21638
|
} catch {
|
|
20959
21639
|
}
|
|
20960
21640
|
}
|
|
20961
21641
|
if (moved) {
|
|
20962
|
-
if ((0,
|
|
21642
|
+
if ((0, import_node_fs36.existsSync)(gateDir)) {
|
|
20963
21643
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
20964
21644
|
if (carried > 0) {
|
|
20965
21645
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
20966
21646
|
}
|
|
20967
21647
|
try {
|
|
20968
|
-
(0,
|
|
21648
|
+
(0, import_node_fs36.rmSync)(gateDir, { recursive: true, force: true });
|
|
20969
21649
|
} catch {
|
|
20970
21650
|
}
|
|
20971
21651
|
}
|
|
@@ -20981,18 +21661,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
20981
21661
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
20982
21662
|
}
|
|
20983
21663
|
try {
|
|
20984
|
-
(0,
|
|
21664
|
+
(0, import_node_fs36.rmSync)(gateDir, { recursive: true, force: true });
|
|
20985
21665
|
} catch {
|
|
20986
21666
|
}
|
|
20987
21667
|
return carried > 0;
|
|
20988
21668
|
}
|
|
20989
21669
|
function migrateGlobalCredentials(home, actions) {
|
|
20990
21670
|
if (!home) return;
|
|
20991
|
-
const gateCreds = (0,
|
|
20992
|
-
const verityCreds = (0,
|
|
20993
|
-
if (!(0,
|
|
20994
|
-
if (!(0,
|
|
20995
|
-
(0,
|
|
21671
|
+
const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
|
|
21672
|
+
const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
|
|
21673
|
+
if (!(0, import_node_fs36.existsSync)(gateCreds)) return;
|
|
21674
|
+
if (!(0, import_node_fs36.existsSync)(verityCreds)) {
|
|
21675
|
+
(0, import_node_fs36.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
|
|
20996
21676
|
moveFile(gateCreds, verityCreds);
|
|
20997
21677
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
20998
21678
|
return;
|
|
@@ -21014,8 +21694,8 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
21014
21694
|
}
|
|
21015
21695
|
}
|
|
21016
21696
|
async function migrateClaudeMd(root, actions) {
|
|
21017
|
-
const claudeMd = (0,
|
|
21018
|
-
const hadLegacyBlock = (0,
|
|
21697
|
+
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
21698
|
+
const hadLegacyBlock = (0, import_node_fs36.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
21019
21699
|
if (!hadLegacyBlock) return;
|
|
21020
21700
|
try {
|
|
21021
21701
|
await ensureClaudeMdPointer(root);
|
|
@@ -21025,13 +21705,13 @@ async function migrateClaudeMd(root, actions) {
|
|
|
21025
21705
|
}
|
|
21026
21706
|
}
|
|
21027
21707
|
function migrateStandardFile(root, actions) {
|
|
21028
|
-
const gateMd = (0,
|
|
21029
|
-
const verityMd = (0,
|
|
21030
|
-
if (!(0,
|
|
21708
|
+
const gateMd = (0, import_node_path28.join)(root, "GATE.md");
|
|
21709
|
+
const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
|
|
21710
|
+
if (!(0, import_node_fs36.existsSync)(gateMd) || (0, import_node_fs36.existsSync)(verityMd)) return;
|
|
21031
21711
|
let moved = false;
|
|
21032
21712
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
21033
21713
|
try {
|
|
21034
|
-
(0,
|
|
21714
|
+
(0, import_node_child_process10.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
21035
21715
|
moved = true;
|
|
21036
21716
|
} catch {
|
|
21037
21717
|
}
|
|
@@ -21039,12 +21719,12 @@ function migrateStandardFile(root, actions) {
|
|
|
21039
21719
|
if (!moved) moveFile(gateMd, verityMd);
|
|
21040
21720
|
const content = readFileSyncSafe(verityMd);
|
|
21041
21721
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
21042
|
-
if (refreshed !== content) (0,
|
|
21722
|
+
if (refreshed !== content) (0, import_node_fs36.writeFileSync)(verityMd, refreshed);
|
|
21043
21723
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
21044
21724
|
}
|
|
21045
21725
|
async function migrateTelemetryHeaders(root, actions) {
|
|
21046
|
-
const file = (0,
|
|
21047
|
-
if (!(0,
|
|
21726
|
+
const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
|
|
21727
|
+
if (!(0, import_node_fs36.existsSync)(file)) return;
|
|
21048
21728
|
let settings;
|
|
21049
21729
|
try {
|
|
21050
21730
|
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
@@ -21091,22 +21771,22 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
21091
21771
|
toAppend.push(line.replace(/\r$/, ""));
|
|
21092
21772
|
}
|
|
21093
21773
|
if (toAppend.length > 0) {
|
|
21094
|
-
const
|
|
21095
|
-
(0,
|
|
21774
|
+
const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
21775
|
+
(0, import_node_fs36.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
|
|
21096
21776
|
}
|
|
21097
|
-
(0,
|
|
21777
|
+
(0, import_node_fs36.rmSync)(gateCreds, { force: true });
|
|
21098
21778
|
return toAppend.length;
|
|
21099
21779
|
}
|
|
21100
21780
|
function readFileSyncSafe(path) {
|
|
21101
21781
|
try {
|
|
21102
|
-
return (0,
|
|
21782
|
+
return (0, import_node_fs36.readFileSync)(path, "utf-8");
|
|
21103
21783
|
} catch {
|
|
21104
21784
|
return "";
|
|
21105
21785
|
}
|
|
21106
21786
|
}
|
|
21107
21787
|
function hasStagedChanges(root) {
|
|
21108
21788
|
try {
|
|
21109
|
-
(0,
|
|
21789
|
+
(0, import_node_child_process10.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
21110
21790
|
return false;
|
|
21111
21791
|
} catch {
|
|
21112
21792
|
return true;
|
|
@@ -21114,35 +21794,35 @@ function hasStagedChanges(root) {
|
|
|
21114
21794
|
}
|
|
21115
21795
|
function moveDir(from, to) {
|
|
21116
21796
|
try {
|
|
21117
|
-
(0,
|
|
21797
|
+
(0, import_node_fs36.renameSync)(from, to);
|
|
21118
21798
|
} catch (err) {
|
|
21119
21799
|
if (err.code !== "EXDEV") throw err;
|
|
21120
|
-
(0,
|
|
21121
|
-
(0,
|
|
21800
|
+
(0, import_node_fs36.cpSync)(from, to, { recursive: true });
|
|
21801
|
+
(0, import_node_fs36.rmSync)(from, { recursive: true, force: true });
|
|
21122
21802
|
}
|
|
21123
21803
|
}
|
|
21124
21804
|
function moveFile(from, to) {
|
|
21125
21805
|
try {
|
|
21126
|
-
(0,
|
|
21806
|
+
(0, import_node_fs36.renameSync)(from, to);
|
|
21127
21807
|
} catch (err) {
|
|
21128
21808
|
if (err.code !== "EXDEV") throw err;
|
|
21129
|
-
(0,
|
|
21130
|
-
(0,
|
|
21809
|
+
(0, import_node_fs36.cpSync)(from, to);
|
|
21810
|
+
(0, import_node_fs36.rmSync)(from, { force: true });
|
|
21131
21811
|
}
|
|
21132
21812
|
}
|
|
21133
21813
|
function carryLegacyContents(gateDir, verityDir) {
|
|
21134
21814
|
let copied = 0;
|
|
21135
21815
|
const walk = (relDir) => {
|
|
21136
|
-
const srcDir = (0,
|
|
21137
|
-
for (const entry of (0,
|
|
21138
|
-
const rel = relDir ? (0,
|
|
21139
|
-
const src = (0,
|
|
21140
|
-
const dest = (0,
|
|
21141
|
-
if ((0,
|
|
21816
|
+
const srcDir = (0, import_node_path28.join)(gateDir, relDir);
|
|
21817
|
+
for (const entry of (0, import_node_fs36.readdirSync)(srcDir)) {
|
|
21818
|
+
const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
|
|
21819
|
+
const src = (0, import_node_path28.join)(gateDir, rel);
|
|
21820
|
+
const dest = (0, import_node_path28.join)(verityDir, rel);
|
|
21821
|
+
if ((0, import_node_fs36.statSync)(src).isDirectory()) {
|
|
21142
21822
|
walk(rel);
|
|
21143
|
-
} else if (!(0,
|
|
21144
|
-
(0,
|
|
21145
|
-
(0,
|
|
21823
|
+
} else if (!(0, import_node_fs36.existsSync)(dest)) {
|
|
21824
|
+
(0, import_node_fs36.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
|
|
21825
|
+
(0, import_node_fs36.cpSync)(src, dest);
|
|
21146
21826
|
copied++;
|
|
21147
21827
|
}
|
|
21148
21828
|
}
|
|
@@ -21151,22 +21831,22 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
21151
21831
|
return copied;
|
|
21152
21832
|
}
|
|
21153
21833
|
async function needsMigration(root = repoRoot()) {
|
|
21154
|
-
const gateDir = (0,
|
|
21155
|
-
const verityDir = (0,
|
|
21156
|
-
if ((0,
|
|
21157
|
-
if ((0,
|
|
21158
|
-
if ((0,
|
|
21834
|
+
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
21835
|
+
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
21836
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && !(0, import_node_fs36.existsSync)(verityDir)) return true;
|
|
21837
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && (0, import_node_fs36.existsSync)(verityDir)) {
|
|
21838
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs36.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
|
|
21159
21839
|
return true;
|
|
21160
21840
|
}
|
|
21161
|
-
if ((0,
|
|
21841
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs36.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
|
|
21162
21842
|
return true;
|
|
21163
21843
|
}
|
|
21164
21844
|
}
|
|
21165
|
-
const claudeMd = (0,
|
|
21166
|
-
if ((0,
|
|
21845
|
+
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
21846
|
+
if ((0, import_node_fs36.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
21167
21847
|
return true;
|
|
21168
21848
|
}
|
|
21169
|
-
if ((0,
|
|
21849
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs36.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
|
|
21170
21850
|
return true;
|
|
21171
21851
|
}
|
|
21172
21852
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -21248,6 +21928,8 @@ async function confirmExistingLogin(serviceUrl, remote, opts) {
|
|
|
21248
21928
|
async function runOptionalAuth(resolution, opts = {}) {
|
|
21249
21929
|
if (resolution.source === "default") {
|
|
21250
21930
|
printInfo(`No Verity service configured on this machine \u2014 using the default: ${resolution.url}`);
|
|
21931
|
+
} else {
|
|
21932
|
+
printInfo(`Authenticating against ${resolution.url} (source: ${resolution.source}).`);
|
|
21251
21933
|
}
|
|
21252
21934
|
const heal = await maybeHealServiceUrl(resolution, opts.verbose);
|
|
21253
21935
|
const serviceUrl = heal.serviceUrl;
|
|
@@ -21257,7 +21939,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
21257
21939
|
}
|
|
21258
21940
|
let remote = "";
|
|
21259
21941
|
try {
|
|
21260
|
-
remote = (0,
|
|
21942
|
+
remote = (0, import_node_child_process11.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
21261
21943
|
} catch {
|
|
21262
21944
|
}
|
|
21263
21945
|
if (!healed) {
|
|
@@ -21300,15 +21982,15 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
21300
21982
|
}
|
|
21301
21983
|
function resolveDataDir() {
|
|
21302
21984
|
const candidates = [
|
|
21303
|
-
(0,
|
|
21985
|
+
(0, import_node_path29.join)(__dirname, "..", "data"),
|
|
21304
21986
|
// installed: node_modules/@codacy/verity-cli/data
|
|
21305
|
-
(0,
|
|
21987
|
+
(0, import_node_path29.join)(__dirname, "..", "..", "data"),
|
|
21306
21988
|
// edge case: nested resolution
|
|
21307
|
-
(0,
|
|
21989
|
+
(0, import_node_path29.join)(process.cwd(), "cli", "data")
|
|
21308
21990
|
// local dev: running from repo root
|
|
21309
21991
|
];
|
|
21310
21992
|
for (const candidate of candidates) {
|
|
21311
|
-
if ((0,
|
|
21993
|
+
if ((0, import_node_fs37.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
|
|
21312
21994
|
return candidate;
|
|
21313
21995
|
}
|
|
21314
21996
|
}
|
|
@@ -21324,7 +22006,7 @@ function registerInitCommand(program2) {
|
|
|
21324
22006
|
program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
21325
22007
|
const force = opts.force ?? false;
|
|
21326
22008
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
21327
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
22009
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs37.existsSync)(m));
|
|
21328
22010
|
if (!isProject) {
|
|
21329
22011
|
printError("No project detected in the current directory.");
|
|
21330
22012
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -21352,30 +22034,30 @@ function registerInitCommand(program2) {
|
|
|
21352
22034
|
}
|
|
21353
22035
|
printInfo(` Node.js ${nodeVersion} \u2713`);
|
|
21354
22036
|
try {
|
|
21355
|
-
const gitVersion = (0,
|
|
22037
|
+
const gitVersion = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8" }).trim();
|
|
21356
22038
|
printInfo(` ${gitVersion} \u2713`);
|
|
21357
22039
|
} catch {
|
|
21358
22040
|
printError("git is required but not installed. Install from https://git-scm.com");
|
|
21359
22041
|
process.exit(1);
|
|
21360
22042
|
}
|
|
21361
22043
|
try {
|
|
21362
|
-
(0,
|
|
22044
|
+
(0, import_node_child_process11.execSync)("which claude", { encoding: "utf-8" });
|
|
21363
22045
|
printInfo(" Claude Code \u2713");
|
|
21364
22046
|
} catch {
|
|
21365
22047
|
printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
|
|
21366
22048
|
}
|
|
21367
22049
|
try {
|
|
21368
|
-
(0,
|
|
22050
|
+
(0, import_node_child_process11.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
|
|
21369
22051
|
printInfo(" @codacy/analysis-cli \u2713");
|
|
21370
22052
|
} catch {
|
|
21371
22053
|
printInfo(" Installing @codacy/analysis-cli...");
|
|
21372
22054
|
try {
|
|
21373
|
-
(0,
|
|
22055
|
+
(0, import_node_child_process11.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
|
|
21374
22056
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
21375
22057
|
} catch {
|
|
21376
22058
|
try {
|
|
21377
22059
|
printWarn(" Retrying with sudo...");
|
|
21378
|
-
(0,
|
|
22060
|
+
(0, import_node_child_process11.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
21379
22061
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
21380
22062
|
} catch {
|
|
21381
22063
|
printWarn(" Could not install @codacy/analysis-cli automatically.");
|
|
@@ -21387,21 +22069,21 @@ function registerInitCommand(program2) {
|
|
|
21387
22069
|
console.log("");
|
|
21388
22070
|
printInfo("Installing skills...");
|
|
21389
22071
|
const dataDir = resolveDataDir();
|
|
21390
|
-
const skillsSource = (0,
|
|
22072
|
+
const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
|
|
21391
22073
|
const skillsDest = ".claude/skills";
|
|
21392
22074
|
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
21393
22075
|
let skillsInstalled = 0;
|
|
21394
22076
|
for (const skill of skills) {
|
|
21395
|
-
const src = (0,
|
|
21396
|
-
const dest = (0,
|
|
21397
|
-
if (!(0,
|
|
22077
|
+
const src = (0, import_node_path29.join)(skillsSource, skill);
|
|
22078
|
+
const dest = (0, import_node_path29.join)(skillsDest, skill);
|
|
22079
|
+
if (!(0, import_node_fs37.existsSync)(src)) {
|
|
21398
22080
|
printWarn(` Skill data not found: ${skill}`);
|
|
21399
22081
|
continue;
|
|
21400
22082
|
}
|
|
21401
|
-
if ((0,
|
|
21402
|
-
const srcSkill = (0,
|
|
21403
|
-
const destSkill = (0,
|
|
21404
|
-
if ((0,
|
|
22083
|
+
if ((0, import_node_fs37.existsSync)(dest) && !force) {
|
|
22084
|
+
const srcSkill = (0, import_node_path29.join)(src, "SKILL.md");
|
|
22085
|
+
const destSkill = (0, import_node_path29.join)(dest, "SKILL.md");
|
|
22086
|
+
if ((0, import_node_fs37.existsSync)(destSkill)) {
|
|
21405
22087
|
try {
|
|
21406
22088
|
const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
|
|
21407
22089
|
const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
|
|
@@ -21432,13 +22114,19 @@ function registerInitCommand(program2) {
|
|
|
21432
22114
|
}
|
|
21433
22115
|
await (0, import_promises13.mkdir)(VERITY_DIR, { recursive: true });
|
|
21434
22116
|
await ensureMemoryDir();
|
|
22117
|
+
const ignoreResult = ensureSnapshotGitignored();
|
|
22118
|
+
if (ignoreResult === "failed") {
|
|
22119
|
+
printWarn(" .gitignore: could not add .verity/.snapshot/ \u2014 add it manually (it holds copies of analyzed files)");
|
|
22120
|
+
} else {
|
|
22121
|
+
printInfo(` .gitignore: .verity/.snapshot/ ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
|
|
22122
|
+
}
|
|
21435
22123
|
try {
|
|
21436
22124
|
await ensureClaudeMdPointer();
|
|
21437
22125
|
printInfo(" CLAUDE.md memory pointer \u2713");
|
|
21438
22126
|
} catch (err) {
|
|
21439
22127
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
21440
22128
|
}
|
|
21441
|
-
const globalVerityDir = (0,
|
|
22129
|
+
const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
|
|
21442
22130
|
await (0, import_promises13.mkdir)(globalVerityDir, { recursive: true });
|
|
21443
22131
|
console.log("");
|
|
21444
22132
|
try {
|
|
@@ -21473,8 +22161,8 @@ function registerInitCommand(program2) {
|
|
|
21473
22161
|
}
|
|
21474
22162
|
|
|
21475
22163
|
// src/commands/uninstall.ts
|
|
21476
|
-
var
|
|
21477
|
-
var
|
|
22164
|
+
var import_node_fs38 = require("node:fs");
|
|
22165
|
+
var import_node_path30 = require("node:path");
|
|
21478
22166
|
var SKILL_NAMES = [
|
|
21479
22167
|
"verity-setup",
|
|
21480
22168
|
"verity-analyze",
|
|
@@ -21493,11 +22181,11 @@ function registerUninstallCommand(program2) {
|
|
|
21493
22181
|
const actions = [];
|
|
21494
22182
|
const skillsRoot = projectPath(".claude/skills");
|
|
21495
22183
|
for (const name of SKILL_NAMES) {
|
|
21496
|
-
const dir = (0,
|
|
21497
|
-
if ((0,
|
|
22184
|
+
const dir = (0, import_node_path30.join)(skillsRoot, name);
|
|
22185
|
+
if ((0, import_node_fs38.existsSync)(dir)) {
|
|
21498
22186
|
actions.push({
|
|
21499
22187
|
label: `Remove .claude/skills/${name}/`,
|
|
21500
|
-
apply: () => (0,
|
|
22188
|
+
apply: () => (0, import_node_fs38.rmSync)(dir, { recursive: true, force: true })
|
|
21501
22189
|
});
|
|
21502
22190
|
}
|
|
21503
22191
|
}
|
|
@@ -21511,24 +22199,24 @@ function registerUninstallCommand(program2) {
|
|
|
21511
22199
|
});
|
|
21512
22200
|
}
|
|
21513
22201
|
const verityDir = projectPath(VERITY_DIR);
|
|
21514
|
-
if ((0,
|
|
22202
|
+
if ((0, import_node_fs38.existsSync)(verityDir)) {
|
|
21515
22203
|
actions.push({
|
|
21516
22204
|
label: `Remove ${VERITY_DIR}/`,
|
|
21517
|
-
apply: () => (0,
|
|
22205
|
+
apply: () => (0, import_node_fs38.rmSync)(verityDir, { recursive: true, force: true })
|
|
21518
22206
|
});
|
|
21519
22207
|
}
|
|
21520
22208
|
if (!keepVerityMd) {
|
|
21521
22209
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
21522
|
-
if ((0,
|
|
22210
|
+
if ((0, import_node_fs38.existsSync)(verityMd)) {
|
|
21523
22211
|
actions.push({
|
|
21524
22212
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
21525
|
-
apply: () => (0,
|
|
22213
|
+
apply: () => (0, import_node_fs38.rmSync)(verityMd, { force: true })
|
|
21526
22214
|
});
|
|
21527
22215
|
}
|
|
21528
22216
|
}
|
|
21529
22217
|
const cleanupEmptyDir = (path) => {
|
|
21530
|
-
if ((0,
|
|
21531
|
-
(0,
|
|
22218
|
+
if ((0, import_node_fs38.existsSync)(path) && (0, import_node_fs38.statSync)(path).isDirectory() && (0, import_node_fs38.readdirSync)(path).length === 0) {
|
|
22219
|
+
(0, import_node_fs38.rmdirSync)(path);
|
|
21532
22220
|
}
|
|
21533
22221
|
};
|
|
21534
22222
|
actions.push({
|
|
@@ -21539,11 +22227,11 @@ function registerUninstallCommand(program2) {
|
|
|
21539
22227
|
}
|
|
21540
22228
|
});
|
|
21541
22229
|
const home = process.env.HOME ?? "";
|
|
21542
|
-
const globalVerityDir = (0,
|
|
21543
|
-
if (purgeGlobal && (0,
|
|
22230
|
+
const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
|
|
22231
|
+
if (purgeGlobal && (0, import_node_fs38.existsSync)(globalVerityDir)) {
|
|
21544
22232
|
actions.push({
|
|
21545
22233
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
21546
|
-
apply: () => (0,
|
|
22234
|
+
apply: () => (0, import_node_fs38.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
21547
22235
|
});
|
|
21548
22236
|
}
|
|
21549
22237
|
if (actions.length === 0) {
|
|
@@ -21737,8 +22425,8 @@ function registerTaskCommands(program2) {
|
|
|
21737
22425
|
}
|
|
21738
22426
|
|
|
21739
22427
|
// src/commands/reset.ts
|
|
21740
|
-
var
|
|
21741
|
-
var
|
|
22428
|
+
var import_node_fs39 = require("node:fs");
|
|
22429
|
+
var import_node_path31 = require("node:path");
|
|
21742
22430
|
function registerResetCommand(program2) {
|
|
21743
22431
|
program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
|
|
21744
22432
|
const globals = program2.opts();
|
|
@@ -21775,11 +22463,11 @@ function registerResetCommand(program2) {
|
|
|
21775
22463
|
}
|
|
21776
22464
|
const cacheDir = projectPath(CACHE_DIR);
|
|
21777
22465
|
let purged = 0;
|
|
21778
|
-
if ((0,
|
|
21779
|
-
for (const entry of (0,
|
|
22466
|
+
if ((0, import_node_fs39.existsSync)(cacheDir)) {
|
|
22467
|
+
for (const entry of (0, import_node_fs39.readdirSync)(cacheDir)) {
|
|
21780
22468
|
if (entry.startsWith("pending-")) {
|
|
21781
22469
|
try {
|
|
21782
|
-
(0,
|
|
22470
|
+
(0, import_node_fs39.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
|
|
21783
22471
|
purged++;
|
|
21784
22472
|
} catch {
|
|
21785
22473
|
}
|
|
@@ -21794,19 +22482,19 @@ function registerResetCommand(program2) {
|
|
|
21794
22482
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
21795
22483
|
];
|
|
21796
22484
|
for (const file of filesToClear) {
|
|
21797
|
-
if ((0,
|
|
22485
|
+
if ((0, import_node_fs39.existsSync)(file)) {
|
|
21798
22486
|
try {
|
|
21799
|
-
(0,
|
|
22487
|
+
(0, import_node_fs39.writeFileSync)(file, "");
|
|
21800
22488
|
} catch {
|
|
21801
22489
|
}
|
|
21802
22490
|
}
|
|
21803
22491
|
}
|
|
21804
22492
|
if (opts.all) {
|
|
21805
22493
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
21806
|
-
if ((0,
|
|
21807
|
-
for (const entry of (0,
|
|
22494
|
+
if ((0, import_node_fs39.existsSync)(logsDir)) {
|
|
22495
|
+
for (const entry of (0, import_node_fs39.readdirSync)(logsDir)) {
|
|
21808
22496
|
try {
|
|
21809
|
-
(0,
|
|
22497
|
+
(0, import_node_fs39.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
|
|
21810
22498
|
} catch {
|
|
21811
22499
|
}
|
|
21812
22500
|
}
|
|
@@ -22114,8 +22802,8 @@ function registerTelemetryCommands(program2) {
|
|
|
22114
22802
|
}
|
|
22115
22803
|
|
|
22116
22804
|
// src/cli.ts
|
|
22117
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.
|
|
22118
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.
|
|
22805
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.30.0-experimental.43e7755").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
|
|
22806
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.30.0-experimental.43e7755");
|
|
22119
22807
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
22120
22808
|
try {
|
|
22121
22809
|
await foldLegacyLocalCredential();
|