@codacy/verity-cli 0.32.6-experimental.df0a578 → 0.32.7
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 +16 -0
- package/bin/verity.js +582 -1980
- package/data/skills/verity-reflect/SKILL.md +31 -55
- package/package.json +2 -2
package/bin/verity.js
CHANGED
|
@@ -6975,10 +6975,10 @@ var require_resolve_block_map = __commonJS({
|
|
|
6975
6975
|
let offset = bm.offset;
|
|
6976
6976
|
let commentEnd = null;
|
|
6977
6977
|
for (const collItem of bm.items) {
|
|
6978
|
-
const { start, key, sep:
|
|
6978
|
+
const { start, key, sep: sep3, value } = collItem;
|
|
6979
6979
|
const keyProps = resolveProps.resolveProps(start, {
|
|
6980
6980
|
indicator: "explicit-key-ind",
|
|
6981
|
-
next: key ??
|
|
6981
|
+
next: key ?? sep3?.[0],
|
|
6982
6982
|
offset,
|
|
6983
6983
|
onError,
|
|
6984
6984
|
parentIndent: bm.indent,
|
|
@@ -6992,7 +6992,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
6992
6992
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
6993
6993
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
6994
6994
|
}
|
|
6995
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
6995
|
+
if (!keyProps.anchor && !keyProps.tag && !sep3) {
|
|
6996
6996
|
commentEnd = keyProps.end;
|
|
6997
6997
|
if (keyProps.comment) {
|
|
6998
6998
|
if (map.comment)
|
|
@@ -7016,7 +7016,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
7016
7016
|
ctx.atKey = false;
|
|
7017
7017
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
7018
7018
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
7019
|
-
const valueProps = resolveProps.resolveProps(
|
|
7019
|
+
const valueProps = resolveProps.resolveProps(sep3 ?? [], {
|
|
7020
7020
|
indicator: "map-value-ind",
|
|
7021
7021
|
next: value,
|
|
7022
7022
|
offset: keyNode.range[2],
|
|
@@ -7032,7 +7032,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
7032
7032
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
7033
7033
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
7034
7034
|
}
|
|
7035
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
7035
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
|
|
7036
7036
|
if (ctx.schema.compat)
|
|
7037
7037
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
7038
7038
|
offset = valueNode.range[2];
|
|
@@ -7123,7 +7123,7 @@ var require_resolve_end = __commonJS({
|
|
|
7123
7123
|
let comment = "";
|
|
7124
7124
|
if (end) {
|
|
7125
7125
|
let hasSpace = false;
|
|
7126
|
-
let
|
|
7126
|
+
let sep3 = "";
|
|
7127
7127
|
for (const token of end) {
|
|
7128
7128
|
const { source, type } = token;
|
|
7129
7129
|
switch (type) {
|
|
@@ -7137,13 +7137,13 @@ var require_resolve_end = __commonJS({
|
|
|
7137
7137
|
if (!comment)
|
|
7138
7138
|
comment = cb;
|
|
7139
7139
|
else
|
|
7140
|
-
comment +=
|
|
7141
|
-
|
|
7140
|
+
comment += sep3 + cb;
|
|
7141
|
+
sep3 = "";
|
|
7142
7142
|
break;
|
|
7143
7143
|
}
|
|
7144
7144
|
case "newline":
|
|
7145
7145
|
if (comment)
|
|
7146
|
-
|
|
7146
|
+
sep3 += source;
|
|
7147
7147
|
hasSpace = true;
|
|
7148
7148
|
break;
|
|
7149
7149
|
default:
|
|
@@ -7186,18 +7186,18 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7186
7186
|
let offset = fc.offset + fc.start.source.length;
|
|
7187
7187
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
7188
7188
|
const collItem = fc.items[i];
|
|
7189
|
-
const { start, key, sep:
|
|
7189
|
+
const { start, key, sep: sep3, value } = collItem;
|
|
7190
7190
|
const props = resolveProps.resolveProps(start, {
|
|
7191
7191
|
flow: fcName,
|
|
7192
7192
|
indicator: "explicit-key-ind",
|
|
7193
|
-
next: key ??
|
|
7193
|
+
next: key ?? sep3?.[0],
|
|
7194
7194
|
offset,
|
|
7195
7195
|
onError,
|
|
7196
7196
|
parentIndent: fc.indent,
|
|
7197
7197
|
startOnNewline: false
|
|
7198
7198
|
});
|
|
7199
7199
|
if (!props.found) {
|
|
7200
|
-
if (!props.anchor && !props.tag && !
|
|
7200
|
+
if (!props.anchor && !props.tag && !sep3 && !value) {
|
|
7201
7201
|
if (i === 0 && props.comma)
|
|
7202
7202
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
7203
7203
|
else if (i < fc.items.length - 1)
|
|
@@ -7251,8 +7251,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7251
7251
|
}
|
|
7252
7252
|
}
|
|
7253
7253
|
}
|
|
7254
|
-
if (!isMap && !
|
|
7255
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
7254
|
+
if (!isMap && !sep3 && !props.found) {
|
|
7255
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
|
|
7256
7256
|
coll.items.push(valueNode);
|
|
7257
7257
|
offset = valueNode.range[2];
|
|
7258
7258
|
if (isBlock(value))
|
|
@@ -7264,7 +7264,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7264
7264
|
if (isBlock(key))
|
|
7265
7265
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
7266
7266
|
ctx.atKey = false;
|
|
7267
|
-
const valueProps = resolveProps.resolveProps(
|
|
7267
|
+
const valueProps = resolveProps.resolveProps(sep3 ?? [], {
|
|
7268
7268
|
flow: fcName,
|
|
7269
7269
|
indicator: "map-value-ind",
|
|
7270
7270
|
next: value,
|
|
@@ -7275,8 +7275,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7275
7275
|
});
|
|
7276
7276
|
if (valueProps.found) {
|
|
7277
7277
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
7278
|
-
if (
|
|
7279
|
-
for (const st of
|
|
7278
|
+
if (sep3)
|
|
7279
|
+
for (const st of sep3) {
|
|
7280
7280
|
if (st === valueProps.found)
|
|
7281
7281
|
break;
|
|
7282
7282
|
if (st.type === "newline") {
|
|
@@ -7293,7 +7293,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
7293
7293
|
else
|
|
7294
7294
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
7295
7295
|
}
|
|
7296
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
7296
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
|
|
7297
7297
|
if (valueNode) {
|
|
7298
7298
|
if (isBlock(value))
|
|
7299
7299
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -7473,7 +7473,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
7473
7473
|
chompStart = i + 1;
|
|
7474
7474
|
}
|
|
7475
7475
|
let value = "";
|
|
7476
|
-
let
|
|
7476
|
+
let sep3 = "";
|
|
7477
7477
|
let prevMoreIndented = false;
|
|
7478
7478
|
for (let i = 0; i < contentStart; ++i)
|
|
7479
7479
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -7490,24 +7490,24 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
7490
7490
|
indent = "";
|
|
7491
7491
|
}
|
|
7492
7492
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
7493
|
-
value +=
|
|
7494
|
-
|
|
7493
|
+
value += sep3 + indent.slice(trimIndent) + content;
|
|
7494
|
+
sep3 = "\n";
|
|
7495
7495
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
7496
|
-
if (
|
|
7497
|
-
|
|
7498
|
-
else if (!prevMoreIndented &&
|
|
7499
|
-
|
|
7500
|
-
value +=
|
|
7501
|
-
|
|
7496
|
+
if (sep3 === " ")
|
|
7497
|
+
sep3 = "\n";
|
|
7498
|
+
else if (!prevMoreIndented && sep3 === "\n")
|
|
7499
|
+
sep3 = "\n\n";
|
|
7500
|
+
value += sep3 + indent.slice(trimIndent) + content;
|
|
7501
|
+
sep3 = "\n";
|
|
7502
7502
|
prevMoreIndented = true;
|
|
7503
7503
|
} else if (content === "") {
|
|
7504
|
-
if (
|
|
7504
|
+
if (sep3 === "\n")
|
|
7505
7505
|
value += "\n";
|
|
7506
7506
|
else
|
|
7507
|
-
|
|
7507
|
+
sep3 = "\n";
|
|
7508
7508
|
} else {
|
|
7509
|
-
value +=
|
|
7510
|
-
|
|
7509
|
+
value += sep3 + content;
|
|
7510
|
+
sep3 = " ";
|
|
7511
7511
|
prevMoreIndented = false;
|
|
7512
7512
|
}
|
|
7513
7513
|
}
|
|
@@ -7689,25 +7689,25 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
7689
7689
|
if (!match)
|
|
7690
7690
|
return source;
|
|
7691
7691
|
let res = match[1];
|
|
7692
|
-
let
|
|
7692
|
+
let sep3 = " ";
|
|
7693
7693
|
let pos = first.lastIndex;
|
|
7694
7694
|
line.lastIndex = pos;
|
|
7695
7695
|
while (match = line.exec(source)) {
|
|
7696
7696
|
if (match[1] === "") {
|
|
7697
|
-
if (
|
|
7698
|
-
res +=
|
|
7697
|
+
if (sep3 === "\n")
|
|
7698
|
+
res += sep3;
|
|
7699
7699
|
else
|
|
7700
|
-
|
|
7700
|
+
sep3 = "\n";
|
|
7701
7701
|
} else {
|
|
7702
|
-
res +=
|
|
7703
|
-
|
|
7702
|
+
res += sep3 + match[1];
|
|
7703
|
+
sep3 = " ";
|
|
7704
7704
|
}
|
|
7705
7705
|
pos = line.lastIndex;
|
|
7706
7706
|
}
|
|
7707
7707
|
const last = /[ \t]*(.*)/sy;
|
|
7708
7708
|
last.lastIndex = pos;
|
|
7709
7709
|
match = last.exec(source);
|
|
7710
|
-
return res +
|
|
7710
|
+
return res + sep3 + (match?.[1] ?? "");
|
|
7711
7711
|
}
|
|
7712
7712
|
function doubleQuotedValue(source, onError) {
|
|
7713
7713
|
let res = "";
|
|
@@ -8514,14 +8514,14 @@ var require_cst_stringify = __commonJS({
|
|
|
8514
8514
|
}
|
|
8515
8515
|
}
|
|
8516
8516
|
}
|
|
8517
|
-
function stringifyItem({ start, key, sep:
|
|
8517
|
+
function stringifyItem({ start, key, sep: sep3, value }) {
|
|
8518
8518
|
let res = "";
|
|
8519
8519
|
for (const st of start)
|
|
8520
8520
|
res += st.source;
|
|
8521
8521
|
if (key)
|
|
8522
8522
|
res += stringifyToken(key);
|
|
8523
|
-
if (
|
|
8524
|
-
for (const st of
|
|
8523
|
+
if (sep3)
|
|
8524
|
+
for (const st of sep3)
|
|
8525
8525
|
res += st.source;
|
|
8526
8526
|
if (value)
|
|
8527
8527
|
res += stringifyToken(value);
|
|
@@ -9671,18 +9671,18 @@ var require_parser = __commonJS({
|
|
|
9671
9671
|
if (this.type === "map-value-ind") {
|
|
9672
9672
|
const prev = getPrevProps(this.peek(2));
|
|
9673
9673
|
const start = getFirstKeyStartProps(prev);
|
|
9674
|
-
let
|
|
9674
|
+
let sep3;
|
|
9675
9675
|
if (scalar.end) {
|
|
9676
|
-
|
|
9677
|
-
|
|
9676
|
+
sep3 = scalar.end;
|
|
9677
|
+
sep3.push(this.sourceToken);
|
|
9678
9678
|
delete scalar.end;
|
|
9679
9679
|
} else
|
|
9680
|
-
|
|
9680
|
+
sep3 = [this.sourceToken];
|
|
9681
9681
|
const map = {
|
|
9682
9682
|
type: "block-map",
|
|
9683
9683
|
offset: scalar.offset,
|
|
9684
9684
|
indent: scalar.indent,
|
|
9685
|
-
items: [{ start, key: scalar, sep:
|
|
9685
|
+
items: [{ start, key: scalar, sep: sep3 }]
|
|
9686
9686
|
};
|
|
9687
9687
|
this.onKeyLine = true;
|
|
9688
9688
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -9835,15 +9835,15 @@ var require_parser = __commonJS({
|
|
|
9835
9835
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
9836
9836
|
const start2 = getFirstKeyStartProps(it.start);
|
|
9837
9837
|
const key = it.key;
|
|
9838
|
-
const
|
|
9839
|
-
|
|
9838
|
+
const sep3 = it.sep;
|
|
9839
|
+
sep3.push(this.sourceToken);
|
|
9840
9840
|
delete it.key;
|
|
9841
9841
|
delete it.sep;
|
|
9842
9842
|
this.stack.push({
|
|
9843
9843
|
type: "block-map",
|
|
9844
9844
|
offset: this.offset,
|
|
9845
9845
|
indent: this.indent,
|
|
9846
|
-
items: [{ start: start2, key, sep:
|
|
9846
|
+
items: [{ start: start2, key, sep: sep3 }]
|
|
9847
9847
|
});
|
|
9848
9848
|
} else if (start.length > 0) {
|
|
9849
9849
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -10037,13 +10037,13 @@ var require_parser = __commonJS({
|
|
|
10037
10037
|
const prev = getPrevProps(parent);
|
|
10038
10038
|
const start = getFirstKeyStartProps(prev);
|
|
10039
10039
|
fixFlowSeqItems(fc);
|
|
10040
|
-
const
|
|
10041
|
-
|
|
10040
|
+
const sep3 = fc.end.splice(1, fc.end.length);
|
|
10041
|
+
sep3.push(this.sourceToken);
|
|
10042
10042
|
const map = {
|
|
10043
10043
|
type: "block-map",
|
|
10044
10044
|
offset: fc.offset,
|
|
10045
10045
|
indent: fc.indent,
|
|
10046
|
-
items: [{ start, key: fc, sep:
|
|
10046
|
+
items: [{ start, key: fc, sep: sep3 }]
|
|
10047
10047
|
};
|
|
10048
10048
|
this.onKeyLine = true;
|
|
10049
10049
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -10413,6 +10413,7 @@ var MAX_ITERATIONS = 2;
|
|
|
10413
10413
|
var MAX_SPEC_FILES = 6;
|
|
10414
10414
|
var MAX_SPEC_FILE_BYTES = 512e3;
|
|
10415
10415
|
var MAX_TOTAL_SPEC_BYTES = 512e3;
|
|
10416
|
+
var MAX_EXPLICIT_SPEC_FILE_BYTES = 10240;
|
|
10416
10417
|
var MAX_PLAN_FILES = 3;
|
|
10417
10418
|
var MAX_PLAN_FILE_BYTES = 512e3;
|
|
10418
10419
|
var MAX_INTENT_CHARS = 2e3;
|
|
@@ -10523,7 +10524,7 @@ var SECURITY_PATTERNS = [
|
|
|
10523
10524
|
/Dockerfile/
|
|
10524
10525
|
];
|
|
10525
10526
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10526
|
-
var DEFAULT_SERVICE_URL = "
|
|
10527
|
+
var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
|
|
10527
10528
|
var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
|
|
10528
10529
|
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10529
10530
|
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
@@ -10969,8 +10970,8 @@ function filterReviewable(files) {
|
|
|
10969
10970
|
const ext = (0, import_node_path3.extname)(f).slice(1);
|
|
10970
10971
|
if (ANALYZABLE_EXTENSIONS.has(ext)) return false;
|
|
10971
10972
|
if (REVIEWABLE_EXTENSIONS.has(ext)) return true;
|
|
10972
|
-
const
|
|
10973
|
-
if (REVIEWABLE_FILENAMES.has(
|
|
10973
|
+
const basename5 = f.split("/").pop() ?? "";
|
|
10974
|
+
if (REVIEWABLE_FILENAMES.has(basename5)) return true;
|
|
10974
10975
|
if (REVIEWABLE_PATH_PATTERNS.some((p) => p.test(f))) return true;
|
|
10975
10976
|
return false;
|
|
10976
10977
|
});
|
|
@@ -11533,7 +11534,7 @@ function startSpinner(label2, opts = {}) {
|
|
|
11533
11534
|
}
|
|
11534
11535
|
|
|
11535
11536
|
// src/lib/provider-auth.ts
|
|
11536
|
-
var sleep = (ms) => new Promise((
|
|
11537
|
+
var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
11537
11538
|
var form = (fields) => new URLSearchParams(fields).toString();
|
|
11538
11539
|
async function githubAccountId(owner) {
|
|
11539
11540
|
try {
|
|
@@ -12800,12 +12801,50 @@ function resolveGuardMoments(explicit) {
|
|
|
12800
12801
|
}
|
|
12801
12802
|
|
|
12802
12803
|
// src/lib/plugin-ownership.ts
|
|
12803
|
-
var
|
|
12804
|
-
|
|
12804
|
+
var import_node_fs7 = require("node:fs");
|
|
12805
|
+
|
|
12806
|
+
// src/lib/which.ts
|
|
12807
|
+
var import_node_fs5 = require("node:fs");
|
|
12805
12808
|
var import_node_path7 = require("node:path");
|
|
12809
|
+
function executableExtensions(platform, pathext) {
|
|
12810
|
+
if (platform !== "win32") return [""];
|
|
12811
|
+
const raw = (pathext ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
12812
|
+
const out = [];
|
|
12813
|
+
for (const ext of raw) {
|
|
12814
|
+
const lower = ext.toLowerCase();
|
|
12815
|
+
if (!out.includes(lower)) out.push(lower);
|
|
12816
|
+
if (!out.includes(ext)) out.push(ext);
|
|
12817
|
+
}
|
|
12818
|
+
return out;
|
|
12819
|
+
}
|
|
12820
|
+
function whichSync(bin, opts = {}) {
|
|
12821
|
+
const platform = opts.platform ?? process.platform;
|
|
12822
|
+
const rawPath = opts.path ?? process.env.PATH ?? "";
|
|
12823
|
+
if (!rawPath) return null;
|
|
12824
|
+
const exts = executableExtensions(platform, opts.pathext ?? process.env.PATHEXT);
|
|
12825
|
+
const real = opts.real ?? true;
|
|
12826
|
+
for (const dir of rawPath.split(import_node_path7.delimiter)) {
|
|
12827
|
+
if (!dir) continue;
|
|
12828
|
+
for (const ext of exts) {
|
|
12829
|
+
const candidate = (0, import_node_path7.join)(dir, bin + ext);
|
|
12830
|
+
try {
|
|
12831
|
+
if (!(0, import_node_fs5.statSync)(candidate).isFile()) continue;
|
|
12832
|
+
if (platform !== "win32") (0, import_node_fs5.accessSync)(candidate, import_node_fs5.constants.X_OK);
|
|
12833
|
+
return real ? (0, import_node_fs5.realpathSync)(candidate) : candidate;
|
|
12834
|
+
} catch {
|
|
12835
|
+
continue;
|
|
12836
|
+
}
|
|
12837
|
+
}
|
|
12838
|
+
}
|
|
12839
|
+
return null;
|
|
12840
|
+
}
|
|
12841
|
+
|
|
12842
|
+
// src/lib/plugin-ownership.ts
|
|
12843
|
+
var import_node_os2 = require("node:os");
|
|
12844
|
+
var import_node_path8 = require("node:path");
|
|
12806
12845
|
|
|
12807
12846
|
// src/lib/stderr-log.ts
|
|
12808
|
-
var
|
|
12847
|
+
var import_node_fs6 = require("node:fs");
|
|
12809
12848
|
var TOKEN_RE2 = /verity_[0-9a-f]{16,}/g;
|
|
12810
12849
|
var ANSI_RE = /\u001b\[[0-?]*[ -/]*[@-~]/g;
|
|
12811
12850
|
function scrub(s) {
|
|
@@ -12818,9 +12857,9 @@ function append(text) {
|
|
|
12818
12857
|
try {
|
|
12819
12858
|
const dir = projectPath(DEBUG_LOG_DIR);
|
|
12820
12859
|
const file = projectPath(STDERR_LOG_FILE);
|
|
12821
|
-
(0,
|
|
12860
|
+
(0, import_node_fs6.mkdirSync)(dir, { recursive: true });
|
|
12822
12861
|
rotateIfNeeded(file);
|
|
12823
|
-
(0,
|
|
12862
|
+
(0, import_node_fs6.appendFileSync)(file, text);
|
|
12824
12863
|
} catch {
|
|
12825
12864
|
}
|
|
12826
12865
|
}
|
|
@@ -12868,8 +12907,8 @@ function markerPath() {
|
|
|
12868
12907
|
}
|
|
12869
12908
|
function readMarker() {
|
|
12870
12909
|
try {
|
|
12871
|
-
if (!(0,
|
|
12872
|
-
const raw = JSON.parse((0,
|
|
12910
|
+
if (!(0, import_node_fs7.existsSync)(markerPath())) return null;
|
|
12911
|
+
const raw = JSON.parse((0, import_node_fs7.readFileSync)(markerPath(), "utf-8"));
|
|
12873
12912
|
const pluginRoot = typeof raw.plugin_root === "string" ? raw.plugin_root : "";
|
|
12874
12913
|
if (!pluginRoot || CONTROL_CHARS.test(pluginRoot)) return null;
|
|
12875
12914
|
return {
|
|
@@ -12886,23 +12925,23 @@ function recordPluginOwnership(sessionId) {
|
|
|
12886
12925
|
const pluginRoot = process.env.VERITY_PLUGIN_ROOT;
|
|
12887
12926
|
if (!pluginRoot) return;
|
|
12888
12927
|
try {
|
|
12889
|
-
(0,
|
|
12928
|
+
(0, import_node_fs7.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
|
|
12890
12929
|
const marker = {
|
|
12891
12930
|
session_id: sessionId,
|
|
12892
12931
|
plugin_root: pluginRoot,
|
|
12893
12932
|
version: process.env.VERITY_PLUGIN_VERSION || null,
|
|
12894
12933
|
ts: Math.floor(Date.now() / 1e3)
|
|
12895
12934
|
};
|
|
12896
|
-
(0,
|
|
12935
|
+
(0, import_node_fs7.writeFileSync)(markerPath(), JSON.stringify(marker));
|
|
12897
12936
|
} catch {
|
|
12898
12937
|
}
|
|
12899
12938
|
}
|
|
12900
12939
|
function claudeConfigDir() {
|
|
12901
|
-
return process.env.CLAUDE_CONFIG_DIR || (0,
|
|
12940
|
+
return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path8.join)((0, import_node_os2.homedir)(), ".claude");
|
|
12902
12941
|
}
|
|
12903
12942
|
function readJsonFile(path) {
|
|
12904
12943
|
try {
|
|
12905
|
-
const parsed = JSON.parse((0,
|
|
12944
|
+
const parsed = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf-8"));
|
|
12906
12945
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
12907
12946
|
} catch {
|
|
12908
12947
|
return null;
|
|
@@ -12910,9 +12949,9 @@ function readJsonFile(path) {
|
|
|
12910
12949
|
}
|
|
12911
12950
|
function enabledPluginSetting(key) {
|
|
12912
12951
|
const files = [
|
|
12913
|
-
projectPath((0,
|
|
12914
|
-
projectPath((0,
|
|
12915
|
-
(0,
|
|
12952
|
+
projectPath((0, import_node_path8.join)(".claude", "settings.local.json")),
|
|
12953
|
+
projectPath((0, import_node_path8.join)(".claude", "settings.json")),
|
|
12954
|
+
(0, import_node_path8.join)(claudeConfigDir(), "settings.json")
|
|
12916
12955
|
];
|
|
12917
12956
|
for (const file of files) {
|
|
12918
12957
|
const map = readJsonFile(file)?.enabledPlugins;
|
|
@@ -12924,27 +12963,16 @@ function enabledPluginSetting(key) {
|
|
|
12924
12963
|
}
|
|
12925
12964
|
function marketplaceLocations() {
|
|
12926
12965
|
const out = /* @__PURE__ */ new Map();
|
|
12927
|
-
const known = readJsonFile((0,
|
|
12966
|
+
const known = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
|
|
12928
12967
|
if (!known) return out;
|
|
12929
12968
|
for (const [name, entry] of Object.entries(known)) {
|
|
12930
12969
|
const loc2 = entry?.installLocation;
|
|
12931
|
-
if (typeof loc2 === "string" && loc2) out.set(name, (0,
|
|
12970
|
+
if (typeof loc2 === "string" && loc2) out.set(name, (0, import_node_path8.resolve)(loc2));
|
|
12932
12971
|
}
|
|
12933
12972
|
return out;
|
|
12934
12973
|
}
|
|
12935
12974
|
function verityPathEntry() {
|
|
12936
|
-
|
|
12937
|
-
if (!path) return null;
|
|
12938
|
-
for (const dir of path.split(import_node_path7.delimiter)) {
|
|
12939
|
-
if (!dir) continue;
|
|
12940
|
-
const candidate = (0, import_node_path7.join)(dir, "verity");
|
|
12941
|
-
try {
|
|
12942
|
-
(0, import_node_fs6.accessSync)(candidate, import_node_fs6.constants.X_OK);
|
|
12943
|
-
return candidate;
|
|
12944
|
-
} catch {
|
|
12945
|
-
}
|
|
12946
|
-
}
|
|
12947
|
-
return null;
|
|
12975
|
+
return whichSync("verity", { real: false });
|
|
12948
12976
|
}
|
|
12949
12977
|
function verityOnPath() {
|
|
12950
12978
|
return verityPathEntry() !== null;
|
|
@@ -12953,7 +12981,7 @@ function globalVerityVersion() {
|
|
|
12953
12981
|
const entry = verityPathEntry();
|
|
12954
12982
|
if (!entry) return null;
|
|
12955
12983
|
try {
|
|
12956
|
-
const pkg = readJsonFile((0,
|
|
12984
|
+
const pkg = readJsonFile((0, import_node_path8.join)((0, import_node_path8.dirname)((0, import_node_fs7.realpathSync)(entry)), "..", "package.json"));
|
|
12957
12985
|
if (pkg?.name !== "@codacy/verity-cli" || typeof pkg.version !== "string") return null;
|
|
12958
12986
|
return pkg.version;
|
|
12959
12987
|
} catch {
|
|
@@ -12965,7 +12993,7 @@ function pluginCliInvocation() {
|
|
|
12965
12993
|
if (!root) return null;
|
|
12966
12994
|
const onPath = globalVerityVersion();
|
|
12967
12995
|
if (onPath !== null && onPath === activePluginVersion()) return null;
|
|
12968
|
-
return `node ${JSON.stringify((0,
|
|
12996
|
+
return `node ${JSON.stringify((0, import_node_path8.join)(root, "scripts", "verity.mjs"))}`;
|
|
12969
12997
|
}
|
|
12970
12998
|
function cliVersionSkew() {
|
|
12971
12999
|
if (!process.env.VERITY_PLUGIN_ROOT) return null;
|
|
@@ -12984,7 +13012,7 @@ var VERITY_MARKETPLACE_REPO = "codacy/verity";
|
|
|
12984
13012
|
function marketplaceConflict() {
|
|
12985
13013
|
const wanted = VERITY_MARKETPLACE;
|
|
12986
13014
|
for (const file of ["settings.json", "settings.local.json"]) {
|
|
12987
|
-
const path = (0,
|
|
13015
|
+
const path = (0, import_node_path8.join)(claudeConfigDir(), file);
|
|
12988
13016
|
const declared = readJsonFile(path)?.extraKnownMarketplaces ?? null;
|
|
12989
13017
|
const entry2 = declared && typeof declared === "object" ? declared[wanted] : void 0;
|
|
12990
13018
|
if (entry2 && typeof entry2 === "object") {
|
|
@@ -12994,7 +13022,7 @@ function marketplaceConflict() {
|
|
|
12994
13022
|
}
|
|
12995
13023
|
}
|
|
12996
13024
|
}
|
|
12997
|
-
const known = readJsonFile((0,
|
|
13025
|
+
const known = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "known_marketplaces.json"));
|
|
12998
13026
|
const entry = known?.[wanted];
|
|
12999
13027
|
if (entry && typeof entry === "object" && !pointsAtVerity(entry.source)) {
|
|
13000
13028
|
return { name: wanted, declaredAs: describeSource(entry.source) };
|
|
@@ -13015,7 +13043,7 @@ function describeSource(src) {
|
|
|
13015
13043
|
return target ? `${kind} \u2192 ${target}` : kind;
|
|
13016
13044
|
}
|
|
13017
13045
|
function legacyMarketplaceInstall() {
|
|
13018
|
-
const plugins = readJsonFile((0,
|
|
13046
|
+
const plugins = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
|
|
13019
13047
|
if (!plugins || typeof plugins !== "object") return null;
|
|
13020
13048
|
for (const key of Object.keys(plugins)) {
|
|
13021
13049
|
const at = key.lastIndexOf("@");
|
|
@@ -13027,13 +13055,13 @@ function legacyMarketplaceInstall() {
|
|
|
13027
13055
|
}
|
|
13028
13056
|
function realpathOr(p) {
|
|
13029
13057
|
try {
|
|
13030
|
-
return
|
|
13058
|
+
return import_node_fs7.realpathSync.native(p);
|
|
13031
13059
|
} catch {
|
|
13032
|
-
return (0,
|
|
13060
|
+
return (0, import_node_path8.resolve)(p);
|
|
13033
13061
|
}
|
|
13034
13062
|
}
|
|
13035
13063
|
function isWithin(want, dir) {
|
|
13036
|
-
return want === dir || want.startsWith(dir +
|
|
13064
|
+
return want === dir || want.startsWith(dir + import_node_path8.sep);
|
|
13037
13065
|
}
|
|
13038
13066
|
function entryAppliesHere(entry, here) {
|
|
13039
13067
|
const e = entry;
|
|
@@ -13043,9 +13071,9 @@ function entryAppliesHere(entry, here) {
|
|
|
13043
13071
|
return forProject === here;
|
|
13044
13072
|
}
|
|
13045
13073
|
function registrySays(pluginRoot) {
|
|
13046
|
-
const plugins = readJsonFile((0,
|
|
13074
|
+
const plugins = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
|
|
13047
13075
|
if (!plugins || typeof plugins !== "object") return "unverified";
|
|
13048
|
-
const want = (0,
|
|
13076
|
+
const want = (0, import_node_path8.resolve)(pluginRoot);
|
|
13049
13077
|
const here = realpathOr(repoRoot());
|
|
13050
13078
|
const markets = marketplaceLocations();
|
|
13051
13079
|
for (const [key, value] of Object.entries(plugins)) {
|
|
@@ -13054,15 +13082,15 @@ function registrySays(pluginRoot) {
|
|
|
13054
13082
|
const applicable = (Array.isArray(value) ? value : []).filter((entry) => entryAppliesHere(entry, here));
|
|
13055
13083
|
const claims = applicable.some((entry) => {
|
|
13056
13084
|
const installPath = entry?.installPath;
|
|
13057
|
-
return typeof installPath === "string" && (0,
|
|
13085
|
+
return typeof installPath === "string" && (0, import_node_path8.resolve)(installPath) === want;
|
|
13058
13086
|
}) || source !== void 0 && applicable.length > 0 && isWithin(want, source);
|
|
13059
13087
|
if (claims) {
|
|
13060
13088
|
if (enabledPluginSetting(key) === false) return "gone";
|
|
13061
|
-
return (0,
|
|
13089
|
+
return (0, import_node_fs7.existsSync)(pluginRoot) ? "live" : "gone";
|
|
13062
13090
|
}
|
|
13063
13091
|
}
|
|
13064
|
-
const managed = (0,
|
|
13065
|
-
return want === managed || want.startsWith(managed +
|
|
13092
|
+
const managed = (0, import_node_path8.resolve)((0, import_node_path8.join)(claudeConfigDir(), "plugins", "cache"));
|
|
13093
|
+
return want === managed || want.startsWith(managed + import_node_path8.sep) ? "gone" : "unverified";
|
|
13066
13094
|
}
|
|
13067
13095
|
var _live = /* @__PURE__ */ new Map();
|
|
13068
13096
|
function pluginLiveness(pluginRoot) {
|
|
@@ -13076,7 +13104,7 @@ function clearStalePluginMarker() {
|
|
|
13076
13104
|
const marker = readMarker();
|
|
13077
13105
|
if (!marker || pluginLiveness(marker.plugin_root) !== "gone") return null;
|
|
13078
13106
|
try {
|
|
13079
|
-
(0,
|
|
13107
|
+
(0, import_node_fs7.rmSync)(markerPath(), { force: true });
|
|
13080
13108
|
} catch {
|
|
13081
13109
|
return null;
|
|
13082
13110
|
}
|
|
@@ -13104,7 +13132,7 @@ function pluginActiveHere() {
|
|
|
13104
13132
|
return activePluginInstall() !== null;
|
|
13105
13133
|
}
|
|
13106
13134
|
function registeredVerityPlugin() {
|
|
13107
|
-
const plugins = readJsonFile((0,
|
|
13135
|
+
const plugins = readJsonFile((0, import_node_path8.join)(claudeConfigDir(), "plugins", "installed_plugins.json"))?.plugins;
|
|
13108
13136
|
if (!plugins || typeof plugins !== "object") return null;
|
|
13109
13137
|
const here = realpathOr(repoRoot());
|
|
13110
13138
|
for (const [key, value] of Object.entries(plugins)) {
|
|
@@ -13114,7 +13142,7 @@ function registeredVerityPlugin() {
|
|
|
13114
13142
|
for (const entry of Array.isArray(value) ? value : []) {
|
|
13115
13143
|
const e = entry;
|
|
13116
13144
|
const installPath = typeof e.installPath === "string" ? e.installPath : "";
|
|
13117
|
-
if (!installPath || !(0,
|
|
13145
|
+
if (!installPath || !(0, import_node_fs7.existsSync)(installPath)) continue;
|
|
13118
13146
|
if (!entryAppliesHere(entry, here)) continue;
|
|
13119
13147
|
return { pluginRoot: installPath, version: typeof e.version === "string" ? e.version : null };
|
|
13120
13148
|
}
|
|
@@ -13299,7 +13327,7 @@ var import_node_crypto8 = require("node:crypto");
|
|
|
13299
13327
|
|
|
13300
13328
|
// src/lib/conversation-buffer.ts
|
|
13301
13329
|
var import_promises5 = require("node:fs/promises");
|
|
13302
|
-
var
|
|
13330
|
+
var import_node_fs8 = require("node:fs");
|
|
13303
13331
|
var import_node_child_process5 = require("node:child_process");
|
|
13304
13332
|
var import_node_crypto = require("node:crypto");
|
|
13305
13333
|
function stripImageReferences(text) {
|
|
@@ -13335,7 +13363,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
13335
13363
|
}
|
|
13336
13364
|
async function readAndClearConversationBuffer(currentSessionId) {
|
|
13337
13365
|
try {
|
|
13338
|
-
if ((0,
|
|
13366
|
+
if ((0, import_node_fs8.existsSync)(CONVERSATION_BUFFER_FILE)) {
|
|
13339
13367
|
const entries = await readBufferEntries();
|
|
13340
13368
|
let mine = entries;
|
|
13341
13369
|
let others = [];
|
|
@@ -13359,7 +13387,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
13359
13387
|
};
|
|
13360
13388
|
}
|
|
13361
13389
|
}
|
|
13362
|
-
if ((0,
|
|
13390
|
+
if ((0, import_node_fs8.existsSync)(INTENT_FILE)) {
|
|
13363
13391
|
try {
|
|
13364
13392
|
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
13365
13393
|
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
@@ -13652,9 +13680,9 @@ function isCommandOnlyTurn(input) {
|
|
|
13652
13680
|
|
|
13653
13681
|
// src/lib/context-identity.ts
|
|
13654
13682
|
var import_node_crypto2 = require("node:crypto");
|
|
13655
|
-
var
|
|
13683
|
+
var import_node_fs9 = require("node:fs");
|
|
13656
13684
|
var import_node_os3 = require("node:os");
|
|
13657
|
-
var
|
|
13685
|
+
var import_node_path9 = require("node:path");
|
|
13658
13686
|
var SHARED_SENTINELS = /* @__PURE__ */ new Set([
|
|
13659
13687
|
"",
|
|
13660
13688
|
"-",
|
|
@@ -13692,7 +13720,7 @@ function contextIdentity(input) {
|
|
|
13692
13720
|
if (rawTree && !isSharedSentinel(rawTree)) {
|
|
13693
13721
|
let resolved = rawTree;
|
|
13694
13722
|
try {
|
|
13695
|
-
resolved =
|
|
13723
|
+
resolved = import_node_fs9.realpathSync.native(rawTree);
|
|
13696
13724
|
} catch {
|
|
13697
13725
|
}
|
|
13698
13726
|
treeKey = (0, import_node_crypto2.createHash)("sha256").update(resolved).digest("hex").slice(0, 12);
|
|
@@ -13708,13 +13736,13 @@ function contextIdentity(input) {
|
|
|
13708
13736
|
}
|
|
13709
13737
|
function verityHome() {
|
|
13710
13738
|
const override = process.env.VERITY_HOME;
|
|
13711
|
-
return override && override.trim() ? (0,
|
|
13739
|
+
return override && override.trim() ? (0, import_node_path9.resolve)(override) : (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".verity");
|
|
13712
13740
|
}
|
|
13713
13741
|
function dossierDir(identity) {
|
|
13714
|
-
return (0,
|
|
13742
|
+
return (0, import_node_path9.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
|
|
13715
13743
|
}
|
|
13716
13744
|
function treeDir(identity) {
|
|
13717
|
-
return (0,
|
|
13745
|
+
return (0, import_node_path9.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
|
|
13718
13746
|
}
|
|
13719
13747
|
function scopeIdentity(token, sessionId) {
|
|
13720
13748
|
const t = (token ?? "").trim();
|
|
@@ -13729,8 +13757,8 @@ function sessionScopeKey(token, sessionId) {
|
|
|
13729
13757
|
|
|
13730
13758
|
// src/lib/task-context-buffer.ts
|
|
13731
13759
|
var import_promises6 = require("node:fs/promises");
|
|
13732
|
-
var
|
|
13733
|
-
var
|
|
13760
|
+
var import_node_fs10 = require("node:fs");
|
|
13761
|
+
var import_node_path10 = require("node:path");
|
|
13734
13762
|
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
13735
13763
|
var MAX_BUFFER_BYTES = 500 * 1024;
|
|
13736
13764
|
var MAX_PROMPT_CHARS = 2e3;
|
|
@@ -13769,7 +13797,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
|
|
|
13769
13797
|
}
|
|
13770
13798
|
async function readTaskContextBuffer(taskId) {
|
|
13771
13799
|
const filePath = bufferPath(taskId);
|
|
13772
|
-
if (!(0,
|
|
13800
|
+
if (!(0, import_node_fs10.existsSync)(filePath)) return null;
|
|
13773
13801
|
try {
|
|
13774
13802
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
13775
13803
|
if (!content.trim()) return null;
|
|
@@ -13803,12 +13831,12 @@ async function readTaskContextBuffer(taskId) {
|
|
|
13803
13831
|
}
|
|
13804
13832
|
async function cleanupTaskContextBuffers() {
|
|
13805
13833
|
try {
|
|
13806
|
-
if (!(0,
|
|
13834
|
+
if (!(0, import_node_fs10.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
13807
13835
|
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
13808
13836
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
13809
13837
|
for (const file of files) {
|
|
13810
13838
|
if (!file.endsWith(".jsonl")) continue;
|
|
13811
|
-
const filePath = (0,
|
|
13839
|
+
const filePath = (0, import_node_path10.join)(TASK_CONTEXT_DIR, file);
|
|
13812
13840
|
try {
|
|
13813
13841
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
13814
13842
|
if (stats.mtimeMs < cutoffMs) {
|
|
@@ -13822,13 +13850,13 @@ async function cleanupTaskContextBuffers() {
|
|
|
13822
13850
|
}
|
|
13823
13851
|
function bufferPath(taskId) {
|
|
13824
13852
|
const safe = taskId.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
13825
|
-
return (0,
|
|
13853
|
+
return (0, import_node_path10.join)(TASK_CONTEXT_DIR, `${safe}.jsonl`);
|
|
13826
13854
|
}
|
|
13827
13855
|
async function appendEntry(taskId, entry) {
|
|
13828
13856
|
try {
|
|
13829
13857
|
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
13830
13858
|
const filePath = bufferPath(taskId);
|
|
13831
|
-
if ((0,
|
|
13859
|
+
if ((0, import_node_fs10.existsSync)(filePath)) {
|
|
13832
13860
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
13833
13861
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
13834
13862
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
@@ -13839,213 +13867,21 @@ async function appendEntry(taskId, entry) {
|
|
|
13839
13867
|
}
|
|
13840
13868
|
}
|
|
13841
13869
|
const line = JSON.stringify(entry) + "\n";
|
|
13842
|
-
const existing = (0,
|
|
13870
|
+
const existing = (0, import_node_fs10.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
|
|
13843
13871
|
await (0, import_promises6.writeFile)(filePath, existing + line);
|
|
13844
13872
|
} catch {
|
|
13845
13873
|
}
|
|
13846
13874
|
}
|
|
13847
13875
|
|
|
13848
13876
|
// src/lib/memory-retrieval.ts
|
|
13849
|
-
var
|
|
13877
|
+
var import_promises7 = require("node:fs/promises");
|
|
13850
13878
|
var import_node_fs11 = require("node:fs");
|
|
13851
13879
|
var import_node_path11 = require("node:path");
|
|
13852
|
-
|
|
13853
|
-
// src/lib/org-mirror.ts
|
|
13854
|
-
var import_node_fs10 = require("node:fs");
|
|
13855
|
-
var import_promises7 = require("node:fs/promises");
|
|
13856
|
-
var import_node_path10 = require("node:path");
|
|
13857
|
-
var ORG_KINDS = ["decision", "security", "gotcha", "pattern", "domain", "integration"];
|
|
13858
|
-
var STATE_FILE = ".org-pull-state.json";
|
|
13859
|
-
var BODY_MAX = 8192;
|
|
13860
|
-
function orgMirrorDir(remote = requestRemote()) {
|
|
13861
|
-
const parsed = parseRemote(remote);
|
|
13862
|
-
if (!parsed) return null;
|
|
13863
|
-
return (0, import_node_path10.join)(verityHome(), "orgs", parsed.host, parsed.owner.toLowerCase(), "memory");
|
|
13864
|
-
}
|
|
13865
|
-
function slugify(s) {
|
|
13866
|
-
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
13867
|
-
}
|
|
13868
|
-
function orgNodePath(node) {
|
|
13869
|
-
const kind = ORG_KINDS.includes(node.kind) ? node.kind : "domain";
|
|
13870
|
-
return `${kind}/${slugify(node.title) || "claim"}-${node.tag_id.replace(/-/g, "").slice(0, 8)}.md`;
|
|
13871
|
-
}
|
|
13872
|
-
function renderOrgNode(node, orgName) {
|
|
13873
|
-
const fm = [
|
|
13874
|
-
"---",
|
|
13875
|
-
`id: ${JSON.stringify(node.tag_id)}`,
|
|
13876
|
-
`node_id: ${JSON.stringify(node.node_id)}`,
|
|
13877
|
-
'scope: "org"',
|
|
13878
|
-
`org: ${JSON.stringify(orgName)}`,
|
|
13879
|
-
`kind: ${JSON.stringify(node.kind)}`,
|
|
13880
|
-
`title: ${JSON.stringify(node.title)}`,
|
|
13881
|
-
`origin: ${JSON.stringify(node.origin)}`,
|
|
13882
|
-
`signal: ${JSON.stringify(node.signal)}`,
|
|
13883
|
-
`tier: ${node.tier === null ? "null" : JSON.stringify(node.tier)}`,
|
|
13884
|
-
`applies_to: ${JSON.stringify(node.applies_to)}`,
|
|
13885
|
-
`source_repo_count: ${JSON.stringify(node.source_repo_count)}`,
|
|
13886
|
-
`promoted_at: ${JSON.stringify(node.promoted_at)}`,
|
|
13887
|
-
'status: "active"',
|
|
13888
|
-
"---"
|
|
13889
|
-
];
|
|
13890
|
-
return `${fm.join("\n")}
|
|
13891
|
-
|
|
13892
|
-
# ${node.title}
|
|
13893
|
-
|
|
13894
|
-
${node.body.trim()}
|
|
13895
|
-
`;
|
|
13896
|
-
}
|
|
13897
|
-
function parseOrgNode(content) {
|
|
13898
|
-
const m = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
13899
|
-
if (!m) return null;
|
|
13900
|
-
const fm = {};
|
|
13901
|
-
for (const line of m[1].split("\n")) {
|
|
13902
|
-
const kv = line.match(/^([a-z_]+):\s*(.*)$/);
|
|
13903
|
-
if (!kv) continue;
|
|
13904
|
-
try {
|
|
13905
|
-
fm[kv[1]] = JSON.parse(kv[2]);
|
|
13906
|
-
} catch {
|
|
13907
|
-
fm[kv[1]] = kv[2];
|
|
13908
|
-
}
|
|
13909
|
-
}
|
|
13910
|
-
if (fm.scope !== "org" || typeof fm.id !== "string" || typeof fm.title !== "string") return null;
|
|
13911
|
-
if (fm.status && fm.status !== "active") return null;
|
|
13912
|
-
const body = m[2].replace(/^\s*#[^\n]*\n/, "").trim();
|
|
13913
|
-
return {
|
|
13914
|
-
tag_id: fm.id,
|
|
13915
|
-
node_id: typeof fm.node_id === "string" ? fm.node_id : "",
|
|
13916
|
-
kind: typeof fm.kind === "string" ? fm.kind : "domain",
|
|
13917
|
-
title: fm.title,
|
|
13918
|
-
body,
|
|
13919
|
-
origin: typeof fm.origin === "string" ? fm.origin : "",
|
|
13920
|
-
signal: typeof fm.signal === "string" ? fm.signal : "",
|
|
13921
|
-
tier: typeof fm.tier === "number" ? fm.tier : null,
|
|
13922
|
-
applies_to: Array.isArray(fm.applies_to) ? fm.applies_to.filter((v) => typeof v === "string") : [],
|
|
13923
|
-
source_repo_count: typeof fm.source_repo_count === "number" ? fm.source_repo_count : 1,
|
|
13924
|
-
promoted_at: typeof fm.promoted_at === "string" ? fm.promoted_at : ""
|
|
13925
|
-
};
|
|
13926
|
-
}
|
|
13927
|
-
async function readState(dir) {
|
|
13928
|
-
try {
|
|
13929
|
-
const parsed = JSON.parse(await (0, import_promises7.readFile)((0, import_node_path10.join)(dir, STATE_FILE), "utf-8"));
|
|
13930
|
-
return {
|
|
13931
|
-
version: typeof parsed?.version === "string" ? parsed.version : null,
|
|
13932
|
-
organization: parsed?.organization && typeof parsed.organization.id === "string" ? parsed.organization : null,
|
|
13933
|
-
files: Array.isArray(parsed?.files) ? parsed.files.filter((f) => typeof f === "string") : []
|
|
13934
|
-
};
|
|
13935
|
-
} catch {
|
|
13936
|
-
return { version: null, organization: null, files: [] };
|
|
13937
|
-
}
|
|
13938
|
-
}
|
|
13939
|
-
async function writeState(dir, state) {
|
|
13940
|
-
await (0, import_promises7.mkdir)(dir, { recursive: true });
|
|
13941
|
-
await (0, import_promises7.writeFile)((0, import_node_path10.join)(dir, STATE_FILE), JSON.stringify({
|
|
13942
|
-
...state.version ? { version: state.version } : {},
|
|
13943
|
-
organization: state.organization,
|
|
13944
|
-
pulled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13945
|
-
files: state.files
|
|
13946
|
-
}, null, 2) + "\n");
|
|
13947
|
-
}
|
|
13948
|
-
function renderIndex(orgName, nodes) {
|
|
13949
|
-
const lines = [
|
|
13950
|
-
`# Org Knowledge \u2014 ${orgName}`,
|
|
13951
|
-
"",
|
|
13952
|
-
"*Auto-generated by verity CLI from what this organization's repositories learned. Do not hand-edit; `verity memory pull` overwrites it.*",
|
|
13953
|
-
"",
|
|
13954
|
-
'> These claims hold across the organization\'s repositories, not only this one. Each names where it came from. A claim that is wrong for this repository is demoted with `verity memory demote <id> --reason "\u2026"`, for everyone.',
|
|
13955
|
-
""
|
|
13956
|
-
];
|
|
13957
|
-
const byKind = /* @__PURE__ */ new Map();
|
|
13958
|
-
for (const n of nodes) byKind.set(n.kind, [...byKind.get(n.kind) ?? [], n]);
|
|
13959
|
-
for (const kind of ORG_KINDS) {
|
|
13960
|
-
const list2 = byKind.get(kind);
|
|
13961
|
-
if (!list2?.length) continue;
|
|
13962
|
-
lines.push(`## ${kind}/ (${list2.length})`);
|
|
13963
|
-
for (const n of list2) lines.push(`- [[${orgNodePath(n).split("/")[1].replace(/\.md$/, "")}]] \u2014 ${n.title} \xB7 ${n.origin}`);
|
|
13964
|
-
lines.push("");
|
|
13965
|
-
}
|
|
13966
|
-
if (nodes.length === 0) lines.push("No org knowledge yet.", "");
|
|
13967
|
-
return lines.join("\n");
|
|
13968
|
-
}
|
|
13969
|
-
async function pullOrgKnowledge(opts) {
|
|
13970
|
-
const dir = orgMirrorDir(opts.remote ?? requestRemote());
|
|
13971
|
-
if (!dir) return { ok: true, status: "no_remote", received: 0, dir: null };
|
|
13972
|
-
const state = await readState(dir);
|
|
13973
|
-
const params = new URLSearchParams({ for: "agent" });
|
|
13974
|
-
if (!opts.force && state.version) params.set("if_version", state.version);
|
|
13975
|
-
const res = await apiRequest({
|
|
13976
|
-
method: "GET",
|
|
13977
|
-
path: `/memory/org?${params.toString()}`,
|
|
13978
|
-
serviceUrl: opts.serviceUrl,
|
|
13979
|
-
token: opts.token,
|
|
13980
|
-
verbose: opts.verbose,
|
|
13981
|
-
timeout: opts.timeoutMs ?? 3e4,
|
|
13982
|
-
cmd: "memory-org-pull"
|
|
13983
|
-
});
|
|
13984
|
-
if (!res.ok) return { ok: false, error: res.error, category: res.category };
|
|
13985
|
-
const page = res.data;
|
|
13986
|
-
if (page.unchanged) return { ok: true, status: "unchanged", received: 0, dir };
|
|
13987
|
-
const org = page.organization;
|
|
13988
|
-
const nodes = org ? (page.nodes ?? []).flatMap((n) => typeof n.tag_id === "string" && typeof n.title === "string" && typeof n.body === "string" ? [{
|
|
13989
|
-
tag_id: n.tag_id,
|
|
13990
|
-
node_id: typeof n.node_id === "string" ? n.node_id : "",
|
|
13991
|
-
kind: typeof n.kind === "string" ? n.kind : "domain",
|
|
13992
|
-
title: n.title,
|
|
13993
|
-
body: n.body.slice(0, BODY_MAX),
|
|
13994
|
-
origin: typeof n.origin === "string" ? n.origin : "",
|
|
13995
|
-
signal: typeof n.signal === "string" ? n.signal : "",
|
|
13996
|
-
tier: typeof n.tier === "number" ? n.tier : null,
|
|
13997
|
-
applies_to: Array.isArray(n.applies_to) ? n.applies_to.filter((v) => typeof v === "string") : [],
|
|
13998
|
-
source_repo_count: typeof n.source_repo_count === "number" ? n.source_repo_count : 1,
|
|
13999
|
-
promoted_at: typeof n.promoted_at === "string" ? n.promoted_at : ""
|
|
14000
|
-
}] : []) : [];
|
|
14001
|
-
const written = /* @__PURE__ */ new Set();
|
|
14002
|
-
await (0, import_promises7.mkdir)(dir, { recursive: true });
|
|
14003
|
-
for (const n of nodes) {
|
|
14004
|
-
const rel = orgNodePath(n);
|
|
14005
|
-
if (written.has(rel)) continue;
|
|
14006
|
-
await (0, import_promises7.mkdir)((0, import_node_path10.join)(dir, rel.split("/")[0]), { recursive: true });
|
|
14007
|
-
await (0, import_promises7.writeFile)((0, import_node_path10.join)(dir, rel), renderOrgNode(n, org?.name ?? ""));
|
|
14008
|
-
written.add(rel);
|
|
14009
|
-
}
|
|
14010
|
-
for (const rel of state.files) {
|
|
14011
|
-
if (!written.has(rel)) await (0, import_promises7.rm)((0, import_node_path10.join)(dir, rel), { force: true });
|
|
14012
|
-
}
|
|
14013
|
-
await (0, import_promises7.writeFile)((0, import_node_path10.join)(dir, "index.md"), renderIndex(org?.name ?? "no organization", nodes));
|
|
14014
|
-
await writeState(dir, { version: org ? page.version : null, organization: org, files: [...written].sort() });
|
|
14015
|
-
return { ok: true, status: org ? "pulled" : "none", received: nodes.length, dir };
|
|
14016
|
-
}
|
|
14017
|
-
async function readOrgMirror(remote = requestRemote()) {
|
|
14018
|
-
const dir = orgMirrorDir(remote);
|
|
14019
|
-
if (!dir || !(0, import_node_fs10.existsSync)(dir)) return [];
|
|
14020
|
-
const out = [];
|
|
14021
|
-
for (const kind of ORG_KINDS) {
|
|
14022
|
-
const kindDir = (0, import_node_path10.join)(dir, kind);
|
|
14023
|
-
if (!(0, import_node_fs10.existsSync)(kindDir)) continue;
|
|
14024
|
-
let files;
|
|
14025
|
-
try {
|
|
14026
|
-
files = await (0, import_promises7.readdir)(kindDir);
|
|
14027
|
-
} catch {
|
|
14028
|
-
continue;
|
|
14029
|
-
}
|
|
14030
|
-
for (const file of files) {
|
|
14031
|
-
if (!file.endsWith(".md")) continue;
|
|
14032
|
-
try {
|
|
14033
|
-
const node = parseOrgNode(await (0, import_promises7.readFile)((0, import_node_path10.join)(kindDir, file), "utf-8"));
|
|
14034
|
-
if (node) out.push(node);
|
|
14035
|
-
} catch {
|
|
14036
|
-
}
|
|
14037
|
-
}
|
|
14038
|
-
}
|
|
14039
|
-
return out;
|
|
14040
|
-
}
|
|
14041
|
-
|
|
14042
|
-
// src/lib/memory-retrieval.ts
|
|
14043
13880
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
14044
13881
|
var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
14045
13882
|
var DEFAULT_BUDGET_TOKENS = 2e3;
|
|
14046
13883
|
var MAX_BUDGET_TOKENS = 4e3;
|
|
14047
13884
|
var MIN_SCORE = 0.5;
|
|
14048
|
-
var ORG_MIN_SHARED_TERMS = 2;
|
|
14049
13885
|
function tokenize(text) {
|
|
14050
13886
|
return new Set(
|
|
14051
13887
|
text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((w) => w.length > 2)
|
|
@@ -14058,26 +13894,6 @@ function jaccardKeywords(a, b) {
|
|
|
14058
13894
|
const union = a.size + b.size - inter;
|
|
14059
13895
|
return union === 0 ? 0 : inter / union;
|
|
14060
13896
|
}
|
|
14061
|
-
function sharedTerms(a, b) {
|
|
14062
|
-
let n = 0;
|
|
14063
|
-
for (const w of a) if (b.has(w)) n++;
|
|
14064
|
-
return n;
|
|
14065
|
-
}
|
|
14066
|
-
function orgNodeToLocal(n) {
|
|
14067
|
-
return {
|
|
14068
|
-
path: `org:${n.tag_id}`,
|
|
14069
|
-
nodeId: n.tag_id,
|
|
14070
|
-
kind: n.kind,
|
|
14071
|
-
title: n.title,
|
|
14072
|
-
body: n.body.slice(0, 2e3),
|
|
14073
|
-
fileGlobs: [],
|
|
14074
|
-
confidence: 0.5,
|
|
14075
|
-
citedCount: 0,
|
|
14076
|
-
tokenEstimate: Math.ceil((n.title.length + n.body.length) / 4) + 20,
|
|
14077
|
-
scope: "org",
|
|
14078
|
-
origin: n.origin
|
|
14079
|
-
};
|
|
14080
|
-
}
|
|
14081
13897
|
function globMatch(glob, filePath) {
|
|
14082
13898
|
if (glob === filePath) return true;
|
|
14083
13899
|
const suffixMatch = glob.match(/^\*\*\/\*(.+)$/);
|
|
@@ -14153,19 +13969,19 @@ function parseFrontmatter(content) {
|
|
|
14153
13969
|
return { fm, body: match[2].trim() };
|
|
14154
13970
|
}
|
|
14155
13971
|
async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
|
|
14156
|
-
|
|
13972
|
+
if (!(0, import_node_fs11.existsSync)(memoryDir())) return null;
|
|
14157
13973
|
const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
|
|
14158
13974
|
const promptTokens = tokenize(promptText);
|
|
14159
13975
|
const nodes = [];
|
|
14160
|
-
for (const domain of
|
|
13976
|
+
for (const domain of DOMAINS) {
|
|
14161
13977
|
const domainDir = (0, import_node_path11.join)(memoryDir(), domain);
|
|
14162
13978
|
if (!(0, import_node_fs11.existsSync)(domainDir)) continue;
|
|
14163
13979
|
try {
|
|
14164
|
-
const files = await (0,
|
|
13980
|
+
const files = await (0, import_promises7.readdir)(domainDir);
|
|
14165
13981
|
for (const file of files) {
|
|
14166
13982
|
if (!file.endsWith(".md")) continue;
|
|
14167
13983
|
try {
|
|
14168
|
-
const content = await (0,
|
|
13984
|
+
const content = await (0, import_promises7.readFile)((0, import_node_path11.join)(domainDir, file), "utf-8");
|
|
14169
13985
|
const { fm, body } = parseFrontmatter(content);
|
|
14170
13986
|
if (fm.status && fm.status !== "active") continue;
|
|
14171
13987
|
nodes.push({
|
|
@@ -14185,13 +14001,6 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
14185
14001
|
} catch {
|
|
14186
14002
|
}
|
|
14187
14003
|
}
|
|
14188
|
-
try {
|
|
14189
|
-
for (const claim of await readOrgMirror()) {
|
|
14190
|
-
const local = orgNodeToLocal(claim);
|
|
14191
|
-
if (sharedTerms(tokenize(`${local.title} ${local.body}`), promptTokens) >= ORG_MIN_SHARED_TERMS) nodes.push(local);
|
|
14192
|
-
}
|
|
14193
|
-
} catch {
|
|
14194
|
-
}
|
|
14195
14004
|
if (nodes.length === 0) return null;
|
|
14196
14005
|
const scored = nodes.map((n) => ({
|
|
14197
14006
|
...n,
|
|
@@ -14210,13 +14019,12 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
14210
14019
|
const lines = [
|
|
14211
14020
|
"## Project Knowledge (auto-injected)",
|
|
14212
14021
|
"",
|
|
14213
|
-
`*${selected.length} node(s) relevant to your current task. See \`.verity/memory/index.md\` for the full knowledge base
|
|
14022
|
+
`*${selected.length} node(s) relevant to your current task. See \`.verity/memory/index.md\` for the full knowledge base.*`,
|
|
14214
14023
|
""
|
|
14215
14024
|
];
|
|
14216
14025
|
for (const node of selected) {
|
|
14217
14026
|
lines.push(`### ${node.title}`);
|
|
14218
|
-
|
|
14219
|
-
else lines.push(`*${node.kind}* \xB7 confidence ${Math.round(node.confidence * 100)}%`);
|
|
14027
|
+
lines.push(`*${node.kind}* \xB7 confidence ${Math.round(node.confidence * 100)}%`);
|
|
14220
14028
|
lines.push("");
|
|
14221
14029
|
lines.push(node.body.slice(0, 800));
|
|
14222
14030
|
lines.push("");
|
|
@@ -14231,7 +14039,7 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
14231
14039
|
}
|
|
14232
14040
|
|
|
14233
14041
|
// src/lib/memory-sync.ts
|
|
14234
|
-
var
|
|
14042
|
+
var import_promises8 = require("node:fs/promises");
|
|
14235
14043
|
var import_node_fs14 = require("node:fs");
|
|
14236
14044
|
var import_node_path13 = require("node:path");
|
|
14237
14045
|
var import_node_crypto3 = require("node:crypto");
|
|
@@ -14346,8 +14154,8 @@ function ensureVerityGitignore() {
|
|
|
14346
14154
|
}
|
|
14347
14155
|
let text = next.join("\n");
|
|
14348
14156
|
if (!hasMarker) {
|
|
14349
|
-
const
|
|
14350
|
-
text = text +
|
|
14157
|
+
const sep3 = text === "" ? "" : text.endsWith("\n") ? "\n" : "\n\n";
|
|
14158
|
+
text = text + sep3 + VERITY_GITIGNORE_BLOCK;
|
|
14351
14159
|
}
|
|
14352
14160
|
if (text !== content) (0, import_node_fs12.writeFileSync)(".gitignore", text);
|
|
14353
14161
|
return verified(
|
|
@@ -14377,8 +14185,8 @@ function writeFencedBlock() {
|
|
|
14377
14185
|
lines = fenceMemoryLines(lines);
|
|
14378
14186
|
let text = lines.join("\n");
|
|
14379
14187
|
if (!hasMarker) {
|
|
14380
|
-
const
|
|
14381
|
-
text = text +
|
|
14188
|
+
const sep3 = text === "" ? "" : text.endsWith("\n") ? "\n" : "\n\n";
|
|
14189
|
+
text = text + sep3 + VERITY_GITIGNORE_BLOCK;
|
|
14382
14190
|
}
|
|
14383
14191
|
try {
|
|
14384
14192
|
(0, import_node_fs12.writeFileSync)(".gitignore", text);
|
|
@@ -14428,8 +14236,8 @@ function keepMemoryTracked() {
|
|
|
14428
14236
|
}
|
|
14429
14237
|
if (content.includes(MEMORY_OPT_OUT_MARKER)) return "already";
|
|
14430
14238
|
try {
|
|
14431
|
-
const
|
|
14432
|
-
(0, import_node_fs12.writeFileSync)(".gitignore", content +
|
|
14239
|
+
const sep3 = content === "" ? "" : content.endsWith("\n") ? "\n" : "\n\n";
|
|
14240
|
+
(0, import_node_fs12.writeFileSync)(".gitignore", content + sep3 + MEMORY_OPT_OUT_STANZA);
|
|
14433
14241
|
} catch {
|
|
14434
14242
|
return "failed";
|
|
14435
14243
|
}
|
|
@@ -14491,6 +14299,42 @@ function resolveInside(baseDir, candidate) {
|
|
|
14491
14299
|
}
|
|
14492
14300
|
return full;
|
|
14493
14301
|
}
|
|
14302
|
+
var O_NOFOLLOW = typeof import_node_fs13.constants.O_NOFOLLOW === "number" ? import_node_fs13.constants.O_NOFOLLOW : 0;
|
|
14303
|
+
function readFileInside(baseDir, candidate, maxBytes) {
|
|
14304
|
+
const full = resolveInside(baseDir, candidate);
|
|
14305
|
+
if (!full) return null;
|
|
14306
|
+
let realParent;
|
|
14307
|
+
let realBase;
|
|
14308
|
+
try {
|
|
14309
|
+
realParent = (0, import_node_fs13.realpathSync)((0, import_node_path12.dirname)(full));
|
|
14310
|
+
realBase = (0, import_node_fs13.realpathSync)((0, import_node_path12.resolve)(baseDir));
|
|
14311
|
+
} catch {
|
|
14312
|
+
return null;
|
|
14313
|
+
}
|
|
14314
|
+
const realBaseSep = realBase.endsWith(import_node_path12.sep) ? realBase : realBase + import_node_path12.sep;
|
|
14315
|
+
if (realParent !== realBase && !realParent.startsWith(realBaseSep)) return null;
|
|
14316
|
+
const target = (0, import_node_path12.join)(realParent, (0, import_node_path12.basename)(full));
|
|
14317
|
+
let fd = null;
|
|
14318
|
+
try {
|
|
14319
|
+
fd = (0, import_node_fs13.openSync)(target, import_node_fs13.constants.O_RDONLY | O_NOFOLLOW);
|
|
14320
|
+
const st = (0, import_node_fs13.fstatSync)(fd);
|
|
14321
|
+
if (!st.isFile()) return null;
|
|
14322
|
+
const cap = Math.min(maxBytes, st.size);
|
|
14323
|
+
if (cap <= 0) return "";
|
|
14324
|
+
const buf = Buffer.alloc(cap);
|
|
14325
|
+
const bytesRead = (0, import_node_fs13.readSync)(fd, buf, 0, cap, 0);
|
|
14326
|
+
return buf.subarray(0, bytesRead).toString("utf-8");
|
|
14327
|
+
} catch {
|
|
14328
|
+
return null;
|
|
14329
|
+
} finally {
|
|
14330
|
+
if (fd !== null) {
|
|
14331
|
+
try {
|
|
14332
|
+
(0, import_node_fs13.closeSync)(fd);
|
|
14333
|
+
} catch {
|
|
14334
|
+
}
|
|
14335
|
+
}
|
|
14336
|
+
}
|
|
14337
|
+
}
|
|
14494
14338
|
|
|
14495
14339
|
// src/lib/glob-match.ts
|
|
14496
14340
|
function globToRegex(glob) {
|
|
@@ -14540,14 +14384,6 @@ function globToRegex(glob) {
|
|
|
14540
14384
|
out = out.replace(/(?:\.\*)+/g, ".*");
|
|
14541
14385
|
return new RegExp(`^${out}$`);
|
|
14542
14386
|
}
|
|
14543
|
-
function globMatch2(glob, filePath) {
|
|
14544
|
-
if (glob === filePath) return true;
|
|
14545
|
-
try {
|
|
14546
|
-
return globToRegex(glob).test(filePath);
|
|
14547
|
-
} catch {
|
|
14548
|
-
return false;
|
|
14549
|
-
}
|
|
14550
|
-
}
|
|
14551
14387
|
function anyPathMatches(glob, treePaths) {
|
|
14552
14388
|
let regex;
|
|
14553
14389
|
try {
|
|
@@ -14566,18 +14402,18 @@ var memoryDir2 = () => projectPath(`${VERITY_DIR}/memory`);
|
|
|
14566
14402
|
var DOMAINS2 = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations", "_archive"];
|
|
14567
14403
|
var syncStateFile = () => projectPath(`${VERITY_DIR}/.memory-sync-state.json`);
|
|
14568
14404
|
async function ensureMemoryDir() {
|
|
14569
|
-
await (0,
|
|
14405
|
+
await (0, import_promises8.mkdir)(memoryDir2(), { recursive: true });
|
|
14570
14406
|
for (const domain of DOMAINS2) {
|
|
14571
|
-
await (0,
|
|
14407
|
+
await (0, import_promises8.mkdir)((0, import_node_path13.join)(memoryDir2(), domain), { recursive: true });
|
|
14572
14408
|
}
|
|
14573
14409
|
if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
14574
|
-
await (0,
|
|
14410
|
+
await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
14575
14411
|
}
|
|
14576
14412
|
if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "index.md"))) {
|
|
14577
|
-
await (0,
|
|
14413
|
+
await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
14578
14414
|
}
|
|
14579
14415
|
if (!(0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "log.md"))) {
|
|
14580
|
-
await (0,
|
|
14416
|
+
await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
14581
14417
|
}
|
|
14582
14418
|
}
|
|
14583
14419
|
async function buildManifest() {
|
|
@@ -14589,13 +14425,13 @@ async function buildManifest() {
|
|
|
14589
14425
|
const domainDir = (0, import_node_path13.join)(memoryDir2(), domain);
|
|
14590
14426
|
if (!(0, import_node_fs14.existsSync)(domainDir)) continue;
|
|
14591
14427
|
try {
|
|
14592
|
-
const files = await (0,
|
|
14428
|
+
const files = await (0, import_promises8.readdir)(domainDir);
|
|
14593
14429
|
for (const file of files) {
|
|
14594
14430
|
if (!file.endsWith(".md")) continue;
|
|
14595
14431
|
const filePath = `${domain}/${file}`;
|
|
14596
14432
|
const fullPath = (0, import_node_path13.join)(memoryDir2(), filePath);
|
|
14597
14433
|
try {
|
|
14598
|
-
const content = await (0,
|
|
14434
|
+
const content = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
14599
14435
|
const hash = (0, import_node_crypto3.createHash)("sha256").update(content).digest("hex").slice(0, 16);
|
|
14600
14436
|
nodes.push({ path: filePath, content_hash: `sha256:${hash}` });
|
|
14601
14437
|
} catch {
|
|
@@ -14606,13 +14442,13 @@ async function buildManifest() {
|
|
|
14606
14442
|
}
|
|
14607
14443
|
let indexHash = null;
|
|
14608
14444
|
try {
|
|
14609
|
-
const indexContent = await (0,
|
|
14445
|
+
const indexContent = await (0, import_promises8.readFile)((0, import_node_path13.join)(memoryDir2(), "index.md"), "utf-8");
|
|
14610
14446
|
indexHash = `sha256:${(0, import_node_crypto3.createHash)("sha256").update(indexContent).digest("hex").slice(0, 16)}`;
|
|
14611
14447
|
} catch {
|
|
14612
14448
|
}
|
|
14613
14449
|
let logLength = 0;
|
|
14614
14450
|
try {
|
|
14615
|
-
const logContent = await (0,
|
|
14451
|
+
const logContent = await (0, import_promises8.readFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "utf-8");
|
|
14616
14452
|
logLength = logContent.split("\n").length;
|
|
14617
14453
|
} catch {
|
|
14618
14454
|
}
|
|
@@ -14628,10 +14464,10 @@ async function readOnDiskNodes() {
|
|
|
14628
14464
|
const domainDir = (0, import_node_path13.join)(memoryDir2(), domain);
|
|
14629
14465
|
if (!(0, import_node_fs14.existsSync)(domainDir)) continue;
|
|
14630
14466
|
try {
|
|
14631
|
-
for (const file of await (0,
|
|
14467
|
+
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
14632
14468
|
if (!file.endsWith(".md")) continue;
|
|
14633
14469
|
try {
|
|
14634
|
-
out.set(`${domain}/${file}`, hashContent(await (0,
|
|
14470
|
+
out.set(`${domain}/${file}`, hashContent(await (0, import_promises8.readFile)((0, import_node_path13.join)(domainDir, file), "utf-8")));
|
|
14635
14471
|
} catch {
|
|
14636
14472
|
}
|
|
14637
14473
|
}
|
|
@@ -14643,7 +14479,7 @@ async function readOnDiskNodes() {
|
|
|
14643
14479
|
async function readSyncBaseline() {
|
|
14644
14480
|
const out = /* @__PURE__ */ new Map();
|
|
14645
14481
|
try {
|
|
14646
|
-
const parsed = JSON.parse(await (0,
|
|
14482
|
+
const parsed = JSON.parse(await (0, import_promises8.readFile)(syncStateFile(), "utf-8"));
|
|
14647
14483
|
if (Array.isArray(parsed?.nodes)) {
|
|
14648
14484
|
for (const n of parsed.nodes) if (n?.path) out.set(n.path, n.hash ?? null);
|
|
14649
14485
|
} else if (Array.isArray(parsed?.paths)) {
|
|
@@ -14659,20 +14495,15 @@ async function recordSyncedNodePaths() {
|
|
|
14659
14495
|
const next = JSON.stringify({ schema: 2, nodes }) + "\n";
|
|
14660
14496
|
let existing = "";
|
|
14661
14497
|
try {
|
|
14662
|
-
existing = await (0,
|
|
14498
|
+
existing = await (0, import_promises8.readFile)(syncStateFile(), "utf-8");
|
|
14663
14499
|
} catch {
|
|
14664
14500
|
}
|
|
14665
14501
|
if (existing === next) return;
|
|
14666
|
-
await
|
|
14502
|
+
await (0, import_promises8.mkdir)(projectPath(VERITY_DIR), { recursive: true });
|
|
14503
|
+
await (0, import_promises8.writeFile)(syncStateFile(), next);
|
|
14667
14504
|
} catch {
|
|
14668
14505
|
}
|
|
14669
14506
|
}
|
|
14670
|
-
async function writeFileAtomic(path, content) {
|
|
14671
|
-
await (0, import_promises9.mkdir)((0, import_node_path13.dirname)(path), { recursive: true });
|
|
14672
|
-
const tmp = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
14673
|
-
await (0, import_promises9.writeFile)(tmp, content);
|
|
14674
|
-
await (0, import_promises9.rename)(tmp, path);
|
|
14675
|
-
}
|
|
14676
14507
|
async function recordSyncBaseline() {
|
|
14677
14508
|
await recordSyncedNodePaths();
|
|
14678
14509
|
}
|
|
@@ -14686,7 +14517,7 @@ async function computeEditedNodeUploads() {
|
|
|
14686
14517
|
if (!(0, import_node_fs14.existsSync)(full)) continue;
|
|
14687
14518
|
let content;
|
|
14688
14519
|
try {
|
|
14689
|
-
content = await (0,
|
|
14520
|
+
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
14690
14521
|
} catch {
|
|
14691
14522
|
continue;
|
|
14692
14523
|
}
|
|
@@ -14707,56 +14538,31 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
14707
14538
|
let count = 0;
|
|
14708
14539
|
const notes = [];
|
|
14709
14540
|
const treePaths = opts.treePaths;
|
|
14710
|
-
const synced = /* @__PURE__ */ new Map();
|
|
14711
14541
|
for (const write of writes) {
|
|
14712
|
-
const
|
|
14713
|
-
if (
|
|
14714
|
-
|
|
14715
|
-
synced.set(write.path, outcome.syncedHash);
|
|
14716
|
-
opts.served?.set(write.path, outcome.syncedHash);
|
|
14717
|
-
}
|
|
14718
|
-
notes.push(...outcome.notes);
|
|
14542
|
+
const { written, notes: writeNotes } = await applyOneWrite(write, treePaths);
|
|
14543
|
+
if (written) count++;
|
|
14544
|
+
notes.push(...writeNotes);
|
|
14719
14545
|
}
|
|
14720
14546
|
await regenerateIndex();
|
|
14721
|
-
|
|
14722
|
-
|
|
14723
|
-
|
|
14724
|
-
} catch {
|
|
14725
|
-
}
|
|
14547
|
+
try {
|
|
14548
|
+
await ensureClaudeMdPointer();
|
|
14549
|
+
} catch {
|
|
14726
14550
|
}
|
|
14727
14551
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
14728
14552
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
14729
14553
|
try {
|
|
14730
|
-
const existing = (0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "log.md")) ? await (0,
|
|
14731
|
-
await (0,
|
|
14554
|
+
const existing = (0, import_node_fs14.existsSync)((0, import_node_path13.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
|
|
14555
|
+
await (0, import_promises8.writeFile)((0, import_node_path13.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
14732
14556
|
} catch {
|
|
14733
14557
|
}
|
|
14734
|
-
|
|
14735
|
-
else if (opts.recordBaseline !== false) await recordSyncedNodePaths();
|
|
14558
|
+
await recordSyncedNodePaths();
|
|
14736
14559
|
return count;
|
|
14737
14560
|
}
|
|
14738
|
-
async function
|
|
14739
|
-
if (entries.size === 0) return;
|
|
14740
|
-
try {
|
|
14741
|
-
if ((0, import_node_fs14.existsSync)(syncStateFile())) {
|
|
14742
|
-
try {
|
|
14743
|
-
JSON.parse(await (0, import_promises9.readFile)(syncStateFile(), "utf-8"));
|
|
14744
|
-
} catch {
|
|
14745
|
-
return;
|
|
14746
|
-
}
|
|
14747
|
-
}
|
|
14748
|
-
const merged = await readSyncBaseline();
|
|
14749
|
-
for (const [path, hash] of entries) merged.set(path, hash);
|
|
14750
|
-
const nodes = [...merged].map(([path, hash]) => ({ path, hash }));
|
|
14751
|
-
await writeFileAtomic(syncStateFile(), JSON.stringify({ schema: 2, nodes }) + "\n");
|
|
14752
|
-
} catch {
|
|
14753
|
-
}
|
|
14754
|
-
}
|
|
14755
|
-
async function applyOneWrite(write, treePaths, lastServed) {
|
|
14561
|
+
async function applyOneWrite(write, treePaths) {
|
|
14756
14562
|
const fullPath = resolveInside(memoryDir2(), write.path);
|
|
14757
14563
|
const notes = [];
|
|
14758
14564
|
if (!fullPath) {
|
|
14759
|
-
return { written: false,
|
|
14565
|
+
return { written: false, notes: [`${String(write.path)}: rejected \u2014 path escapes the memory directory`] };
|
|
14760
14566
|
}
|
|
14761
14567
|
let content = write.content;
|
|
14762
14568
|
if (treePaths && treePaths.length > 0) {
|
|
@@ -14769,22 +14575,18 @@ async function applyOneWrite(write, treePaths, lastServed) {
|
|
|
14769
14575
|
if ((0, import_node_fs14.existsSync)(fullPath)) {
|
|
14770
14576
|
let existing = "";
|
|
14771
14577
|
try {
|
|
14772
|
-
existing = await (0,
|
|
14578
|
+
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
14773
14579
|
} catch {
|
|
14774
14580
|
}
|
|
14775
|
-
if (existing === content) return { written: false,
|
|
14581
|
+
if (existing === content) return { written: false, notes };
|
|
14776
14582
|
if (existing !== "") {
|
|
14777
|
-
|
|
14778
|
-
|
|
14779
|
-
notes.push(`${write.path}: kept local edit (differs from server)`);
|
|
14780
|
-
return { written: false, syncedHash: null, notes };
|
|
14781
|
-
}
|
|
14782
|
-
notes.push(`${write.path}: updated from server (untouched here since the server delivered it)`);
|
|
14583
|
+
notes.push(`${write.path}: kept local edit (differs from server)`);
|
|
14584
|
+
return { written: false, notes };
|
|
14783
14585
|
}
|
|
14784
14586
|
}
|
|
14785
|
-
await (0,
|
|
14786
|
-
await (0,
|
|
14787
|
-
return { written: true,
|
|
14587
|
+
await (0, import_promises8.mkdir)((0, import_node_path13.dirname)(fullPath), { recursive: true });
|
|
14588
|
+
await (0, import_promises8.writeFile)(fullPath, content);
|
|
14589
|
+
return { written: true, notes };
|
|
14788
14590
|
}
|
|
14789
14591
|
function groundFileGlobs(content, treePaths) {
|
|
14790
14592
|
const globs = pickFrontmatterArray(content, "file_globs");
|
|
@@ -14826,7 +14628,7 @@ async function regenerateIndex() {
|
|
|
14826
14628
|
const domainDir = (0, import_node_path13.join)(memoryDir2(), domain);
|
|
14827
14629
|
if (!(0, import_node_fs14.existsSync)(domainDir)) continue;
|
|
14828
14630
|
try {
|
|
14829
|
-
const files = await (0,
|
|
14631
|
+
const files = await (0, import_promises8.readdir)(domainDir);
|
|
14830
14632
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
14831
14633
|
if (mdFiles.length === 0) continue;
|
|
14832
14634
|
lines.push(`## ${domain}/ (${mdFiles.length})`);
|
|
@@ -14834,7 +14636,7 @@ async function regenerateIndex() {
|
|
|
14834
14636
|
for (const file of mdFiles.sort()) {
|
|
14835
14637
|
const slug = file.replace(/\.md$/, "");
|
|
14836
14638
|
try {
|
|
14837
|
-
const content = await (0,
|
|
14639
|
+
const content = await (0, import_promises8.readFile)((0, import_node_path13.join)(domainDir, file), "utf-8");
|
|
14838
14640
|
const title = pickFrontmatter(content, "title") ?? slug;
|
|
14839
14641
|
const kind = pickFrontmatter(content, "kind") ?? "-";
|
|
14840
14642
|
const confidence = pickFrontmatter(content, "confidence");
|
|
@@ -14861,11 +14663,11 @@ async function regenerateIndex() {
|
|
|
14861
14663
|
const indexPath = (0, import_node_path13.join)(memoryDir2(), "index.md");
|
|
14862
14664
|
let existing = null;
|
|
14863
14665
|
try {
|
|
14864
|
-
existing = await (0,
|
|
14666
|
+
existing = await (0, import_promises8.readFile)(indexPath, "utf-8");
|
|
14865
14667
|
} catch {
|
|
14866
14668
|
}
|
|
14867
14669
|
if (existing === next) return;
|
|
14868
|
-
await (0,
|
|
14670
|
+
await (0, import_promises8.writeFile)(indexPath, next);
|
|
14869
14671
|
}
|
|
14870
14672
|
function pickFrontmatter(content, key) {
|
|
14871
14673
|
const re = new RegExp(`^${key}:\\s*"?([^"\\n]+?)"?\\s*$`, "m");
|
|
@@ -14892,169 +14694,6 @@ var LEGACY_MD_END = "<!-- gate-memory:end -->";
|
|
|
14892
14694
|
var LEGACY_PRESERVE_START = "<!-- gate-memory:preserve -->";
|
|
14893
14695
|
var LEGACY_PRESERVE_END = "<!-- /gate-memory:preserve -->";
|
|
14894
14696
|
var CLAUDE_MD_PROSE = [
|
|
14895
|
-
"## Project Memory",
|
|
14896
|
-
"",
|
|
14897
|
-
"This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
|
|
14898
|
-
"non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
|
|
14899
|
-
"that may apply to the change you are about to make. Open specific node files via",
|
|
14900
|
-
"the Read tool when the title or scope suggests relevance.",
|
|
14901
|
-
"",
|
|
14902
|
-
"The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
|
|
14903
|
-
"superseded \u2014 ignore them unless investigating history.",
|
|
14904
|
-
"",
|
|
14905
|
-
"Knowledge the organization's other repositories learned is mirrored outside the",
|
|
14906
|
-
"repo at `~/.verity/orgs/<host>/<owner>/memory/` (`verity memory org` lists it).",
|
|
14907
|
-
"Each claim says where it came from; one that is wrong here is demoted for",
|
|
14908
|
-
'everyone with `verity memory demote <id> --reason "\u2026"`.',
|
|
14909
|
-
"",
|
|
14910
|
-
"## Quality gate: accepted risks",
|
|
14911
|
-
"",
|
|
14912
|
-
"When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
|
|
14913
|
-
'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
|
|
14914
|
-
"a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
|
|
14915
|
-
"the user saying so in this conversation. The --reason must cite that source.",
|
|
14916
|
-
"",
|
|
14917
|
-
"Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
|
|
14918
|
-
"binds to the file's current bytes and voids automatically when the file changes,",
|
|
14919
|
-
"and every waive is recorded in the run ledger. For a pattern-level false positive",
|
|
14920
|
-
"use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
|
|
14921
|
-
"",
|
|
14922
|
-
"## Post-task reflection",
|
|
14923
|
-
"",
|
|
14924
|
-
`When a task is complete (you've created a PR, the user says "done" or "ship it",`,
|
|
14925
|
-
"or the work is clearly finished), **draft the reflection yourself** \u2014 1\u20133",
|
|
14926
|
-
"concrete things worth remembering (a decision, a gotcha, or a pattern), each",
|
|
14927
|
-
"cited with the files / PR / commands / error-signatures it came from. Skip",
|
|
14928
|
-
"entirely if nothing non-obvious happened \u2014 that judgement is the ONLY filter,",
|
|
14929
|
-
"because nothing reviews the reflection before it lands.",
|
|
14930
|
-
"",
|
|
14931
|
-
"Then record it straight away. There is no confirm step, in any environment:",
|
|
14932
|
-
"",
|
|
14933
|
-
"```bash",
|
|
14934
|
-
'verity reflect --user-input "<your draft>" --kind <kind>',
|
|
14935
|
-
"```",
|
|
14936
|
-
"",
|
|
14937
|
-
"Add `--confirmed` ONLY when the user authored or dictated the words. Without",
|
|
14938
|
-
"it the node is stored as `source: agent` \u2014 Verity thought this, nobody checked",
|
|
14939
|
-
"it. With it, `source: user` at full confidence \u2014 a person stands behind this.",
|
|
14940
|
-
"Never claim the second for your own draft, however good it is.",
|
|
14941
|
-
"",
|
|
14942
|
-
"**Name the files in the text.** Verity scopes the reflection to the paths it",
|
|
14943
|
-
"cites, and a reflection that names no file in this repo is never retrieved for",
|
|
14944
|
-
"a later review \u2014 it is recorded and then invisible. The command says so when it",
|
|
14945
|
-
'happens; `--file-globs "<path or glob>"` is the fix when the prose cannot carry',
|
|
14946
|
-
"the paths.",
|
|
14947
|
-
"",
|
|
14948
|
-
"Then tell the user, in one line, what you recorded and where: the command",
|
|
14949
|
-
"prints the node id, the path under `.verity/memory/`, and a dashboard link.",
|
|
14950
|
-
"They did not agree to it in advance, so say it happened \u2014 editing or deleting",
|
|
14951
|
-
"that file is how they correct it.",
|
|
14952
|
-
"",
|
|
14953
|
-
"> Durable, hand-curated guidance goes in the preserve region below (it survives",
|
|
14954
|
-
"> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
|
|
14955
|
-
"> markers is tool-owned and overwritten on each run.",
|
|
14956
|
-
"",
|
|
14957
|
-
"## Housekeeping Turns",
|
|
14958
|
-
"",
|
|
14959
|
-
"When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
|
|
14960
|
-
"rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
|
|
14961
|
-
"",
|
|
14962
|
-
"```bash",
|
|
14963
|
-
'verity ignore --turn --agent --reason "pulling latest before starting"',
|
|
14964
|
-
"```",
|
|
14965
|
-
"",
|
|
14966
|
-
"This skips the review for that turn, which saves the turn Verity would",
|
|
14967
|
-
"otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
|
|
14968
|
-
"`--turn` when a single piece of housekeeping spans several turns.",
|
|
14969
|
-
"",
|
|
14970
|
-
"**It is a claim about the turn, not a way to silence review.** The declaration",
|
|
14971
|
-
"is checked against what the turn actually did: if anything is authored \u2014 by you,",
|
|
14972
|
-
"by a subagent, or by a shell command that can write files \u2014 it voids, the review",
|
|
14973
|
-
"runs anyway, and the broken declaration is reported. So declare housekeeping you",
|
|
14974
|
-
"are about to do, never work you have already done, and never as a way to get past",
|
|
14975
|
-
"a finding. Declarations are budgeted per session and every one is recorded with",
|
|
14976
|
-
"its reason."
|
|
14977
|
-
].join("\n");
|
|
14978
|
-
var CLAUDE_MD_PROSE_PRE_ORG = [
|
|
14979
|
-
"## Project Memory",
|
|
14980
|
-
"",
|
|
14981
|
-
"This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
|
|
14982
|
-
"non-trivial work, scan `.verity/memory/index.md` for decisions, gotchas, and patterns",
|
|
14983
|
-
"that may apply to the change you are about to make. Open specific node files via",
|
|
14984
|
-
"the Read tool when the title or scope suggests relevance.",
|
|
14985
|
-
"",
|
|
14986
|
-
"The graph is auto-maintained by Verity. Files at `.verity/memory/_archive/` are",
|
|
14987
|
-
"superseded \u2014 ignore them unless investigating history.",
|
|
14988
|
-
"",
|
|
14989
|
-
"## Quality gate: accepted risks",
|
|
14990
|
-
"",
|
|
14991
|
-
"When the Verity pre-commit/pre-push gate FAILs, fix the findings \u2014 that is the",
|
|
14992
|
-
'default. Use `verity waive <pattern-id> --file <path> --reason "\u2026"` ONLY to relay',
|
|
14993
|
-
"a risk a human has explicitly accepted: a named code-review finding, an ADR, or",
|
|
14994
|
-
"the user saying so in this conversation. The --reason must cite that source.",
|
|
14995
|
-
"",
|
|
14996
|
-
"Never waive on your own judgment, to get past a block, or pre-emptively. A waive",
|
|
14997
|
-
"binds to the file's current bytes and voids automatically when the file changes,",
|
|
14998
|
-
"and every waive is recorded in the run ledger. For a pattern-level false positive",
|
|
14999
|
-
"use `verity feedback finding <run-id> <pattern-id> false_positive` instead.",
|
|
15000
|
-
"",
|
|
15001
|
-
"## Post-task reflection",
|
|
15002
|
-
"",
|
|
15003
|
-
`When a task is complete (you've created a PR, the user says "done" or "ship it",`,
|
|
15004
|
-
"or the work is clearly finished), **draft the reflection yourself** \u2014 1\u20133",
|
|
15005
|
-
"concrete things worth remembering (a decision, a gotcha, or a pattern), each",
|
|
15006
|
-
"cited with the files / PR / commands / error-signatures it came from. Skip",
|
|
15007
|
-
"entirely if nothing non-obvious happened \u2014 that judgement is the ONLY filter,",
|
|
15008
|
-
"because nothing reviews the reflection before it lands.",
|
|
15009
|
-
"",
|
|
15010
|
-
"Then record it straight away. There is no confirm step, in any environment:",
|
|
15011
|
-
"",
|
|
15012
|
-
"```bash",
|
|
15013
|
-
'verity reflect --user-input "<your draft>" --kind <kind>',
|
|
15014
|
-
"```",
|
|
15015
|
-
"",
|
|
15016
|
-
"Add `--confirmed` ONLY when the user authored or dictated the words. Without",
|
|
15017
|
-
"it the node is stored as `source: agent` \u2014 Verity thought this, nobody checked",
|
|
15018
|
-
"it. With it, `source: user` at full confidence \u2014 a person stands behind this.",
|
|
15019
|
-
"Never claim the second for your own draft, however good it is.",
|
|
15020
|
-
"",
|
|
15021
|
-
"**Name the files in the text.** Verity scopes the reflection to the paths it",
|
|
15022
|
-
"cites, and a reflection that names no file in this repo is never retrieved for",
|
|
15023
|
-
"a later review \u2014 it is recorded and then invisible. The command says so when it",
|
|
15024
|
-
'happens; `--file-globs "<path or glob>"` is the fix when the prose cannot carry',
|
|
15025
|
-
"the paths.",
|
|
15026
|
-
"",
|
|
15027
|
-
"Then tell the user, in one line, what you recorded and where: the command",
|
|
15028
|
-
"prints the node id, the path under `.verity/memory/`, and a dashboard link.",
|
|
15029
|
-
"They did not agree to it in advance, so say it happened \u2014 editing or deleting",
|
|
15030
|
-
"that file is how they correct it.",
|
|
15031
|
-
"",
|
|
15032
|
-
"> Durable, hand-curated guidance goes in the preserve region below (it survives",
|
|
15033
|
-
"> regeneration) or anywhere OUTSIDE these markers. Everything else between the",
|
|
15034
|
-
"> markers is tool-owned and overwritten on each run.",
|
|
15035
|
-
"",
|
|
15036
|
-
"## Housekeeping Turns",
|
|
15037
|
-
"",
|
|
15038
|
-
"When a turn will be pure housekeeping \u2014 pulling, installing dependencies,",
|
|
15039
|
-
"rebasing, a formatting sweep you are not authoring \u2014 declare it BEFORE doing it:",
|
|
15040
|
-
"",
|
|
15041
|
-
"```bash",
|
|
15042
|
-
'verity ignore --turn --agent --reason "pulling latest before starting"',
|
|
15043
|
-
"```",
|
|
15044
|
-
"",
|
|
15045
|
-
"This skips the review for that turn, which saves the turn Verity would",
|
|
15046
|
-
"otherwise spend saying it had nothing to say. Use `--for 30m` instead of",
|
|
15047
|
-
"`--turn` when a single piece of housekeeping spans several turns.",
|
|
15048
|
-
"",
|
|
15049
|
-
"**It is a claim about the turn, not a way to silence review.** The declaration",
|
|
15050
|
-
"is checked against what the turn actually did: if anything is authored \u2014 by you,",
|
|
15051
|
-
"by a subagent, or by a shell command that can write files \u2014 it voids, the review",
|
|
15052
|
-
"runs anyway, and the broken declaration is reported. So declare housekeeping you",
|
|
15053
|
-
"are about to do, never work you have already done, and never as a way to get past",
|
|
15054
|
-
"a finding. Declarations are budgeted per session and every one is recorded with",
|
|
15055
|
-
"its reason."
|
|
15056
|
-
].join("\n");
|
|
15057
|
-
var CLAUDE_MD_PROSE_PRE_AUTORECORD = [
|
|
15058
14697
|
"## Project Memory",
|
|
15059
14698
|
"",
|
|
15060
14699
|
"This project has a knowledge graph maintained at `.verity/memory/`. Before starting",
|
|
@@ -15258,7 +14897,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
|
15258
14897
|
const claudeMdPath = (0, import_node_path13.join)(cwd, "CLAUDE.md");
|
|
15259
14898
|
let existing = "";
|
|
15260
14899
|
if ((0, import_node_fs14.existsSync)(claudeMdPath)) {
|
|
15261
|
-
existing = await (0,
|
|
14900
|
+
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
15262
14901
|
}
|
|
15263
14902
|
let startTag = CLAUDE_MD_START;
|
|
15264
14903
|
let endTag = CLAUDE_MD_END;
|
|
@@ -15314,7 +14953,7 @@ async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
|
15314
14953
|
next = existing.replace(/\n*$/, "") + "\n\n" + block + "\n";
|
|
15315
14954
|
}
|
|
15316
14955
|
if (next === existing) return;
|
|
15317
|
-
await (0,
|
|
14956
|
+
await (0, import_promises8.writeFile)(claudeMdPath, next);
|
|
15318
14957
|
}
|
|
15319
14958
|
function extractPreserveContent(interior) {
|
|
15320
14959
|
for (const [start, end] of [
|
|
@@ -15333,8 +14972,6 @@ function stripKnownProse(interior) {
|
|
|
15333
14972
|
const trimmed = interior.replace(/^\n+/, "");
|
|
15334
14973
|
for (const prose of [
|
|
15335
14974
|
CLAUDE_MD_PROSE,
|
|
15336
|
-
CLAUDE_MD_PROSE_PRE_ORG,
|
|
15337
|
-
CLAUDE_MD_PROSE_PRE_AUTORECORD,
|
|
15338
14975
|
CLAUDE_MD_PROSE_PRE_REFLECT,
|
|
15339
14976
|
CLAUDE_MD_PROSE_PRE_WAIVE,
|
|
15340
14977
|
CLAUDE_MD_PROSE_PRE_IGNORE,
|
|
@@ -16954,112 +16591,21 @@ async function fireClassify(prompt, sessionId, globals) {
|
|
|
16954
16591
|
|
|
16955
16592
|
// src/commands/lifecycle.ts
|
|
16956
16593
|
var import_node_fs23 = require("node:fs");
|
|
16957
|
-
var
|
|
16594
|
+
var import_node_path20 = require("node:path");
|
|
16958
16595
|
|
|
16959
16596
|
// src/lib/baseline.ts
|
|
16960
16597
|
var import_node_fs22 = require("node:fs");
|
|
16961
|
-
var
|
|
16598
|
+
var import_node_path19 = require("node:path");
|
|
16962
16599
|
var import_node_crypto9 = require("node:crypto");
|
|
16963
16600
|
|
|
16964
16601
|
// src/lib/snapshot.ts
|
|
16965
16602
|
var import_node_fs21 = require("node:fs");
|
|
16966
|
-
var
|
|
16603
|
+
var import_node_path18 = require("node:path");
|
|
16967
16604
|
var import_node_child_process7 = require("node:child_process");
|
|
16968
16605
|
|
|
16969
16606
|
// src/lib/files.ts
|
|
16970
|
-
var import_node_path18 = require("node:path");
|
|
16971
|
-
|
|
16972
|
-
// src/lib/safe-read.ts
|
|
16973
16607
|
var import_node_fs20 = require("node:fs");
|
|
16974
16608
|
var import_node_path17 = require("node:path");
|
|
16975
|
-
var FLAGS = import_node_fs20.constants;
|
|
16976
|
-
var NOFOLLOW = FLAGS.O_NOFOLLOW;
|
|
16977
|
-
var NONBLOCK = FLAGS.O_NONBLOCK;
|
|
16978
|
-
function closeOpened(fd) {
|
|
16979
|
-
try {
|
|
16980
|
-
(0, import_node_fs20.closeSync)(fd);
|
|
16981
|
-
} catch {
|
|
16982
|
-
}
|
|
16983
|
-
}
|
|
16984
|
-
function isInsideRoot(realRoot, realPath) {
|
|
16985
|
-
return realPath === realRoot || realPath.startsWith(realRoot.endsWith(import_node_path17.sep) ? realRoot : realRoot + import_node_path17.sep);
|
|
16986
|
-
}
|
|
16987
|
-
function sameOpenedFile(opened, current, nofollowAvailable) {
|
|
16988
|
-
if (!nofollowAvailable && (opened.ino === 0n || current.ino === 0n)) return false;
|
|
16989
|
-
return opened.ino === current.ino && opened.dev === current.dev;
|
|
16990
|
-
}
|
|
16991
|
-
function openRegularInRoot(root, path) {
|
|
16992
|
-
const full = (0, import_node_path17.isAbsolute)(path) ? path : (0, import_node_path17.join)(root, path);
|
|
16993
|
-
let fd;
|
|
16994
|
-
try {
|
|
16995
|
-
fd = (0, import_node_fs20.openSync)(full, import_node_fs20.constants.O_RDONLY | (NOFOLLOW ?? 0) | (NONBLOCK ?? 0));
|
|
16996
|
-
} catch (err) {
|
|
16997
|
-
const code = err.code;
|
|
16998
|
-
if (code === "ELOOP" || code === "EMLINK" || code === "EFTYPE") return { ok: false, reason: "symlink" };
|
|
16999
|
-
if (code === "ENOENT" || code === "ENOTDIR") return { ok: false, reason: "missing" };
|
|
17000
|
-
return { ok: false, reason: "unreadable" };
|
|
17001
|
-
}
|
|
17002
|
-
try {
|
|
17003
|
-
const opened = (0, import_node_fs20.fstatSync)(fd, { bigint: true });
|
|
17004
|
-
if (!opened.isFile()) {
|
|
17005
|
-
closeOpened(fd);
|
|
17006
|
-
return { ok: false, reason: "not-regular" };
|
|
17007
|
-
}
|
|
17008
|
-
const realRoot = import_node_fs20.realpathSync.native(root);
|
|
17009
|
-
const realPath = import_node_fs20.realpathSync.native(full);
|
|
17010
|
-
if (!isInsideRoot(realRoot, realPath)) {
|
|
17011
|
-
closeOpened(fd);
|
|
17012
|
-
return { ok: false, reason: "outside-root" };
|
|
17013
|
-
}
|
|
17014
|
-
const current = (0, import_node_fs20.statSync)(realPath, { bigint: true });
|
|
17015
|
-
if (!sameOpenedFile(opened, current, NOFOLLOW !== void 0)) {
|
|
17016
|
-
closeOpened(fd);
|
|
17017
|
-
return { ok: false, reason: NOFOLLOW === void 0 ? "unreadable" : "outside-root" };
|
|
17018
|
-
}
|
|
17019
|
-
return { ok: true, fd, size: Number(opened.size), mtimeMs: Number(opened.mtimeMs) };
|
|
17020
|
-
} catch {
|
|
17021
|
-
closeOpened(fd);
|
|
17022
|
-
return { ok: false, reason: "unreadable" };
|
|
17023
|
-
}
|
|
17024
|
-
}
|
|
17025
|
-
function statRegularInRoot(root, path) {
|
|
17026
|
-
const opened = openRegularInRoot(root, path);
|
|
17027
|
-
if (!opened.ok) return opened;
|
|
17028
|
-
closeOpened(opened.fd);
|
|
17029
|
-
return { ok: true, size: opened.size, mtimeMs: opened.mtimeMs };
|
|
17030
|
-
}
|
|
17031
|
-
function readOpened(fd, size) {
|
|
17032
|
-
try {
|
|
17033
|
-
const buffer = Buffer.alloc(size);
|
|
17034
|
-
let offset = 0;
|
|
17035
|
-
while (offset < size) {
|
|
17036
|
-
const n = (0, import_node_fs20.readSync)(fd, buffer, offset, size - offset, offset);
|
|
17037
|
-
if (n === 0) break;
|
|
17038
|
-
offset += n;
|
|
17039
|
-
}
|
|
17040
|
-
return buffer.subarray(0, offset);
|
|
17041
|
-
} finally {
|
|
17042
|
-
closeOpened(fd);
|
|
17043
|
-
}
|
|
17044
|
-
}
|
|
17045
|
-
function readOpenedUtf8(fd, size) {
|
|
17046
|
-
return readOpened(fd, size).toString("utf-8");
|
|
17047
|
-
}
|
|
17048
|
-
function readRegularFileInRoot(root, path, maxBytes) {
|
|
17049
|
-
const opened = openRegularInRoot(root, path);
|
|
17050
|
-
if (!opened.ok) return opened;
|
|
17051
|
-
if (opened.size > maxBytes) {
|
|
17052
|
-
closeOpened(opened.fd);
|
|
17053
|
-
return { ok: false, reason: "too-large", size: opened.size };
|
|
17054
|
-
}
|
|
17055
|
-
try {
|
|
17056
|
-
return { ok: true, content: readOpenedUtf8(opened.fd, opened.size), size: opened.size };
|
|
17057
|
-
} catch {
|
|
17058
|
-
return { ok: false, reason: "unreadable" };
|
|
17059
|
-
}
|
|
17060
|
-
}
|
|
17061
|
-
|
|
17062
|
-
// src/lib/files.ts
|
|
17063
16609
|
var LANG_MAP = {
|
|
17064
16610
|
// Analyzable (static analysis + Gemini)
|
|
17065
16611
|
ts: "typescript",
|
|
@@ -17127,14 +16673,19 @@ var LANG_MAP = {
|
|
|
17127
16673
|
mk: "make"
|
|
17128
16674
|
};
|
|
17129
16675
|
function detectLanguage(filepath) {
|
|
17130
|
-
const ext = (0,
|
|
16676
|
+
const ext = (0, import_node_path17.extname)(filepath).slice(1);
|
|
17131
16677
|
return LANG_MAP[ext] ?? ext;
|
|
17132
16678
|
}
|
|
17133
16679
|
function sortByMtime(files) {
|
|
17134
16680
|
const withMtime = files.map((f) => {
|
|
17135
|
-
const
|
|
17136
|
-
if (
|
|
17137
|
-
|
|
16681
|
+
const resolved = resolveFile(f);
|
|
16682
|
+
if (!resolved) return null;
|
|
16683
|
+
try {
|
|
16684
|
+
const stat3 = (0, import_node_fs20.statSync)(resolved);
|
|
16685
|
+
return { path: f, resolved, mtime: stat3.mtimeMs };
|
|
16686
|
+
} catch {
|
|
16687
|
+
return null;
|
|
16688
|
+
}
|
|
17138
16689
|
}).filter((x) => x !== null);
|
|
17139
16690
|
withMtime.sort((a, b) => b.mtime - a.mtime);
|
|
17140
16691
|
return withMtime.map((x) => x.path);
|
|
@@ -17156,22 +16707,23 @@ function collectCodeDelta(files, opts) {
|
|
|
17156
16707
|
droppedPaths.push(filepath);
|
|
17157
16708
|
continue;
|
|
17158
16709
|
}
|
|
17159
|
-
const
|
|
17160
|
-
if (!
|
|
17161
|
-
exclude(
|
|
17162
|
-
|
|
17163
|
-
|
|
17164
|
-
|
|
16710
|
+
const resolved = resolveFile(filepath);
|
|
16711
|
+
if (!resolved) {
|
|
16712
|
+
exclude(filepath, "path-not-resolvable");
|
|
16713
|
+
continue;
|
|
16714
|
+
}
|
|
16715
|
+
let size;
|
|
16716
|
+
try {
|
|
16717
|
+
size = (0, import_node_fs20.statSync)(resolved).size;
|
|
16718
|
+
} catch {
|
|
16719
|
+
exclude(filepath, "not-stattable");
|
|
17165
16720
|
continue;
|
|
17166
16721
|
}
|
|
17167
|
-
const size = opened.size;
|
|
17168
16722
|
if (size > maxFileBytes) {
|
|
17169
|
-
closeOpened(opened.fd);
|
|
17170
16723
|
exclude(filepath, `over-file-size-limit-${maxFileBytes}b`);
|
|
17171
16724
|
continue;
|
|
17172
16725
|
}
|
|
17173
16726
|
if (totalSize + size > maxTotalBytes) {
|
|
17174
|
-
closeOpened(opened.fd);
|
|
17175
16727
|
truncationReason ??= "max_total_bytes";
|
|
17176
16728
|
const idx = sorted.indexOf(filepath);
|
|
17177
16729
|
droppedPaths.push(...sorted.slice(idx));
|
|
@@ -17179,7 +16731,7 @@ function collectCodeDelta(files, opts) {
|
|
|
17179
16731
|
}
|
|
17180
16732
|
let content;
|
|
17181
16733
|
try {
|
|
17182
|
-
content =
|
|
16734
|
+
content = (0, import_node_fs20.readFileSync)(resolved, "utf-8");
|
|
17183
16735
|
} catch {
|
|
17184
16736
|
exclude(filepath, "not-readable");
|
|
17185
16737
|
continue;
|
|
@@ -17224,7 +16776,7 @@ function generateSnapshotDiffs(files) {
|
|
|
17224
16776
|
const diffs = [];
|
|
17225
16777
|
for (const file of files) {
|
|
17226
16778
|
if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
|
|
17227
|
-
const snapshotPath = (0,
|
|
16779
|
+
const snapshotPath = (0, import_node_path18.join)(SNAPSHOT_DIR, file.path);
|
|
17228
16780
|
const language = file.language ?? detectLanguage(file.path);
|
|
17229
16781
|
if ((0, import_node_fs21.existsSync)(snapshotPath)) {
|
|
17230
16782
|
const oldContent = (0, import_node_fs21.readFileSync)(snapshotPath, "utf-8");
|
|
@@ -17252,16 +16804,16 @@ function saveSnapshots(files) {
|
|
|
17252
16804
|
const snapshotPaths = /* @__PURE__ */ new Set();
|
|
17253
16805
|
for (const file of files) {
|
|
17254
16806
|
if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
|
|
17255
|
-
const snapshotPath = (0,
|
|
16807
|
+
const snapshotPath = (0, import_node_path18.join)(SNAPSHOT_DIR, file.path);
|
|
17256
16808
|
snapshotPaths.add(snapshotPath);
|
|
17257
|
-
(0, import_node_fs21.mkdirSync)((0,
|
|
16809
|
+
(0, import_node_fs21.mkdirSync)((0, import_node_path18.dirname)(snapshotPath), { recursive: true });
|
|
17258
16810
|
(0, import_node_fs21.writeFileSync)(snapshotPath, file.content);
|
|
17259
16811
|
}
|
|
17260
16812
|
cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
|
|
17261
16813
|
}
|
|
17262
16814
|
function computeDiff(oldContent, newContent, filePath) {
|
|
17263
|
-
const tmpOld = (0,
|
|
17264
|
-
const tmpNew = (0,
|
|
16815
|
+
const tmpOld = (0, import_node_path18.join)(SNAPSHOT_DIR, ".diff-old.tmp");
|
|
16816
|
+
const tmpNew = (0, import_node_path18.join)(SNAPSHOT_DIR, ".diff-new.tmp");
|
|
17265
16817
|
try {
|
|
17266
16818
|
(0, import_node_fs21.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
|
|
17267
16819
|
(0, import_node_fs21.writeFileSync)(tmpOld, oldContent);
|
|
@@ -17294,7 +16846,7 @@ function cleanStaleSnapshots(dir, keepSet) {
|
|
|
17294
16846
|
const entries = (0, import_node_fs21.readdirSync)(dir, { withFileTypes: true });
|
|
17295
16847
|
for (const entry of entries) {
|
|
17296
16848
|
if (dir === SNAPSHOT_DIR && (entry.name === ".diff-old.tmp" || entry.name === ".diff-new.tmp")) continue;
|
|
17297
|
-
const fullPath = (0,
|
|
16849
|
+
const fullPath = (0, import_node_path18.join)(dir, entry.name);
|
|
17298
16850
|
if (entry.isDirectory()) {
|
|
17299
16851
|
cleanStaleSnapshots(fullPath, keepSet);
|
|
17300
16852
|
try {
|
|
@@ -17323,13 +16875,13 @@ function sessionKey(sessionId) {
|
|
|
17323
16875
|
return (0, import_node_crypto9.createHash)("sha256").update(sessionId).digest("hex").slice(0, 16);
|
|
17324
16876
|
}
|
|
17325
16877
|
function sessionDir(key) {
|
|
17326
|
-
return (0,
|
|
16878
|
+
return (0, import_node_path19.join)(projectPath(BASELINE_DIR), key);
|
|
17327
16879
|
}
|
|
17328
16880
|
function manifestPath(dir) {
|
|
17329
|
-
return (0,
|
|
16881
|
+
return (0, import_node_path19.join)(dir, "manifest.json");
|
|
17330
16882
|
}
|
|
17331
16883
|
function mirrorPath(dir, repoRelPath) {
|
|
17332
|
-
return (0,
|
|
16884
|
+
return (0, import_node_path19.join)(dir, "files", repoRelPath);
|
|
17333
16885
|
}
|
|
17334
16886
|
var CARRY_FILE = `${BASELINE_DIR}/.carry`;
|
|
17335
16887
|
var CARRY_WINDOW_MS = 12e4;
|
|
@@ -17391,7 +16943,7 @@ function captureBaseline(opts = {}) {
|
|
|
17391
16943
|
(0, import_node_fs22.rmSync)(dir, { recursive: true, force: true });
|
|
17392
16944
|
} catch {
|
|
17393
16945
|
}
|
|
17394
|
-
const filesDir = (0,
|
|
16946
|
+
const filesDir = (0, import_node_path19.join)(dir, "files");
|
|
17395
16947
|
const mirrored = [];
|
|
17396
16948
|
try {
|
|
17397
16949
|
(0, import_node_fs22.mkdirSync)(filesDir, { recursive: true });
|
|
@@ -17401,7 +16953,7 @@ function captureBaseline(opts = {}) {
|
|
|
17401
16953
|
if (content === null) continue;
|
|
17402
16954
|
const dest = mirrorPath(dir, p);
|
|
17403
16955
|
try {
|
|
17404
|
-
(0, import_node_fs22.mkdirSync)((0,
|
|
16956
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path19.dirname)(dest), { recursive: true });
|
|
17405
16957
|
(0, import_node_fs22.writeFileSync)(dest, content);
|
|
17406
16958
|
mirrored.push(p);
|
|
17407
16959
|
} catch {
|
|
@@ -17509,7 +17061,7 @@ function absorbIntoBaseline(paths, sessionId) {
|
|
|
17509
17061
|
const content = safeReadForMirror(projectPath(p));
|
|
17510
17062
|
if (content === null) continue;
|
|
17511
17063
|
const dest = mirrorPath(dir, p);
|
|
17512
|
-
(0, import_node_fs22.mkdirSync)((0,
|
|
17064
|
+
(0, import_node_fs22.mkdirSync)((0, import_node_path19.dirname)(dest), { recursive: true });
|
|
17513
17065
|
(0, import_node_fs22.writeFileSync)(dest, content);
|
|
17514
17066
|
dirty.add(p);
|
|
17515
17067
|
adopted++;
|
|
@@ -17557,7 +17109,7 @@ function pruneOldBaselines() {
|
|
|
17557
17109
|
}
|
|
17558
17110
|
const now = Date.now();
|
|
17559
17111
|
for (const name of entries) {
|
|
17560
|
-
const dir = (0,
|
|
17112
|
+
const dir = (0, import_node_path19.join)(root, name);
|
|
17561
17113
|
const manifest = readManifest(dir);
|
|
17562
17114
|
if (!manifest) {
|
|
17563
17115
|
try {
|
|
@@ -17745,7 +17297,7 @@ function buildCompactionContext(session) {
|
|
|
17745
17297
|
commitsSince: commitsSincePaths(watermark, (state.authored ?? []).map((a) => a.path)),
|
|
17746
17298
|
readFileLines: (file) => {
|
|
17747
17299
|
try {
|
|
17748
|
-
const abs = (0,
|
|
17300
|
+
const abs = (0, import_node_path20.join)(root, file);
|
|
17749
17301
|
return (0, import_node_fs23.existsSync)(abs) ? (0, import_node_fs23.readFileSync)(abs, "utf8").split("\n") : null;
|
|
17750
17302
|
} catch {
|
|
17751
17303
|
return null;
|
|
@@ -17782,17 +17334,17 @@ async function readHookStdin() {
|
|
|
17782
17334
|
try {
|
|
17783
17335
|
if (process.stdin.isTTY) return {};
|
|
17784
17336
|
const chunks = [];
|
|
17785
|
-
const timeout = new Promise((
|
|
17786
|
-
const read = new Promise((
|
|
17337
|
+
const timeout = new Promise((resolve5) => setTimeout(() => resolve5({}), 500));
|
|
17338
|
+
const read = new Promise((resolve5) => {
|
|
17787
17339
|
process.stdin.on("data", (c) => chunks.push(c));
|
|
17788
17340
|
process.stdin.on("end", () => {
|
|
17789
17341
|
try {
|
|
17790
|
-
|
|
17342
|
+
resolve5(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
|
|
17791
17343
|
} catch {
|
|
17792
|
-
|
|
17344
|
+
resolve5({});
|
|
17793
17345
|
}
|
|
17794
17346
|
});
|
|
17795
|
-
process.stdin.on("error", () =>
|
|
17347
|
+
process.stdin.on("error", () => resolve5({}));
|
|
17796
17348
|
process.stdin.resume();
|
|
17797
17349
|
});
|
|
17798
17350
|
return await Promise.race([read, timeout]);
|
|
@@ -17802,25 +17354,25 @@ async function readHookStdin() {
|
|
|
17802
17354
|
}
|
|
17803
17355
|
|
|
17804
17356
|
// src/commands/standard.ts
|
|
17805
|
-
var
|
|
17357
|
+
var import_promises12 = require("node:fs/promises");
|
|
17806
17358
|
var import_node_fs30 = require("node:fs");
|
|
17807
17359
|
var import_yaml3 = __toESM(require_dist());
|
|
17808
17360
|
|
|
17809
17361
|
// src/lib/synthesize.ts
|
|
17810
17362
|
var import_node_child_process9 = require("node:child_process");
|
|
17811
17363
|
var import_node_fs26 = require("node:fs");
|
|
17812
|
-
var
|
|
17813
|
-
var
|
|
17364
|
+
var import_promises9 = require("node:fs/promises");
|
|
17365
|
+
var import_node_path23 = require("node:path");
|
|
17814
17366
|
var import_yaml = __toESM(require_dist());
|
|
17815
17367
|
|
|
17816
17368
|
// src/lib/data-dir.ts
|
|
17817
17369
|
var import_node_fs24 = require("node:fs");
|
|
17818
|
-
var
|
|
17370
|
+
var import_node_path21 = require("node:path");
|
|
17819
17371
|
function resolveDataDir() {
|
|
17820
17372
|
const candidates2 = [
|
|
17821
|
-
(0,
|
|
17373
|
+
(0, import_node_path21.join)(__dirname, "..", "data"),
|
|
17822
17374
|
// installed: node_modules/@codacy/verity-cli/data
|
|
17823
|
-
(0,
|
|
17375
|
+
(0, import_node_path21.join)(__dirname, "..", "..", "data"),
|
|
17824
17376
|
// edge case: nested resolution
|
|
17825
17377
|
// THE COMMITTED SOURCE, for a source checkout that has not been built.
|
|
17826
17378
|
// cli/data/skills/ is a BUILD ARTIFACT (scripts/build.js copies client/skills
|
|
@@ -17829,14 +17381,14 @@ function resolveDataDir() {
|
|
|
17829
17381
|
// without this the synthesizer throws "Could not find Verity skill data"
|
|
17830
17382
|
// for every test and every `verity` run from source. Resolved from this
|
|
17831
17383
|
// module's own location, never the cwd: see the warning below.
|
|
17832
|
-
(0,
|
|
17384
|
+
(0, import_node_path21.join)(__dirname, "..", "..", "client"),
|
|
17833
17385
|
// bundled: cli/bin/ → ../../client
|
|
17834
|
-
(0,
|
|
17386
|
+
(0, import_node_path21.join)(__dirname, "..", "..", "..", "client"),
|
|
17835
17387
|
// tsx: cli/src/lib/ → ../../../client
|
|
17836
17388
|
...process.env.VERITY_DEV_DATA_DIR ? [process.env.VERITY_DEV_DATA_DIR] : []
|
|
17837
17389
|
];
|
|
17838
17390
|
for (const candidate of candidates2) {
|
|
17839
|
-
if ((0, import_node_fs24.existsSync)((0,
|
|
17391
|
+
if ((0, import_node_fs24.existsSync)((0, import_node_path21.join)(candidate, "skills"))) {
|
|
17840
17392
|
return candidate;
|
|
17841
17393
|
}
|
|
17842
17394
|
}
|
|
@@ -17845,13 +17397,13 @@ function resolveDataDir() {
|
|
|
17845
17397
|
);
|
|
17846
17398
|
}
|
|
17847
17399
|
function setupDataPath(file) {
|
|
17848
|
-
return (0,
|
|
17400
|
+
return (0, import_node_path21.join)(resolveDataDir(), "skills", "verity-setup", file);
|
|
17849
17401
|
}
|
|
17850
17402
|
|
|
17851
17403
|
// src/lib/detect.ts
|
|
17852
17404
|
var import_node_child_process8 = require("node:child_process");
|
|
17853
17405
|
var import_node_fs25 = require("node:fs");
|
|
17854
|
-
var
|
|
17406
|
+
var import_node_path22 = require("node:path");
|
|
17855
17407
|
var TOOLED_LANGUAGES = /* @__PURE__ */ new Set([
|
|
17856
17408
|
"typescript",
|
|
17857
17409
|
"javascript",
|
|
@@ -17915,18 +17467,18 @@ function walk(root) {
|
|
|
17915
17467
|
for (const entry of entries) {
|
|
17916
17468
|
if (found.length >= WALK_MAX_FILES) return;
|
|
17917
17469
|
if (IGNORED_SEGMENTS.includes(entry.name)) continue;
|
|
17918
|
-
const full = (0,
|
|
17470
|
+
const full = (0, import_node_path22.join)(dir, entry.name);
|
|
17919
17471
|
if (entry.isDirectory()) visit(full, depth + 1);
|
|
17920
|
-
else if (entry.isFile()) found.push((0,
|
|
17472
|
+
else if (entry.isFile()) found.push((0, import_node_path22.relative)(root, full));
|
|
17921
17473
|
}
|
|
17922
17474
|
};
|
|
17923
17475
|
visit(root, 0);
|
|
17924
17476
|
return found;
|
|
17925
17477
|
}
|
|
17926
17478
|
function languageOf(path) {
|
|
17927
|
-
const name = (0,
|
|
17479
|
+
const name = (0, import_node_path22.basename)(path);
|
|
17928
17480
|
if (/^Dockerfile(\..+)?$/i.test(name)) return "dockerfile";
|
|
17929
|
-
if (!(0,
|
|
17481
|
+
if (!(0, import_node_path22.extname)(name)) return null;
|
|
17930
17482
|
const lang = detectLanguage(path);
|
|
17931
17483
|
return lang || null;
|
|
17932
17484
|
}
|
|
@@ -18008,12 +17560,12 @@ function declaredDependencies(root, files) {
|
|
|
18008
17560
|
if (deps && typeof deps === "object") names2.push(...Object.keys(deps));
|
|
18009
17561
|
}
|
|
18010
17562
|
};
|
|
18011
|
-
readPackageJson((0,
|
|
18012
|
-
const nested = files.filter((f) => f.includes("/") && (0,
|
|
18013
|
-
for (const rel of nested) readPackageJson((0,
|
|
17563
|
+
readPackageJson((0, import_node_path22.join)(root, "package.json"));
|
|
17564
|
+
const nested = files.filter((f) => f.includes("/") && (0, import_node_path22.basename)(f) === "package.json").slice(0, NESTED_MANIFEST_LIMIT);
|
|
17565
|
+
for (const rel of nested) readPackageJson((0, import_node_path22.join)(root, rel));
|
|
18014
17566
|
const pythonManifests = [
|
|
18015
|
-
...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0,
|
|
18016
|
-
...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0,
|
|
17567
|
+
...["pyproject.toml", "requirements.txt", "Pipfile", "setup.py"].map((f) => (0, import_node_path22.join)(root, f)),
|
|
17568
|
+
...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path22.join)(root, f))
|
|
18017
17569
|
];
|
|
18018
17570
|
for (const path of pythonManifests) {
|
|
18019
17571
|
if (!(0, import_node_fs25.existsSync)(path)) continue;
|
|
@@ -18028,8 +17580,8 @@ function declaredDependencies(root, files) {
|
|
|
18028
17580
|
}
|
|
18029
17581
|
}
|
|
18030
17582
|
const goMods = [
|
|
18031
|
-
(0,
|
|
18032
|
-
...files.filter((f) => f.includes("/") && (0,
|
|
17583
|
+
(0, import_node_path22.join)(root, "go.mod"),
|
|
17584
|
+
...files.filter((f) => f.includes("/") && (0, import_node_path22.basename)(f) === "go.mod").slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path22.join)(root, f))
|
|
18033
17585
|
];
|
|
18034
17586
|
for (const path of goMods) {
|
|
18035
17587
|
if (!(0, import_node_fs25.existsSync)(path)) continue;
|
|
@@ -18042,7 +17594,7 @@ function declaredDependencies(root, files) {
|
|
|
18042
17594
|
}
|
|
18043
17595
|
}
|
|
18044
17596
|
for (const file of ["pom.xml", "build.gradle", "build.gradle.kts", "Gemfile", "Cargo.toml"]) {
|
|
18045
|
-
const path = (0,
|
|
17597
|
+
const path = (0, import_node_path22.join)(root, file);
|
|
18046
17598
|
if (!(0, import_node_fs25.existsSync)(path)) continue;
|
|
18047
17599
|
try {
|
|
18048
17600
|
const text = (0, import_node_fs25.readFileSync)(path, "utf-8");
|
|
@@ -18053,7 +17605,7 @@ function declaredDependencies(root, files) {
|
|
|
18053
17605
|
return names2;
|
|
18054
17606
|
}
|
|
18055
17607
|
function detectBuildSystem(root, files) {
|
|
18056
|
-
const has = (f) => (0, import_node_fs25.existsSync)((0,
|
|
17608
|
+
const has = (f) => (0, import_node_fs25.existsSync)((0, import_node_path22.join)(root, f)) || files.some((p) => (0, import_node_path22.basename)(p) === f);
|
|
18057
17609
|
if (has("pnpm-lock.yaml")) return "pnpm";
|
|
18058
17610
|
if (has("yarn.lock")) return "yarn";
|
|
18059
17611
|
if (has("bun.lock") || has("bun.lockb")) return "bun";
|
|
@@ -18070,8 +17622,8 @@ function detectBuildSystem(root, files) {
|
|
|
18070
17622
|
}
|
|
18071
17623
|
function detectArchitecture(root, files) {
|
|
18072
17624
|
const workspaceMarkers = ["lerna.json", "pnpm-workspace.yaml", "nx.json", "turbo.json", "rush.json"];
|
|
18073
|
-
if (workspaceMarkers.some((m) => (0, import_node_fs25.existsSync)((0,
|
|
18074
|
-
const pkg = readJson((0,
|
|
17625
|
+
if (workspaceMarkers.some((m) => (0, import_node_fs25.existsSync)((0, import_node_path22.join)(root, m)))) return "monorepo";
|
|
17626
|
+
const pkg = readJson((0, import_node_path22.join)(root, "package.json"));
|
|
18075
17627
|
if (pkg && "workspaces" in pkg) return "monorepo";
|
|
18076
17628
|
const manifests = files.filter((f) => /(^|\/)(package\.json|go\.mod|pyproject\.toml|Cargo\.toml|pom\.xml)$/.test(f));
|
|
18077
17629
|
const nested = manifests.filter((f) => f.includes("/"));
|
|
@@ -18093,7 +17645,7 @@ function measureAvgFileLength(root, files, languages) {
|
|
|
18093
17645
|
let total = 0;
|
|
18094
17646
|
let counted = 0;
|
|
18095
17647
|
for (let i = 0; i < candidates2.length; i += stride) {
|
|
18096
|
-
const path = (0,
|
|
17648
|
+
const path = (0, import_node_path22.join)(root, candidates2[i]);
|
|
18097
17649
|
try {
|
|
18098
17650
|
if ((0, import_node_fs25.statSync)(path).size > 2 * 1024 * 1024) continue;
|
|
18099
17651
|
total += (0, import_node_fs25.readFileSync)(path, "utf-8").split("\n").length;
|
|
@@ -18126,14 +17678,14 @@ function detectProject(root = repoRoot()) {
|
|
|
18126
17678
|
const existingToolConfigs = [];
|
|
18127
17679
|
for (const [tool, markers] of TOOL_CONFIG_MARKERS) {
|
|
18128
17680
|
for (const marker of markers) {
|
|
18129
|
-
if ((0, import_node_fs25.existsSync)((0,
|
|
17681
|
+
if ((0, import_node_fs25.existsSync)((0, import_node_path22.join)(root, marker))) {
|
|
18130
17682
|
existingToolConfigs.push({ tool, path: `./${marker}` });
|
|
18131
17683
|
break;
|
|
18132
17684
|
}
|
|
18133
17685
|
}
|
|
18134
17686
|
}
|
|
18135
17687
|
return {
|
|
18136
|
-
projectName: (0,
|
|
17688
|
+
projectName: (0, import_node_path22.basename)(root),
|
|
18137
17689
|
languages,
|
|
18138
17690
|
languageCounts,
|
|
18139
17691
|
frameworks: matchAll(dependencies, FRAMEWORK_BY_DEPENDENCY),
|
|
@@ -18577,10 +18129,10 @@ async function correctVerityMdVersion(opts) {
|
|
|
18577
18129
|
origin: { kind: "synthesized", tools: opts.tools }
|
|
18578
18130
|
}));
|
|
18579
18131
|
}
|
|
18580
|
-
async function writeFileTo(
|
|
18581
|
-
const target = projectPath(
|
|
18582
|
-
await (0,
|
|
18583
|
-
await (0,
|
|
18132
|
+
async function writeFileTo(relative3, body) {
|
|
18133
|
+
const target = projectPath(relative3);
|
|
18134
|
+
await (0, import_promises9.mkdir)((0, import_node_path23.dirname)(target), { recursive: true });
|
|
18135
|
+
await (0, import_promises9.writeFile)(target, body);
|
|
18584
18136
|
}
|
|
18585
18137
|
async function deriveConfigForStandard(standard) {
|
|
18586
18138
|
const spec = standard.knowledge_spec ?? {};
|
|
@@ -18637,14 +18189,14 @@ ${validation.detail}`);
|
|
|
18637
18189
|
}
|
|
18638
18190
|
|
|
18639
18191
|
// src/lib/setup-state.ts
|
|
18640
|
-
var
|
|
18192
|
+
var import_promises10 = require("node:fs/promises");
|
|
18641
18193
|
var import_node_fs27 = require("node:fs");
|
|
18642
18194
|
var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
|
|
18643
18195
|
async function readSetupState() {
|
|
18644
18196
|
const path = projectPath(SETUP_STATE_FILE);
|
|
18645
18197
|
if (!(0, import_node_fs27.existsSync)(path)) return null;
|
|
18646
18198
|
try {
|
|
18647
|
-
const parsed = JSON.parse(await (0,
|
|
18199
|
+
const parsed = JSON.parse(await (0, import_promises10.readFile)(path, "utf-8"));
|
|
18648
18200
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
18649
18201
|
} catch {
|
|
18650
18202
|
return null;
|
|
@@ -18659,7 +18211,7 @@ async function writeSetupState(patch) {
|
|
|
18659
18211
|
|
|
18660
18212
|
// src/lib/push-setup.ts
|
|
18661
18213
|
var import_node_fs29 = require("node:fs");
|
|
18662
|
-
var
|
|
18214
|
+
var import_promises11 = require("node:fs/promises");
|
|
18663
18215
|
var import_yaml2 = __toESM(require_dist());
|
|
18664
18216
|
|
|
18665
18217
|
// src/lib/verityignore.ts
|
|
@@ -18828,7 +18380,7 @@ async function pushStandardAndConfig(globals, what = {}) {
|
|
|
18828
18380
|
const standardPath = projectPath(STANDARD_FILE);
|
|
18829
18381
|
if (pushStandard && (0, import_node_fs29.existsSync)(standardPath)) {
|
|
18830
18382
|
try {
|
|
18831
|
-
const content = (0, import_yaml2.parse)(await (0,
|
|
18383
|
+
const content = (0, import_yaml2.parse)(await (0, import_promises11.readFile)(standardPath, "utf-8"));
|
|
18832
18384
|
const upload = buildStandardUpload(content, readVerityIgnoreRaw());
|
|
18833
18385
|
if (upload.warning) lines.push(upload.warning);
|
|
18834
18386
|
const result = await apiRequest({
|
|
@@ -18853,7 +18405,7 @@ async function pushStandardAndConfig(globals, what = {}) {
|
|
|
18853
18405
|
const configPath = projectPath(CODACY_CONFIG_FILE);
|
|
18854
18406
|
if (pushConfig && (0, import_node_fs29.existsSync)(configPath)) {
|
|
18855
18407
|
try {
|
|
18856
|
-
const content = JSON.parse(await (0,
|
|
18408
|
+
const content = JSON.parse(await (0, import_promises11.readFile)(configPath, "utf-8"));
|
|
18857
18409
|
const result = await apiRequest({
|
|
18858
18410
|
method: "POST",
|
|
18859
18411
|
path: "/analysis-configs",
|
|
@@ -18887,7 +18439,7 @@ function registerStandardCommands(program2) {
|
|
|
18887
18439
|
printError(`No ${STANDARD_FILE} here \u2014 run "verity standard synthesize" to create one.`);
|
|
18888
18440
|
process.exit(1);
|
|
18889
18441
|
}
|
|
18890
|
-
const content = (0, import_yaml3.parse)(await (0,
|
|
18442
|
+
const content = (0, import_yaml3.parse)(await (0, import_promises12.readFile)(standardPath, "utf-8"));
|
|
18891
18443
|
const derived = await deriveConfigForStandard(content);
|
|
18892
18444
|
for (const path of derived.written) printInfo(` ${path} \u2713`);
|
|
18893
18445
|
for (const note of derived.notes) printWarn(` ${note}`);
|
|
@@ -18943,7 +18495,7 @@ function registerStandardCommands(program2) {
|
|
|
18943
18495
|
}
|
|
18944
18496
|
let yamlContent;
|
|
18945
18497
|
try {
|
|
18946
|
-
yamlContent = await (0,
|
|
18498
|
+
yamlContent = await (0, import_promises12.readFile)(opts.file, "utf-8");
|
|
18947
18499
|
} catch {
|
|
18948
18500
|
printError(`Cannot read ${opts.file}`);
|
|
18949
18501
|
process.exit(1);
|
|
@@ -19040,7 +18592,7 @@ function parseIntensity(value) {
|
|
|
19040
18592
|
}
|
|
19041
18593
|
|
|
19042
18594
|
// src/commands/config.ts
|
|
19043
|
-
var
|
|
18595
|
+
var import_promises13 = require("node:fs/promises");
|
|
19044
18596
|
function registerConfigCommands(program2) {
|
|
19045
18597
|
const config = program2.command("config").description("Manage analysis configuration");
|
|
19046
18598
|
config.command("service-url").description("Print the resolved service URL (used by the shipped skills)").action(async () => {
|
|
@@ -19086,7 +18638,7 @@ function registerConfigCommands(program2) {
|
|
|
19086
18638
|
}
|
|
19087
18639
|
let content;
|
|
19088
18640
|
try {
|
|
19089
|
-
const raw = await (0,
|
|
18641
|
+
const raw = await (0, import_promises13.readFile)(opts.file, "utf-8");
|
|
19090
18642
|
content = JSON.parse(raw);
|
|
19091
18643
|
} catch {
|
|
19092
18644
|
printError(`Cannot read or parse ${opts.file}`);
|
|
@@ -19895,10 +19447,6 @@ function rgInvocations(env = process.env) {
|
|
|
19895
19447
|
out.push({ cmd: `${(0, import_node_os4.homedir)()}/.local/bin/claude`, argv0: "rg" });
|
|
19896
19448
|
return out;
|
|
19897
19449
|
}
|
|
19898
|
-
function ripgrepEnv(base = process.env) {
|
|
19899
|
-
const { RIPGREP_CONFIG_PATH: _dropped, ...rest } = base;
|
|
19900
|
-
return rest;
|
|
19901
|
-
}
|
|
19902
19450
|
var MAX_SYMBOLS = 12;
|
|
19903
19451
|
var MAX_SITES = 24;
|
|
19904
19452
|
var MAX_SITES_PER_FILE = 3;
|
|
@@ -20438,9 +19986,6 @@ function buildRepoContext(input) {
|
|
|
20438
19986
|
};
|
|
20439
19987
|
}
|
|
20440
19988
|
const args = [
|
|
20441
|
-
// `--no-config` + RIPGREP_CONFIG_PATH scrubbed below: a config file can add
|
|
20442
|
-
// `--pre=<command>`, which runs a program per searched file.
|
|
20443
|
-
"--no-config",
|
|
20444
19989
|
"-n",
|
|
20445
19990
|
"-w",
|
|
20446
19991
|
"-F",
|
|
@@ -20473,7 +20018,6 @@ function buildRepoContext(input) {
|
|
|
20473
20018
|
res = (0, import_node_child_process10.spawnSync)(inv.cmd, args, {
|
|
20474
20019
|
...inv.argv0 ? { argv0: inv.argv0 } : {},
|
|
20475
20020
|
cwd: input.cwd ?? process.cwd(),
|
|
20476
|
-
env: ripgrepEnv(),
|
|
20477
20021
|
timeout: input.timeoutMs ?? RG_TIMEOUT_MS,
|
|
20478
20022
|
maxBuffer: 4 * 1024 * 1024,
|
|
20479
20023
|
encoding: "utf8"
|
|
@@ -20712,85 +20256,11 @@ function installRunEvidence(run2) {
|
|
|
20712
20256
|
}
|
|
20713
20257
|
|
|
20714
20258
|
// src/lib/git-frame.ts
|
|
20259
|
+
var import_node_child_process11 = require("node:child_process");
|
|
20715
20260
|
var import_node_fs33 = require("node:fs");
|
|
20716
20261
|
var import_node_os5 = require("node:os");
|
|
20262
|
+
var import_node_path24 = require("node:path");
|
|
20717
20263
|
var import_node_path25 = require("node:path");
|
|
20718
|
-
var import_node_path26 = require("node:path");
|
|
20719
|
-
|
|
20720
|
-
// src/lib/hardened-git.ts
|
|
20721
|
-
var import_node_child_process11 = require("node:child_process");
|
|
20722
|
-
var HARDENED_CONFIG_ARGS = [
|
|
20723
|
-
"-c",
|
|
20724
|
-
"core.fsmonitor=false",
|
|
20725
|
-
"-c",
|
|
20726
|
-
"core.pager=cat",
|
|
20727
|
-
"-c",
|
|
20728
|
-
"log.showSignature=false"
|
|
20729
|
-
];
|
|
20730
|
-
var DIFF_READ_FLAGS = ["--no-ext-diff", "--no-textconv"];
|
|
20731
|
-
var DEFAULT_GIT_TIMEOUT_MS = 6e4;
|
|
20732
|
-
var DEFAULT_GIT_MAX_BUFFER = 64 * 1024 * 1024;
|
|
20733
|
-
function hardenedGitEnv(base = process.env) {
|
|
20734
|
-
const env = {};
|
|
20735
|
-
for (const [key, value] of Object.entries(base)) {
|
|
20736
|
-
if (key.startsWith("GIT_")) continue;
|
|
20737
|
-
env[key] = value;
|
|
20738
|
-
}
|
|
20739
|
-
env.GIT_OPTIONAL_LOCKS = "0";
|
|
20740
|
-
env.GIT_TERMINAL_PROMPT = "0";
|
|
20741
|
-
return env;
|
|
20742
|
-
}
|
|
20743
|
-
function hardenedGitArgv(args) {
|
|
20744
|
-
return [...HARDENED_CONFIG_ARGS, ...args];
|
|
20745
|
-
}
|
|
20746
|
-
function classifyError(err) {
|
|
20747
|
-
const e = err;
|
|
20748
|
-
const stderr = typeof e?.stderr === "string" ? e.stderr : e?.stderr ? e.stderr.toString("utf-8") : "";
|
|
20749
|
-
if (e?.code === "ETIMEDOUT" || e?.killed && e?.signal === "SIGTERM") return { ok: false, error: "timeout", stderr };
|
|
20750
|
-
if (e?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return { ok: false, error: "too_large", stderr };
|
|
20751
|
-
return { ok: false, error: "failed", stderr };
|
|
20752
|
-
}
|
|
20753
|
-
function runGitSync(cwd, args, opts = {}) {
|
|
20754
|
-
try {
|
|
20755
|
-
const stdout = (0, import_node_child_process11.execFileSync)("git", hardenedGitArgv(args), {
|
|
20756
|
-
cwd,
|
|
20757
|
-
env: hardenedGitEnv(),
|
|
20758
|
-
encoding: "utf-8",
|
|
20759
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
20760
|
-
timeout: opts.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS,
|
|
20761
|
-
maxBuffer: opts.maxBuffer ?? DEFAULT_GIT_MAX_BUFFER
|
|
20762
|
-
});
|
|
20763
|
-
return { ok: true, stdout };
|
|
20764
|
-
} catch (err) {
|
|
20765
|
-
return classifyError(err);
|
|
20766
|
-
}
|
|
20767
|
-
}
|
|
20768
|
-
function runGit(cwd, args, opts = {}) {
|
|
20769
|
-
return new Promise((resolvePromise) => {
|
|
20770
|
-
(0, import_node_child_process11.execFile)(
|
|
20771
|
-
"git",
|
|
20772
|
-
hardenedGitArgv(args),
|
|
20773
|
-
{
|
|
20774
|
-
cwd,
|
|
20775
|
-
env: hardenedGitEnv(),
|
|
20776
|
-
encoding: "utf-8",
|
|
20777
|
-
timeout: opts.timeoutMs ?? DEFAULT_GIT_TIMEOUT_MS,
|
|
20778
|
-
maxBuffer: opts.maxBuffer ?? DEFAULT_GIT_MAX_BUFFER
|
|
20779
|
-
},
|
|
20780
|
-
(err, stdout, stderr) => {
|
|
20781
|
-
if (err) {
|
|
20782
|
-
const e = err;
|
|
20783
|
-
e.stderr = stderr;
|
|
20784
|
-
resolvePromise(classifyError(e));
|
|
20785
|
-
return;
|
|
20786
|
-
}
|
|
20787
|
-
resolvePromise({ ok: true, stdout });
|
|
20788
|
-
}
|
|
20789
|
-
);
|
|
20790
|
-
});
|
|
20791
|
-
}
|
|
20792
|
-
|
|
20793
|
-
// src/lib/git-frame.ts
|
|
20794
20264
|
var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
|
|
20795
20265
|
var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
|
|
20796
20266
|
var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
|
|
@@ -20800,41 +20270,7 @@ var COMMIT_RE = new RegExp(`(?:^|[\\s;&|(])${COMMIT_HEAD}|(?:^|[;&|(])\\s*[^\\s;
|
|
|
20800
20270
|
var PUSH_RE = new RegExp(`(?:^|[\\s;&|(])${PUSH_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${PUSH_HEAD}`);
|
|
20801
20271
|
var GH_PR_RE = new RegExp(`(?:^|[\\s;&|(])${GH_PR_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${GH_PR_HEAD}`);
|
|
20802
20272
|
function splitSegments(command) {
|
|
20803
|
-
return
|
|
20804
|
-
}
|
|
20805
|
-
var HEREDOC_OPERATOR = /(^|[^<])<<(-?)[ \t]*(?:'([^'\n]+)'|"([^"\n]+)"|\\?([A-Za-z_][A-Za-z0-9_]*))/g;
|
|
20806
|
-
var SHELL_CONSUMER = /(^|[\s;&|(])(bash|sh|zsh|dash|ksh|fish|ssh|eval|exec|sudo|su|xargs|env|nohup|time|docker|kubectl|podman)(\s|$)/;
|
|
20807
|
-
function stripHeredocBodies(command) {
|
|
20808
|
-
const lines = command.split("\n");
|
|
20809
|
-
const out = [];
|
|
20810
|
-
let i = 0;
|
|
20811
|
-
while (i < lines.length) {
|
|
20812
|
-
const line = lines[i];
|
|
20813
|
-
out.push(line);
|
|
20814
|
-
i++;
|
|
20815
|
-
const words = [];
|
|
20816
|
-
HEREDOC_OPERATOR.lastIndex = 0;
|
|
20817
|
-
let m;
|
|
20818
|
-
while ((m = HEREDOC_OPERATOR.exec(line)) !== null) {
|
|
20819
|
-
words.push({ word: m[3] ?? m[4] ?? m[5], dash: m[2] === "-" });
|
|
20820
|
-
}
|
|
20821
|
-
if (words.length === 0 || SHELL_CONSUMER.test(line)) continue;
|
|
20822
|
-
let cursor = i;
|
|
20823
|
-
let end = -1;
|
|
20824
|
-
for (const { word, dash } of words) {
|
|
20825
|
-
let k = cursor;
|
|
20826
|
-
while (k < lines.length && (dash ? lines[k].replace(/^\t+/, "") : lines[k]) !== word) k++;
|
|
20827
|
-
if (k >= lines.length) {
|
|
20828
|
-
end = -1;
|
|
20829
|
-
break;
|
|
20830
|
-
}
|
|
20831
|
-
end = k;
|
|
20832
|
-
cursor = k + 1;
|
|
20833
|
-
}
|
|
20834
|
-
if (end === -1) continue;
|
|
20835
|
-
i = end + 1;
|
|
20836
|
-
}
|
|
20837
|
-
return out.join("\n");
|
|
20273
|
+
return (command ?? "").split(/&&|\|\||;|\n/);
|
|
20838
20274
|
}
|
|
20839
20275
|
function findMomentSegment(command, on) {
|
|
20840
20276
|
const segments = splitSegments(command);
|
|
@@ -20877,8 +20313,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
20877
20313
|
if (SHELL_DYNAMIC.test(raw) || raw === "-") {
|
|
20878
20314
|
return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
|
|
20879
20315
|
}
|
|
20880
|
-
const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0,
|
|
20881
|
-
dir = (0,
|
|
20316
|
+
const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path25.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
|
|
20317
|
+
dir = (0, import_node_path24.isAbsolute)(expanded) ? expanded : (0, import_node_path24.resolve)(dir, expanded);
|
|
20882
20318
|
}
|
|
20883
20319
|
const seg = segments[segmentIndex];
|
|
20884
20320
|
const overrideMatch = /--(?:git-dir|work-tree)(?:=|\s)|\bGIT_(?:DIR|WORK_TREE|INDEX_FILE)=/.exec(seg);
|
|
@@ -20896,8 +20332,8 @@ function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
|
20896
20332
|
if (SHELL_DYNAMIC.test(raw)) {
|
|
20897
20333
|
return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
|
|
20898
20334
|
}
|
|
20899
|
-
const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0,
|
|
20900
|
-
dir = (0,
|
|
20335
|
+
const expanded = raw === "~" ? (0, import_node_os5.homedir)() : raw.startsWith("~/") ? (0, import_node_path25.join)((0, import_node_os5.homedir)(), raw.slice(2)) : raw;
|
|
20336
|
+
dir = (0, import_node_path24.isAbsolute)(expanded) ? expanded : (0, import_node_path24.resolve)(dir, expanded);
|
|
20901
20337
|
}
|
|
20902
20338
|
}
|
|
20903
20339
|
return { dir, named, unresolvable: null };
|
|
@@ -20952,14 +20388,17 @@ function parsePushTarget(segment) {
|
|
|
20952
20388
|
return { remote, srcRef: src, dstRef: dst || null, isDelete };
|
|
20953
20389
|
}
|
|
20954
20390
|
function gitAt(dir, args) {
|
|
20955
|
-
|
|
20956
|
-
|
|
20391
|
+
try {
|
|
20392
|
+
return (0, import_node_child_process11.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
20393
|
+
} catch {
|
|
20394
|
+
return "";
|
|
20395
|
+
}
|
|
20957
20396
|
}
|
|
20958
20397
|
function realpathOr2(p) {
|
|
20959
20398
|
try {
|
|
20960
20399
|
return import_node_fs33.realpathSync.native(p);
|
|
20961
20400
|
} catch {
|
|
20962
|
-
return (0,
|
|
20401
|
+
return (0, import_node_path24.resolve)(p);
|
|
20963
20402
|
}
|
|
20964
20403
|
}
|
|
20965
20404
|
function resolveFrame(input) {
|
|
@@ -20998,7 +20437,7 @@ function resolveFrame(input) {
|
|
|
20998
20437
|
const gitDirRaw = gitAt(dir, ["rev-parse", "--absolute-git-dir"]);
|
|
20999
20438
|
const commonRaw = gitAt(dir, ["rev-parse", "--git-common-dir"]);
|
|
21000
20439
|
const gitDir = gitDirRaw ? realpathOr2(gitDirRaw) : null;
|
|
21001
|
-
const commonDir = commonRaw ? realpathOr2((0,
|
|
20440
|
+
const commonDir = commonRaw ? realpathOr2((0, import_node_path24.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path24.resolve)(dir, commonRaw)) : null;
|
|
21002
20441
|
const branchRaw = gitAt(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
21003
20442
|
return {
|
|
21004
20443
|
moment: found?.moment ?? null,
|
|
@@ -21173,7 +20612,7 @@ function stagedRange(frame, command) {
|
|
|
21173
20612
|
if (plan.kind === "unpredictable") {
|
|
21174
20613
|
return { kind: "staged", base: "HEAD", head: "INDEX", via: "staged-in-command", refusal: plan.reason };
|
|
21175
20614
|
}
|
|
21176
|
-
const mergeHead = frame.gitDir ? (0,
|
|
20615
|
+
const mergeHead = frame.gitDir ? (0, import_node_path25.join)(frame.gitDir, "MERGE_HEAD") : null;
|
|
21177
20616
|
if (mergeHead && (0, import_node_fs33.existsSync)(mergeHead)) {
|
|
21178
20617
|
const vsHead = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "HEAD"]).split("\n").filter(Boolean));
|
|
21179
20618
|
const vsMerge = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
|
|
@@ -21226,7 +20665,7 @@ function rangeChangeSignals(frame, range, paths) {
|
|
|
21226
20665
|
const out = /* @__PURE__ */ new Map();
|
|
21227
20666
|
if (range.kind === "nothing" || paths.length === 0) return out;
|
|
21228
20667
|
if (range.kind === "push" && !range.base) return out;
|
|
21229
|
-
const args = range.kind === "staged" || range.kind === "merge" ? ["diff",
|
|
20668
|
+
const args = range.kind === "staged" || range.kind === "merge" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
|
|
21230
20669
|
const diff = frameGit(frame, [...args, "--", ...paths]);
|
|
21231
20670
|
let current = null;
|
|
21232
20671
|
let oldSide = null;
|
|
@@ -21589,7 +21028,7 @@ function sanitizeCommandWithLoss(rawCmd) {
|
|
|
21589
21028
|
const lost = lines.slice(1).some((l) => l.trim().length > 0);
|
|
21590
21029
|
const first = lines[0];
|
|
21591
21030
|
const cmd = sanitizeCommand(first);
|
|
21592
|
-
const hadSeparator = SEPARATORS.some((
|
|
21031
|
+
const hadSeparator = SEPARATORS.some((sep3) => first.indexOf(sep3) > 0);
|
|
21593
21032
|
return { cmd, lost: lost || !hadSeparator && first.length > MAX_COMMAND_CHARS };
|
|
21594
21033
|
}
|
|
21595
21034
|
var SEPARATORS = [" | ", " > ", " >> ", " 2>", " && ", " ; "];
|
|
@@ -21598,11 +21037,11 @@ function sanitizeCommand(rawCmd) {
|
|
|
21598
21037
|
let cmd = rawCmd.split("\n")[0];
|
|
21599
21038
|
let cut = -1;
|
|
21600
21039
|
let marker = "";
|
|
21601
|
-
for (const
|
|
21602
|
-
const idx = cmd.indexOf(
|
|
21040
|
+
for (const sep3 of SEPARATORS) {
|
|
21041
|
+
const idx = cmd.indexOf(sep3);
|
|
21603
21042
|
if (idx > 0 && (cut === -1 || idx < cut)) {
|
|
21604
21043
|
cut = idx;
|
|
21605
|
-
marker =
|
|
21044
|
+
marker = sep3.trim();
|
|
21606
21045
|
}
|
|
21607
21046
|
}
|
|
21608
21047
|
if (cut > -1) cmd = cmd.slice(0, cut);
|
|
@@ -21626,28 +21065,28 @@ async function readStopHookStdin() {
|
|
|
21626
21065
|
try {
|
|
21627
21066
|
if (process.stdin.isTTY) return empty;
|
|
21628
21067
|
const chunks = [];
|
|
21629
|
-
const timeout = new Promise((
|
|
21630
|
-
const read = new Promise((
|
|
21068
|
+
const timeout = new Promise((resolve5) => setTimeout(() => resolve5(empty), 500));
|
|
21069
|
+
const read = new Promise((resolve5) => {
|
|
21631
21070
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
21632
21071
|
process.stdin.on("end", () => {
|
|
21633
21072
|
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
21634
21073
|
if (!raw) {
|
|
21635
|
-
|
|
21074
|
+
resolve5(empty);
|
|
21636
21075
|
return;
|
|
21637
21076
|
}
|
|
21638
21077
|
try {
|
|
21639
21078
|
const data = JSON.parse(raw);
|
|
21640
|
-
|
|
21079
|
+
resolve5({
|
|
21641
21080
|
assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
|
|
21642
21081
|
stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null,
|
|
21643
21082
|
transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null,
|
|
21644
21083
|
sessionId: typeof data.session_id === "string" ? data.session_id : null
|
|
21645
21084
|
});
|
|
21646
21085
|
} catch {
|
|
21647
|
-
|
|
21086
|
+
resolve5(empty);
|
|
21648
21087
|
}
|
|
21649
21088
|
});
|
|
21650
|
-
process.stdin.on("error", () =>
|
|
21089
|
+
process.stdin.on("error", () => resolve5(empty));
|
|
21651
21090
|
process.stdin.resume();
|
|
21652
21091
|
});
|
|
21653
21092
|
return await Promise.race([read, timeout]);
|
|
@@ -21851,7 +21290,7 @@ function channelSilence(input) {
|
|
|
21851
21290
|
// src/lib/cli-version.ts
|
|
21852
21291
|
function cliVersion() {
|
|
21853
21292
|
try {
|
|
21854
|
-
return true ? "0.32.
|
|
21293
|
+
return true ? "0.32.7" : "dev";
|
|
21855
21294
|
} catch {
|
|
21856
21295
|
return "dev";
|
|
21857
21296
|
}
|
|
@@ -21903,12 +21342,10 @@ var SEVERITY_ORDER = {
|
|
|
21903
21342
|
Low: 3
|
|
21904
21343
|
};
|
|
21905
21344
|
function isCodacyAvailable() {
|
|
21906
|
-
|
|
21907
|
-
|
|
21908
|
-
|
|
21909
|
-
|
|
21910
|
-
return false;
|
|
21911
|
-
}
|
|
21345
|
+
return codacyAnalysisPath() !== null;
|
|
21346
|
+
}
|
|
21347
|
+
function codacyAnalysisPath() {
|
|
21348
|
+
return whichSync("codacy-analysis");
|
|
21912
21349
|
}
|
|
21913
21350
|
function buildAnalyzerArgv(files) {
|
|
21914
21351
|
return [
|
|
@@ -21935,6 +21372,16 @@ function withFailure(kind, detail) {
|
|
|
21935
21372
|
summary: { ...EMPTY_RESULT.summary, failure: { kind, detail: detail.slice(0, 300) } }
|
|
21936
21373
|
};
|
|
21937
21374
|
}
|
|
21375
|
+
function runCodacyAnalysisIfAvailable(files) {
|
|
21376
|
+
if (files.length === 0) return EMPTY_RESULT;
|
|
21377
|
+
if (!isCodacyAvailable()) {
|
|
21378
|
+
return withFailure(
|
|
21379
|
+
"analyzer_unavailable",
|
|
21380
|
+
"@codacy/analysis-cli was not found on PATH \u2014 no static analysis ran"
|
|
21381
|
+
);
|
|
21382
|
+
}
|
|
21383
|
+
return runCodacyAnalysis(files);
|
|
21384
|
+
}
|
|
21938
21385
|
function runCodacyAnalysis(files) {
|
|
21939
21386
|
const empty = EMPTY_RESULT;
|
|
21940
21387
|
if (files.length === 0) return empty;
|
|
@@ -21946,7 +21393,8 @@ function runCodacyAnalysis(files) {
|
|
|
21946
21393
|
}
|
|
21947
21394
|
});
|
|
21948
21395
|
if (existingFiles.length === 0) return empty;
|
|
21949
|
-
const
|
|
21396
|
+
const analyzer = codacyAnalysisPath() ?? "codacy-analysis";
|
|
21397
|
+
const proc = (0, import_node_child_process12.spawnSync)(analyzer, buildAnalyzerArgv(existingFiles), {
|
|
21950
21398
|
encoding: "utf-8",
|
|
21951
21399
|
maxBuffer: 10 * 1024 * 1024
|
|
21952
21400
|
});
|
|
@@ -22114,11 +21562,10 @@ var EMPTY_STATIC = {
|
|
|
22114
21562
|
summary: { total_findings: 0, by_severity: {}, tools_run: [] }
|
|
22115
21563
|
};
|
|
22116
21564
|
function runLocalStatic(analyzable, securityFiles, baseline, skipStatic) {
|
|
22117
|
-
if (skipStatic
|
|
21565
|
+
if (skipStatic) return EMPTY_STATIC;
|
|
22118
21566
|
let scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
22119
21567
|
if (baseline) scannable = scannable.filter((f) => changedSinceBaseline(f, baseline));
|
|
22120
|
-
|
|
22121
|
-
return runCodacyAnalysis(scannable);
|
|
21568
|
+
return runCodacyAnalysisIfAvailable(scannable);
|
|
22122
21569
|
}
|
|
22123
21570
|
function localOnlyAndExit(staticResults) {
|
|
22124
21571
|
printJsonCompact({
|
|
@@ -22210,7 +21657,7 @@ async function scope(run2) {
|
|
|
22210
21657
|
|
|
22211
21658
|
// src/lib/specs.ts
|
|
22212
21659
|
var import_node_fs36 = require("node:fs");
|
|
22213
|
-
var
|
|
21660
|
+
var import_node_path26 = require("node:path");
|
|
22214
21661
|
var SPEC_CANDIDATES = [
|
|
22215
21662
|
"CLAUDE.md",
|
|
22216
21663
|
"AGENTS.md",
|
|
@@ -22229,6 +21676,16 @@ var SPEC_CANDIDATES = [
|
|
|
22229
21676
|
var DOC_EXT = /\.(md|mdx|ya?ml|txt|rst|adoc)$/i;
|
|
22230
21677
|
var UNCONSULTED_FILE_BYTES = 10240;
|
|
22231
21678
|
var UNCONSULTED_TOTAL_BYTES = 30720;
|
|
21679
|
+
function readSpecFiles(specsOpt, root) {
|
|
21680
|
+
const out = [];
|
|
21681
|
+
for (const raw of specsOpt.split(",").map((f) => f.trim()).filter(Boolean)) {
|
|
21682
|
+
const candidate = (0, import_node_path26.isAbsolute)(raw) ? (0, import_node_path26.relative)(root, raw) : raw;
|
|
21683
|
+
const content = readFileInside(root, candidate, MAX_EXPLICIT_SPEC_FILE_BYTES);
|
|
21684
|
+
if (content === null) continue;
|
|
21685
|
+
out.push({ path: raw, content });
|
|
21686
|
+
}
|
|
21687
|
+
return out;
|
|
21688
|
+
}
|
|
22232
21689
|
function discoverSpecs(consulted = []) {
|
|
22233
21690
|
const result = [];
|
|
22234
21691
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -22246,13 +21703,15 @@ function discoverSpecs(consulted = []) {
|
|
|
22246
21703
|
const remaining = totalCap - totalBytes;
|
|
22247
21704
|
const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
|
|
22248
21705
|
const readBytes = Math.min(fileCap, remaining);
|
|
22249
|
-
const opened = openRegularInRoot(process.cwd(), specPath);
|
|
22250
|
-
if (!opened.ok) return true;
|
|
22251
21706
|
try {
|
|
22252
|
-
const
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
21707
|
+
const buf = Buffer.alloc(readBytes);
|
|
21708
|
+
const fd = (0, import_node_fs36.openSync)(specPath, "r");
|
|
21709
|
+
const bytesRead = (0, import_node_fs36.readSync)(fd, buf, 0, readBytes, 0);
|
|
21710
|
+
(0, import_node_fs36.closeSync)(fd);
|
|
21711
|
+
const content = buf.slice(0, bytesRead).toString("utf-8");
|
|
21712
|
+
if (!content) return true;
|
|
21713
|
+
result.push({ path: specPath, content });
|
|
21714
|
+
totalBytes += content.length;
|
|
22256
21715
|
} catch {
|
|
22257
21716
|
}
|
|
22258
21717
|
return true;
|
|
@@ -22281,7 +21740,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
22281
21740
|
try {
|
|
22282
21741
|
const entries = (0, import_node_fs36.readdirSync)(dir, { withFileTypes: true });
|
|
22283
21742
|
for (const entry of entries) {
|
|
22284
|
-
const fullPath = (0,
|
|
21743
|
+
const fullPath = (0, import_node_path26.join)(dir, entry.name);
|
|
22285
21744
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
22286
21745
|
result.push(fullPath);
|
|
22287
21746
|
} else if (entry.isDirectory() && depth < maxDepth - 1) {
|
|
@@ -22293,51 +21752,37 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
22293
21752
|
return result;
|
|
22294
21753
|
}
|
|
22295
21754
|
function discoverPlans() {
|
|
22296
|
-
const
|
|
22297
|
-
const
|
|
22298
|
-
const sources = [];
|
|
22299
|
-
if (isRealDirectoryChain(process.cwd(), [".claude", "plans"])) {
|
|
22300
|
-
sources.push({ root: process.cwd(), prefix: (0, import_node_path27.join)(".claude", "plans") });
|
|
22301
|
-
}
|
|
22302
|
-
if (home && (0, import_node_fs36.existsSync)(homePlansDir)) sources.push({ root: homePlansDir, prefix: "" });
|
|
21755
|
+
const homePlansDir = (0, import_node_path26.join)(process.env.HOME ?? "", ".claude", "plans");
|
|
21756
|
+
const localPlansDir = ".claude/plans";
|
|
22303
21757
|
const candidates2 = [];
|
|
22304
21758
|
const seen = /* @__PURE__ */ new Set();
|
|
22305
|
-
for (const
|
|
22306
|
-
|
|
21759
|
+
for (const plansDir of [localPlansDir, homePlansDir]) {
|
|
21760
|
+
if (!(0, import_node_fs36.existsSync)(plansDir)) continue;
|
|
22307
21761
|
try {
|
|
22308
|
-
|
|
21762
|
+
for (const f of (0, import_node_fs36.readdirSync)(plansDir)) {
|
|
21763
|
+
if (!f.endsWith(".md") || seen.has(f)) continue;
|
|
21764
|
+
seen.add(f);
|
|
21765
|
+
const fullPath = (0, import_node_path26.join)(plansDir, f);
|
|
21766
|
+
try {
|
|
21767
|
+
const stat3 = (0, import_node_fs36.statSync)(fullPath);
|
|
21768
|
+
candidates2.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
|
|
21769
|
+
} catch {
|
|
21770
|
+
}
|
|
21771
|
+
}
|
|
22309
21772
|
} catch {
|
|
22310
|
-
continue;
|
|
22311
|
-
}
|
|
22312
|
-
for (const f of names2) {
|
|
22313
|
-
if (!f.endsWith(".md") || seen.has(f)) continue;
|
|
22314
|
-
seen.add(f);
|
|
22315
|
-
const relPath = source.prefix ? (0, import_node_path27.join)(source.prefix, f) : f;
|
|
22316
|
-
const stat3 = statRegularInRoot(source.root, relPath);
|
|
22317
|
-
if (!stat3.ok) continue;
|
|
22318
|
-
candidates2.push({ name: f, root: source.root, relPath, mtime: stat3.mtimeMs, size: stat3.size });
|
|
22319
21773
|
}
|
|
22320
21774
|
}
|
|
22321
21775
|
candidates2.sort((a, b) => b.mtime - a.mtime);
|
|
22322
21776
|
const result = [];
|
|
22323
21777
|
for (const entry of candidates2.slice(0, MAX_PLAN_FILES)) {
|
|
22324
21778
|
if (entry.size > MAX_PLAN_FILE_BYTES) continue;
|
|
22325
|
-
const read = readRegularFileInRoot(entry.root, entry.relPath, MAX_PLAN_FILE_BYTES);
|
|
22326
|
-
if (read.ok) result.push({ name: entry.name, content: read.content });
|
|
22327
|
-
}
|
|
22328
|
-
return result;
|
|
22329
|
-
}
|
|
22330
|
-
function isRealDirectoryChain(root, parts) {
|
|
22331
|
-
let current = root;
|
|
22332
|
-
for (const part of parts) {
|
|
22333
|
-
current = (0, import_node_path27.join)(current, part);
|
|
22334
21779
|
try {
|
|
22335
|
-
|
|
21780
|
+
const content = (0, import_node_fs36.readFileSync)(entry.path, "utf-8");
|
|
21781
|
+
result.push({ name: entry.name, content });
|
|
22336
21782
|
} catch {
|
|
22337
|
-
return false;
|
|
22338
21783
|
}
|
|
22339
21784
|
}
|
|
22340
|
-
return
|
|
21785
|
+
return result;
|
|
22341
21786
|
}
|
|
22342
21787
|
var GUARD_DOC_EXT = /\.(md|mdx|txt|rst|adoc)$/i;
|
|
22343
21788
|
function discoverGuardDocs(rangeFiles2) {
|
|
@@ -22348,21 +21793,16 @@ function discoverGuardDocs(rangeFiles2) {
|
|
|
22348
21793
|
if (!GUARD_DOC_EXT.test(path)) continue;
|
|
22349
21794
|
if (path.startsWith("/") || path.includes("..")) continue;
|
|
22350
21795
|
if (!(0, import_node_fs36.existsSync)(path)) continue;
|
|
22351
|
-
const opened = openRegularInRoot(process.cwd(), path);
|
|
22352
|
-
if (!opened.ok) continue;
|
|
22353
|
-
if (opened.size > MAX_PLAN_FILE_BYTES || totalBytes + opened.size > MAX_TOTAL_SPEC_BYTES) {
|
|
22354
|
-
closeOpened(opened.fd);
|
|
22355
|
-
continue;
|
|
22356
|
-
}
|
|
22357
|
-
let bytes;
|
|
22358
21796
|
try {
|
|
22359
|
-
|
|
21797
|
+
const stat3 = (0, import_node_fs36.statSync)(path);
|
|
21798
|
+
if (stat3.size > MAX_PLAN_FILE_BYTES) continue;
|
|
21799
|
+
if (totalBytes + stat3.size > MAX_TOTAL_SPEC_BYTES) continue;
|
|
21800
|
+
const content = (0, import_node_fs36.readFileSync)(path, "utf-8");
|
|
21801
|
+
if (!content) continue;
|
|
21802
|
+
result.push({ name: path, content });
|
|
21803
|
+
totalBytes += content.length;
|
|
22360
21804
|
} catch {
|
|
22361
|
-
continue;
|
|
22362
21805
|
}
|
|
22363
|
-
if (bytes.length === 0) continue;
|
|
22364
|
-
result.push({ name: path, content: bytes.toString("utf-8") });
|
|
22365
|
-
totalBytes += bytes.length;
|
|
22366
21806
|
}
|
|
22367
21807
|
return result;
|
|
22368
21808
|
}
|
|
@@ -22534,7 +21974,7 @@ async function mode(run2) {
|
|
|
22534
21974
|
|
|
22535
21975
|
// src/lib/fold.ts
|
|
22536
21976
|
var import_node_fs37 = require("node:fs");
|
|
22537
|
-
var
|
|
21977
|
+
var import_node_path27 = require("node:path");
|
|
22538
21978
|
var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
22539
21979
|
"user",
|
|
22540
21980
|
"assistant",
|
|
@@ -22766,9 +22206,9 @@ function fold(transcriptPath, opts = {}) {
|
|
|
22766
22206
|
return result;
|
|
22767
22207
|
}
|
|
22768
22208
|
try {
|
|
22769
|
-
const sidecarDir = (0,
|
|
22770
|
-
(0,
|
|
22771
|
-
(0,
|
|
22209
|
+
const sidecarDir = (0, import_node_path27.join)(
|
|
22210
|
+
(0, import_node_path27.dirname)(transcriptPath),
|
|
22211
|
+
(0, import_node_path27.basename)(transcriptPath).replace(/\.jsonl$/, ""),
|
|
22772
22212
|
"subagents"
|
|
22773
22213
|
);
|
|
22774
22214
|
if ((0, import_node_fs37.existsSync)(sidecarDir)) {
|
|
@@ -22778,7 +22218,7 @@ function fold(transcriptPath, opts = {}) {
|
|
|
22778
22218
|
const walk2 = (d, depth) => {
|
|
22779
22219
|
if (depth > 4) return;
|
|
22780
22220
|
for (const e of (0, import_node_fs37.readdirSync)(d, { withFileTypes: true })) {
|
|
22781
|
-
const p = (0,
|
|
22221
|
+
const p = (0, import_node_path27.join)(d, e.name);
|
|
22782
22222
|
if (e.isDirectory()) {
|
|
22783
22223
|
walk2(p, depth + 1);
|
|
22784
22224
|
} else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
|
|
@@ -23102,14 +22542,12 @@ async function evidence(run2) {
|
|
|
23102
22542
|
authorshipWasObservable
|
|
23103
22543
|
});
|
|
23104
22544
|
const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
|
|
23105
|
-
if (!opts.skipStatic
|
|
22545
|
+
if (!opts.skipStatic) {
|
|
23106
22546
|
let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
23107
22547
|
if (baseline) {
|
|
23108
22548
|
allScannable = allScannable.filter((f) => changedSinceBaseline(f, baseline));
|
|
23109
22549
|
}
|
|
23110
|
-
|
|
23111
|
-
staticResults = runCodacyAnalysis(allScannable);
|
|
23112
|
-
}
|
|
22550
|
+
staticResults = runCodacyAnalysisIfAvailable(allScannable);
|
|
23113
22551
|
}
|
|
23114
22552
|
const deltaSet = baseline ? recentForReview.filter((f) => changedSinceBaseline(f, baseline)) : recentForReview;
|
|
23115
22553
|
codeDelta = collectCodeDelta(deltaSet, {
|
|
@@ -23153,7 +22591,7 @@ async function evidence(run2) {
|
|
|
23153
22591
|
|
|
23154
22592
|
// src/lib/cache-cleanup.ts
|
|
23155
22593
|
var import_node_fs38 = require("node:fs");
|
|
23156
|
-
var
|
|
22594
|
+
var import_node_path28 = require("node:path");
|
|
23157
22595
|
var CACHE_TTL_DAYS = 7;
|
|
23158
22596
|
function pruneStaleCache() {
|
|
23159
22597
|
try {
|
|
@@ -23161,7 +22599,7 @@ function pruneStaleCache() {
|
|
|
23161
22599
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
23162
22600
|
for (const entry of (0, import_node_fs38.readdirSync)(dir)) {
|
|
23163
22601
|
if (!entry.startsWith("pending-")) continue;
|
|
23164
|
-
const path = (0,
|
|
22602
|
+
const path = (0, import_node_path28.join)(dir, entry);
|
|
23165
22603
|
try {
|
|
23166
22604
|
const stat3 = (0, import_node_fs38.statSync)(path);
|
|
23167
22605
|
if (stat3.mtimeMs < cutoff) {
|
|
@@ -23327,9 +22765,9 @@ async function repoContext(run2) {
|
|
|
23327
22765
|
}
|
|
23328
22766
|
|
|
23329
22767
|
// src/lib/seed-runner.ts
|
|
23330
|
-
var
|
|
22768
|
+
var import_promises14 = require("node:fs/promises");
|
|
23331
22769
|
var import_node_fs40 = require("node:fs");
|
|
23332
|
-
var
|
|
22770
|
+
var import_node_path29 = require("node:path");
|
|
23333
22771
|
var import_yaml4 = __toESM(require_dist());
|
|
23334
22772
|
|
|
23335
22773
|
// src/lib/seed.ts
|
|
@@ -23405,7 +22843,7 @@ function classifyClaudeSection(heading) {
|
|
|
23405
22843
|
if (hasAny(["integration", "webhook", "third-party", "third party", "provider"])) return "integration";
|
|
23406
22844
|
return "domain";
|
|
23407
22845
|
}
|
|
23408
|
-
function
|
|
22846
|
+
function slugify(s) {
|
|
23409
22847
|
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
23410
22848
|
}
|
|
23411
22849
|
var FRAMEWORK_GLOBS = [
|
|
@@ -23502,7 +22940,7 @@ _Extracted from README on setup. Update when project direction changes._`,
|
|
|
23502
22940
|
for (const fwRaw of frameworks) {
|
|
23503
22941
|
const fw = fwRaw.trim();
|
|
23504
22942
|
if (!fw) continue;
|
|
23505
|
-
const slug =
|
|
22943
|
+
const slug = slugify(fw);
|
|
23506
22944
|
if (!slug || seenFrameworkSlugs.has(slug)) continue;
|
|
23507
22945
|
seenFrameworkSlugs.add(slug);
|
|
23508
22946
|
out.push({
|
|
@@ -23573,7 +23011,7 @@ async function runSeed(opts) {
|
|
|
23573
23011
|
}
|
|
23574
23012
|
let standardDoc;
|
|
23575
23013
|
try {
|
|
23576
|
-
const raw = await (0,
|
|
23014
|
+
const raw = await (0, import_promises14.readFile)(STANDARD_FILE, "utf-8");
|
|
23577
23015
|
standardDoc = (0, import_yaml4.parse)(raw);
|
|
23578
23016
|
} catch {
|
|
23579
23017
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
@@ -23582,7 +23020,7 @@ async function runSeed(opts) {
|
|
|
23582
23020
|
let readmeContent;
|
|
23583
23021
|
if ((0, import_node_fs40.existsSync)("README.md")) {
|
|
23584
23022
|
try {
|
|
23585
|
-
readmeContent = await (0,
|
|
23023
|
+
readmeContent = await (0, import_promises14.readFile)("README.md", "utf-8");
|
|
23586
23024
|
} catch {
|
|
23587
23025
|
}
|
|
23588
23026
|
}
|
|
@@ -23590,7 +23028,7 @@ async function runSeed(opts) {
|
|
|
23590
23028
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
23591
23029
|
if ((0, import_node_fs40.existsSync)(p)) {
|
|
23592
23030
|
try {
|
|
23593
|
-
claudeMdContent = await (0,
|
|
23031
|
+
claudeMdContent = await (0, import_promises14.readFile)(p, "utf-8");
|
|
23594
23032
|
break;
|
|
23595
23033
|
} catch {
|
|
23596
23034
|
}
|
|
@@ -23611,7 +23049,7 @@ async function runSeed(opts) {
|
|
|
23611
23049
|
if (candidates2.length === 0) {
|
|
23612
23050
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
23613
23051
|
}
|
|
23614
|
-
const overviewPath = (0,
|
|
23052
|
+
const overviewPath = (0, import_node_path29.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
23615
23053
|
if ((0, import_node_fs40.existsSync)(overviewPath) && !opts.force) {
|
|
23616
23054
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates: candidates2 };
|
|
23617
23055
|
}
|
|
@@ -23654,8 +23092,8 @@ async function runSeed(opts) {
|
|
|
23654
23092
|
continue;
|
|
23655
23093
|
}
|
|
23656
23094
|
try {
|
|
23657
|
-
await (0,
|
|
23658
|
-
await (0,
|
|
23095
|
+
await (0, import_promises14.mkdir)((0, import_node_path29.dirname)(targetPath), { recursive: true });
|
|
23096
|
+
await (0, import_promises14.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
23659
23097
|
created++;
|
|
23660
23098
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
23661
23099
|
} catch (err) {
|
|
@@ -23668,7 +23106,7 @@ async function runSeed(opts) {
|
|
|
23668
23106
|
|
|
23669
23107
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
23670
23108
|
var import_node_fs41 = require("node:fs");
|
|
23671
|
-
var
|
|
23109
|
+
var import_node_path30 = require("node:path");
|
|
23672
23110
|
async function memoryManifest(run2) {
|
|
23673
23111
|
const { globals } = run2;
|
|
23674
23112
|
const { serviceUrl, token } = run2;
|
|
@@ -23678,7 +23116,7 @@ async function memoryManifest(run2) {
|
|
|
23678
23116
|
let autoSeedNotice = null;
|
|
23679
23117
|
try {
|
|
23680
23118
|
await ensureMemoryDir();
|
|
23681
|
-
const seedMarker = (0,
|
|
23119
|
+
const seedMarker = (0, import_node_path30.join)(VERITY_DIR, ".seeded");
|
|
23682
23120
|
const hasStandard = (0, import_node_fs41.existsSync)(STANDARD_FILE);
|
|
23683
23121
|
const alreadyTried = (0, import_node_fs41.existsSync)(seedMarker);
|
|
23684
23122
|
if (hasStandard && !alreadyTried) {
|
|
@@ -23796,7 +23234,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
|
|
|
23796
23234
|
}
|
|
23797
23235
|
|
|
23798
23236
|
// src/commands/analyze/phases/10-working-memory.ts
|
|
23799
|
-
var
|
|
23237
|
+
var import_node_path31 = require("node:path");
|
|
23800
23238
|
async function workingMemory(run2) {
|
|
23801
23239
|
const { opts } = run2;
|
|
23802
23240
|
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run2;
|
|
@@ -23808,7 +23246,7 @@ async function workingMemory(run2) {
|
|
|
23808
23246
|
const priorState = foldForMarks(memorySession.d);
|
|
23809
23247
|
incrementReport = computeIncrement(
|
|
23810
23248
|
allForReview,
|
|
23811
|
-
(p) => fileHash((0,
|
|
23249
|
+
(p) => fileHash((0, import_node_path31.join)(repoRoot(), p)),
|
|
23812
23250
|
priorState.authored_all.map((a) => ({
|
|
23813
23251
|
path: a.path,
|
|
23814
23252
|
hash_at_last_verdict: a.hash_at_last_verdict
|
|
@@ -23945,6 +23383,15 @@ function parseAutonomousEnv(raw) {
|
|
|
23945
23383
|
if (v === "0" || v === "false" || v === "off" || v === "no") return false;
|
|
23946
23384
|
return true;
|
|
23947
23385
|
}
|
|
23386
|
+
function resolveRunMode(inputs = {}) {
|
|
23387
|
+
if (inputs.autonomousFlag === true) return "autonomous";
|
|
23388
|
+
if (inputs.autonomousFlag === false) return "interactive";
|
|
23389
|
+
const env = inputs.env ?? process.env;
|
|
23390
|
+
const envDecision = parseAutonomousEnv(env.VERITY_AUTONOMOUS);
|
|
23391
|
+
if (envDecision !== void 0) return envDecision ? "autonomous" : "interactive";
|
|
23392
|
+
const isTTY = inputs.isTTY ?? Boolean(process.stdin?.isTTY);
|
|
23393
|
+
return isTTY ? "interactive" : "autonomous";
|
|
23394
|
+
}
|
|
23948
23395
|
function isExplicitlyAutonomous(env = process.env) {
|
|
23949
23396
|
return parseAutonomousEnv(env.VERITY_AUTONOMOUS) === true || parseAutonomousEnv(env.CI) === true || parseAutonomousEnv(env.GITHUB_ACTIONS) === true;
|
|
23950
23397
|
}
|
|
@@ -24364,7 +23811,7 @@ async function transmit(run2) {
|
|
|
24364
23811
|
|
|
24365
23812
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
24366
23813
|
var import_node_fs44 = require("node:fs");
|
|
24367
|
-
var
|
|
23814
|
+
var import_node_path32 = require("node:path");
|
|
24368
23815
|
async function reconcile(run2) {
|
|
24369
23816
|
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
|
|
24370
23817
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
@@ -24393,7 +23840,7 @@ async function reconcile(run2) {
|
|
|
24393
23840
|
const st = foldDossier(memorySession.d);
|
|
24394
23841
|
openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
|
|
24395
23842
|
try {
|
|
24396
|
-
const src = (0, import_node_fs44.readFileSync)((0,
|
|
23843
|
+
const src = (0, import_node_fs44.readFileSync)((0, import_node_path32.join)(repoRoot(), file), "utf8").split("\n");
|
|
24397
23844
|
const at = src[line - 1];
|
|
24398
23845
|
return at === void 0 ? null : lineSha(at);
|
|
24399
23846
|
} catch {
|
|
@@ -24656,146 +24103,6 @@ function describeRelease(release, input) {
|
|
|
24656
24103
|
}
|
|
24657
24104
|
}
|
|
24658
24105
|
|
|
24659
|
-
// src/lib/memory-pull.ts
|
|
24660
|
-
var import_node_child_process14 = require("node:child_process");
|
|
24661
|
-
var import_promises16 = require("node:fs/promises");
|
|
24662
|
-
var pullStateFile = () => projectPath(`${VERITY_DIR}/.memory-pull-state.json`);
|
|
24663
|
-
var PAGE_SIZE = 200;
|
|
24664
|
-
var MAX_PAGES = 500;
|
|
24665
|
-
var memoryIsTracked = () => committedMemoryFiles().length > 0;
|
|
24666
|
-
async function pullRepoMemory(opts) {
|
|
24667
|
-
if (memoryIsTracked()) return { ok: true, status: "tracked", received: 0, written: 0 };
|
|
24668
|
-
const state = await readPullState();
|
|
24669
|
-
const known = opts.force ? null : state.version;
|
|
24670
|
-
let treePaths;
|
|
24671
|
-
try {
|
|
24672
|
-
treePaths = listTrackedFiles();
|
|
24673
|
-
} catch {
|
|
24674
|
-
treePaths = void 0;
|
|
24675
|
-
}
|
|
24676
|
-
const served = /* @__PURE__ */ new Map();
|
|
24677
|
-
let received = 0;
|
|
24678
|
-
let written = 0;
|
|
24679
|
-
let version = null;
|
|
24680
|
-
let cursor = null;
|
|
24681
|
-
let pages = 0;
|
|
24682
|
-
const remember = (complete) => writePullState({
|
|
24683
|
-
version: complete ? version : null,
|
|
24684
|
-
// A complete pull saw every file the server holds, so its ledger replaces the
|
|
24685
|
-
// old one (a path that stopped arriving was archived). A partial one only adds.
|
|
24686
|
-
served: complete ? served : new Map([...state.served, ...served])
|
|
24687
|
-
});
|
|
24688
|
-
do {
|
|
24689
|
-
const params = new URLSearchParams({ limit: String(PAGE_SIZE) });
|
|
24690
|
-
if (cursor) params.set("cursor", cursor);
|
|
24691
|
-
else if (known) params.set("if_version", known);
|
|
24692
|
-
const res = await apiRequest({
|
|
24693
|
-
method: "GET",
|
|
24694
|
-
path: `/memory/files?${params.toString()}`,
|
|
24695
|
-
serviceUrl: opts.serviceUrl,
|
|
24696
|
-
token: opts.token,
|
|
24697
|
-
verbose: opts.verbose,
|
|
24698
|
-
timeout: opts.timeoutMs ?? 3e4,
|
|
24699
|
-
cmd: "memory-pull"
|
|
24700
|
-
});
|
|
24701
|
-
if (!res.ok) {
|
|
24702
|
-
if (pages > 0) await remember(false);
|
|
24703
|
-
return { ok: false, error: res.error, category: res.category };
|
|
24704
|
-
}
|
|
24705
|
-
const page = res.data;
|
|
24706
|
-
if (page.unchanged) return { ok: true, status: "unchanged", received: 0, written: 0 };
|
|
24707
|
-
if (pages === 0) version = page.version;
|
|
24708
|
-
else if (page.version !== version) version = null;
|
|
24709
|
-
const files = (page.files ?? []).map((f) => ({
|
|
24710
|
-
path: f.path,
|
|
24711
|
-
content: f.content,
|
|
24712
|
-
node_id: f.node_id,
|
|
24713
|
-
op: "create"
|
|
24714
|
-
}));
|
|
24715
|
-
received += files.length;
|
|
24716
|
-
try {
|
|
24717
|
-
written += await applyMemoryWrites(files, {
|
|
24718
|
-
treePaths,
|
|
24719
|
-
baseline: "written",
|
|
24720
|
-
lastServed: state.served,
|
|
24721
|
-
served,
|
|
24722
|
-
claudeMdPointer: false
|
|
24723
|
-
});
|
|
24724
|
-
} catch (err) {
|
|
24725
|
-
await remember(false);
|
|
24726
|
-
throw err;
|
|
24727
|
-
}
|
|
24728
|
-
cursor = page.next_cursor;
|
|
24729
|
-
pages++;
|
|
24730
|
-
} while (cursor && pages < MAX_PAGES);
|
|
24731
|
-
await remember(!cursor);
|
|
24732
|
-
return { ok: true, status: "pulled", received, written };
|
|
24733
|
-
}
|
|
24734
|
-
async function readServedLedger() {
|
|
24735
|
-
return (await readPullState()).served;
|
|
24736
|
-
}
|
|
24737
|
-
async function recordServedFiles(served) {
|
|
24738
|
-
if (served.size === 0) return;
|
|
24739
|
-
const state = await readPullState();
|
|
24740
|
-
for (const [path, hash] of served) state.served.set(path, hash);
|
|
24741
|
-
await writePullState(state);
|
|
24742
|
-
}
|
|
24743
|
-
async function readPullState() {
|
|
24744
|
-
try {
|
|
24745
|
-
const parsed = JSON.parse(await (0, import_promises16.readFile)(pullStateFile(), "utf-8"));
|
|
24746
|
-
const served = /* @__PURE__ */ new Map();
|
|
24747
|
-
if (parsed?.served && typeof parsed.served === "object") {
|
|
24748
|
-
for (const [path, hash] of Object.entries(parsed.served)) {
|
|
24749
|
-
if (typeof hash === "string") served.set(path, hash);
|
|
24750
|
-
}
|
|
24751
|
-
}
|
|
24752
|
-
return { version: typeof parsed?.version === "string" ? parsed.version : null, served };
|
|
24753
|
-
} catch {
|
|
24754
|
-
return { version: null, served: /* @__PURE__ */ new Map() };
|
|
24755
|
-
}
|
|
24756
|
-
}
|
|
24757
|
-
async function writePullState(state) {
|
|
24758
|
-
try {
|
|
24759
|
-
await (0, import_promises16.mkdir)(projectPath(VERITY_DIR), { recursive: true });
|
|
24760
|
-
await (0, import_promises16.writeFile)(pullStateFile(), JSON.stringify({
|
|
24761
|
-
...state.version ? { version: state.version } : {},
|
|
24762
|
-
pulled_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24763
|
-
served: Object.fromEntries(state.served)
|
|
24764
|
-
}) + "\n");
|
|
24765
|
-
} catch {
|
|
24766
|
-
}
|
|
24767
|
-
}
|
|
24768
|
-
function pullsAtSessionStart(auth2, source) {
|
|
24769
|
-
if (source === "compact") return false;
|
|
24770
|
-
if (auth2.source === "env" || auth2.source === "flag") return true;
|
|
24771
|
-
return auth2.userId != null;
|
|
24772
|
-
}
|
|
24773
|
-
function spawnBackgroundPull(deps = {}) {
|
|
24774
|
-
const d = {
|
|
24775
|
-
spawn: import_node_child_process14.spawn,
|
|
24776
|
-
execPath: process.execPath,
|
|
24777
|
-
script: process.argv[1],
|
|
24778
|
-
cwd: process.cwd(),
|
|
24779
|
-
env: process.env,
|
|
24780
|
-
...deps
|
|
24781
|
-
};
|
|
24782
|
-
if (d.env.VERITY_NO_BACKGROUND_PULL === "1" || !d.script) return false;
|
|
24783
|
-
try {
|
|
24784
|
-
const child = d.spawn(d.execPath, [d.script, "memory", "pull", "--quiet"], {
|
|
24785
|
-
cwd: d.cwd,
|
|
24786
|
-
env: d.env,
|
|
24787
|
-
detached: true,
|
|
24788
|
-
stdio: "ignore"
|
|
24789
|
-
});
|
|
24790
|
-
child.on("error", () => {
|
|
24791
|
-
});
|
|
24792
|
-
child.unref();
|
|
24793
|
-
return true;
|
|
24794
|
-
} catch {
|
|
24795
|
-
return false;
|
|
24796
|
-
}
|
|
24797
|
-
}
|
|
24798
|
-
|
|
24799
24106
|
// src/lib/remediation-guard.ts
|
|
24800
24107
|
var TOOL_CONFIG_PATTERNS = [
|
|
24801
24108
|
/(^|\/)\.codacy\//,
|
|
@@ -24894,9 +24201,7 @@ async function render(run2) {
|
|
|
24894
24201
|
if (memoryWritesRaw && memoryWritesRaw.length > 0) {
|
|
24895
24202
|
try {
|
|
24896
24203
|
const treePaths = listTrackedFiles();
|
|
24897
|
-
|
|
24898
|
-
await applyMemoryWrites(memoryWritesRaw, { treePaths, lastServed: await readServedLedger(), served });
|
|
24899
|
-
await recordServedFiles(served);
|
|
24204
|
+
await applyMemoryWrites(memoryWritesRaw, { treePaths });
|
|
24900
24205
|
} catch (err) {
|
|
24901
24206
|
process.stderr.write(`verity: memory sync write failed: ${err.message}
|
|
24902
24207
|
`);
|
|
@@ -25225,7 +24530,7 @@ var import_node_fs47 = require("node:fs");
|
|
|
25225
24530
|
|
|
25226
24531
|
// src/lib/project-skills.ts
|
|
25227
24532
|
var import_node_fs46 = require("node:fs");
|
|
25228
|
-
var
|
|
24533
|
+
var import_node_path33 = require("node:path");
|
|
25229
24534
|
var PROJECT_SKILL_NAMES = [
|
|
25230
24535
|
"verity-setup",
|
|
25231
24536
|
"verity-analyze",
|
|
@@ -25251,13 +24556,13 @@ var ALL = [...PROJECT_SKILL_NAMES, ...LEGACY_SKILL_NAMES];
|
|
|
25251
24556
|
function staleProjectSkills() {
|
|
25252
24557
|
const root = projectPath(".claude/skills");
|
|
25253
24558
|
if (!(0, import_node_fs46.existsSync)(root)) return [];
|
|
25254
|
-
return ALL.filter((name) => (0, import_node_fs46.existsSync)((0,
|
|
24559
|
+
return ALL.filter((name) => (0, import_node_fs46.existsSync)((0, import_node_path33.join)(root, name)));
|
|
25255
24560
|
}
|
|
25256
24561
|
function removeProjectSkills() {
|
|
25257
24562
|
const root = projectPath(".claude/skills");
|
|
25258
24563
|
const removed = [];
|
|
25259
24564
|
for (const name of staleProjectSkills()) {
|
|
25260
|
-
(0, import_node_fs46.rmSync)((0,
|
|
24565
|
+
(0, import_node_fs46.rmSync)((0, import_node_path33.join)(root, name), { recursive: true, force: true });
|
|
25261
24566
|
removed.push(name);
|
|
25262
24567
|
}
|
|
25263
24568
|
return removed;
|
|
@@ -25383,7 +24688,6 @@ function registerBaselineCommands(program2) {
|
|
|
25383
24688
|
}
|
|
25384
24689
|
const authForScope = await resolveToken(program2.opts().token);
|
|
25385
24690
|
const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
|
|
25386
|
-
if (authForScope.ok && pullsAtSessionStart(authForScope.data, source)) spawnBackgroundPull();
|
|
25387
24691
|
const scopeSession = session;
|
|
25388
24692
|
const result = captureBaseline({ sessionId: sessionScopeKey(scopeToken, scopeSession), source });
|
|
25389
24693
|
logEvent("baseline_capture", {
|
|
@@ -25446,45 +24750,22 @@ async function runReview(opts, globals) {
|
|
|
25446
24750
|
const changedFiles = opts.changed ? opts.changed.split(",").map((f) => f.trim()).filter(Boolean) : allFiles;
|
|
25447
24751
|
const analyzable = filterAnalyzable(allFiles);
|
|
25448
24752
|
const securityFiles = filterSecurity(allFiles);
|
|
25449
|
-
|
|
25450
|
-
|
|
25451
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs48.existsSync)(f) || resolveFile(f) !== null);
|
|
25452
|
-
staticResults = runCodacyAnalysis(scannable);
|
|
25453
|
-
} else {
|
|
25454
|
-
staticResults = {
|
|
25455
|
-
tool: "@codacy/analysis-cli",
|
|
25456
|
-
findings: [],
|
|
25457
|
-
summary: { total_findings: 0, by_severity: {}, tools_run: [] }
|
|
25458
|
-
};
|
|
25459
|
-
}
|
|
24753
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs48.existsSync)(f) || resolveFile(f) !== null);
|
|
24754
|
+
const staticResults = runCodacyAnalysisIfAvailable(scannable);
|
|
25460
24755
|
const codeDelta = collectCodeDelta(allFiles);
|
|
25461
24756
|
const tokenResult = await resolveToken(globals.token);
|
|
25462
24757
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
25463
24758
|
if (!tokenResult.ok || !urlResult.ok) {
|
|
24759
|
+
const staticFailure = staticResults.summary.failure;
|
|
25464
24760
|
printJsonCompact({
|
|
25465
|
-
gate_decision: "
|
|
25466
|
-
systemMessage: "Verity: not authenticated \u2014 showing local static results only (no deep review, no upload). Run `verity init` to authenticate and unlock the full review.",
|
|
24761
|
+
gate_decision: "WARN",
|
|
24762
|
+
systemMessage: staticFailure ? `Verity: not authenticated, and local static analysis did not run (${staticFailure.kind}) \u2014 nothing was examined. Run \`verity init\` to authenticate and unlock the full review.` : "Verity: not authenticated \u2014 showing local static results only (no deep review, no upload). Run `verity init` to authenticate and unlock the full review.",
|
|
25467
24763
|
unauthenticated: true,
|
|
25468
24764
|
static_results: staticResults
|
|
25469
24765
|
});
|
|
25470
24766
|
process.exit(0);
|
|
25471
24767
|
}
|
|
25472
|
-
|
|
25473
|
-
if (opts.specs) {
|
|
25474
|
-
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
25475
|
-
specs = [];
|
|
25476
|
-
for (const p of specPaths) {
|
|
25477
|
-
if (!(0, import_node_fs48.existsSync)(p)) continue;
|
|
25478
|
-
try {
|
|
25479
|
-
const { readFileSync: readFileSync26 } = await import("node:fs");
|
|
25480
|
-
const content = readFileSync26(p, "utf-8");
|
|
25481
|
-
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
25482
|
-
} catch {
|
|
25483
|
-
}
|
|
25484
|
-
}
|
|
25485
|
-
} else {
|
|
25486
|
-
specs = discoverSpecs();
|
|
25487
|
-
}
|
|
24768
|
+
const specs = opts.specs ? readSpecFiles(opts.specs, repoRoot()) : discoverSpecs();
|
|
25488
24769
|
const plans = discoverPlans();
|
|
25489
24770
|
const requestBody = {
|
|
25490
24771
|
static_results: staticResults,
|
|
@@ -25517,9 +24798,10 @@ async function runReview(opts, globals) {
|
|
|
25517
24798
|
});
|
|
25518
24799
|
if (!result.ok) {
|
|
25519
24800
|
printError(`Service error: ${result.error}`);
|
|
24801
|
+
const staticFailure = staticResults.summary.failure;
|
|
25520
24802
|
printJsonCompact({
|
|
25521
|
-
gate_decision: "
|
|
25522
|
-
systemMessage: "Verity:
|
|
24803
|
+
gate_decision: "WARN",
|
|
24804
|
+
systemMessage: staticFailure ? `Verity: service unavailable, and local static analysis did not run (${staticFailure.kind}) \u2014 nothing was examined` : "Verity: service unavailable \u2014 showing local static results only",
|
|
25523
24805
|
offline: true,
|
|
25524
24806
|
static_results: staticResults
|
|
25525
24807
|
});
|
|
@@ -25535,387 +24817,14 @@ async function runReview(opts, globals) {
|
|
|
25535
24817
|
|
|
25536
24818
|
// src/commands/guard.ts
|
|
25537
24819
|
var import_node_fs49 = require("node:fs");
|
|
25538
|
-
var
|
|
25539
|
-
|
|
25540
|
-
// src/lib/terminal-text.ts
|
|
25541
|
-
var CONTROL = /[\x00-\x08\x0b-\x1f\x7f-\x9f\u061c\u200e\u200f\u2028-\u202e\u2066-\u2069]/g;
|
|
25542
|
-
function terminalText(value, max = 4e3) {
|
|
25543
|
-
if (typeof value !== "string") return "";
|
|
25544
|
-
const clean2 = value.replace(CONTROL, "");
|
|
25545
|
-
return clean2.length > max ? `${clean2.slice(0, max - 1)}\u2026` : clean2;
|
|
25546
|
-
}
|
|
25547
|
-
function terminalUrl(value) {
|
|
25548
|
-
const clean2 = terminalText(value, 2e3);
|
|
25549
|
-
return /^https?:\/\/\S+$/.test(clean2) ? clean2 : "";
|
|
25550
|
-
}
|
|
25551
|
-
|
|
25552
|
-
// src/lib/agent-driver.ts
|
|
25553
|
-
var AGENT_DEADLINE_MS = {
|
|
25554
|
-
"pre-commit": 9e4,
|
|
25555
|
-
"pre-push": 18e4
|
|
25556
|
-
};
|
|
25557
|
-
var MIN_AGENT_START_MS = 25e3;
|
|
25558
|
-
var MIN_TURN_MS = 12e3;
|
|
25559
|
-
var FINISH_TIMEOUT_MS = 15e3;
|
|
25560
|
-
var CANCEL_TIMEOUT_MS = 3e3;
|
|
25561
|
-
var MAX_TURNS = 16;
|
|
25562
|
-
var ONE_SHOT_FINDING_FIELDS = ["severity", "category", "file", "line", "title", "description", "scope", "pattern_id"];
|
|
25563
|
-
function summarizeFindings(response) {
|
|
25564
|
-
const findings = Array.isArray(response.findings) ? response.findings : [];
|
|
25565
|
-
return findings.slice(0, 40).map((f) => {
|
|
25566
|
-
const out = {};
|
|
25567
|
-
const finding = f ?? {};
|
|
25568
|
-
for (const key of ONE_SHOT_FINDING_FIELDS) if (finding[key] !== void 0) out[key] = finding[key];
|
|
25569
|
-
return out;
|
|
25570
|
-
});
|
|
25571
|
-
}
|
|
25572
|
-
function finishOneShot(settled) {
|
|
25573
|
-
if (!settled) return { status: "pending" };
|
|
25574
|
-
if (!settled.ok) return { status: "failed" };
|
|
25575
|
-
const r = settled.response;
|
|
25576
|
-
return {
|
|
25577
|
-
status: "arrived",
|
|
25578
|
-
...typeof r.agent_attestation === "string" ? { attestation: r.agent_attestation } : {},
|
|
25579
|
-
...typeof r.run_id === "string" ? { run_id: r.run_id } : {},
|
|
25580
|
-
...typeof r.gate_decision === "string" ? { gate_decision: r.gate_decision } : {}
|
|
25581
|
-
};
|
|
25582
|
-
}
|
|
25583
|
-
async function runAgentReview(options) {
|
|
25584
|
-
const now = options.now ?? Date.now;
|
|
25585
|
-
const remaining = () => options.deadlineMs - (now() - options.startedAt);
|
|
25586
|
-
let settled = null;
|
|
25587
|
-
const oneShotDone = options.oneShot.then(
|
|
25588
|
-
(outcome) => {
|
|
25589
|
-
settled = outcome;
|
|
25590
|
-
},
|
|
25591
|
-
() => {
|
|
25592
|
-
settled = { ok: false };
|
|
25593
|
-
}
|
|
25594
|
-
);
|
|
25595
|
-
if (remaining() < MIN_AGENT_START_MS) return { kind: "skipped", reason: "not enough time left after prep" };
|
|
25596
|
-
const started = await options.http.post(
|
|
25597
|
-
"/agent-review/start",
|
|
25598
|
-
{ ...options.start, remaining_ms: remaining() },
|
|
25599
|
-
Math.max(1, remaining())
|
|
25600
|
-
);
|
|
25601
|
-
if (!started.ok) return { kind: "failed", reason: `start: ${started.error}` };
|
|
25602
|
-
if (!started.data.enabled) return { kind: "disabled", reason: started.data.reason };
|
|
25603
|
-
let state = started.data.state;
|
|
25604
|
-
let calls = started.data.calls ?? [];
|
|
25605
|
-
let done = started.data.done;
|
|
25606
|
-
let sentOneShot = "pending";
|
|
25607
|
-
const cancel = async () => {
|
|
25608
|
-
await options.http.post("/agent-review/cancel", { state }, CANCEL_TIMEOUT_MS);
|
|
25609
|
-
};
|
|
25610
|
-
for (let turn = 0; !done; turn++) {
|
|
25611
|
-
if (turn >= MAX_TURNS || remaining() < MIN_TURN_MS) break;
|
|
25612
|
-
const results = await options.executeTools(calls);
|
|
25613
|
-
let oneShot;
|
|
25614
|
-
const current = settled;
|
|
25615
|
-
if (current && sentOneShot === "pending") {
|
|
25616
|
-
oneShot = current.ok ? { status: "arrived", findings: summarizeFindings(current.response) } : { status: "failed" };
|
|
25617
|
-
sentOneShot = oneShot.status;
|
|
25618
|
-
}
|
|
25619
|
-
const turnRequest = { state, remaining_ms: remaining(), results, ...oneShot ? { one_shot: oneShot } : {} };
|
|
25620
|
-
const next = await options.http.post("/agent-review/turn", turnRequest, Math.max(1, remaining()));
|
|
25621
|
-
if (!next.ok) {
|
|
25622
|
-
await cancel();
|
|
25623
|
-
return { kind: "failed", reason: `turn: ${next.error}` };
|
|
25624
|
-
}
|
|
25625
|
-
state = next.data.state;
|
|
25626
|
-
calls = next.data.calls ?? [];
|
|
25627
|
-
done = next.data.done;
|
|
25628
|
-
}
|
|
25629
|
-
if (!settled) {
|
|
25630
|
-
const wait = Math.max(0, remaining() - FINISH_TIMEOUT_MS);
|
|
25631
|
-
await Promise.race([oneShotDone, new Promise((resolve6) => setTimeout(resolve6, wait))]);
|
|
25632
|
-
}
|
|
25633
|
-
const finished = await options.http.post(
|
|
25634
|
-
"/agent-review/finish",
|
|
25635
|
-
{ state, session_id: options.sessionId, one_shot: finishOneShot(settled) },
|
|
25636
|
-
FINISH_TIMEOUT_MS
|
|
25637
|
-
);
|
|
25638
|
-
if (!finished.ok) {
|
|
25639
|
-
await cancel();
|
|
25640
|
-
return { kind: "failed", reason: `finish: ${finished.error}` };
|
|
25641
|
-
}
|
|
25642
|
-
return { kind: "finished", response: finished.data };
|
|
25643
|
-
}
|
|
25644
|
-
|
|
25645
|
-
// src/lib/agent-tools.ts
|
|
25646
|
-
var TOOL_LIMITS = {
|
|
25647
|
-
outputChars: 16e3,
|
|
25648
|
-
readLines: 400,
|
|
25649
|
-
blameLines: 200,
|
|
25650
|
-
searchHits: 50,
|
|
25651
|
-
searchLineChars: 300,
|
|
25652
|
-
listEntries: 200,
|
|
25653
|
-
logCommits: 8,
|
|
25654
|
-
textChars: 200,
|
|
25655
|
-
pathChars: 400,
|
|
25656
|
-
fileBytes: 4 * 1024 * 1024,
|
|
25657
|
-
gitTimeoutMs: 2e3,
|
|
25658
|
-
diffTimeoutMs: 1e4,
|
|
25659
|
-
concurrency: 4
|
|
25660
|
-
};
|
|
25661
|
-
var DENIED_PATHS = [
|
|
25662
|
-
/(^|\/)\.env(\.[^/]*)?$/i,
|
|
25663
|
-
/\.(pem|key|p12|pfx|jks|keystore|asc|gpg|kdbx)$/i,
|
|
25664
|
-
/(^|\/)id_(rsa|dsa|ecdsa|ed25519)(\.pub)?$/i,
|
|
25665
|
-
/(^|\/)\.(npmrc|netrc|pypirc|pgpass|git-credentials)$/i,
|
|
25666
|
-
/(^|\/)(credentials|secrets?)(\.[^/]*)?$/i,
|
|
25667
|
-
/(^|\/)\.(ssh|aws|gnupg|docker)(\/|$)/i,
|
|
25668
|
-
/(^|\/)\.git(\/|$)/
|
|
25669
|
-
];
|
|
25670
|
-
function isDeniedPath(path) {
|
|
25671
|
-
return DENIED_PATHS.some((pattern) => pattern.test(path)) || isVerityOwnedPath(path);
|
|
25672
|
-
}
|
|
25673
|
-
function validateRepoPath(value, { allowRoot = false } = {}) {
|
|
25674
|
-
if (typeof value !== "string") return { ok: false, error: "path must be a string" };
|
|
25675
|
-
let path = value.replace(/\\/g, "/").replace(/^\.\/+/, "");
|
|
25676
|
-
if (allowRoot && (path === "" || path === ".")) return { ok: true, value: "" };
|
|
25677
|
-
path = path.replace(/\/+$/, "");
|
|
25678
|
-
if (!path || path.length > TOOL_LIMITS.pathChars) return { ok: false, error: "path is empty or too long" };
|
|
25679
|
-
if (/[-]/.test(path)) return { ok: false, error: "path contains control characters" };
|
|
25680
|
-
if (path.startsWith("/") || /^[A-Za-z]:/.test(path)) return { ok: false, error: "path must be relative to the repository root" };
|
|
25681
|
-
if (path.split("/").some((segment) => segment === "..")) return { ok: false, error: "path must not leave the repository" };
|
|
25682
|
-
if (path.startsWith("-") || path.startsWith(":")) return { ok: false, error: 'path must not start with "-" or ":"' };
|
|
25683
|
-
if (isDeniedPath(path)) return { ok: false, error: "reading this path is not allowed" };
|
|
25684
|
-
return { ok: true, value: path };
|
|
25685
|
-
}
|
|
25686
|
-
function lineNumber(value, fallback) {
|
|
25687
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : fallback;
|
|
25688
|
-
}
|
|
25689
|
-
function capOutput(text) {
|
|
25690
|
-
return text.length > TOOL_LIMITS.outputChars ? `${text.slice(0, TOOL_LIMITS.outputChars)}
|
|
25691
|
-
[truncated at ${TOOL_LIMITS.outputChars} characters]` : text;
|
|
25692
|
-
}
|
|
25693
|
-
function git(ctx, args, opts = {}) {
|
|
25694
|
-
return runGit(ctx.root, args, { timeoutMs: opts.timeoutMs ?? TOOL_LIMITS.gitTimeoutMs, maxBuffer: opts.maxBuffer ?? TOOL_LIMITS.fileBytes });
|
|
25695
|
-
}
|
|
25696
|
-
function gitFailure(res, what) {
|
|
25697
|
-
if (res.error === "timeout") return `${what} timed out`;
|
|
25698
|
-
if (res.error === "too_large") return `${what} is too large`;
|
|
25699
|
-
const detail = res.stderr.trim().split("\n")[0]?.slice(0, 160);
|
|
25700
|
-
return detail ? `${what} failed: ${detail}` : `${what} failed`;
|
|
25701
|
-
}
|
|
25702
|
-
var INDEX = Symbol("index");
|
|
25703
|
-
function changeRevision(range) {
|
|
25704
|
-
return range.kind === "push" ? range.head : INDEX;
|
|
25705
|
-
}
|
|
25706
|
-
function baseRevision(range) {
|
|
25707
|
-
return range.kind === "push" ? range.base : "HEAD";
|
|
25708
|
-
}
|
|
25709
|
-
function objectSpec(revision, path) {
|
|
25710
|
-
return revision === INDEX ? `:${path}` : `${revision}:${path}`;
|
|
25711
|
-
}
|
|
25712
|
-
async function readBlob(ctx, revision, path) {
|
|
25713
|
-
const res = await git(ctx, ["cat-file", "blob", objectSpec(revision, path)]);
|
|
25714
|
-
if (!res.ok) {
|
|
25715
|
-
if (res.error === "failed") return { ok: false, error: `${path} is not a file in ${revision === INDEX ? "the staged version" : "that version"}` };
|
|
25716
|
-
return { ok: false, error: gitFailure(res, `reading ${path}`) };
|
|
25717
|
-
}
|
|
25718
|
-
if (res.stdout.slice(0, 8e3).includes("\0")) return { ok: false, error: `${path} is a binary file` };
|
|
25719
|
-
return { ok: true, value: res.stdout };
|
|
25720
|
-
}
|
|
25721
|
-
function numberedRange(content, startLine, endLine, maxLines) {
|
|
25722
|
-
const lines = content.split("\n");
|
|
25723
|
-
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
25724
|
-
const total = lines.length;
|
|
25725
|
-
const from = Math.min(Math.max(1, startLine), Math.max(1, total));
|
|
25726
|
-
const to = Math.min(total, Math.max(from, endLine), from + maxLines - 1);
|
|
25727
|
-
const width = String(to).length;
|
|
25728
|
-
const body = lines.slice(from - 1, to).map((line, i) => `${String(from + i).padStart(width, " ")}| ${line}`).join("\n");
|
|
25729
|
-
return `lines ${total === 0 ? 0 : from}-${to} of ${total}
|
|
25730
|
-
${body}`;
|
|
25731
|
-
}
|
|
25732
|
-
async function resolveRevision(ctx, value) {
|
|
25733
|
-
if (value === "head") return { ok: true, value: changeRevision(ctx.range) };
|
|
25734
|
-
if (value === "base") {
|
|
25735
|
-
const base = baseRevision(ctx.range);
|
|
25736
|
-
return base ? { ok: true, value: base } : { ok: false, error: "this change has no base version" };
|
|
25737
|
-
}
|
|
25738
|
-
if (typeof value === "string" && /^[0-9a-f]{7,40}$/.test(value)) {
|
|
25739
|
-
const res = await git(ctx, ["rev-parse", "--verify", "--quiet", "--end-of-options", `${value}^{commit}`]);
|
|
25740
|
-
if (res.ok && res.stdout.trim()) return { ok: true, value: res.stdout.trim() };
|
|
25741
|
-
return { ok: false, error: `unknown commit ${value}` };
|
|
25742
|
-
}
|
|
25743
|
-
return { ok: false, error: 'revision must be "base", "head", or a commit hash from git_log' };
|
|
25744
|
-
}
|
|
25745
|
-
var readFileTool = async (ctx, args) => {
|
|
25746
|
-
const path = validateRepoPath(args.path);
|
|
25747
|
-
if (!path.ok) return path;
|
|
25748
|
-
const blob = await readBlob(ctx, changeRevision(ctx.range), path.value);
|
|
25749
|
-
if (!blob.ok) return blob;
|
|
25750
|
-
const start = lineNumber(args.start_line, 1);
|
|
25751
|
-
const end = lineNumber(args.end_line, start + TOOL_LIMITS.readLines - 1);
|
|
25752
|
-
return { ok: true, value: `${path.value} (${ctx.range.kind === "push" ? "pushed version" : "staged version"}), ${numberedRange(blob.value, start, end, TOOL_LIMITS.readLines)}` };
|
|
25753
|
-
};
|
|
25754
|
-
var gitShowTool = async (ctx, args) => {
|
|
25755
|
-
const path = validateRepoPath(args.path);
|
|
25756
|
-
if (!path.ok) return path;
|
|
25757
|
-
const revision = await resolveRevision(ctx, args.revision);
|
|
25758
|
-
if (!revision.ok) return revision;
|
|
25759
|
-
const blob = await readBlob(ctx, revision.value, path.value);
|
|
25760
|
-
if (!blob.ok) return blob;
|
|
25761
|
-
const start = lineNumber(args.start_line, 1);
|
|
25762
|
-
const end = lineNumber(args.end_line, start + TOOL_LIMITS.readLines - 1);
|
|
25763
|
-
return { ok: true, value: `${path.value} at ${String(args.revision)}, ${numberedRange(blob.value, start, end, TOOL_LIMITS.readLines)}` };
|
|
25764
|
-
};
|
|
25765
|
-
var gitDiffTool = async (ctx, args) => {
|
|
25766
|
-
const path = validateRepoPath(args.path);
|
|
25767
|
-
if (!path.ok) return path;
|
|
25768
|
-
const range = ctx.range;
|
|
25769
|
-
let diffArgs;
|
|
25770
|
-
if (range.kind === "push") {
|
|
25771
|
-
if (!range.base) return { ok: false, error: "this push has no base to diff against" };
|
|
25772
|
-
diffArgs = ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", range.base, range.head];
|
|
25773
|
-
} else {
|
|
25774
|
-
diffArgs = ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", "--cached"];
|
|
25775
|
-
}
|
|
25776
|
-
const res = await git(ctx, ["--literal-pathspecs", ...diffArgs, "--", path.value]);
|
|
25777
|
-
if (!res.ok) return { ok: false, error: gitFailure(res, "diff") };
|
|
25778
|
-
return { ok: true, value: res.stdout.trim() ? capOutput(res.stdout) : `no changes to ${path.value} in the change under review` };
|
|
25779
|
-
};
|
|
25780
|
-
var gitLogTool = async (ctx, args) => {
|
|
25781
|
-
const path = validateRepoPath(args.path);
|
|
25782
|
-
if (!path.ok) return path;
|
|
25783
|
-
const count = Math.min(TOOL_LIMITS.logCommits, lineNumber(args.max_count, TOOL_LIMITS.logCommits));
|
|
25784
|
-
const revision = ctx.range.kind === "push" ? ctx.range.head : "HEAD";
|
|
25785
|
-
const res = await git(ctx, [
|
|
25786
|
-
"--literal-pathspecs",
|
|
25787
|
-
"log",
|
|
25788
|
-
"--no-show-signature",
|
|
25789
|
-
"--no-color",
|
|
25790
|
-
"-n",
|
|
25791
|
-
String(count),
|
|
25792
|
-
"--date=short",
|
|
25793
|
-
"--format=%h %ad %s",
|
|
25794
|
-
revision,
|
|
25795
|
-
"--",
|
|
25796
|
-
path.value
|
|
25797
|
-
]);
|
|
25798
|
-
if (!res.ok) return { ok: false, error: gitFailure(res, "log") };
|
|
25799
|
-
return { ok: true, value: res.stdout.trim() ? capOutput(res.stdout.trim()) : `no commits touch ${path.value}` };
|
|
25800
|
-
};
|
|
25801
|
-
var gitBlameTool = async (ctx, args) => {
|
|
25802
|
-
const path = validateRepoPath(args.path);
|
|
25803
|
-
if (!path.ok) return path;
|
|
25804
|
-
const start = lineNumber(args.start_line, 1);
|
|
25805
|
-
const end = Math.min(lineNumber(args.end_line, start), start + TOOL_LIMITS.blameLines - 1);
|
|
25806
|
-
const revision = ctx.range.kind === "push" ? ctx.range.head : "HEAD";
|
|
25807
|
-
const res = await git(ctx, ["blame", "--no-textconv", "-s", "-L", `${start},${Math.max(start, end)}`, revision, "--", path.value]);
|
|
25808
|
-
if (!res.ok) return { ok: false, error: gitFailure(res, "blame") };
|
|
25809
|
-
return { ok: true, value: capOutput(res.stdout.trimEnd()) };
|
|
25810
|
-
};
|
|
25811
|
-
var listFilesTool = async (ctx, args) => {
|
|
25812
|
-
const dir = validateRepoPath(args.dir, { allowRoot: true });
|
|
25813
|
-
if (!dir.ok) return dir;
|
|
25814
|
-
const glob = typeof args.glob === "string" && args.glob.length > 0 && args.glob.length <= 200 ? args.glob : null;
|
|
25815
|
-
const scope2 = dir.value || ".";
|
|
25816
|
-
const res = ctx.range.kind === "push" ? await git(ctx, ["--literal-pathspecs", "ls-tree", "-r", "--name-only", "-z", ctx.range.head, "--", scope2]) : await git(ctx, ["--literal-pathspecs", "ls-files", "-z", "--cached", "--", scope2]);
|
|
25817
|
-
if (!res.ok) return { ok: false, error: gitFailure(res, "listing") };
|
|
25818
|
-
const paths = res.stdout.split("\0").filter((p) => p && !isDeniedPath(p) && (!glob || globMatch2(glob, p)));
|
|
25819
|
-
const shown = paths.slice(0, TOOL_LIMITS.listEntries);
|
|
25820
|
-
const more = paths.length - shown.length;
|
|
25821
|
-
return { ok: true, value: shown.length ? `${shown.join("\n")}${more > 0 ? `
|
|
25822
|
-
\u2026 ${more} more` : ""}` : "no matching files" };
|
|
25823
|
-
};
|
|
25824
|
-
var searchTool = async (ctx, args) => {
|
|
25825
|
-
const text = args.text;
|
|
25826
|
-
if (typeof text !== "string" || text.length === 0 || text.length > TOOL_LIMITS.textChars || /[\n\r]/.test(text)) {
|
|
25827
|
-
return { ok: false, error: `text must be 1-${TOOL_LIMITS.textChars} characters on one line` };
|
|
25828
|
-
}
|
|
25829
|
-
const glob = typeof args.path_glob === "string" && args.path_glob.length > 0 ? args.path_glob : null;
|
|
25830
|
-
if (glob && (glob.length > 200 || /[-]/.test(glob) || glob.startsWith("-"))) {
|
|
25831
|
-
return { ok: false, error: "path_glob is not a valid glob" };
|
|
25832
|
-
}
|
|
25833
|
-
const pathspec = glob ? [`:(glob)${glob}`] : [];
|
|
25834
|
-
const revision = ctx.range.kind === "push" ? [ctx.range.head] : ["--cached"];
|
|
25835
|
-
const res = await git(ctx, ["grep", "-n", "-I", "-F", "--no-color", ...revision.filter((r) => r === "--cached"), "-e", text, ...revision.filter((r) => r !== "--cached"), "--", ...pathspec]);
|
|
25836
|
-
if (!res.ok) {
|
|
25837
|
-
if (res.error === "failed" && res.stderr.trim() === "") return { ok: true, value: "no matches" };
|
|
25838
|
-
return { ok: false, error: gitFailure(res, "search") };
|
|
25839
|
-
}
|
|
25840
|
-
const prefix = ctx.range.kind === "push" ? `${ctx.range.head}:` : "";
|
|
25841
|
-
const hits = [];
|
|
25842
|
-
for (const raw of res.stdout.split("\n")) {
|
|
25843
|
-
if (!raw) continue;
|
|
25844
|
-
const line = prefix && raw.startsWith(prefix) ? raw.slice(prefix.length) : raw;
|
|
25845
|
-
const file = line.slice(0, line.indexOf(":"));
|
|
25846
|
-
if (isDeniedPath(file)) continue;
|
|
25847
|
-
hits.push(line.length > TOOL_LIMITS.searchLineChars ? `${line.slice(0, TOOL_LIMITS.searchLineChars)}\u2026` : line);
|
|
25848
|
-
if (hits.length >= TOOL_LIMITS.searchHits) break;
|
|
25849
|
-
}
|
|
25850
|
-
return { ok: true, value: hits.length ? hits.join("\n") : "no matches" };
|
|
25851
|
-
};
|
|
25852
|
-
var TOOLS = {
|
|
25853
|
-
read_file: readFileTool,
|
|
25854
|
-
search: searchTool,
|
|
25855
|
-
list_files: listFilesTool,
|
|
25856
|
-
git_log: gitLogTool,
|
|
25857
|
-
git_blame: gitBlameTool,
|
|
25858
|
-
git_show: gitShowTool,
|
|
25859
|
-
git_diff: gitDiffTool
|
|
25860
|
-
};
|
|
25861
|
-
function makeToolExecutor(ctx) {
|
|
25862
|
-
return async (calls) => {
|
|
25863
|
-
const results = new Array(calls.length);
|
|
25864
|
-
let next = 0;
|
|
25865
|
-
const worker = async () => {
|
|
25866
|
-
while (next < calls.length) {
|
|
25867
|
-
const index = next++;
|
|
25868
|
-
const call = calls[index];
|
|
25869
|
-
const tool = TOOLS[call.name];
|
|
25870
|
-
if (!tool) {
|
|
25871
|
-
results[index] = { call_id: call.call_id, ok: false, error: `unknown tool ${call.name}` };
|
|
25872
|
-
continue;
|
|
25873
|
-
}
|
|
25874
|
-
try {
|
|
25875
|
-
const outcome = await tool(ctx, call.arguments ?? {});
|
|
25876
|
-
results[index] = outcome.ok ? { call_id: call.call_id, ok: true, output: capOutput(outcome.value) } : { call_id: call.call_id, ok: false, error: outcome.error };
|
|
25877
|
-
} catch (err) {
|
|
25878
|
-
results[index] = { call_id: call.call_id, ok: false, error: `tool failed: ${String(err?.message ?? err).slice(0, 160)}` };
|
|
25879
|
-
}
|
|
25880
|
-
}
|
|
25881
|
-
};
|
|
25882
|
-
await Promise.all(Array.from({ length: Math.min(TOOL_LIMITS.concurrency, calls.length) }, worker));
|
|
25883
|
-
return results;
|
|
25884
|
-
};
|
|
25885
|
-
}
|
|
25886
|
-
async function buildAgentDiffs(ctx, paths, limits = { perFileChars: 24e3, totalChars: 12e4 }) {
|
|
25887
|
-
const valid = paths.filter((p) => validateRepoPath(p).ok);
|
|
25888
|
-
if (valid.length === 0 || ctx.range.kind === "nothing") return [];
|
|
25889
|
-
if (ctx.range.kind === "push" && !ctx.range.base) return [];
|
|
25890
|
-
const args = ctx.range.kind === "push" ? ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", ctx.range.base, ctx.range.head] : ["diff", ...DIFF_READ_FLAGS, "--no-color", "-U10", "--cached"];
|
|
25891
|
-
const res = await git(ctx, ["--literal-pathspecs", ...args, "--", ...valid], { timeoutMs: TOOL_LIMITS.diffTimeoutMs, maxBuffer: 16 * 1024 * 1024 });
|
|
25892
|
-
if (!res.ok) return [];
|
|
25893
|
-
const out = [];
|
|
25894
|
-
let total = 0;
|
|
25895
|
-
for (const chunk of res.stdout.split(/^(?=diff --git )/m)) {
|
|
25896
|
-
if (!chunk.startsWith("diff --git ")) continue;
|
|
25897
|
-
const plus = /^\+\+\+ b\/(.+)$/m.exec(chunk);
|
|
25898
|
-
const minus = /^--- a\/(.+)$/m.exec(chunk);
|
|
25899
|
-
const path = plus?.[1] ?? minus?.[1] ?? /^diff --git a\/(\S+)/.exec(chunk)?.[1];
|
|
25900
|
-
if (!path || isDeniedPath(path)) continue;
|
|
25901
|
-
const room = Math.min(limits.perFileChars, limits.totalChars - total);
|
|
25902
|
-
if (room <= 0) break;
|
|
25903
|
-
const diff = chunk.length > room ? chunk.slice(0, room) : chunk;
|
|
25904
|
-
total += diff.length;
|
|
25905
|
-
out.push({ path, diff });
|
|
25906
|
-
}
|
|
25907
|
-
return out;
|
|
25908
|
-
}
|
|
25909
|
-
|
|
25910
|
-
// src/commands/guard.ts
|
|
25911
|
-
var EXCERPT_SOURCE_MAX_BYTES = 2 * 1024 * 1024;
|
|
24820
|
+
var import_node_path34 = require("node:path");
|
|
25912
24821
|
var GUARD_BLOCK_CAP = 2;
|
|
25913
|
-
var GUARD_ITER_FILE = (0,
|
|
24822
|
+
var GUARD_ITER_FILE = (0, import_node_path34.join)(VERITY_DIR, ".guard-iteration");
|
|
25914
24823
|
function readPreToolUseStdin() {
|
|
25915
24824
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
25916
|
-
return new Promise((
|
|
24825
|
+
return new Promise((resolve5) => {
|
|
25917
24826
|
try {
|
|
25918
|
-
if (process.stdin.isTTY) return
|
|
24827
|
+
if (process.stdin.isTTY) return resolve5(empty);
|
|
25919
24828
|
const chunks = [];
|
|
25920
24829
|
let timer;
|
|
25921
24830
|
let settled = false;
|
|
@@ -25928,7 +24837,7 @@ function readPreToolUseStdin() {
|
|
|
25928
24837
|
process.stdin.removeListener("end", onEnd);
|
|
25929
24838
|
process.stdin.removeListener("error", onError);
|
|
25930
24839
|
process.stdin.pause();
|
|
25931
|
-
|
|
24840
|
+
resolve5(value);
|
|
25932
24841
|
};
|
|
25933
24842
|
const onEnd = () => {
|
|
25934
24843
|
try {
|
|
@@ -25949,7 +24858,7 @@ function readPreToolUseStdin() {
|
|
|
25949
24858
|
process.stdin.on("error", onError);
|
|
25950
24859
|
process.stdin.resume();
|
|
25951
24860
|
} catch {
|
|
25952
|
-
|
|
24861
|
+
resolve5(empty);
|
|
25953
24862
|
}
|
|
25954
24863
|
});
|
|
25955
24864
|
}
|
|
@@ -26061,13 +24970,8 @@ function hasBlockingFinding(response, sentFiles) {
|
|
|
26061
24970
|
function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedIntent, coverageTelemetry) {
|
|
26062
24971
|
const analyzable = filterAnalyzable(files);
|
|
26063
24972
|
const securityFiles = filterSecurity(files);
|
|
26064
|
-
|
|
26065
|
-
|
|
26066
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs49.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
26067
|
-
staticResults = runCodacyAnalysis(scannable);
|
|
26068
|
-
} else {
|
|
26069
|
-
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
26070
|
-
}
|
|
24973
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs49.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
24974
|
+
const staticResults = runCodacyAnalysisIfAvailable(scannable);
|
|
26071
24975
|
const trigger = moment === "pre-commit" ? "hook:pre-commit" : "hook:pre-push";
|
|
26072
24976
|
const requestBody = {
|
|
26073
24977
|
static_results: staticResults,
|
|
@@ -26130,100 +25034,12 @@ function coverageBlock(c) {
|
|
|
26130
25034
|
}
|
|
26131
25035
|
function emitAllowNotice(userMsg, agentMsg) {
|
|
26132
25036
|
process.stdout.write(JSON.stringify({
|
|
26133
|
-
systemMessage:
|
|
26134
|
-
hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext:
|
|
25037
|
+
systemMessage: userMsg,
|
|
25038
|
+
hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: agentMsg }
|
|
26135
25039
|
}) + "\n");
|
|
26136
25040
|
process.exit(0);
|
|
26137
25041
|
}
|
|
26138
|
-
var GATES = ["PASS", "WARN", "FAIL"];
|
|
26139
|
-
function worstGate(...gates) {
|
|
26140
|
-
const rank2 = (gate) => {
|
|
26141
|
-
const index = GATES.indexOf(String(gate ?? "").toUpperCase());
|
|
26142
|
-
return index === -1 ? 1 : index;
|
|
26143
|
-
};
|
|
26144
|
-
return GATES[Math.max(...gates.map(rank2))];
|
|
26145
|
-
}
|
|
26146
|
-
function withAgentVerdict(oneShot, agent) {
|
|
26147
|
-
if (agent.one_shot_source === "none" && typeof oneShot.gate_decision === "string") {
|
|
26148
|
-
const added = agent.findings.filter((f) => f.agent_added === true);
|
|
26149
|
-
return {
|
|
26150
|
-
...oneShot,
|
|
26151
|
-
gate_decision: worstGate(oneShot.gate_decision, agent.gate_decision ?? "WARN", "WARN"),
|
|
26152
|
-
findings: [...Array.isArray(oneShot.findings) ? oneShot.findings : [], ...added],
|
|
26153
|
-
agent_review: {
|
|
26154
|
-
...agent.agent_review,
|
|
26155
|
-
gate_decision_before: oneShot.gate_decision,
|
|
26156
|
-
one_shot_source: "none",
|
|
26157
|
-
cleared: [],
|
|
26158
|
-
downgraded: []
|
|
26159
|
-
}
|
|
26160
|
-
};
|
|
26161
|
-
}
|
|
26162
|
-
return {
|
|
26163
|
-
...oneShot,
|
|
26164
|
-
gate_decision: agent.gate_decision,
|
|
26165
|
-
findings: agent.findings,
|
|
26166
|
-
agent_review: {
|
|
26167
|
-
...agent.agent_review,
|
|
26168
|
-
gate_decision_before: agent.gate_decision_before,
|
|
26169
|
-
one_shot_source: agent.one_shot_source,
|
|
26170
|
-
cleared: agent.cleared,
|
|
26171
|
-
downgraded: agent.downgraded ?? []
|
|
26172
|
-
}
|
|
26173
|
-
};
|
|
26174
|
-
}
|
|
26175
|
-
function describeAgentOutcome(outcome) {
|
|
26176
|
-
if (!outcome || outcome.kind === "disabled") return null;
|
|
26177
|
-
if (outcome.kind !== "finished") return `Agent review: not used (${outcome.reason})`;
|
|
26178
|
-
const { agent_review: r, gate_decision: after, gate_decision_before: before } = outcome.response;
|
|
26179
|
-
const checks = Number(r.code_runtime_checks ?? 0) + Number(r.container_checks ?? 0);
|
|
26180
|
-
const verdict = before && after && before !== after ? `, verdict changed from ${before} to ${after}` : "";
|
|
26181
|
-
return `Agent review: ${r.outcome}, ${r.turns} turn(s), ${r.local_calls} read(s), ${checks} code check(s), ${r.cleared} cleared, ${Number(r.downgraded ?? 0)} downgraded, ${r.added} added${verdict}`;
|
|
26182
|
-
}
|
|
26183
|
-
function startAgentReview(input) {
|
|
26184
|
-
if (process.env.VERITY_GIT_MOMENT_AGENT === "off") return null;
|
|
26185
|
-
const ctx = { root: input.root, range: input.range };
|
|
26186
|
-
const http = {
|
|
26187
|
-
async post(path, body, timeoutMs) {
|
|
26188
|
-
const res = await apiRequest({
|
|
26189
|
-
method: "POST",
|
|
26190
|
-
path,
|
|
26191
|
-
serviceUrl: input.serviceUrl,
|
|
26192
|
-
token: input.token,
|
|
26193
|
-
body,
|
|
26194
|
-
timeout: timeoutMs,
|
|
26195
|
-
cmd: "guard-agent",
|
|
26196
|
-
verbose: input.verbose,
|
|
26197
|
-
encodeBody: true
|
|
26198
|
-
});
|
|
26199
|
-
return res.ok ? { ok: true, data: res.data } : { ok: false, error: res.error };
|
|
26200
|
-
}
|
|
26201
|
-
};
|
|
26202
|
-
const sentPaths = input.codeDelta.files.map((f) => f.path);
|
|
26203
|
-
const run2 = async () => runAgentReview({
|
|
26204
|
-
http,
|
|
26205
|
-
executeTools: makeToolExecutor(ctx),
|
|
26206
|
-
startedAt: input.hookStartedAt,
|
|
26207
|
-
deadlineMs: AGENT_DEADLINE_MS[input.moment],
|
|
26208
|
-
start: {
|
|
26209
|
-
moment: input.moment,
|
|
26210
|
-
iteration: input.iteration,
|
|
26211
|
-
session_id: input.sessionId,
|
|
26212
|
-
stated_intent: input.statedIntent,
|
|
26213
|
-
changed_files: input.files,
|
|
26214
|
-
sent_paths: sentPaths,
|
|
26215
|
-
diffs: await buildAgentDiffs(ctx, sentPaths),
|
|
26216
|
-
repo_context: input.requestBody.repo_context,
|
|
26217
|
-
static_results: input.requestBody.static_results,
|
|
26218
|
-
one_shot_chars: input.codeDelta.files.reduce((n, f) => n + f.content.length, 0)
|
|
26219
|
-
},
|
|
26220
|
-
oneShot: input.oneShot.then((r) => r.ok ? { ok: true, response: r.data } : { ok: false }),
|
|
26221
|
-
sessionId: input.sessionId
|
|
26222
|
-
});
|
|
26223
|
-
return run2().catch((err) => ({ kind: "failed", reason: String(err?.message ?? err) }));
|
|
26224
|
-
}
|
|
26225
25042
|
async function runGuard(opts, globals) {
|
|
26226
|
-
const hookStartedAt = Date.now();
|
|
26227
25043
|
const on = resolveGuardMoments(opts.on);
|
|
26228
25044
|
if (on.length === 0) process.exit(0);
|
|
26229
25045
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
@@ -26295,12 +25111,12 @@ async function runGuard(opts, globals) {
|
|
|
26295
25111
|
cwd: frame.worktreeRoot ?? process.cwd()
|
|
26296
25112
|
});
|
|
26297
25113
|
upgradeToExcerpts(repoContext2, {
|
|
26298
|
-
// Rooted, no-follow read from the validated descriptor (safe-read.ts):
|
|
26299
|
-
// site paths come from a search, and a site under a symlinked directory
|
|
26300
|
-
// must not pull a file from outside the tree into the request.
|
|
26301
25114
|
readFile: (rel) => {
|
|
26302
|
-
|
|
26303
|
-
|
|
25115
|
+
try {
|
|
25116
|
+
return (0, import_node_fs49.readFileSync)((0, import_node_path34.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
|
|
25117
|
+
} catch {
|
|
25118
|
+
return null;
|
|
25119
|
+
}
|
|
26304
25120
|
}
|
|
26305
25121
|
});
|
|
26306
25122
|
requestBody.repo_context = repoContext2;
|
|
@@ -26329,7 +25145,7 @@ async function runGuard(opts, globals) {
|
|
|
26329
25145
|
};
|
|
26330
25146
|
logToFileOnly(coverageBlock(coverage));
|
|
26331
25147
|
const reviewStart = Date.now();
|
|
26332
|
-
const
|
|
25148
|
+
const result = await analyzeRequest({
|
|
26333
25149
|
serviceUrl: urlResult.data,
|
|
26334
25150
|
token: tokenResult.data.token,
|
|
26335
25151
|
body: requestBody,
|
|
@@ -26337,45 +25153,22 @@ async function runGuard(opts, globals) {
|
|
|
26337
25153
|
timeout: 29e4,
|
|
26338
25154
|
cmd: "guard"
|
|
26339
25155
|
});
|
|
26340
|
-
const agent = startAgentReview({
|
|
26341
|
-
serviceUrl: urlResult.data,
|
|
26342
|
-
token: tokenResult.data.token,
|
|
26343
|
-
verbose: globals.verbose,
|
|
26344
|
-
hookStartedAt,
|
|
26345
|
-
moment,
|
|
26346
|
-
root: frame.worktreeRoot,
|
|
26347
|
-
range,
|
|
26348
|
-
iteration: iter + 1,
|
|
26349
|
-
sessionId,
|
|
26350
|
-
statedIntent,
|
|
26351
|
-
files,
|
|
26352
|
-
codeDelta,
|
|
26353
|
-
requestBody,
|
|
26354
|
-
oneShot
|
|
26355
|
-
});
|
|
26356
|
-
const result = await oneShot;
|
|
26357
|
-
const agentOutcome = agent ? await agent : null;
|
|
26358
|
-
const agentVerdict = agentOutcome?.kind === "finished" && agentOutcome.response.gate_decision ? agentOutcome.response : null;
|
|
26359
25156
|
const reviewSecs = Math.max(1, Math.round((Date.now() - reviewStart) / 1e3));
|
|
26360
|
-
if (!result.ok
|
|
25157
|
+
if (!result.ok) {
|
|
26361
25158
|
const authRemedy = result.error.startsWith("STALE_VERIFICATION") ? " Your GitHub verification expired \u2014 run `verity login` to re-verify." : result.error.startsWith("FORBIDDEN") ? " No access grant for this repository \u2014 run `verity login` to refresh your grants." : result.error.startsWith("INVALID_TOKEN") ? " Your Verity login expired or was revoked \u2014 run `verity login` to sign in again." : "";
|
|
26362
25159
|
emitAllowNotice(
|
|
26363
25160
|
`\u26A0 Verity ${moment}: ${authRemedy ? "not authorized" : "service offline"} \u2014 ${verb}ed WITHOUT review${authRemedy}`,
|
|
26364
25161
|
`Verity ${moment}: ${authRemedy ? "not authorized" : "service unavailable"} (${result.error}); the ${verb} was allowed WITHOUT a Verity review.${authRemedy}`
|
|
26365
25162
|
);
|
|
26366
25163
|
}
|
|
26367
|
-
|
|
26368
|
-
const response =
|
|
26369
|
-
if (opts.json) process.stderr.write(JSON.stringify(response) + "\n");
|
|
25164
|
+
if (opts.json) process.stderr.write(JSON.stringify(result.data) + "\n");
|
|
25165
|
+
const response = result.data;
|
|
26370
25166
|
const decision = response.gate_decision ?? "(unrecognised)";
|
|
26371
25167
|
const viewUrl = response.view_url ?? "";
|
|
26372
25168
|
const link = viewUrl ? ` \u2014 ${viewUrl}` : "";
|
|
26373
25169
|
const covLine = `${coverageSummary(coverage)} in ${reviewSecs}s`;
|
|
26374
|
-
const
|
|
26375
|
-
|
|
26376
|
-
${agentLine}` : coverageBlock(coverage);
|
|
26377
|
-
const witnessed = [...codeDelta.files.map((f) => f.path), ...agentVerdict?.witnessed_files ?? []];
|
|
26378
|
-
if (decision === "FAIL" && hasBlockingFinding(response, witnessed)) {
|
|
25170
|
+
const covDetail = coverageBlock(coverage);
|
|
25171
|
+
if (decision === "FAIL" && hasBlockingFinding(response, codeDelta.files.map((f) => f.path))) {
|
|
26379
25172
|
writeIter(moment, iter + 1);
|
|
26380
25173
|
writeBlockMessage(moment, response, covDetail);
|
|
26381
25174
|
process.exit(2);
|
|
@@ -26394,16 +25187,12 @@ async function runGuard(opts, globals) {
|
|
|
26394
25187
|
emitAllowNotice(notice.user, notice.agent);
|
|
26395
25188
|
}
|
|
26396
25189
|
function verdictNotice(ctx) {
|
|
26397
|
-
const { moment, verb } = ctx;
|
|
26398
|
-
const
|
|
26399
|
-
const covDetail = terminalText(ctx.covDetail, 2e4);
|
|
26400
|
-
const link = terminalText(ctx.link, 2100);
|
|
26401
|
-
const viewUrl = terminalUrl(ctx.viewUrl);
|
|
26402
|
-
const decision = ctx.decision == null ? "(unrecognised)" : terminalText(ctx.decision, 60);
|
|
25190
|
+
const { moment, verb, covLine, covDetail, link, viewUrl } = ctx;
|
|
25191
|
+
const decision = ctx.decision ?? "(unrecognised)";
|
|
26403
25192
|
const report = viewUrl ? `
|
|
26404
25193
|
Report: ${viewUrl}` : "";
|
|
26405
25194
|
if (decision === "FAIL") {
|
|
26406
|
-
const narrative =
|
|
25195
|
+
const narrative = ctx.narrative ?? "";
|
|
26407
25196
|
return {
|
|
26408
25197
|
user: `\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
|
|
26409
25198
|
agent: `Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
|
|
@@ -26434,9 +25223,9 @@ function writeBlockMessage(moment, response, covDetail) {
|
|
|
26434
25223
|
const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
|
|
26435
25224
|
const verb = moment === "pre-commit" ? "commit" : "push";
|
|
26436
25225
|
const assessment = response.assessment;
|
|
26437
|
-
const narrative =
|
|
26438
|
-
const findings =
|
|
26439
|
-
const viewUrl =
|
|
25226
|
+
const narrative = assessment?.narrative ?? "";
|
|
25227
|
+
const findings = response.findings ?? [];
|
|
25228
|
+
const viewUrl = response.view_url ?? "";
|
|
26440
25229
|
process.stderr.write(`${RED}${BOLD}\u2501\u2501\u2501 Verity ${label2} gate: FAIL \u2501\u2501\u2501${NC}
|
|
26441
25230
|
|
|
26442
25231
|
`);
|
|
@@ -26449,18 +25238,18 @@ function writeBlockMessage(moment, response, covDetail) {
|
|
|
26449
25238
|
|
|
26450
25239
|
`);
|
|
26451
25240
|
for (const f of agentFindings) {
|
|
26452
|
-
const severity =
|
|
25241
|
+
const severity = (f.severity ?? "").toUpperCase();
|
|
26453
25242
|
const fix = f.fix;
|
|
26454
|
-
process.stderr.write(`[${severity}] ${
|
|
25243
|
+
process.stderr.write(`[${severity}] ${f.title ?? ""}
|
|
26455
25244
|
`);
|
|
26456
|
-
process.stderr.write(` File: ${
|
|
25245
|
+
process.stderr.write(` File: ${f.file ?? ""}:${f.line ?? ""}
|
|
26457
25246
|
`);
|
|
26458
|
-
process.stderr.write(` Fix: ${
|
|
25247
|
+
process.stderr.write(` Fix: ${fix?.description ?? "See description"}
|
|
26459
25248
|
|
|
26460
25249
|
`);
|
|
26461
25250
|
}
|
|
26462
25251
|
}
|
|
26463
|
-
process.stderr.write(`${DIM}${
|
|
25252
|
+
process.stderr.write(`${DIM}${covDetail}${NC}
|
|
26464
25253
|
|
|
26465
25254
|
`);
|
|
26466
25255
|
if (viewUrl) process.stderr.write(`${CYAN}Full report: ${viewUrl}${NC}
|
|
@@ -26621,10 +25410,10 @@ function registerWaiveCommand(program2) {
|
|
|
26621
25410
|
|
|
26622
25411
|
// src/commands/init.ts
|
|
26623
25412
|
var import_node_fs54 = require("node:fs");
|
|
26624
|
-
var
|
|
25413
|
+
var import_promises17 = require("node:fs/promises");
|
|
26625
25414
|
var import_yaml6 = __toESM(require_dist());
|
|
26626
|
-
var
|
|
26627
|
-
var
|
|
25415
|
+
var import_node_path37 = require("node:path");
|
|
25416
|
+
var import_node_child_process16 = require("node:child_process");
|
|
26628
25417
|
|
|
26629
25418
|
// src/lib/banner.ts
|
|
26630
25419
|
var WORDMARK = [
|
|
@@ -26705,15 +25494,10 @@ function printPhase(n, of, title, subtitle) {
|
|
|
26705
25494
|
var import_node_fs51 = require("node:fs");
|
|
26706
25495
|
|
|
26707
25496
|
// src/lib/prereqs.ts
|
|
26708
|
-
var
|
|
25497
|
+
var import_node_child_process14 = require("node:child_process");
|
|
26709
25498
|
var MIN_NODE_MAJOR = 20;
|
|
26710
25499
|
function which(bin) {
|
|
26711
|
-
|
|
26712
|
-
const out = (0, import_node_child_process15.execSync)(`command -v ${bin}`, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
26713
|
-
return out || null;
|
|
26714
|
-
} catch {
|
|
26715
|
-
return null;
|
|
26716
|
-
}
|
|
25500
|
+
return whichSync(bin, { real: false });
|
|
26717
25501
|
}
|
|
26718
25502
|
function checkNode() {
|
|
26719
25503
|
const version = process.version;
|
|
@@ -26731,7 +25515,7 @@ function checkNode() {
|
|
|
26731
25515
|
function checkGit() {
|
|
26732
25516
|
let detail = "";
|
|
26733
25517
|
try {
|
|
26734
|
-
detail = (0,
|
|
25518
|
+
detail = (0, import_node_child_process14.execSync)("git --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
26735
25519
|
} catch {
|
|
26736
25520
|
return {
|
|
26737
25521
|
id: "git",
|
|
@@ -26768,13 +25552,13 @@ function checkAnalysisCli() {
|
|
|
26768
25552
|
}
|
|
26769
25553
|
var INSTALL_TIMEOUT_MS = 12e4;
|
|
26770
25554
|
function run(command, args, opts = {}) {
|
|
26771
|
-
return new Promise((
|
|
26772
|
-
const child = (0,
|
|
25555
|
+
return new Promise((resolve5) => {
|
|
25556
|
+
const child = (0, import_node_child_process14.spawn)(command, args, {
|
|
26773
25557
|
stdio: opts.inherit ? "inherit" : "pipe",
|
|
26774
25558
|
timeout: INSTALL_TIMEOUT_MS
|
|
26775
25559
|
});
|
|
26776
|
-
child.on("error", () =>
|
|
26777
|
-
child.on("close", (code) =>
|
|
25560
|
+
child.on("error", () => resolve5(false));
|
|
25561
|
+
child.on("close", (code) => resolve5(code === 0));
|
|
26778
25562
|
});
|
|
26779
25563
|
}
|
|
26780
25564
|
async function installAnalysisCli() {
|
|
@@ -26818,7 +25602,7 @@ async function checkPrereqs(opts = {}) {
|
|
|
26818
25602
|
}
|
|
26819
25603
|
|
|
26820
25604
|
// src/lib/telemetry.ts
|
|
26821
|
-
var
|
|
25605
|
+
var import_promises15 = require("node:fs/promises");
|
|
26822
25606
|
var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
|
|
26823
25607
|
var GITIGNORE_FILE = ".gitignore";
|
|
26824
25608
|
var GITIGNORE_ENTRY = SETTINGS_LOCAL_IGNORE_ENTRY;
|
|
@@ -26848,7 +25632,7 @@ function buildTelemetryEnv(serviceUrl) {
|
|
|
26848
25632
|
var VERITY_TELEMETRY_KEYS = Object.keys(buildTelemetryEnv(""));
|
|
26849
25633
|
async function readSettingsLocal() {
|
|
26850
25634
|
try {
|
|
26851
|
-
return JSON.parse(await (0,
|
|
25635
|
+
return JSON.parse(await (0, import_promises15.readFile)(projectPath(SETTINGS_LOCAL_FILE2), "utf-8"));
|
|
26852
25636
|
} catch {
|
|
26853
25637
|
return {};
|
|
26854
25638
|
}
|
|
@@ -26860,7 +25644,7 @@ async function ensureGitignore() {
|
|
|
26860
25644
|
const file = projectPath(GITIGNORE_FILE);
|
|
26861
25645
|
let content = "";
|
|
26862
25646
|
try {
|
|
26863
|
-
content = await (0,
|
|
25647
|
+
content = await (0, import_promises15.readFile)(file, "utf-8");
|
|
26864
25648
|
} catch {
|
|
26865
25649
|
}
|
|
26866
25650
|
const lines = content.split("\n").map((l) => l.trim());
|
|
@@ -26869,7 +25653,7 @@ async function ensureGitignore() {
|
|
|
26869
25653
|
}
|
|
26870
25654
|
const block = "# Verity telemetry \u2014 machine-local Claude Code settings\n" + GITIGNORE_ENTRY + "\n";
|
|
26871
25655
|
const next = content ? content + (content.endsWith("\n") ? "" : "\n") + "\n" + block : block;
|
|
26872
|
-
await (0,
|
|
25656
|
+
await (0, import_promises15.writeFile)(file, next);
|
|
26873
25657
|
}
|
|
26874
25658
|
async function installTelemetry(serviceUrl) {
|
|
26875
25659
|
const env = buildTelemetryEnv(serviceUrl);
|
|
@@ -27050,15 +25834,15 @@ function registerDoctorCommand(program2) {
|
|
|
27050
25834
|
|
|
27051
25835
|
// src/commands/migrate.ts
|
|
27052
25836
|
var import_node_fs52 = require("node:fs");
|
|
27053
|
-
var
|
|
27054
|
-
var
|
|
25837
|
+
var import_node_path35 = require("node:path");
|
|
25838
|
+
var import_node_child_process15 = require("node:child_process");
|
|
27055
25839
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
27056
25840
|
function defaultNpmRemover(pkg) {
|
|
27057
|
-
(0,
|
|
25841
|
+
(0, import_node_child_process15.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
27058
25842
|
}
|
|
27059
25843
|
function isGitTracked(cwd, relPath) {
|
|
27060
25844
|
try {
|
|
27061
|
-
(0,
|
|
25845
|
+
(0, import_node_child_process15.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
27062
25846
|
return true;
|
|
27063
25847
|
} catch {
|
|
27064
25848
|
return false;
|
|
@@ -27066,7 +25850,7 @@ function isGitTracked(cwd, relPath) {
|
|
|
27066
25850
|
}
|
|
27067
25851
|
function isGitRepo(cwd) {
|
|
27068
25852
|
try {
|
|
27069
|
-
(0,
|
|
25853
|
+
(0, import_node_child_process15.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
27070
25854
|
return true;
|
|
27071
25855
|
} catch {
|
|
27072
25856
|
return false;
|
|
@@ -27087,8 +25871,8 @@ async function runMigration(opts = {}) {
|
|
|
27087
25871
|
return { actions, migrated: actions.length > 0 };
|
|
27088
25872
|
}
|
|
27089
25873
|
function migrateProjectDir(root, actions) {
|
|
27090
|
-
const gateDir = (0,
|
|
27091
|
-
const verityDir = (0,
|
|
25874
|
+
const gateDir = (0, import_node_path35.join)(root, ".gate");
|
|
25875
|
+
const verityDir = (0, import_node_path35.join)(root, ".verity");
|
|
27092
25876
|
if ((0, import_node_fs52.existsSync)(gateDir) && !(0, import_node_fs52.existsSync)(verityDir)) {
|
|
27093
25877
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
27094
25878
|
}
|
|
@@ -27106,7 +25890,7 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
27106
25890
|
);
|
|
27107
25891
|
}
|
|
27108
25892
|
try {
|
|
27109
|
-
(0,
|
|
25893
|
+
(0, import_node_child_process15.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
27110
25894
|
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
27111
25895
|
moved = true;
|
|
27112
25896
|
} catch {
|
|
@@ -27142,11 +25926,11 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
27142
25926
|
}
|
|
27143
25927
|
function migrateGlobalCredentials(home, actions) {
|
|
27144
25928
|
if (!home) return;
|
|
27145
|
-
const gateCreds = (0,
|
|
27146
|
-
const verityCreds = (0,
|
|
25929
|
+
const gateCreds = (0, import_node_path35.join)(home, ".gate", "credentials");
|
|
25930
|
+
const verityCreds = (0, import_node_path35.join)(home, ".verity", "credentials");
|
|
27147
25931
|
if (!(0, import_node_fs52.existsSync)(gateCreds)) return;
|
|
27148
25932
|
if (!(0, import_node_fs52.existsSync)(verityCreds)) {
|
|
27149
|
-
(0, import_node_fs52.mkdirSync)((0,
|
|
25933
|
+
(0, import_node_fs52.mkdirSync)((0, import_node_path35.join)(home, ".verity"), { recursive: true });
|
|
27150
25934
|
moveFile(gateCreds, verityCreds);
|
|
27151
25935
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
27152
25936
|
return;
|
|
@@ -27168,7 +25952,7 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
27168
25952
|
}
|
|
27169
25953
|
}
|
|
27170
25954
|
async function migrateClaudeMd(root, actions) {
|
|
27171
|
-
const claudeMd = (0,
|
|
25955
|
+
const claudeMd = (0, import_node_path35.join)(root, "CLAUDE.md");
|
|
27172
25956
|
const hadLegacyBlock = (0, import_node_fs52.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
27173
25957
|
if (!hadLegacyBlock) return;
|
|
27174
25958
|
try {
|
|
@@ -27179,13 +25963,13 @@ async function migrateClaudeMd(root, actions) {
|
|
|
27179
25963
|
}
|
|
27180
25964
|
}
|
|
27181
25965
|
function migrateStandardFile(root, actions) {
|
|
27182
|
-
const gateMd = (0,
|
|
27183
|
-
const verityMd = (0,
|
|
25966
|
+
const gateMd = (0, import_node_path35.join)(root, "GATE.md");
|
|
25967
|
+
const verityMd = (0, import_node_path35.join)(root, "VERITY.md");
|
|
27184
25968
|
if (!(0, import_node_fs52.existsSync)(gateMd) || (0, import_node_fs52.existsSync)(verityMd)) return;
|
|
27185
25969
|
let moved = false;
|
|
27186
25970
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
27187
25971
|
try {
|
|
27188
|
-
(0,
|
|
25972
|
+
(0, import_node_child_process15.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
27189
25973
|
moved = true;
|
|
27190
25974
|
} catch {
|
|
27191
25975
|
}
|
|
@@ -27197,7 +25981,7 @@ function migrateStandardFile(root, actions) {
|
|
|
27197
25981
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
27198
25982
|
}
|
|
27199
25983
|
async function migrateTelemetryHeaders(root, actions) {
|
|
27200
|
-
const file = (0,
|
|
25984
|
+
const file = (0, import_node_path35.join)(root, ".claude", "settings.local.json");
|
|
27201
25985
|
if (!(0, import_node_fs52.existsSync)(file)) return;
|
|
27202
25986
|
let settings;
|
|
27203
25987
|
try {
|
|
@@ -27245,8 +26029,8 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
27245
26029
|
toAppend.push(line.replace(/\r$/, ""));
|
|
27246
26030
|
}
|
|
27247
26031
|
if (toAppend.length > 0) {
|
|
27248
|
-
const
|
|
27249
|
-
(0, import_node_fs52.writeFileSync)(verityCreds, verityContent +
|
|
26032
|
+
const sep3 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
26033
|
+
(0, import_node_fs52.writeFileSync)(verityCreds, verityContent + sep3 + toAppend.join("\n") + "\n");
|
|
27250
26034
|
}
|
|
27251
26035
|
(0, import_node_fs52.rmSync)(gateCreds, { force: true });
|
|
27252
26036
|
return toAppend.length;
|
|
@@ -27260,7 +26044,7 @@ function readFileSyncSafe(path) {
|
|
|
27260
26044
|
}
|
|
27261
26045
|
function hasStagedChanges(root) {
|
|
27262
26046
|
try {
|
|
27263
|
-
(0,
|
|
26047
|
+
(0, import_node_child_process15.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
27264
26048
|
return false;
|
|
27265
26049
|
} catch {
|
|
27266
26050
|
return true;
|
|
@@ -27287,15 +26071,15 @@ function moveFile(from, to) {
|
|
|
27287
26071
|
function carryLegacyContents(gateDir, verityDir) {
|
|
27288
26072
|
let copied = 0;
|
|
27289
26073
|
const walk2 = (relDir) => {
|
|
27290
|
-
const srcDir = (0,
|
|
26074
|
+
const srcDir = (0, import_node_path35.join)(gateDir, relDir);
|
|
27291
26075
|
for (const entry of (0, import_node_fs52.readdirSync)(srcDir)) {
|
|
27292
|
-
const rel = relDir ? (0,
|
|
27293
|
-
const src = (0,
|
|
27294
|
-
const dest = (0,
|
|
26076
|
+
const rel = relDir ? (0, import_node_path35.join)(relDir, entry) : entry;
|
|
26077
|
+
const src = (0, import_node_path35.join)(gateDir, rel);
|
|
26078
|
+
const dest = (0, import_node_path35.join)(verityDir, rel);
|
|
27295
26079
|
if ((0, import_node_fs52.statSync)(src).isDirectory()) {
|
|
27296
26080
|
walk2(rel);
|
|
27297
26081
|
} else if (!(0, import_node_fs52.existsSync)(dest)) {
|
|
27298
|
-
(0, import_node_fs52.mkdirSync)((0,
|
|
26082
|
+
(0, import_node_fs52.mkdirSync)((0, import_node_path35.dirname)(dest), { recursive: true });
|
|
27299
26083
|
(0, import_node_fs52.cpSync)(src, dest);
|
|
27300
26084
|
copied++;
|
|
27301
26085
|
}
|
|
@@ -27305,22 +26089,22 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
27305
26089
|
return copied;
|
|
27306
26090
|
}
|
|
27307
26091
|
async function needsMigration(root = repoRoot()) {
|
|
27308
|
-
const gateDir = (0,
|
|
27309
|
-
const verityDir = (0,
|
|
26092
|
+
const gateDir = (0, import_node_path35.join)(root, ".gate");
|
|
26093
|
+
const verityDir = (0, import_node_path35.join)(root, ".verity");
|
|
27310
26094
|
if ((0, import_node_fs52.existsSync)(gateDir) && !(0, import_node_fs52.existsSync)(verityDir)) return true;
|
|
27311
26095
|
if ((0, import_node_fs52.existsSync)(gateDir) && (0, import_node_fs52.existsSync)(verityDir)) {
|
|
27312
|
-
if ((0, import_node_fs52.existsSync)((0,
|
|
26096
|
+
if ((0, import_node_fs52.existsSync)((0, import_node_path35.join)(gateDir, "credentials")) && !(0, import_node_fs52.existsSync)((0, import_node_path35.join)(verityDir, "credentials"))) {
|
|
27313
26097
|
return true;
|
|
27314
26098
|
}
|
|
27315
|
-
if ((0, import_node_fs52.existsSync)((0,
|
|
26099
|
+
if ((0, import_node_fs52.existsSync)((0, import_node_path35.join)(gateDir, "memory")) && !(0, import_node_fs52.existsSync)((0, import_node_path35.join)(verityDir, "memory"))) {
|
|
27316
26100
|
return true;
|
|
27317
26101
|
}
|
|
27318
26102
|
}
|
|
27319
|
-
const claudeMd = (0,
|
|
26103
|
+
const claudeMd = (0, import_node_path35.join)(root, "CLAUDE.md");
|
|
27320
26104
|
if ((0, import_node_fs52.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
27321
26105
|
return true;
|
|
27322
26106
|
}
|
|
27323
|
-
if ((0, import_node_fs52.existsSync)((0,
|
|
26107
|
+
if ((0, import_node_fs52.existsSync)((0, import_node_path35.join)(root, "GATE.md")) && !(0, import_node_fs52.existsSync)((0, import_node_path35.join)(root, "VERITY.md"))) {
|
|
27324
26108
|
return true;
|
|
27325
26109
|
}
|
|
27326
26110
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -27449,7 +26233,7 @@ function runSelect(opts) {
|
|
|
27449
26233
|
cursor: initialIdx[0] ?? 0,
|
|
27450
26234
|
chosen: new Set(mode2 === "single" ? [initialIdx[0] ?? 0] : initialIdx)
|
|
27451
26235
|
};
|
|
27452
|
-
return new Promise((
|
|
26236
|
+
return new Promise((resolve5) => {
|
|
27453
26237
|
let painted = 0;
|
|
27454
26238
|
let settled = false;
|
|
27455
26239
|
const draw = () => {
|
|
@@ -27497,7 +26281,7 @@ function runSelect(opts) {
|
|
|
27497
26281
|
if (settled) return;
|
|
27498
26282
|
settled = true;
|
|
27499
26283
|
restore();
|
|
27500
|
-
|
|
26284
|
+
resolve5(value);
|
|
27501
26285
|
};
|
|
27502
26286
|
readline2.emitKeypressEvents(input);
|
|
27503
26287
|
input.setRawMode(true);
|
|
@@ -27518,9 +26302,9 @@ async function askLine(question, io = {}) {
|
|
|
27518
26302
|
output: io.output ?? process.stdout
|
|
27519
26303
|
});
|
|
27520
26304
|
try {
|
|
27521
|
-
return await new Promise((
|
|
27522
|
-
rl.question(question).then((a) =>
|
|
27523
|
-
rl.once("close", () => setImmediate(() =>
|
|
26305
|
+
return await new Promise((resolve5) => {
|
|
26306
|
+
rl.question(question).then((a) => resolve5(a.trim())).catch(() => resolve5(null));
|
|
26307
|
+
rl.once("close", () => setImmediate(() => resolve5(null)));
|
|
27524
26308
|
});
|
|
27525
26309
|
} finally {
|
|
27526
26310
|
rl.close();
|
|
@@ -27602,8 +26386,8 @@ async function promptMultiSelect(question, choices, fallback) {
|
|
|
27602
26386
|
|
|
27603
26387
|
// src/lib/remote-config.ts
|
|
27604
26388
|
var import_node_fs53 = require("node:fs");
|
|
27605
|
-
var
|
|
27606
|
-
var
|
|
26389
|
+
var import_promises16 = require("node:fs/promises");
|
|
26390
|
+
var import_node_path36 = require("node:path");
|
|
27607
26391
|
var import_yaml5 = __toESM(require_dist());
|
|
27608
26392
|
var IGNORE_RIDER = "verityignore";
|
|
27609
26393
|
async function fetchRemoteSetup(opts) {
|
|
@@ -27652,7 +26436,7 @@ async function adoptRemoteSetup(found, opts) {
|
|
|
27652
26436
|
await writeOut(VERITYIGNORE_FILE, rider);
|
|
27653
26437
|
written.push(VERITYIGNORE_FILE);
|
|
27654
26438
|
} else {
|
|
27655
|
-
const local = await (0,
|
|
26439
|
+
const local = await (0, import_promises16.readFile)(localIgnore, "utf-8").catch(() => null);
|
|
27656
26440
|
if (local !== null && local !== rider) {
|
|
27657
26441
|
notes.push(`${VERITYIGNORE_FILE} already exists here and differs from the pushed copy \u2014 kept yours.`);
|
|
27658
26442
|
}
|
|
@@ -27679,10 +26463,10 @@ async function adoptRemoteSetup(found, opts) {
|
|
|
27679
26463
|
written.push(VERITY_MD_FILE);
|
|
27680
26464
|
return { written, notes };
|
|
27681
26465
|
}
|
|
27682
|
-
async function writeOut(
|
|
27683
|
-
const target = projectPath(
|
|
27684
|
-
await (0,
|
|
27685
|
-
await (0,
|
|
26466
|
+
async function writeOut(relative3, body) {
|
|
26467
|
+
const target = projectPath(relative3);
|
|
26468
|
+
await (0, import_promises16.mkdir)((0, import_node_path36.dirname)(target), { recursive: true });
|
|
26469
|
+
await (0, import_promises16.writeFile)(target, body);
|
|
27686
26470
|
}
|
|
27687
26471
|
function describeRemote(found) {
|
|
27688
26472
|
const when = found.standard.createdAt.slice(0, 10);
|
|
@@ -27752,7 +26536,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
27752
26536
|
}
|
|
27753
26537
|
let remote = "";
|
|
27754
26538
|
try {
|
|
27755
|
-
remote = (0,
|
|
26539
|
+
remote = (0, import_node_child_process16.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
27756
26540
|
} catch {
|
|
27757
26541
|
}
|
|
27758
26542
|
if (!healed) {
|
|
@@ -27798,15 +26582,15 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
27798
26582
|
}
|
|
27799
26583
|
function resolveDataDir2() {
|
|
27800
26584
|
const candidates2 = [
|
|
27801
|
-
(0,
|
|
26585
|
+
(0, import_node_path37.join)(__dirname, "..", "data"),
|
|
27802
26586
|
// installed: node_modules/@codacy/verity-cli/data
|
|
27803
|
-
(0,
|
|
26587
|
+
(0, import_node_path37.join)(__dirname, "..", "..", "data"),
|
|
27804
26588
|
// edge case: nested resolution
|
|
27805
|
-
(0,
|
|
26589
|
+
(0, import_node_path37.join)(process.cwd(), "cli", "data")
|
|
27806
26590
|
// local dev: running from repo root
|
|
27807
26591
|
];
|
|
27808
26592
|
for (const candidate of candidates2) {
|
|
27809
|
-
if ((0, import_node_fs54.existsSync)((0,
|
|
26593
|
+
if ((0, import_node_fs54.existsSync)((0, import_node_path37.join)(candidate, "skills"))) {
|
|
27810
26594
|
return candidate;
|
|
27811
26595
|
}
|
|
27812
26596
|
}
|
|
@@ -27815,8 +26599,8 @@ function resolveDataDir2() {
|
|
|
27815
26599
|
);
|
|
27816
26600
|
}
|
|
27817
26601
|
async function copyDir(src, dest) {
|
|
27818
|
-
await (0,
|
|
27819
|
-
await (0,
|
|
26602
|
+
await (0, import_promises17.mkdir)(dest, { recursive: true });
|
|
26603
|
+
await (0, import_promises17.cp)(src, dest, { recursive: true, force: true });
|
|
27820
26604
|
}
|
|
27821
26605
|
async function skillIsCurrent(src, dest) {
|
|
27822
26606
|
const list2 = (dir) => {
|
|
@@ -27824,7 +26608,7 @@ async function skillIsCurrent(src, dest) {
|
|
|
27824
26608
|
const walk2 = (d, prefix) => {
|
|
27825
26609
|
for (const e of (0, import_node_fs54.readdirSync)(d, { withFileTypes: true })) {
|
|
27826
26610
|
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
27827
|
-
if (e.isDirectory()) walk2((0,
|
|
26611
|
+
if (e.isDirectory()) walk2((0, import_node_path37.join)(d, e.name), rel);
|
|
27828
26612
|
else if (e.isFile()) out.push(rel);
|
|
27829
26613
|
}
|
|
27830
26614
|
};
|
|
@@ -27835,8 +26619,8 @@ async function skillIsCurrent(src, dest) {
|
|
|
27835
26619
|
const shipped = list2(src);
|
|
27836
26620
|
if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
|
|
27837
26621
|
for (const rel of shipped) {
|
|
27838
|
-
const a = await (0,
|
|
27839
|
-
const b = await (0,
|
|
26622
|
+
const a = await (0, import_promises17.readFile)((0, import_node_path37.join)(src, rel), "utf-8");
|
|
26623
|
+
const b = await (0, import_promises17.readFile)((0, import_node_path37.join)(dest, rel), "utf-8");
|
|
27840
26624
|
if (a !== b) return false;
|
|
27841
26625
|
}
|
|
27842
26626
|
return true;
|
|
@@ -28023,7 +26807,7 @@ async function healStaleAnalysisConfig(globals) {
|
|
|
28023
26807
|
printWarn(" Your analysis config names pattern ids that no longer resolve \u2014 those tools were");
|
|
28024
26808
|
printWarn(" running silently with nothing enabled. Re-deriving it from your Standard\u2026");
|
|
28025
26809
|
try {
|
|
28026
|
-
const content = (0, import_yaml6.parse)(await (0,
|
|
26810
|
+
const content = (0, import_yaml6.parse)(await (0, import_promises17.readFile)(standardPath, "utf-8"));
|
|
28027
26811
|
const derived = await deriveConfigForStandard(content);
|
|
28028
26812
|
for (const path of derived.written) printInfo(` ${path} \u2713 (re-derived)`);
|
|
28029
26813
|
for (const note of derived.notes) printWarn(` ${note}`);
|
|
@@ -28104,7 +26888,7 @@ async function handoffToSetup(enabled, claudeInstalled) {
|
|
|
28104
26888
|
console.log(" Usually a minute or two. Quit any time \u2014 re-running /verity-setup resumes.");
|
|
28105
26889
|
console.log("");
|
|
28106
26890
|
const startedAt = Date.now();
|
|
28107
|
-
const run2 = (0,
|
|
26891
|
+
const run2 = (0, import_node_child_process16.spawnSync)("claude", ["/verity-setup"], { stdio: "inherit" });
|
|
28108
26892
|
if (run2.error) {
|
|
28109
26893
|
printWarn(`Could not start Claude Code: ${run2.error.message}`);
|
|
28110
26894
|
return instruct("start it yourself and run the skill there");
|
|
@@ -28114,12 +26898,12 @@ async function handoffToSetup(enabled, claudeInstalled) {
|
|
|
28114
26898
|
async function installSkills(force, step) {
|
|
28115
26899
|
step("Installing skills");
|
|
28116
26900
|
const dataDir = resolveDataDir2();
|
|
28117
|
-
const skillsSource = (0,
|
|
26901
|
+
const skillsSource = (0, import_node_path37.join)(dataDir, "skills");
|
|
28118
26902
|
const skillsDest = ".claude/skills";
|
|
28119
26903
|
let skillsInstalled = 0;
|
|
28120
26904
|
for (const skill of SKILLS) {
|
|
28121
|
-
const src = (0,
|
|
28122
|
-
const dest = (0,
|
|
26905
|
+
const src = (0, import_node_path37.join)(skillsSource, skill);
|
|
26906
|
+
const dest = (0, import_node_path37.join)(skillsDest, skill);
|
|
28123
26907
|
if (!(0, import_node_fs54.existsSync)(src)) {
|
|
28124
26908
|
printWarn(` Skill data not found: ${skill}`);
|
|
28125
26909
|
continue;
|
|
@@ -28188,7 +26972,7 @@ async function checkPrerequisites(step) {
|
|
|
28188
26972
|
}
|
|
28189
26973
|
async function scaffoldProject(step, defaultsOnly) {
|
|
28190
26974
|
step("Knowledge base, .gitignore and CLAUDE.md");
|
|
28191
|
-
await (0,
|
|
26975
|
+
await (0, import_promises17.mkdir)(VERITY_DIR, { recursive: true });
|
|
28192
26976
|
await ensureMemoryDir();
|
|
28193
26977
|
const ignoreResult = ensureVerityGitignore();
|
|
28194
26978
|
if (ignoreResult === "failed") {
|
|
@@ -28353,8 +27137,8 @@ function registerInitCommand(program2) {
|
|
|
28353
27137
|
printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
|
|
28354
27138
|
}
|
|
28355
27139
|
await scaffoldProject(step, defaultsOnly);
|
|
28356
|
-
const globalVerityDir = (0,
|
|
28357
|
-
await (0,
|
|
27140
|
+
const globalVerityDir = (0, import_node_path37.join)(process.env.HOME ?? "", ".verity");
|
|
27141
|
+
await (0, import_promises17.mkdir)(globalVerityDir, { recursive: true });
|
|
28358
27142
|
console.log("");
|
|
28359
27143
|
step("Wiring Claude Code hooks");
|
|
28360
27144
|
const gitMoments = [
|
|
@@ -28452,7 +27236,7 @@ function registerInitCommand(program2) {
|
|
|
28452
27236
|
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
28453
27237
|
init: {
|
|
28454
27238
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28455
|
-
cli_version: true ? "0.32.
|
|
27239
|
+
cli_version: true ? "0.32.7" : "dev"
|
|
28456
27240
|
}
|
|
28457
27241
|
});
|
|
28458
27242
|
} catch (err) {
|
|
@@ -28503,7 +27287,7 @@ function registerInitCommand(program2) {
|
|
|
28503
27287
|
|
|
28504
27288
|
// src/commands/uninstall.ts
|
|
28505
27289
|
var import_node_fs55 = require("node:fs");
|
|
28506
|
-
var
|
|
27290
|
+
var import_node_path38 = require("node:path");
|
|
28507
27291
|
function registerUninstallCommand(program2) {
|
|
28508
27292
|
program2.command("uninstall").description("Remove Verity from this project (skills, hooks, .verity/, VERITY.md)").option("--dry-run", "Show what would be removed without doing it").option("--purge-global", "Also remove ~/.verity/ (deletes saved tokens \u2014 reconnect requires re-registration)").option("--keep-verity-md", "Keep the project root VERITY.md file").action(async (opts) => {
|
|
28509
27293
|
const dryRun = opts.dryRun ?? false;
|
|
@@ -28512,7 +27296,7 @@ function registerUninstallCommand(program2) {
|
|
|
28512
27296
|
const actions = [];
|
|
28513
27297
|
const skillsRoot = projectPath(".claude/skills");
|
|
28514
27298
|
for (const name of PROJECT_SKILL_NAMES) {
|
|
28515
|
-
const dir = (0,
|
|
27299
|
+
const dir = (0, import_node_path38.join)(skillsRoot, name);
|
|
28516
27300
|
if ((0, import_node_fs55.existsSync)(dir)) {
|
|
28517
27301
|
actions.push({
|
|
28518
27302
|
label: `Remove .claude/skills/${name}/`,
|
|
@@ -28558,7 +27342,7 @@ function registerUninstallCommand(program2) {
|
|
|
28558
27342
|
}
|
|
28559
27343
|
});
|
|
28560
27344
|
const home = process.env.HOME ?? "";
|
|
28561
|
-
const globalVerityDir = (0,
|
|
27345
|
+
const globalVerityDir = (0, import_node_path38.join)(home, ".verity");
|
|
28562
27346
|
if (purgeGlobal && (0, import_node_fs55.existsSync)(globalVerityDir)) {
|
|
28563
27347
|
actions.push({
|
|
28564
27348
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
@@ -28757,7 +27541,7 @@ function registerTaskCommands(program2) {
|
|
|
28757
27541
|
|
|
28758
27542
|
// src/commands/reset.ts
|
|
28759
27543
|
var import_node_fs56 = require("node:fs");
|
|
28760
|
-
var
|
|
27544
|
+
var import_node_path39 = require("node:path");
|
|
28761
27545
|
function registerResetCommand(program2) {
|
|
28762
27546
|
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) => {
|
|
28763
27547
|
const globals = program2.opts();
|
|
@@ -28798,7 +27582,7 @@ function registerResetCommand(program2) {
|
|
|
28798
27582
|
for (const entry of (0, import_node_fs56.readdirSync)(cacheDir)) {
|
|
28799
27583
|
if (entry.startsWith("pending-")) {
|
|
28800
27584
|
try {
|
|
28801
|
-
(0, import_node_fs56.unlinkSync)((0,
|
|
27585
|
+
(0, import_node_fs56.unlinkSync)((0, import_node_path39.join)(cacheDir, entry));
|
|
28802
27586
|
purged++;
|
|
28803
27587
|
} catch {
|
|
28804
27588
|
}
|
|
@@ -28825,7 +27609,7 @@ function registerResetCommand(program2) {
|
|
|
28825
27609
|
if ((0, import_node_fs56.existsSync)(logsDir)) {
|
|
28826
27610
|
for (const entry of (0, import_node_fs56.readdirSync)(logsDir)) {
|
|
28827
27611
|
try {
|
|
28828
|
-
(0, import_node_fs56.unlinkSync)((0,
|
|
27612
|
+
(0, import_node_fs56.unlinkSync)((0, import_node_path39.join)(logsDir, entry));
|
|
28829
27613
|
} catch {
|
|
28830
27614
|
}
|
|
28831
27615
|
}
|
|
@@ -28837,52 +27621,10 @@ function registerResetCommand(program2) {
|
|
|
28837
27621
|
}
|
|
28838
27622
|
|
|
28839
27623
|
// src/commands/reflect.ts
|
|
28840
|
-
var import_node_fs57 = require("node:fs");
|
|
28841
|
-
|
|
28842
|
-
// src/lib/reflection-globs.ts
|
|
28843
|
-
var MAX_GLOBS = 6;
|
|
28844
|
-
var PATH_TOKEN = /[A-Za-z0-9_./-]*[A-Za-z0-9_-]\.[A-Za-z0-9]{1,8}(?::\d+(?::\d+)?)?/g;
|
|
28845
|
-
function clean(token) {
|
|
28846
|
-
return token.replace(/:\d+(?::\d+)?$/, "").replace(/^[`'"(\[]+/, "").replace(/[`'"),\].;!?]+$/, "").replace(/^\.\//, "");
|
|
28847
|
-
}
|
|
28848
|
-
function resolve5(token, treePaths) {
|
|
28849
|
-
if (treePaths.includes(token)) return token;
|
|
28850
|
-
const suffix = token.startsWith("/") ? token : `/${token}`;
|
|
28851
|
-
const matches = treePaths.filter((p) => p.endsWith(suffix));
|
|
28852
|
-
return matches.length === 1 ? matches[0] : null;
|
|
28853
|
-
}
|
|
28854
|
-
function deriveFileGlobs(text, treePaths) {
|
|
28855
|
-
if (!text || treePaths.length === 0) return [];
|
|
28856
|
-
const out = [];
|
|
28857
|
-
const seen = /* @__PURE__ */ new Set();
|
|
28858
|
-
for (const raw of text.match(PATH_TOKEN) ?? []) {
|
|
28859
|
-
const token = clean(raw);
|
|
28860
|
-
if (!token) continue;
|
|
28861
|
-
const path = resolve5(token, treePaths);
|
|
28862
|
-
if (!path || seen.has(path)) continue;
|
|
28863
|
-
seen.add(path);
|
|
28864
|
-
out.push(path);
|
|
28865
|
-
if (out.length >= MAX_GLOBS) break;
|
|
28866
|
-
}
|
|
28867
|
-
return out;
|
|
28868
|
-
}
|
|
28869
|
-
|
|
28870
|
-
// src/commands/reflect.ts
|
|
28871
|
-
async function writeNodeToDisk(args) {
|
|
28872
|
-
try {
|
|
28873
|
-
const written = await applyMemoryWrites(
|
|
28874
|
-
[{ path: args.filePath, content: args.content, node_id: args.nodeId, op: "create" }],
|
|
28875
|
-
{ treePaths: listTrackedFiles(), recordBaseline: false }
|
|
28876
|
-
);
|
|
28877
|
-
if (written === 0) return false;
|
|
28878
|
-
return (0, import_node_fs57.existsSync)(projectPath(`${VERITY_DIR}/memory/${args.filePath}`));
|
|
28879
|
-
} catch {
|
|
28880
|
-
return false;
|
|
28881
|
-
}
|
|
28882
|
-
}
|
|
28883
27624
|
function registerReflectCommand(program2) {
|
|
28884
|
-
program2.command("reflect").description("Capture learnings \u2014 auto-extract or
|
|
27625
|
+
program2.command("reflect").description("Capture learnings \u2014 auto-extract or submit a human reflection").option("--user-input <text>", "The reflection to record (the agent-drafted or user-confirmed text)").option("--kind <kind>", "Node kind (decision, gotcha, pattern, security, quality, intent, domain, integration)", "gotcha").option("--task-id <id>", "Task to reflect on (defaults to current task)").option("--autonomous", "Record the drafted reflection without a confirm step (auto-detected from TTY / VERITY_AUTONOMOUS when omitted)").action(async (opts) => {
|
|
28885
27626
|
const globals = program2.opts();
|
|
27627
|
+
const mode2 = resolveRunMode({ autonomousFlag: opts.autonomous });
|
|
28886
27628
|
await ensureMemoryDir();
|
|
28887
27629
|
const tokenResult = await resolveToken(globals.token);
|
|
28888
27630
|
if (!tokenResult.ok) {
|
|
@@ -28947,11 +27689,6 @@ function registerReflectCommand(program2) {
|
|
|
28947
27689
|
if (userInput) {
|
|
28948
27690
|
const validKinds = ["decision", "gotcha", "pattern", "security", "quality", "intent", "domain", "integration"];
|
|
28949
27691
|
const kind = validKinds.includes(opts.kind) ? opts.kind : "gotcha";
|
|
28950
|
-
const confirmed = opts.confirmed === true;
|
|
28951
|
-
const source = confirmed ? "user" : "agent";
|
|
28952
|
-
const explicitGlobs = Array.isArray(opts.fileGlobs) ? opts.fileGlobs.map((g) => g.trim()).filter(Boolean) : [];
|
|
28953
|
-
const derivedGlobs = explicitGlobs.length > 0 ? [] : deriveFileGlobs(userInput, listTrackedFiles());
|
|
28954
|
-
const fileGlobs = explicitGlobs.length > 0 ? explicitGlobs : derivedGlobs.length > 0 ? derivedGlobs : inheritedGlobs;
|
|
28955
27692
|
const result = await apiRequest({
|
|
28956
27693
|
method: "POST",
|
|
28957
27694
|
path: "/compound/memory/nodes",
|
|
@@ -28962,23 +27699,14 @@ function registerReflectCommand(program2) {
|
|
|
28962
27699
|
title: userInput.slice(0, 200),
|
|
28963
27700
|
body: userInput,
|
|
28964
27701
|
domains: [],
|
|
28965
|
-
|
|
28966
|
-
|
|
28967
|
-
|
|
28968
|
-
//
|
|
28969
|
-
|
|
28970
|
-
// error. Sending it is what lets an auto-recorded reflection carry
|
|
28971
|
-
// the same attribution a dashboard-written one does; without it the
|
|
28972
|
-
// unreviewed node is the one that cannot say where it came from.
|
|
28973
|
-
...taskId && { task_id: taskId },
|
|
28974
|
-
// Server-side default for each source; sent explicitly so an older
|
|
28975
|
-
// server (which knows 'user'/'extractor'/'linter' only, and would
|
|
28976
|
-
// silently coerce an unknown source to 'user' at 1.0) still records
|
|
28977
|
-
// an honest confidence for a draft nobody confirmed.
|
|
28978
|
-
confidence: confirmed ? 1 : 0.7
|
|
27702
|
+
// Inherit globs from the current task's context_files so the
|
|
27703
|
+
// reflection is actually reachable by reviewer injection.
|
|
27704
|
+
// Falls back to [] when there's no task context — user can
|
|
27705
|
+
// edit the markdown later to tighten retrieval.
|
|
27706
|
+
file_globs: inheritedGlobs
|
|
28979
27707
|
},
|
|
28980
27708
|
verbose: globals.verbose,
|
|
28981
|
-
cmd:
|
|
27709
|
+
cmd: "reflect_user"
|
|
28982
27710
|
});
|
|
28983
27711
|
if (!result.ok) {
|
|
28984
27712
|
printError(`Failed to save reflection: ${result.error}`);
|
|
@@ -28986,23 +27714,12 @@ function registerReflectCommand(program2) {
|
|
|
28986
27714
|
}
|
|
28987
27715
|
const nodeId = result.data.node_id;
|
|
28988
27716
|
const filePath = result.data.file_path;
|
|
28989
|
-
|
|
28990
|
-
const content = typeof result.data.content === "string" ? result.data.content : null;
|
|
28991
|
-
const synced = content !== null && await writeNodeToDisk({ filePath, content, nodeId });
|
|
28992
|
-
printInfo(
|
|
28993
|
-
synced ? `Recorded ${nodeId} \u2192 .verity/memory/${filePath}` : `Recorded ${nodeId} (syncs to .verity/memory/${filePath} on the next analysis).`
|
|
28994
|
-
);
|
|
28995
|
-
if (viewUrl) printInfo(` ${viewUrl}`);
|
|
27717
|
+
printInfo(`Queued: ${nodeId} (will sync to .verity/memory/${filePath} on next analysis).`);
|
|
28996
27718
|
printInfo(
|
|
28997
|
-
|
|
27719
|
+
mode2 === "autonomous" ? "Recorded autonomously (no confirm step) into the project knowledge base." : "Recorded your confirmed reflection into the project knowledge base."
|
|
28998
27720
|
);
|
|
28999
|
-
if (fileGlobs.length === 0) {
|
|
29000
|
-
printWarn(
|
|
29001
|
-
'No files matched \u2014 this reflection will not surface in future reviews. Cite a path in the text, or pass --file-globs "<path or glob>".'
|
|
29002
|
-
);
|
|
29003
|
-
}
|
|
29004
27721
|
if (taskResolution === "none") {
|
|
29005
|
-
printInfo("
|
|
27722
|
+
printInfo("(No task context \u2014 reflection lives at the project level.)");
|
|
29006
27723
|
}
|
|
29007
27724
|
} else {
|
|
29008
27725
|
if (!taskId) {
|
|
@@ -29089,121 +27806,6 @@ function registerMemoryCommand(program2) {
|
|
|
29089
27806
|
}
|
|
29090
27807
|
process.exit(result.failed === 0 ? 0 : 1);
|
|
29091
27808
|
});
|
|
29092
|
-
memory.command("pull").description("Download this repo's knowledge graph into .verity/memory/ (the session-start hook runs it)").option("--force", "Fetch every node even if nothing changed since the last pull").option("--quiet", "Print nothing and always exit 0 (for the session-start hook)").action(async (opts) => {
|
|
29093
|
-
const globals = program2.opts();
|
|
29094
|
-
const quiet = !!opts.quiet;
|
|
29095
|
-
const fail = (message) => {
|
|
29096
|
-
if (!quiet) printError(message);
|
|
29097
|
-
process.exit(quiet ? 0 : 1);
|
|
29098
|
-
};
|
|
29099
|
-
try {
|
|
29100
|
-
process.chdir(repoRoot());
|
|
29101
|
-
} catch {
|
|
29102
|
-
}
|
|
29103
|
-
const tokenResult = await resolveToken(globals.token);
|
|
29104
|
-
if (!tokenResult.ok) return fail(tokenResult.error);
|
|
29105
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
29106
|
-
if (!urlResult.ok) return fail(urlResult.error);
|
|
29107
|
-
let result;
|
|
29108
|
-
try {
|
|
29109
|
-
result = await pullRepoMemory({
|
|
29110
|
-
serviceUrl: urlResult.data,
|
|
29111
|
-
token: tokenResult.data.token,
|
|
29112
|
-
verbose: globals.verbose,
|
|
29113
|
-
force: !!opts.force
|
|
29114
|
-
});
|
|
29115
|
-
} catch (err) {
|
|
29116
|
-
return fail(`Could not pull the knowledge graph: ${err.message}`);
|
|
29117
|
-
}
|
|
29118
|
-
logEvent("memory_pull", result.ok ? { status: result.status, received: result.received, written: result.written } : { status: "failed", category: result.category ?? null });
|
|
29119
|
-
if (!result.ok) return fail(`Could not pull the knowledge graph: ${result.error}`);
|
|
29120
|
-
if (!quiet) {
|
|
29121
|
-
printInfo(result.status === "unchanged" ? "Knowledge graph is up to date." : result.status === "tracked" ? "Skipped: .verity/memory/ is tracked by git here, and the pull never rewrites tracked files. Run `verity memory untrack` to make the graph machine-local." : `Pulled ${result.received} node(s); ${result.written} new or updated file(s) written to .verity/memory/.`);
|
|
29122
|
-
}
|
|
29123
|
-
let org;
|
|
29124
|
-
try {
|
|
29125
|
-
org = await pullOrgKnowledge({
|
|
29126
|
-
serviceUrl: urlResult.data,
|
|
29127
|
-
token: tokenResult.data.token,
|
|
29128
|
-
verbose: globals.verbose,
|
|
29129
|
-
force: !!opts.force
|
|
29130
|
-
});
|
|
29131
|
-
} catch (err) {
|
|
29132
|
-
org = { ok: false, error: err.message };
|
|
29133
|
-
}
|
|
29134
|
-
logEvent("memory_org_pull", org.ok ? { status: org.status, received: org.received } : { status: "failed", category: org.category ?? null });
|
|
29135
|
-
if (!quiet) {
|
|
29136
|
-
if (!org.ok) printWarn(`Could not refresh the org knowledge mirror: ${org.error}`);
|
|
29137
|
-
else if (org.status === "pulled") printInfo(`Org knowledge: ${org.received} claim(s) mirrored at ${org.dir}.`);
|
|
29138
|
-
else if (org.status === "unchanged") printInfo("Org knowledge mirror is up to date.");
|
|
29139
|
-
}
|
|
29140
|
-
process.exit(0);
|
|
29141
|
-
});
|
|
29142
|
-
memory.command("org").description("List the org knowledge mirrored for this repository's organization").action(async () => {
|
|
29143
|
-
try {
|
|
29144
|
-
process.chdir(repoRoot());
|
|
29145
|
-
} catch {
|
|
29146
|
-
}
|
|
29147
|
-
const dir = orgMirrorDir();
|
|
29148
|
-
if (!dir) {
|
|
29149
|
-
printError("Not a repository with an origin remote, so there is no organization to list.");
|
|
29150
|
-
process.exit(1);
|
|
29151
|
-
}
|
|
29152
|
-
const claims = await readOrgMirror();
|
|
29153
|
-
if (claims.length === 0) {
|
|
29154
|
-
printInfo(`No org knowledge mirrored yet (${dir}). Run \`verity memory pull\`.`);
|
|
29155
|
-
process.exit(0);
|
|
29156
|
-
}
|
|
29157
|
-
for (const c of claims) console.log(`${c.tag_id} ${c.kind.padEnd(11)} ${c.title} \xB7 ${c.origin}`);
|
|
29158
|
-
printInfo(`${claims.length} claim(s) at ${dir}. Demote one with \`verity memory demote <id> --reason "\u2026"\`.`);
|
|
29159
|
-
process.exit(0);
|
|
29160
|
-
});
|
|
29161
|
-
memory.command("demote <claim-id>").description("Demote an org knowledge claim for the whole organization, with a reason").requiredOption("--reason <text>", "Why this claim should no longer be shared (1\u2013500 characters)").action(async (claimId, opts) => {
|
|
29162
|
-
const globals = program2.opts();
|
|
29163
|
-
try {
|
|
29164
|
-
process.chdir(repoRoot());
|
|
29165
|
-
} catch {
|
|
29166
|
-
}
|
|
29167
|
-
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(claimId)) {
|
|
29168
|
-
printError("The claim id is the `id` in the mirrored file, a uuid. `verity memory org` lists them.");
|
|
29169
|
-
process.exit(1);
|
|
29170
|
-
}
|
|
29171
|
-
const reason = opts.reason.trim();
|
|
29172
|
-
if (reason.length === 0 || reason.length > 500) {
|
|
29173
|
-
printError("--reason is 1 to 500 characters.");
|
|
29174
|
-
process.exit(1);
|
|
29175
|
-
}
|
|
29176
|
-
const tokenResult = await resolveToken(globals.token);
|
|
29177
|
-
if (!tokenResult.ok) {
|
|
29178
|
-
printError(tokenResult.error);
|
|
29179
|
-
process.exit(1);
|
|
29180
|
-
}
|
|
29181
|
-
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
29182
|
-
if (!urlResult.ok) {
|
|
29183
|
-
printError(urlResult.error);
|
|
29184
|
-
process.exit(1);
|
|
29185
|
-
}
|
|
29186
|
-
const res = await apiRequest({
|
|
29187
|
-
method: "POST",
|
|
29188
|
-
path: `/memory/org/claims/${claimId}/demote`,
|
|
29189
|
-
serviceUrl: urlResult.data,
|
|
29190
|
-
token: tokenResult.data.token,
|
|
29191
|
-
body: { reason },
|
|
29192
|
-
verbose: globals.verbose,
|
|
29193
|
-
cmd: "memory-demote",
|
|
29194
|
-
extraHeaders: { "X-Verity-Via": "cli" }
|
|
29195
|
-
});
|
|
29196
|
-
if (!res.ok) {
|
|
29197
|
-
printError(`Could not demote the claim: ${res.error}`);
|
|
29198
|
-
process.exit(1);
|
|
29199
|
-
}
|
|
29200
|
-
printInfo(`Demoted ${claimId} for the whole organization. Restore it from the dashboard if that was wrong.`);
|
|
29201
|
-
try {
|
|
29202
|
-
await pullOrgKnowledge({ serviceUrl: urlResult.data, token: tokenResult.data.token, verbose: globals.verbose, force: true });
|
|
29203
|
-
} catch {
|
|
29204
|
-
}
|
|
29205
|
-
process.exit(0);
|
|
29206
|
-
});
|
|
29207
27809
|
memory.command("untrack").description("Stop committing .verity/memory/ \u2014 the graph stays on disk and leaves your diffs").action(() => {
|
|
29208
27810
|
try {
|
|
29209
27811
|
process.chdir(repoRoot());
|
|
@@ -29367,8 +27969,8 @@ function registerTelemetryCommands(program2) {
|
|
|
29367
27969
|
}
|
|
29368
27970
|
|
|
29369
27971
|
// src/cli.ts
|
|
29370
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.32.
|
|
29371
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.
|
|
27972
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.32.7").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) => {
|
|
27973
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.7");
|
|
29372
27974
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
29373
27975
|
try {
|
|
29374
27976
|
await foldLegacyLocalCredential();
|