@codacy/verity-cli 0.29.4-experimental.6726d0f → 0.29.4-experimental.6f2ede0
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/bin/verity.js +606 -161
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10390,11 +10390,11 @@ var MAX_DELTA_BYTES = 194560;
|
|
|
10390
10390
|
var MAX_FILES = 40;
|
|
10391
10391
|
var MAX_FILE_BYTES = 51200;
|
|
10392
10392
|
var DEBOUNCE_SECONDS = 30;
|
|
10393
|
-
var MAX_SPEC_FILES =
|
|
10394
|
-
var MAX_SPEC_FILE_BYTES =
|
|
10395
|
-
var MAX_TOTAL_SPEC_BYTES =
|
|
10393
|
+
var MAX_SPEC_FILES = 6;
|
|
10394
|
+
var MAX_SPEC_FILE_BYTES = 512e3;
|
|
10395
|
+
var MAX_TOTAL_SPEC_BYTES = 512e3;
|
|
10396
10396
|
var MAX_PLAN_FILES = 3;
|
|
10397
|
-
var MAX_PLAN_FILE_BYTES =
|
|
10397
|
+
var MAX_PLAN_FILE_BYTES = 512e3;
|
|
10398
10398
|
var MAX_INTENT_CHARS = 2e3;
|
|
10399
10399
|
var SNAPSHOT_DIR = `${VERITY_DIR}/.snapshot`;
|
|
10400
10400
|
var BASELINE_DIR = `${VERITY_DIR}/.baseline`;
|
|
@@ -10502,6 +10502,7 @@ var GITHUB_APP_INSTALL_URL = `https://github.com/apps/${GITHUB_APP_SLUG}/install
|
|
|
10502
10502
|
function githubAppInstallUrl(accountId) {
|
|
10503
10503
|
return accountId != null ? `https://github.com/apps/${GITHUB_APP_SLUG}/installations/new/permissions?target_id=${accountId}` : GITHUB_APP_INSTALL_URL;
|
|
10504
10504
|
}
|
|
10505
|
+
var ADVISORY_EPISODE_FILE = `${VERITY_DIR}/.advisory-episode`;
|
|
10505
10506
|
|
|
10506
10507
|
// src/lib/output.ts
|
|
10507
10508
|
var RED = "\x1B[0;31m";
|
|
@@ -15060,8 +15061,10 @@ function recordVerdict(d, v) {
|
|
|
15060
15061
|
if (summary) appendEvent(d, { k: "goal_delivered", summary });
|
|
15061
15062
|
}
|
|
15062
15063
|
const lines = /* @__PURE__ */ new Map();
|
|
15064
|
+
const sent = new Set(v.sentPaths);
|
|
15063
15065
|
for (const f of v.findings) {
|
|
15064
15066
|
if (!f.file || typeof f.line !== "number" || !f.pattern_id) continue;
|
|
15067
|
+
if (!sent.has(f.file)) continue;
|
|
15065
15068
|
if (!lines.has(f.file)) {
|
|
15066
15069
|
try {
|
|
15067
15070
|
const abs = (0, import_node_path12.join)(root, f.file);
|
|
@@ -16601,6 +16604,7 @@ function createRun(opts, globals) {
|
|
|
16601
16604
|
urlResult: { ok: false, error: "unresolved" },
|
|
16602
16605
|
serviceUrl: "",
|
|
16603
16606
|
token: "",
|
|
16607
|
+
modeDecision: null,
|
|
16604
16608
|
sessionIdForMemory: "",
|
|
16605
16609
|
contextFilePaths: [],
|
|
16606
16610
|
analysisMode: "standard",
|
|
@@ -16713,8 +16717,15 @@ function formatRunEvidence(run, startedAt) {
|
|
|
16713
16717
|
out += row("changed", `${run.changedUniverse.length} from git \xB7 analyzable ${run.analyzable.length} \xB7 reviewable ${run.reviewable.length} \xB7 security ${run.securityFiles.length} \xB7 forReview ${run.allForReview.length}`);
|
|
16714
16718
|
const done = (phase) => run.phasesCompleted.includes(phase);
|
|
16715
16719
|
const ifDone = (phase, value) => done(phase) ? value : "?";
|
|
16716
|
-
|
|
16717
|
-
if (
|
|
16720
|
+
const md = run.modeDecision;
|
|
16721
|
+
if (md) {
|
|
16722
|
+
const how = md.forced ? "forced by --mode" : `predicted=${md.predicted ?? "none"} \u2192 ${md.resolved}`;
|
|
16723
|
+
out += row("mode", `${md.resolved} \xB7 ${how}` + (md.flip ? ` (flipped to plan: no delta at ${md.flip})` : "") + ` \xB7 authored=${md.authored ? "yes" : "no"} \xB7 investigated=${md.investigated ? "yes" : "no"}`);
|
|
16724
|
+
} else {
|
|
16725
|
+
out += row("mode", `? (this run stopped in ${run.phaseReached || "no phase"}, before the mode was decided)`);
|
|
16726
|
+
}
|
|
16727
|
+
out += row("signals", `baseline=${ifDone("bootstrap", run.baseline ? "yes" : "no")} \xB7 authored=${ifDone("intentInputs", run.turnAuthoredCode ? "yes" : "no")} \xB7 observable=${ifDone("intentInputs", run.authorshipIsObservable ? "yes" : "no")}` + (run.actionSummary?.transcript_windowed ? ` \xB7 window=${run.actionSummary.transcript_windowed}` : ""));
|
|
16728
|
+
if (!done("intentInputs")) {
|
|
16718
16729
|
out += row("", `(\`?\` = the phase that determines it did not complete \u2014 this run stopped in ${run.phaseReached})`);
|
|
16719
16730
|
}
|
|
16720
16731
|
out += row("sent", `${sent.length} \xB7 ${list(sent)}`);
|
|
@@ -16741,6 +16752,46 @@ function formatRunEvidence(run, startedAt) {
|
|
|
16741
16752
|
out += row("withheld", "(nothing \u2014 every changed file was reviewed)");
|
|
16742
16753
|
}
|
|
16743
16754
|
}
|
|
16755
|
+
const cov = run.foldResult?.coverage;
|
|
16756
|
+
if (cov) {
|
|
16757
|
+
const delegated = run.foldResult.authored.filter((a) => a.owner === "subagent").length;
|
|
16758
|
+
if (cov.dispatched > 0 || cov.subagentFiles > 0 || cov.subagentSkipped > 0) {
|
|
16759
|
+
out += row("delegated", `${cov.dispatched} dispatched \xB7 ${cov.subagentFiles} agent log(s) read \xB7 ${delegated} path(s) attributed to subagents`);
|
|
16760
|
+
}
|
|
16761
|
+
if (cov.subagentSkipped > 0) {
|
|
16762
|
+
out += row("", `\u26A0 ${cov.subagentSkipped} agent log(s) REFUSED by the byte budget \u2014 the authored set above is PARTIAL`);
|
|
16763
|
+
}
|
|
16764
|
+
if (cov.dispatched > 0 && cov.subagentFiles === 0) {
|
|
16765
|
+
out += row("", `\u26A0 this turn dispatched ${cov.dispatched} agent(s) but no agent log was found \u2014 delegated work is unattributed (has the transcript layout moved?)`);
|
|
16766
|
+
}
|
|
16767
|
+
if (cov.outsideRepo > 0) {
|
|
16768
|
+
out += row("", `${cov.outsideRepo} authored path(s) refused as outside the repo`);
|
|
16769
|
+
}
|
|
16770
|
+
}
|
|
16771
|
+
if (run.foldResult?.tools?.length) {
|
|
16772
|
+
const shown = run.foldResult.tools.slice(0, 6).map((t) => {
|
|
16773
|
+
const outcome = t.failed > 0 ? `${t.failed} failed` : t.last_status === 0 ? "ok" : "?";
|
|
16774
|
+
const where = t.targets.length > 0 ? ` \u2192 ${t.targets.slice(0, 2).join(", ")}` : "";
|
|
16775
|
+
return `${t.runs}\xD7 ${t.name} (${outcome})${where}`;
|
|
16776
|
+
});
|
|
16777
|
+
const more = run.foldResult.tools.length > 6 ? ` \u2026 +${run.foldResult.tools.length - 6} more` : "";
|
|
16778
|
+
out += row("tools", shown.join(" \xB7 ") + more);
|
|
16779
|
+
if (run.foldResult.coverage.toolNamesDropped > 0) {
|
|
16780
|
+
out += row("", `\u26A0 ${run.foldResult.coverage.toolNamesDropped} tool name(s) refused by the cap`);
|
|
16781
|
+
}
|
|
16782
|
+
}
|
|
16783
|
+
if (run.foldResult?.tasks?.length) {
|
|
16784
|
+
const t = run.foldResult.tasks;
|
|
16785
|
+
const done2 = t.filter((x) => x.status === "completed").length;
|
|
16786
|
+
out += row("tasks", `${t.length} \xB7 ${done2} completed \xB7 ` + list(t.slice(0, 4).map((x) => `#${x.id} ${x.name} [${x.status}]`), 4));
|
|
16787
|
+
}
|
|
16788
|
+
if (run.specs?.length) {
|
|
16789
|
+
const readThisSession = new Set(run.actionSummary?.files_read ?? []);
|
|
16790
|
+
const labelled = run.specs.map(
|
|
16791
|
+
(s) => `${s.path}${readThisSession.has(s.path) ? " (read)" : " (positional)"}`
|
|
16792
|
+
);
|
|
16793
|
+
out += row("specs", `${run.specs.length} \xB7 ${list(labelled, 5)}`);
|
|
16794
|
+
}
|
|
16744
16795
|
if (run.staticResults.findings.length > 0) {
|
|
16745
16796
|
out += row("static", `${run.staticResults.findings.length} finding(s) from ${run.staticResults.summary.tools_run.join(", ") || "no tools"}`);
|
|
16746
16797
|
}
|
|
@@ -16936,6 +16987,8 @@ function buildSummary(lines) {
|
|
|
16936
16987
|
break;
|
|
16937
16988
|
case "Agent":
|
|
16938
16989
|
case "Task":
|
|
16990
|
+
case "Workflow":
|
|
16991
|
+
case "SendMessage":
|
|
16939
16992
|
subagents++;
|
|
16940
16993
|
break;
|
|
16941
16994
|
case "WebFetch":
|
|
@@ -17096,6 +17149,39 @@ async function bootstrap(run) {
|
|
|
17096
17149
|
Object.assign(run, { actionSummary, assistantResponse, baseline, baselineSessionId, reachability, sessionId, stopReason, tokenResult, transcriptPath, turnId });
|
|
17097
17150
|
}
|
|
17098
17151
|
|
|
17152
|
+
// src/lib/self-scope.ts
|
|
17153
|
+
var LEGACY_GATE_SKILLS = /* @__PURE__ */ new Set([
|
|
17154
|
+
"gate-setup",
|
|
17155
|
+
"gate-analyze",
|
|
17156
|
+
"gate-review",
|
|
17157
|
+
"gate-status",
|
|
17158
|
+
"gate-feedback",
|
|
17159
|
+
"gate-insights",
|
|
17160
|
+
"gate-learn",
|
|
17161
|
+
"gate-memory",
|
|
17162
|
+
"gate-reflect"
|
|
17163
|
+
]);
|
|
17164
|
+
function isVerityOwned(path) {
|
|
17165
|
+
const segments = path.replace(/\\/g, "/").split("/");
|
|
17166
|
+
for (let i = 0; i < segments.length; i++) {
|
|
17167
|
+
const seg = segments[i];
|
|
17168
|
+
if (seg === ".verity" || seg === ".codacy") return true;
|
|
17169
|
+
if (i === segments.length - 1 && (seg === "VERITY.md" || seg === "GATE.md")) return true;
|
|
17170
|
+
if (seg === ".claude" && segments[i + 1] === "skills" && typeof segments[i + 2] === "string") {
|
|
17171
|
+
const skill = segments[i + 2];
|
|
17172
|
+
if (skill.startsWith("verity-") || LEGACY_GATE_SKILLS.has(skill)) return true;
|
|
17173
|
+
}
|
|
17174
|
+
if (seg === ".claude" && segments[i + 1] === "settings.json") return true;
|
|
17175
|
+
}
|
|
17176
|
+
return false;
|
|
17177
|
+
}
|
|
17178
|
+
function partitionVerityOwned(paths) {
|
|
17179
|
+
const kept = [];
|
|
17180
|
+
const owned = [];
|
|
17181
|
+
for (const p of paths) (isVerityOwned(p) ? owned : kept).push(p);
|
|
17182
|
+
return { kept, owned };
|
|
17183
|
+
}
|
|
17184
|
+
|
|
17099
17185
|
// src/lib/channel.ts
|
|
17100
17186
|
var MAX_AGENT_CONTEXT_CHARS = 1500;
|
|
17101
17187
|
var MAX_AGENT_ITEMS = 5;
|
|
@@ -17105,6 +17191,29 @@ function renderItem(label2, text, patternId, file, line) {
|
|
|
17105
17191
|
const id = patternId ? ` [${patternId}]` : "";
|
|
17106
17192
|
return `- ${label2}${text}${where}${id}`;
|
|
17107
17193
|
}
|
|
17194
|
+
function channelInputFrom(response, intentRepeat = 0, priorPendingFingerprints = []) {
|
|
17195
|
+
const metadata = response.metadata ?? {};
|
|
17196
|
+
const intent = response.intent_alignment ?? {};
|
|
17197
|
+
return {
|
|
17198
|
+
intentRepeat,
|
|
17199
|
+
priorPendingFingerprints,
|
|
17200
|
+
gateDecision: String(response.gate_decision ?? ""),
|
|
17201
|
+
findings: response.findings ?? [],
|
|
17202
|
+
pendingItems: response.pending_items ?? [],
|
|
17203
|
+
reviewStatus: metadata.review_status,
|
|
17204
|
+
coverage: metadata.coverage,
|
|
17205
|
+
intentVerdict: intent.verdict,
|
|
17206
|
+
intentGaps: intent.gaps
|
|
17207
|
+
};
|
|
17208
|
+
}
|
|
17209
|
+
function classifyChannelContent(input) {
|
|
17210
|
+
const refusal = input.reviewStatus === "not_reviewed" || input.reviewStatus === "no_authorship_evidence";
|
|
17211
|
+
const intentFlag = input.intentVerdict === "misaligned" || input.intentVerdict === "partial";
|
|
17212
|
+
const advisory = (input.findings ?? []).some((f) => f.scope !== "pre-existing") || (input.pendingItems ?? []).some(
|
|
17213
|
+
(p) => p.pattern_id !== "intent-misalignment" && !!(p.description ?? p.title ?? p.reason)
|
|
17214
|
+
);
|
|
17215
|
+
return { refusal, intentFlag, advisory };
|
|
17216
|
+
}
|
|
17108
17217
|
function buildAgentContext(input) {
|
|
17109
17218
|
const lines = [];
|
|
17110
17219
|
if (input.reviewStatus === "not_reviewed") {
|
|
@@ -17190,7 +17299,7 @@ function channelSilence(input) {
|
|
|
17190
17299
|
// src/lib/cli-version.ts
|
|
17191
17300
|
function cliVersion() {
|
|
17192
17301
|
try {
|
|
17193
|
-
return true ? "0.29.4-experimental.
|
|
17302
|
+
return true ? "0.29.4-experimental.6f2ede0" : "dev";
|
|
17194
17303
|
} catch {
|
|
17195
17304
|
return "dev";
|
|
17196
17305
|
}
|
|
@@ -17512,9 +17621,10 @@ async function scope(run) {
|
|
|
17512
17621
|
const { assistantResponse } = run;
|
|
17513
17622
|
const { files: allChanged, hasRecentCommitFiles } = getChangedFiles();
|
|
17514
17623
|
run.changedUniverse = allChanged;
|
|
17515
|
-
const
|
|
17516
|
-
const
|
|
17517
|
-
const
|
|
17624
|
+
const { kept: external } = partitionVerityOwned(allChanged);
|
|
17625
|
+
const analyzable = filterAnalyzable(external);
|
|
17626
|
+
const reviewable = filterReviewable(external);
|
|
17627
|
+
const securityFiles = filterSecurity(external);
|
|
17518
17628
|
const noFilesChanged = analyzable.length === 0 && reviewable.length === 0 && securityFiles.length === 0;
|
|
17519
17629
|
if (noFilesChanged && !assistantResponse) {
|
|
17520
17630
|
await passAndExit(run, "No analyzable files changed", "no-analyzable-files");
|
|
@@ -17541,18 +17651,26 @@ var SPEC_CANDIDATES = [
|
|
|
17541
17651
|
"docs/API.md",
|
|
17542
17652
|
"spec/ARCHITECTURE.md"
|
|
17543
17653
|
];
|
|
17544
|
-
|
|
17654
|
+
var DOC_EXT = /\.(md|mdx|ya?ml|txt|rst|adoc)$/i;
|
|
17655
|
+
var UNCONSULTED_FILE_BYTES = 10240;
|
|
17656
|
+
var UNCONSULTED_TOTAL_BYTES = 30720;
|
|
17657
|
+
function discoverSpecs(consulted = []) {
|
|
17545
17658
|
const result = [];
|
|
17546
17659
|
const seen = /* @__PURE__ */ new Set();
|
|
17547
17660
|
let totalBytes = 0;
|
|
17548
|
-
const
|
|
17661
|
+
const consultedDocs = new Set(
|
|
17662
|
+
consulted.filter((p) => DOC_EXT.test(p) && !p.startsWith("/") && !p.includes(".."))
|
|
17663
|
+
);
|
|
17664
|
+
const addSpec = (specPath, relevant = false) => {
|
|
17549
17665
|
if (result.length >= MAX_SPEC_FILES) return false;
|
|
17550
|
-
|
|
17666
|
+
const totalCap = relevant ? MAX_TOTAL_SPEC_BYTES : UNCONSULTED_TOTAL_BYTES;
|
|
17667
|
+
if (totalBytes >= totalCap) return false;
|
|
17551
17668
|
if (seen.has(specPath)) return true;
|
|
17552
17669
|
if (!(0, import_node_fs21.existsSync)(specPath)) return true;
|
|
17553
17670
|
seen.add(specPath);
|
|
17554
|
-
const remaining =
|
|
17555
|
-
const
|
|
17671
|
+
const remaining = totalCap - totalBytes;
|
|
17672
|
+
const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
|
|
17673
|
+
const readBytes = Math.min(fileCap, remaining);
|
|
17556
17674
|
try {
|
|
17557
17675
|
const buf = Buffer.alloc(readBytes);
|
|
17558
17676
|
const fd = (0, import_node_fs21.openSync)(specPath, "r");
|
|
@@ -17566,6 +17684,9 @@ function discoverSpecs() {
|
|
|
17566
17684
|
}
|
|
17567
17685
|
return true;
|
|
17568
17686
|
};
|
|
17687
|
+
for (const doc of consultedDocs) {
|
|
17688
|
+
if (!addSpec(doc, true)) break;
|
|
17689
|
+
}
|
|
17569
17690
|
for (const candidate of SPEC_CANDIDATES) {
|
|
17570
17691
|
if (!addSpec(candidate)) break;
|
|
17571
17692
|
}
|
|
@@ -17636,7 +17757,7 @@ function discoverPlans() {
|
|
|
17636
17757
|
async function intentInputs(run) {
|
|
17637
17758
|
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } = run;
|
|
17638
17759
|
const conversation = await readAndClearConversationBuffer(baselineSessionId);
|
|
17639
|
-
const specs = discoverSpecs();
|
|
17760
|
+
const specs = discoverSpecs(actionSummary?.files_read ?? []);
|
|
17640
17761
|
const plans = discoverPlans();
|
|
17641
17762
|
const agentAuthoredCodeThisTurn = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0));
|
|
17642
17763
|
const turnAuthoredCode = agentAuthoredCodeThisTurn || !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
|
|
@@ -17859,7 +17980,8 @@ async function mode(run) {
|
|
|
17859
17980
|
let analysisMode;
|
|
17860
17981
|
const sessionAuthoredCode = !!baseline && allForReview.some((f) => changedSinceBaseline(f, baseline));
|
|
17861
17982
|
const modeOverride = opts.mode;
|
|
17862
|
-
|
|
17983
|
+
const forced = !!modeOverride && ["standard", "plan", "debug", "skip"].includes(modeOverride);
|
|
17984
|
+
if (forced) {
|
|
17863
17985
|
analysisMode = modeOverride;
|
|
17864
17986
|
} else {
|
|
17865
17987
|
analysisMode = reconcileAnalysisMode(
|
|
@@ -17867,6 +17989,25 @@ async function mode(run) {
|
|
|
17867
17989
|
{ noFilesChanged, assistantResponse, actionSummary, conversationPrompts, sessionAuthoredCode }
|
|
17868
17990
|
);
|
|
17869
17991
|
}
|
|
17992
|
+
const investigated = didAgentInvestigate(actionSummary);
|
|
17993
|
+
run.modeDecision = {
|
|
17994
|
+
predicted: predictedMode ?? null,
|
|
17995
|
+
resolved: analysisMode,
|
|
17996
|
+
authored: turnAuthoredCode,
|
|
17997
|
+
investigated,
|
|
17998
|
+
forced
|
|
17999
|
+
};
|
|
18000
|
+
logEvent("mode_resolved", {
|
|
18001
|
+
predicted: predictedMode ?? null,
|
|
18002
|
+
resolved: analysisMode,
|
|
18003
|
+
forced,
|
|
18004
|
+
authored: turnAuthoredCode,
|
|
18005
|
+
investigated,
|
|
18006
|
+
// The two counters that decide `investigated`, so a false reading is
|
|
18007
|
+
// traceable to the tool that was not recognised.
|
|
18008
|
+
subagents: actionSummary?.subagents ?? null,
|
|
18009
|
+
files_read: actionSummary?.files_read.length ?? null
|
|
18010
|
+
});
|
|
17870
18011
|
if (analysisMode === "skip") {
|
|
17871
18012
|
await passAndExit(
|
|
17872
18013
|
run,
|
|
@@ -17976,26 +18117,50 @@ function narrowToRecent(files, sessionId) {
|
|
|
17976
18117
|
});
|
|
17977
18118
|
return recent.length > 0 ? recent : files;
|
|
17978
18119
|
}
|
|
17979
|
-
function
|
|
17980
|
-
|
|
18120
|
+
function readIteration(currentCommit, _contentHash) {
|
|
18121
|
+
return Math.max(1, readBlockState(currentCommit).attempts);
|
|
18122
|
+
}
|
|
18123
|
+
var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
|
|
18124
|
+
function readBlockState(currentCommit, opts) {
|
|
18125
|
+
if (opts?.newUserPrompt) return NO_BLOCKS;
|
|
18126
|
+
if (!(0, import_node_fs22.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
|
|
17981
18127
|
try {
|
|
17982
18128
|
const stored = (0, import_node_fs22.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
17983
|
-
const
|
|
17984
|
-
|
|
17985
|
-
|
|
17986
|
-
|
|
17987
|
-
|
|
17988
|
-
if (isNaN(iter)) return { iteration: 1, fingerprint: null };
|
|
17989
|
-
if (storedCommit !== currentCommit) return { iteration: 1, fingerprint: null };
|
|
17990
|
-
if (storedTimestamp > 0) {
|
|
17991
|
-
const elapsed = Math.floor(Date.now() / 1e3) - storedTimestamp;
|
|
17992
|
-
if (elapsed > 600) return { iteration: 1, fingerprint: null };
|
|
17993
|
-
}
|
|
17994
|
-
return { iteration: iter, fingerprint };
|
|
18129
|
+
const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
|
|
18130
|
+
if (!parsed) return NO_BLOCKS;
|
|
18131
|
+
if (parsed.commit !== currentCommit) return NO_BLOCKS;
|
|
18132
|
+
if (parsed.ts > 0 && Math.floor(Date.now() / 1e3) - parsed.ts > 600) return NO_BLOCKS;
|
|
18133
|
+
return { attempts: parsed.attempts, blocks: parsed.blocks, fingerprint: parsed.fingerprint };
|
|
17995
18134
|
} catch {
|
|
17996
|
-
return
|
|
18135
|
+
return NO_BLOCKS;
|
|
17997
18136
|
}
|
|
17998
18137
|
}
|
|
18138
|
+
function parseJsonState(raw) {
|
|
18139
|
+
const o = JSON.parse(raw);
|
|
18140
|
+
const attempts = typeof o.attempts === "number" ? o.attempts : NaN;
|
|
18141
|
+
if (isNaN(attempts)) return null;
|
|
18142
|
+
return {
|
|
18143
|
+
attempts,
|
|
18144
|
+
blocks: typeof o.blocks === "number" ? o.blocks : attempts,
|
|
18145
|
+
fingerprint: typeof o.fingerprint === "string" && o.fingerprint ? o.fingerprint : null,
|
|
18146
|
+
commit: typeof o.commit === "string" ? o.commit : "",
|
|
18147
|
+
ts: typeof o.ts === "number" ? o.ts : 0
|
|
18148
|
+
};
|
|
18149
|
+
}
|
|
18150
|
+
function parseLegacyState(raw) {
|
|
18151
|
+
const parts = raw.split(":");
|
|
18152
|
+
const n = parseInt(parts[0], 10);
|
|
18153
|
+
if (isNaN(n)) return null;
|
|
18154
|
+
return {
|
|
18155
|
+
attempts: n,
|
|
18156
|
+
// The old file has no separate block count; the old counter is the closest
|
|
18157
|
+
// honest answer, and it errs toward releasing sooner rather than later.
|
|
18158
|
+
blocks: n,
|
|
18159
|
+
fingerprint: parts.slice(3).join(":") || null,
|
|
18160
|
+
commit: parts[1] ?? "",
|
|
18161
|
+
ts: parseInt(parts[2] ?? "0", 10)
|
|
18162
|
+
};
|
|
18163
|
+
}
|
|
17999
18164
|
function findingsFingerprint(findings) {
|
|
18000
18165
|
const keys = findings.map((f) => `${String(f.pattern_id ?? "?")}|${String(f.file ?? "?")}`).filter((k) => k !== "?|?");
|
|
18001
18166
|
return [...new Set(keys)].sort().join(",");
|
|
@@ -18005,11 +18170,22 @@ function isSameProblem(previous, current) {
|
|
|
18005
18170
|
const prev = new Set(previous.split(","));
|
|
18006
18171
|
return current.split(",").some((k) => prev.has(k));
|
|
18007
18172
|
}
|
|
18008
|
-
function
|
|
18173
|
+
function writeBlockState(commit, state) {
|
|
18009
18174
|
(0, import_node_fs22.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18010
|
-
|
|
18011
|
-
|
|
18012
|
-
|
|
18175
|
+
(0, import_node_fs22.writeFileSync)(
|
|
18176
|
+
ITERATION_FILE,
|
|
18177
|
+
JSON.stringify({
|
|
18178
|
+
v: 2,
|
|
18179
|
+
attempts: state.attempts,
|
|
18180
|
+
blocks: state.blocks,
|
|
18181
|
+
commit,
|
|
18182
|
+
ts: Math.floor(Date.now() / 1e3),
|
|
18183
|
+
fingerprint: state.fingerprint ?? void 0
|
|
18184
|
+
})
|
|
18185
|
+
);
|
|
18186
|
+
}
|
|
18187
|
+
function resetBlockState(commit) {
|
|
18188
|
+
writeBlockState(commit, { attempts: 0, blocks: 0, fingerprint: null });
|
|
18013
18189
|
}
|
|
18014
18190
|
|
|
18015
18191
|
// src/lib/fold.ts
|
|
@@ -18132,6 +18308,18 @@ function commandShape(cmd) {
|
|
|
18132
18308
|
return out.join(" ").slice(0, COMMAND_HEAD_CHARS);
|
|
18133
18309
|
}
|
|
18134
18310
|
var COMMAND_HEAD_CHARS = 80;
|
|
18311
|
+
var MAX_TOOL_NAMES = 64;
|
|
18312
|
+
var MAX_TOOL_TARGETS = 3;
|
|
18313
|
+
var MAX_TASKS = 64;
|
|
18314
|
+
var TASK_NAME_CHARS = 120;
|
|
18315
|
+
function toolTarget(name, input) {
|
|
18316
|
+
if (name === "Bash") return null;
|
|
18317
|
+
for (const key of ["file_path", "notebook_path", "path", "filePath"]) {
|
|
18318
|
+
const v = input[key];
|
|
18319
|
+
if (typeof v === "string" && v) return v.slice(0, 120);
|
|
18320
|
+
}
|
|
18321
|
+
return null;
|
|
18322
|
+
}
|
|
18135
18323
|
var rootCandidateCache = /* @__PURE__ */ new Map();
|
|
18136
18324
|
function candidateRoots(repoRoot2) {
|
|
18137
18325
|
const cached2 = rootCandidateCache.get(repoRoot2);
|
|
@@ -18160,27 +18348,46 @@ function toRepoRelative(path, repoRoot2) {
|
|
|
18160
18348
|
}
|
|
18161
18349
|
return p.replace(/^\/+/, "");
|
|
18162
18350
|
}
|
|
18351
|
+
function isInsideRepo(path, repoRoot2) {
|
|
18352
|
+
const p = path.replace(/\\/g, "/");
|
|
18353
|
+
if (!isAbsolutePath(p)) return true;
|
|
18354
|
+
if (!repoRoot2) return true;
|
|
18355
|
+
return candidateRoots(repoRoot2).some((root) => p === root || p.startsWith(root + "/"));
|
|
18356
|
+
}
|
|
18357
|
+
function isAbsolutePath(p) {
|
|
18358
|
+
return p.startsWith("/") || /^[A-Za-z]:\//.test(p);
|
|
18359
|
+
}
|
|
18163
18360
|
function fold(transcriptPath, opts = {}) {
|
|
18164
18361
|
const result = {
|
|
18165
18362
|
authored: [],
|
|
18166
18363
|
unobserved: [],
|
|
18167
18364
|
commands: [],
|
|
18365
|
+
tools: [],
|
|
18366
|
+
tasks: [],
|
|
18168
18367
|
unknownTypes: [],
|
|
18169
18368
|
coverage: {
|
|
18170
18369
|
recordCounts: {},
|
|
18171
18370
|
totalRecords: 0,
|
|
18172
18371
|
malformed: 0,
|
|
18173
18372
|
subagentFiles: 0,
|
|
18373
|
+
outsideRepo: 0,
|
|
18374
|
+
toolNamesDropped: 0,
|
|
18174
18375
|
dispatched: 0,
|
|
18175
18376
|
userMessages: 0,
|
|
18176
18377
|
subagentSkipped: 0,
|
|
18177
18378
|
compactions: 0,
|
|
18178
18379
|
complete: false
|
|
18179
|
-
}
|
|
18380
|
+
},
|
|
18381
|
+
planApproval: { approvals: 0, activeSinceLastPrompt: false }
|
|
18180
18382
|
};
|
|
18383
|
+
const flow = { seq: 0, lastPrompt: -1, lastApproval: -1, approvals: 0 };
|
|
18181
18384
|
const byPath = /* @__PURE__ */ new Map();
|
|
18182
18385
|
const commandStats = /* @__PURE__ */ new Map();
|
|
18183
18386
|
const pendingByToolUse = /* @__PURE__ */ new Map();
|
|
18387
|
+
const toolStats = /* @__PURE__ */ new Map();
|
|
18388
|
+
const pendingToolName = /* @__PURE__ */ new Map();
|
|
18389
|
+
const taskById = /* @__PURE__ */ new Map();
|
|
18390
|
+
const pendingTaskName = /* @__PURE__ */ new Map();
|
|
18184
18391
|
const unknown = /* @__PURE__ */ new Set();
|
|
18185
18392
|
const ingest = (raw, owner) => {
|
|
18186
18393
|
for (const line of raw.split("\n")) {
|
|
@@ -18199,8 +18406,12 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18199
18406
|
if (type === "system" && record.subtype === "compact_boundary") {
|
|
18200
18407
|
result.coverage.compactions++;
|
|
18201
18408
|
}
|
|
18202
|
-
if (type === "user" && hasUserText(record))
|
|
18203
|
-
|
|
18409
|
+
if (type === "user" && hasUserText(record)) {
|
|
18410
|
+
result.coverage.userMessages++;
|
|
18411
|
+
if (owner === "agent") flow.lastPrompt = flow.seq;
|
|
18412
|
+
}
|
|
18413
|
+
if (owner === "agent") flow.seq++;
|
|
18414
|
+
collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, opts.repoRoot, result.coverage, flow);
|
|
18204
18415
|
}
|
|
18205
18416
|
};
|
|
18206
18417
|
try {
|
|
@@ -18211,7 +18422,11 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18211
18422
|
return result;
|
|
18212
18423
|
}
|
|
18213
18424
|
try {
|
|
18214
|
-
const sidecarDir = (0, import_node_path18.join)(
|
|
18425
|
+
const sidecarDir = (0, import_node_path18.join)(
|
|
18426
|
+
(0, import_node_path18.dirname)(transcriptPath),
|
|
18427
|
+
(0, import_node_path18.basename)(transcriptPath).replace(/\.jsonl$/, ""),
|
|
18428
|
+
"subagents"
|
|
18429
|
+
);
|
|
18215
18430
|
if ((0, import_node_fs23.existsSync)(sidecarDir)) {
|
|
18216
18431
|
const maxFiles = opts.maxSidecars ?? 200;
|
|
18217
18432
|
const maxBytes = opts.maxSidecarBytes ?? 16 * 1024 * 1024;
|
|
@@ -18254,12 +18469,24 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18254
18469
|
result.authored = [...byPath.values()].sort((a, b) => a.p.localeCompare(b.p));
|
|
18255
18470
|
result.unknownTypes = [...unknown].sort();
|
|
18256
18471
|
result.commands = [...commandStats.entries()].map(([cls, s]) => ({ class: cls, ...s })).sort((a, b) => a.class.localeCompare(b.class));
|
|
18472
|
+
result.tools = [...toolStats.entries()].map(([name, s]) => ({
|
|
18473
|
+
name,
|
|
18474
|
+
runs: s.runs,
|
|
18475
|
+
failed: s.failed,
|
|
18476
|
+
last_status: s.last_status,
|
|
18477
|
+
targets: [...s.targets].map((t) => toRepoRelative(t, opts.repoRoot)).sort()
|
|
18478
|
+
})).sort((a, b) => b.runs - a.runs || a.name.localeCompare(b.name));
|
|
18479
|
+
result.tasks = [...taskById.entries()].map(([id, t]) => ({ id, name: t.name, status: t.status })).sort((a, b) => (Number(a.id) || 0) - (Number(b.id) || 0));
|
|
18257
18480
|
const authoredPaths = new Set(result.authored.map((a) => a.p));
|
|
18258
18481
|
for (const raw of opts.changedFiles ?? []) {
|
|
18259
18482
|
const p = toRepoRelative(raw, opts.repoRoot);
|
|
18260
18483
|
if (!p || authoredPaths.has(p)) continue;
|
|
18261
18484
|
result.unobserved.push({ p, cause: classifyUnobserved(raw) });
|
|
18262
18485
|
}
|
|
18486
|
+
result.planApproval = {
|
|
18487
|
+
approvals: flow.approvals,
|
|
18488
|
+
activeSinceLastPrompt: flow.lastApproval >= 0 && flow.lastApproval > flow.lastPrompt
|
|
18489
|
+
};
|
|
18263
18490
|
return result;
|
|
18264
18491
|
}
|
|
18265
18492
|
function classifyUnobserved(path) {
|
|
@@ -18272,7 +18499,7 @@ function classifyUnobserved(path) {
|
|
|
18272
18499
|
if (/\.(png|jpg|jpeg|gif|pdf|zip|woff2?|ico|mp4)$/i.test(path)) return "binary";
|
|
18273
18500
|
return "no_edit_record";
|
|
18274
18501
|
}
|
|
18275
|
-
function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, repoRoot2, tally) {
|
|
18502
|
+
function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse, toolStats, pendingToolName, taskById, pendingTaskName, repoRoot2, tally, flow) {
|
|
18276
18503
|
const message = record.message;
|
|
18277
18504
|
const content = message?.content ?? record.content;
|
|
18278
18505
|
const blocks = Array.isArray(content) ? content : content && typeof content === "object" ? [content] : [];
|
|
@@ -18281,8 +18508,38 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
|
|
|
18281
18508
|
if (blockType === "tool_use") {
|
|
18282
18509
|
const name = String(block.name ?? "");
|
|
18283
18510
|
const input = block.input ?? {};
|
|
18511
|
+
if (name && toolStats.size < MAX_TOOL_NAMES) {
|
|
18512
|
+
const prev = toolStats.get(name) ?? { runs: 0, failed: 0, last_status: null, targets: /* @__PURE__ */ new Set() };
|
|
18513
|
+
prev.targets = prev.targets ?? /* @__PURE__ */ new Set();
|
|
18514
|
+
if (prev.targets.size < MAX_TOOL_TARGETS) {
|
|
18515
|
+
const target = toolTarget(name, input);
|
|
18516
|
+
if (target) prev.targets.add(target);
|
|
18517
|
+
}
|
|
18518
|
+
toolStats.set(name, { runs: prev.runs + 1, failed: prev.failed, last_status: null, targets: prev.targets });
|
|
18519
|
+
const toolId = typeof block.id === "string" ? block.id : null;
|
|
18520
|
+
if (toolId) pendingToolName.set(toolId, name);
|
|
18521
|
+
} else if (name && tally) {
|
|
18522
|
+
tally.toolNamesDropped += 1;
|
|
18523
|
+
}
|
|
18524
|
+
if (name === "TaskCreate") {
|
|
18525
|
+
const subject = typeof input.subject === "string" ? input.subject : "";
|
|
18526
|
+
const taskId = typeof block.id === "string" ? block.id : null;
|
|
18527
|
+
if (subject && taskId) pendingTaskName.set(taskId, subject.slice(0, TASK_NAME_CHARS));
|
|
18528
|
+
}
|
|
18529
|
+
if (name === "TaskUpdate") {
|
|
18530
|
+
const id = typeof input.taskId === "string" ? input.taskId : "";
|
|
18531
|
+
const status = typeof input.status === "string" ? input.status : "";
|
|
18532
|
+
if (id && status) {
|
|
18533
|
+
const entry = taskById.get(id);
|
|
18534
|
+
taskById.set(id, { name: entry?.name ?? `#${id}`, status });
|
|
18535
|
+
}
|
|
18536
|
+
}
|
|
18284
18537
|
if (EDIT_TOOLS.has(name)) {
|
|
18285
18538
|
const rawPath = typeof input.notebook_path === "string" ? input.notebook_path : typeof input.file_path === "string" ? input.file_path : null;
|
|
18539
|
+
if (rawPath && !isInsideRepo(rawPath, repoRoot2)) {
|
|
18540
|
+
if (tally) tally.outsideRepo += 1;
|
|
18541
|
+
continue;
|
|
18542
|
+
}
|
|
18286
18543
|
const path = rawPath ? toRepoRelative(rawPath, repoRoot2) : null;
|
|
18287
18544
|
if (path) {
|
|
18288
18545
|
const entry = byPath.get(path) ?? { p: path, h: 0, a: 0, d: 0, owner };
|
|
@@ -18316,6 +18573,38 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
|
|
|
18316
18573
|
}
|
|
18317
18574
|
if (blockType === "tool_result") {
|
|
18318
18575
|
const id = typeof block.tool_use_id === "string" ? block.tool_use_id : null;
|
|
18576
|
+
const taskName = id ? pendingTaskName.get(id) : void 0;
|
|
18577
|
+
if (taskName) {
|
|
18578
|
+
pendingTaskName.delete(id);
|
|
18579
|
+
const body = typeof block.content === "string" ? block.content : "";
|
|
18580
|
+
const created = /Task #(\d+)/.exec(body);
|
|
18581
|
+
if (created && taskById.size < MAX_TASKS) {
|
|
18582
|
+
taskById.set(created[1], { name: taskName, status: taskById.get(created[1])?.status ?? "created" });
|
|
18583
|
+
}
|
|
18584
|
+
}
|
|
18585
|
+
const toolName = id ? pendingToolName.get(id) : void 0;
|
|
18586
|
+
if (toolName === "ExitPlanMode" && flow && block.is_error !== true) {
|
|
18587
|
+
const body = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => typeof c.text === "string" ? c.text : "").join(" ") : "";
|
|
18588
|
+
if (/approved your plan/i.test(body)) {
|
|
18589
|
+
flow.lastApproval = flow.seq;
|
|
18590
|
+
flow.approvals += 1;
|
|
18591
|
+
}
|
|
18592
|
+
}
|
|
18593
|
+
if (toolName) {
|
|
18594
|
+
pendingToolName.delete(id);
|
|
18595
|
+
const prevTool = toolStats.get(toolName);
|
|
18596
|
+
if (prevTool) {
|
|
18597
|
+
const failed = block.is_error === true;
|
|
18598
|
+
toolStats.set(toolName, {
|
|
18599
|
+
runs: prevTool.runs,
|
|
18600
|
+
failed: prevTool.failed + (failed ? 1 : 0),
|
|
18601
|
+
last_status: block.is_error === true ? 1 : block.is_error === false ? 0 : null,
|
|
18602
|
+
// Carried, not rebuilt: the targets were collected on the `tool_use`
|
|
18603
|
+
// side and dropping them here would empty the ledger on every result.
|
|
18604
|
+
targets: prevTool.targets
|
|
18605
|
+
});
|
|
18606
|
+
}
|
|
18607
|
+
}
|
|
18319
18608
|
const cls = id ? pendingByToolUse.get(id) : void 0;
|
|
18320
18609
|
if (!cls) continue;
|
|
18321
18610
|
pendingByToolUse.delete(id);
|
|
@@ -18358,8 +18647,13 @@ function checkConservation(changedFiles, result, repoRoot2) {
|
|
|
18358
18647
|
// src/commands/analyze/phases/06-evidence.ts
|
|
18359
18648
|
async function evidence(run) {
|
|
18360
18649
|
const { opts } = run;
|
|
18361
|
-
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath } = run;
|
|
18650
|
+
const { actionSummary, allForReview, analyzable, assistantResponse, authorshipIsObservable, baseline, baselineSessionId, hasRecentCommitFiles, reviewable, securityFiles, sessionAuthoredCode, transcriptPath, turnAuthoredCode } = run;
|
|
18362
18651
|
let { analysisMode, earlyFold } = run;
|
|
18652
|
+
const recordFlip = (stage) => {
|
|
18653
|
+
if (run.modeDecision) run.modeDecision = { ...run.modeDecision, resolved: "plan", flip: stage };
|
|
18654
|
+
logEvent("mode_flipped", { stage, to: "plan" });
|
|
18655
|
+
};
|
|
18656
|
+
const planWorthy = !!assistantResponse && !turnAuthoredCode;
|
|
18363
18657
|
let staticResults = {
|
|
18364
18658
|
tool: "@codacy/analysis-cli",
|
|
18365
18659
|
findings: [],
|
|
@@ -18379,8 +18673,9 @@ async function evidence(run) {
|
|
|
18379
18673
|
const debounceSeconds = parseInt(opts.debounce, 10);
|
|
18380
18674
|
const debounceSkip = checkDebounce(debounceSeconds, baselineSessionId);
|
|
18381
18675
|
if (debounceSkip) {
|
|
18382
|
-
if (
|
|
18676
|
+
if (planWorthy) {
|
|
18383
18677
|
analysisMode = "plan";
|
|
18678
|
+
recordFlip("debounce");
|
|
18384
18679
|
} else {
|
|
18385
18680
|
await passAndExit(run, debounceSkip, "debounce");
|
|
18386
18681
|
}
|
|
@@ -18389,8 +18684,9 @@ async function evidence(run) {
|
|
|
18389
18684
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
18390
18685
|
const mtimeSkip = checkMtime(allCheckable, hasRecentCommitFiles, baselineSessionId);
|
|
18391
18686
|
if (mtimeSkip) {
|
|
18392
|
-
if (
|
|
18687
|
+
if (planWorthy) {
|
|
18393
18688
|
analysisMode = "plan";
|
|
18689
|
+
recordFlip("mtime");
|
|
18394
18690
|
} else {
|
|
18395
18691
|
await passAndExit(run, mtimeSkip, "no-delta-since-last-review");
|
|
18396
18692
|
}
|
|
@@ -18401,8 +18697,9 @@ async function evidence(run) {
|
|
|
18401
18697
|
const allCheckable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...reviewable, ...securityFiles]));
|
|
18402
18698
|
const hashResult = checkContentHash(allCheckable, baselineSessionId);
|
|
18403
18699
|
if (hashResult.skip) {
|
|
18404
|
-
if (
|
|
18700
|
+
if (planWorthy) {
|
|
18405
18701
|
analysisMode = "plan";
|
|
18702
|
+
recordFlip("content-hash");
|
|
18406
18703
|
} else {
|
|
18407
18704
|
await passAndExit(run, hashResult.skip, "no-delta-since-last-review");
|
|
18408
18705
|
}
|
|
@@ -18464,8 +18761,9 @@ async function evidence(run) {
|
|
|
18464
18761
|
maxTotalBytes: parseInt(opts.maxTotalSize, 10)
|
|
18465
18762
|
});
|
|
18466
18763
|
if (codeDelta.files.length === 0 && staticResults.findings.length === 0) {
|
|
18467
|
-
if (
|
|
18764
|
+
if (planWorthy) {
|
|
18468
18765
|
analysisMode = "plan";
|
|
18766
|
+
recordFlip("empty-after-scoping");
|
|
18469
18767
|
} else {
|
|
18470
18768
|
await passAndExit(
|
|
18471
18769
|
run,
|
|
@@ -18485,13 +18783,13 @@ async function evidence(run) {
|
|
|
18485
18783
|
snapshotResult = generateSnapshotDiffs(codeDelta.files);
|
|
18486
18784
|
}
|
|
18487
18785
|
currentCommit = getCurrentCommit();
|
|
18488
|
-
iteration =
|
|
18786
|
+
iteration = readIteration(currentCommit);
|
|
18489
18787
|
}
|
|
18490
18788
|
}
|
|
18491
18789
|
if (analysisMode === "plan") {
|
|
18492
18790
|
recordAnalysisStart();
|
|
18493
18791
|
currentCommit = getCurrentCommit();
|
|
18494
|
-
iteration =
|
|
18792
|
+
iteration = readIteration(currentCommit);
|
|
18495
18793
|
}
|
|
18496
18794
|
Object.assign(run, { analysisMode, codeDelta, contentHash, currentCommit, earlyFold, iteration, snapshotResult, staticResults });
|
|
18497
18795
|
}
|
|
@@ -18587,7 +18885,8 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
18587
18885
|
// src/commands/analyze/phases/07-context-files.ts
|
|
18588
18886
|
async function contextFiles(run) {
|
|
18589
18887
|
const { codeDelta, contextFilePaths } = run;
|
|
18590
|
-
const
|
|
18888
|
+
const { kept: externalContext } = partitionVerityOwned(contextFilePaths ?? []);
|
|
18889
|
+
const contextFiles2 = gatherContextFiles(externalContext, codeDelta.files);
|
|
18591
18890
|
for (const f of codeDelta.files) {
|
|
18592
18891
|
f.role = "delta";
|
|
18593
18892
|
}
|
|
@@ -19157,6 +19456,54 @@ async function workingMemory(run) {
|
|
|
19157
19456
|
Object.assign(run, { incrementReport, memory, memorySession, reachability });
|
|
19158
19457
|
}
|
|
19159
19458
|
|
|
19459
|
+
// src/lib/note-budget.ts
|
|
19460
|
+
var import_node_fs28 = require("node:fs");
|
|
19461
|
+
var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
|
|
19462
|
+
var EPISODE_STALE_SECONDS = 30 * 60;
|
|
19463
|
+
var FRESH = { delivered: 0, tasksCompleted: 0, ts: 0 };
|
|
19464
|
+
function resolveEpisode(prev, signals) {
|
|
19465
|
+
if (!prev) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19466
|
+
if (signals.humanSpoke) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19467
|
+
if (signals.rawFail) return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19468
|
+
if (prev.tasksCompleted !== signals.tasksCompleted) {
|
|
19469
|
+
return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19470
|
+
}
|
|
19471
|
+
if (prev.ts > 0 && signals.now - prev.ts > EPISODE_STALE_SECONDS) {
|
|
19472
|
+
return { ...FRESH, tasksCompleted: signals.tasksCompleted, ts: signals.now };
|
|
19473
|
+
}
|
|
19474
|
+
return prev;
|
|
19475
|
+
}
|
|
19476
|
+
function advisoryBudgetSpent(episode, rawDecision) {
|
|
19477
|
+
const budget = ADVISORY_BUDGET[rawDecision] ?? ADVISORY_BUDGET.WARN;
|
|
19478
|
+
return episode.delivered >= budget;
|
|
19479
|
+
}
|
|
19480
|
+
function readAdvisoryEpisode(sessionId) {
|
|
19481
|
+
const file = scopedFile(ADVISORY_EPISODE_FILE, sessionId);
|
|
19482
|
+
if (!(0, import_node_fs28.existsSync)(file)) return null;
|
|
19483
|
+
try {
|
|
19484
|
+
const o = JSON.parse((0, import_node_fs28.readFileSync)(file, "utf-8")) ?? {};
|
|
19485
|
+
const delivered = typeof o.delivered === "number" ? o.delivered : NaN;
|
|
19486
|
+
if (isNaN(delivered)) return null;
|
|
19487
|
+
return {
|
|
19488
|
+
delivered,
|
|
19489
|
+
tasksCompleted: typeof o.tasksCompleted === "number" ? o.tasksCompleted : 0,
|
|
19490
|
+
ts: typeof o.ts === "number" ? o.ts : 0
|
|
19491
|
+
};
|
|
19492
|
+
} catch {
|
|
19493
|
+
return null;
|
|
19494
|
+
}
|
|
19495
|
+
}
|
|
19496
|
+
function writeAdvisoryEpisode(episode, sessionId) {
|
|
19497
|
+
try {
|
|
19498
|
+
(0, import_node_fs28.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
19499
|
+
(0, import_node_fs28.writeFileSync)(
|
|
19500
|
+
scopedFile(ADVISORY_EPISODE_FILE, sessionId),
|
|
19501
|
+
JSON.stringify({ v: 1, ...episode })
|
|
19502
|
+
);
|
|
19503
|
+
} catch {
|
|
19504
|
+
}
|
|
19505
|
+
}
|
|
19506
|
+
|
|
19160
19507
|
// src/lib/run-mode.ts
|
|
19161
19508
|
function parseAutonomousEnv(raw) {
|
|
19162
19509
|
if (raw === void 0) return void 0;
|
|
@@ -19250,7 +19597,13 @@ async function buildRequest(run) {
|
|
|
19250
19597
|
excluded_by_reason: excludedByReason,
|
|
19251
19598
|
// was the transcript itself truncated? The 256 KB window means "this turn"
|
|
19252
19599
|
// can quietly mean "the last 256 KB of it".
|
|
19253
|
-
transcript_windowed: actionSummary?.transcript_windowed ?? null
|
|
19600
|
+
transcript_windowed: actionSummary?.transcript_windowed ?? null,
|
|
19601
|
+
// The advisory budget's fleet counter-metric (note-budget.ts): deliveries in
|
|
19602
|
+
// the episode as of the PREVIOUS turn — this runs before phase 13 updates
|
|
19603
|
+
// the state, so the number is one turn lagged by construction. The
|
|
19604
|
+
// degenerate win for the budget is a dead channel that looks like clean
|
|
19605
|
+
// code; this is what makes "did delivery rate collapse" a query.
|
|
19606
|
+
advisory_delivered_prior: readAdvisoryEpisode(run.baselineSessionId)?.delivered ?? 0
|
|
19254
19607
|
};
|
|
19255
19608
|
const requestBody = {
|
|
19256
19609
|
coverage_telemetry: coverageTelemetry,
|
|
@@ -19309,6 +19662,8 @@ async function buildRequest(run) {
|
|
|
19309
19662
|
authored_since_verdict: memorySession ? foldDossier(memorySession.d).authored_all.filter((a) => a.hash_now !== a.hash_at_last_verdict).map((a) => a.path) : [],
|
|
19310
19663
|
unobserved: foldResult?.unobserved ?? [],
|
|
19311
19664
|
commands: foldResult?.commands ?? [],
|
|
19665
|
+
tools: foldResult?.tools ?? [],
|
|
19666
|
+
tasks: foldResult?.tasks ?? [],
|
|
19312
19667
|
unknown_types: foldResult?.unknownTypes ?? [],
|
|
19313
19668
|
coverage: foldResult?.coverage ?? { recordCounts: {}, totalRecords: 0, malformed: 0, subagentFiles: 0, subagentSkipped: 0, complete: false },
|
|
19314
19669
|
// INV-19, computed client-side and sent so a conservation failure is
|
|
@@ -19387,7 +19742,8 @@ async function buildRequest(run) {
|
|
|
19387
19742
|
}
|
|
19388
19743
|
const noHumanPrompt = (conversation?.prompts?.length ?? 0) === 0;
|
|
19389
19744
|
const w4Task = noHumanPrompt && isExplicitlyAutonomous() ? resolveTaskContext() : null;
|
|
19390
|
-
const
|
|
19745
|
+
const planApprovalActive = foldResult?.planApproval?.activeSinceLastPrompt === true;
|
|
19746
|
+
const hasIntent = (conversation?.prompts?.length ?? 0) > 0 || specs.length > 0 || plans.length > 0 || !!assistantResponse || !!w4Task || planApprovalActive;
|
|
19391
19747
|
if (hasIntent) {
|
|
19392
19748
|
const intentContext = {};
|
|
19393
19749
|
if (conversation && conversation.prompts.length > 0) {
|
|
@@ -19419,6 +19775,10 @@ async function buildRequest(run) {
|
|
|
19419
19775
|
intentContext.user_prompt = w4Task.goal;
|
|
19420
19776
|
logEvent("w4_issue_anchor", { issue: w4Task.number, via: w4Task.via });
|
|
19421
19777
|
}
|
|
19778
|
+
if (planApprovalActive) {
|
|
19779
|
+
intentContext.plan_approved = true;
|
|
19780
|
+
logEvent("plan_approval_carried", { approvals: foldResult.planApproval.approvals });
|
|
19781
|
+
}
|
|
19422
19782
|
if (assistantResponse) {
|
|
19423
19783
|
const cap = analysisMode === "plan" ? MAX_ASSISTANT_RESPONSE_CHARS_PLAN : MAX_ASSISTANT_RESPONSE_CHARS_DEFAULT;
|
|
19424
19784
|
intentContext.assistant_response = assistantResponse.length > cap ? assistantResponse.slice(0, cap) : assistantResponse;
|
|
@@ -19438,14 +19798,14 @@ async function buildRequest(run) {
|
|
|
19438
19798
|
}
|
|
19439
19799
|
|
|
19440
19800
|
// src/lib/offline.ts
|
|
19441
|
-
var
|
|
19801
|
+
var import_node_fs29 = require("node:fs");
|
|
19442
19802
|
var import_node_crypto11 = require("node:crypto");
|
|
19443
19803
|
function cacheRequest(body) {
|
|
19444
19804
|
try {
|
|
19445
|
-
(0,
|
|
19805
|
+
(0, import_node_fs29.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
19446
19806
|
const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
|
|
19447
19807
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
19448
|
-
(0,
|
|
19808
|
+
(0, import_node_fs29.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
|
|
19449
19809
|
} catch {
|
|
19450
19810
|
}
|
|
19451
19811
|
}
|
|
@@ -19564,10 +19924,10 @@ async function transmit(run) {
|
|
|
19564
19924
|
}
|
|
19565
19925
|
|
|
19566
19926
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
19567
|
-
var
|
|
19927
|
+
var import_node_fs30 = require("node:fs");
|
|
19568
19928
|
var import_node_path23 = require("node:path");
|
|
19569
19929
|
async function reconcile(run) {
|
|
19570
|
-
const { actionSummary, allChanged, analyzable, baseline, codeDelta, contentHash, conversation, decision, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
|
|
19930
|
+
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
|
|
19571
19931
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
19572
19932
|
let openElsewhere = [];
|
|
19573
19933
|
if (memorySession) {
|
|
@@ -19575,7 +19935,7 @@ async function reconcile(run) {
|
|
|
19575
19935
|
const st = foldDossier(memorySession.d);
|
|
19576
19936
|
openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
|
|
19577
19937
|
try {
|
|
19578
|
-
const src = (0,
|
|
19938
|
+
const src = (0, import_node_fs30.readFileSync)((0, import_node_path23.join)(repoRoot(), file), "utf8").split("\n");
|
|
19579
19939
|
const at = src[line - 1];
|
|
19580
19940
|
return at === void 0 ? null : lineSha(at);
|
|
19581
19941
|
} catch {
|
|
@@ -19585,6 +19945,7 @@ async function reconcile(run) {
|
|
|
19585
19945
|
} catch {
|
|
19586
19946
|
}
|
|
19587
19947
|
}
|
|
19948
|
+
const { kept: externalChanged, owned: verityOwned } = partitionVerityOwned(allChanged);
|
|
19588
19949
|
const reviewCoverage = {
|
|
19589
19950
|
reviewed: sentPaths,
|
|
19590
19951
|
// Declared drops from the stages that DO report themselves today. The other
|
|
@@ -19626,11 +19987,20 @@ async function reconcile(run) {
|
|
|
19626
19987
|
stage: "baseline-scoping",
|
|
19627
19988
|
kind: "policy"
|
|
19628
19989
|
})),
|
|
19990
|
+
// ⚠ VERITY'S OWN FILES, named as such — not laundered into the
|
|
19991
|
+
// extension bucket below, where "we do not review our own installer's
|
|
19992
|
+
// dirt" would read as "a changed README". See self-scope.ts.
|
|
19993
|
+
...verityOwned.map((path) => ({
|
|
19994
|
+
path,
|
|
19995
|
+
reason: "verity-owned",
|
|
19996
|
+
stage: "self-scope",
|
|
19997
|
+
kind: "policy"
|
|
19998
|
+
})),
|
|
19629
19999
|
// The extension allowlist. POLICY: a changed README was never going to be
|
|
19630
20000
|
// reviewed, and calling that a coverage gap would downgrade nearly every
|
|
19631
20001
|
// PASS to WARN until WARN meant nothing. Recorded so the ledger balances and
|
|
19632
20002
|
// so "what did Verity ignore entirely" is answerable.
|
|
19633
|
-
...
|
|
20003
|
+
...externalChanged.filter((p) => !analyzable.includes(p) && !reviewable.includes(p) && !securityFiles.includes(p)).map((path) => ({
|
|
19634
20004
|
path,
|
|
19635
20005
|
reason: "not-a-reviewed-file-type",
|
|
19636
20006
|
stage: "extension-allowlist",
|
|
@@ -19667,6 +20037,29 @@ async function reconcile(run) {
|
|
|
19667
20037
|
decision
|
|
19668
20038
|
});
|
|
19669
20039
|
}
|
|
20040
|
+
const episodeSignals = {
|
|
20041
|
+
humanSpoke: (conversation?.prompts?.length ?? 0) > 0,
|
|
20042
|
+
rawFail: decision === "FAIL",
|
|
20043
|
+
tasksCompleted: (foldResult?.tasks ?? []).filter((t) => t.status === "completed").length,
|
|
20044
|
+
now: Math.floor(Date.now() / 1e3)
|
|
20045
|
+
};
|
|
20046
|
+
let episode = resolveEpisode(readAdvisoryEpisode(baselineSessionId), episodeSignals);
|
|
20047
|
+
const contentClass = classifyChannelContent(channelInputFrom(response));
|
|
20048
|
+
const wouldCarryAdvisory = contentClass.advisory || openElsewhere.length > 0;
|
|
20049
|
+
if (decision !== "FAIL" && !silenced && wouldCarryAdvisory && !contentClass.refusal && !contentClass.intentFlag && advisoryBudgetSpent(episode, decision)) {
|
|
20050
|
+
silenced = "note-budget";
|
|
20051
|
+
logEvent("channel_silenced", {
|
|
20052
|
+
reason: silenced,
|
|
20053
|
+
run_id: response.run_id ?? turnId,
|
|
20054
|
+
decision,
|
|
20055
|
+
episode_delivered: episode.delivered
|
|
20056
|
+
});
|
|
20057
|
+
}
|
|
20058
|
+
const deliveringAdvisory = decision !== "FAIL" && !silenced && wouldCarryAdvisory;
|
|
20059
|
+
writeAdvisoryEpisode(
|
|
20060
|
+
{ ...episode, delivered: episode.delivered + (deliveringAdvisory ? 1 : 0), ts: episodeSignals.now },
|
|
20061
|
+
baselineSessionId
|
|
20062
|
+
);
|
|
19670
20063
|
let intentRepeatCount = 0;
|
|
19671
20064
|
const priorPendingFingerprints = memorySession ? (() => {
|
|
19672
20065
|
try {
|
|
@@ -19682,6 +20075,11 @@ async function reconcile(run) {
|
|
|
19682
20075
|
decision,
|
|
19683
20076
|
branch: getCurrentBranch(),
|
|
19684
20077
|
watermarkSha: watermarkIsPartial ? null : watermarkHash,
|
|
20078
|
+
// The byte witness — the same "only honest definition of reviewed" the
|
|
20079
|
+
// coverage column uses. A finding on a path outside this set records no
|
|
20080
|
+
// statement (plan-mode prose anchored to unsent files must not become
|
|
20081
|
+
// "STILL OPEN … the tree is not clean").
|
|
20082
|
+
sentPaths,
|
|
19685
20083
|
findings: response.findings?.map((f) => ({
|
|
19686
20084
|
file: f.file,
|
|
19687
20085
|
line: f.line,
|
|
@@ -19748,6 +20146,39 @@ ${YELLOW2}${note}${NC2}
|
|
|
19748
20146
|
return exit(0);
|
|
19749
20147
|
}
|
|
19750
20148
|
|
|
20149
|
+
// src/lib/may-block.ts
|
|
20150
|
+
var HARD_BLOCK_CEILING = 5;
|
|
20151
|
+
function mayBlock(input) {
|
|
20152
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20153
|
+
if (input.reviewedFileCount === 0 && input.staticFindingCount === 0) {
|
|
20154
|
+
return { block: false, release: "no-code-reviewed" };
|
|
20155
|
+
}
|
|
20156
|
+
if (input.cycleCutFired) {
|
|
20157
|
+
return { block: false, release: "nothing-moved" };
|
|
20158
|
+
}
|
|
20159
|
+
if (input.attempts > input.maxIterations) {
|
|
20160
|
+
return { block: false, release: "same-problem-cap" };
|
|
20161
|
+
}
|
|
20162
|
+
if (input.blocks > ceiling) {
|
|
20163
|
+
return { block: false, release: "block-ceiling" };
|
|
20164
|
+
}
|
|
20165
|
+
return { block: true, release: null };
|
|
20166
|
+
}
|
|
20167
|
+
function describeRelease(release, input) {
|
|
20168
|
+
const ceiling = input.ceiling ?? HARD_BLOCK_CEILING;
|
|
20169
|
+
const open = input.findingCount > 0 ? `${input.findingCount} finding(s) remain OPEN and were NOT fixed. Human review required before deploying.` : "Human review required before deploying.";
|
|
20170
|
+
switch (release) {
|
|
20171
|
+
case "no-code-reviewed":
|
|
20172
|
+
return `Verity: WARN \u2014 NOT BLOCKING: no code was reviewed on this turn, so there is nothing here to fix. Reported as advice instead. ${open}`;
|
|
20173
|
+
case "nothing-moved":
|
|
20174
|
+
return `Verity: WARN \u2014 NOT BLOCKING: nothing has changed since the last verdict, so re-raising it cannot move anything forward. ${open}`;
|
|
20175
|
+
case "same-problem-cap":
|
|
20176
|
+
return `Verity: WARN \u2014 self-healing limit (${input.maxIterations}) reached on the same finding. NO LONGER BLOCKING, but ${open}`;
|
|
20177
|
+
case "block-ceiling":
|
|
20178
|
+
return `Verity: WARN \u2014 ${ceiling} consecutive blocking verdicts reached; releasing the block so this cannot loop. ${open}`;
|
|
20179
|
+
}
|
|
20180
|
+
}
|
|
20181
|
+
|
|
19751
20182
|
// src/lib/remediation-guard.ts
|
|
19752
20183
|
var TOOL_CONFIG_PATTERNS = [
|
|
19753
20184
|
/(^|\/)\.codacy\//,
|
|
@@ -19790,19 +20221,7 @@ function screenRemediation(fix, findingFile) {
|
|
|
19790
20221
|
|
|
19791
20222
|
// src/commands/analyze/phases/14-render.ts
|
|
19792
20223
|
function agentContextFor(response, intentRepeat = 0, priorPendingFingerprints = []) {
|
|
19793
|
-
|
|
19794
|
-
const intent = response.intent_alignment ?? {};
|
|
19795
|
-
return buildAgentContext({
|
|
19796
|
-
intentRepeat,
|
|
19797
|
-
priorPendingFingerprints,
|
|
19798
|
-
gateDecision: String(response.gate_decision ?? ""),
|
|
19799
|
-
findings: response.findings ?? [],
|
|
19800
|
-
pendingItems: response.pending_items ?? [],
|
|
19801
|
-
reviewStatus: metadata.review_status,
|
|
19802
|
-
coverage: metadata.coverage,
|
|
19803
|
-
intentVerdict: intent.verdict,
|
|
19804
|
-
intentGaps: intent.gaps
|
|
19805
|
-
});
|
|
20224
|
+
return buildAgentContext(channelInputFrom(response, intentRepeat, priorPendingFingerprints));
|
|
19806
20225
|
}
|
|
19807
20226
|
async function render(run) {
|
|
19808
20227
|
const { opts, globals } = run;
|
|
@@ -19896,38 +20315,63 @@ async function render(run) {
|
|
|
19896
20315
|
reverify_by: response.reverify_by
|
|
19897
20316
|
});
|
|
19898
20317
|
const grantNudge = grantWarning ? ` \u26A0\uFE0F ${grantWarning}` : "";
|
|
19899
|
-
let
|
|
20318
|
+
let release = null;
|
|
19900
20319
|
let effectiveDecision = decision;
|
|
19901
20320
|
if (decision === "FAIL") {
|
|
19902
|
-
const
|
|
20321
|
+
const findings = response.findings ?? [];
|
|
20322
|
+
const blocking = findings.filter((f) => {
|
|
19903
20323
|
const sev = String(f.severity ?? "").toLowerCase();
|
|
19904
20324
|
return sev === "critical" || sev === "high";
|
|
19905
20325
|
});
|
|
19906
20326
|
const fingerprint = findingsFingerprint(blocking);
|
|
19907
|
-
const prior =
|
|
20327
|
+
const prior = readBlockState(currentCommit, {
|
|
20328
|
+
newUserPrompt: (conversation?.prompts?.length ?? 0) > 0
|
|
20329
|
+
});
|
|
19908
20330
|
const sameProblem = isSameProblem(prior.fingerprint, fingerprint);
|
|
19909
|
-
const nextIteration = sameProblem ? prior.iteration + 1 : 1;
|
|
19910
20331
|
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
19914
|
-
|
|
20332
|
+
const attempts = sameProblem ? prior.attempts + 1 : 1;
|
|
20333
|
+
const blocks = prior.blocks + 1;
|
|
20334
|
+
const decisionNow = mayBlock({
|
|
20335
|
+
reviewedFileCount: codeDelta.files.length,
|
|
20336
|
+
staticFindingCount: run.staticResults?.findings?.length ?? 0,
|
|
20337
|
+
cycleCutFired: silenced !== null,
|
|
20338
|
+
attempts,
|
|
20339
|
+
blocks,
|
|
20340
|
+
maxIterations
|
|
20341
|
+
});
|
|
20342
|
+
if (decisionNow.block) {
|
|
20343
|
+
writeBlockState(currentCommit, { attempts, blocks, fingerprint });
|
|
20344
|
+
iteration = attempts;
|
|
20345
|
+
} else {
|
|
20346
|
+
release = decisionNow.release;
|
|
19915
20347
|
effectiveDecision = "WARN";
|
|
19916
|
-
logEvent("
|
|
20348
|
+
logEvent("block_released", {
|
|
20349
|
+
reason: release,
|
|
20350
|
+
attempts,
|
|
20351
|
+
blocks,
|
|
20352
|
+
reviewed_files: codeDelta.files.length,
|
|
20353
|
+
cycle_cut: silenced,
|
|
20354
|
+
fingerprint
|
|
20355
|
+
});
|
|
19917
20356
|
}
|
|
19918
20357
|
}
|
|
19919
|
-
if (
|
|
20358
|
+
if (release) {
|
|
19920
20359
|
const findings = response.findings ?? [];
|
|
19921
20360
|
const lines = findings.slice(0, 5).map((f) => ` [${String(f.severity ?? "?").toUpperCase()}] ${String(f.title ?? f.message ?? "")} (${String(f.file ?? "?")}:${String(f.line ?? "?")})`);
|
|
20361
|
+
const summary = describeRelease(release, {
|
|
20362
|
+
findingCount: findings.length,
|
|
20363
|
+
maxIterations: parseInt(opts.maxIterations, 10)
|
|
20364
|
+
});
|
|
19922
20365
|
emitVerdict({
|
|
19923
20366
|
proposed: "WARN",
|
|
19924
20367
|
changed: run.changedUniverse,
|
|
19925
20368
|
coverage: reviewCoverage,
|
|
19926
|
-
userSummary:
|
|
19927
|
-
${lines.join("\n")}
|
|
20369
|
+
userSummary: lines.length > 0 ? `${summary}
|
|
20370
|
+
${lines.join("\n")}` : summary,
|
|
19928
20371
|
agentContext: null,
|
|
19929
20372
|
silenced: true
|
|
19930
20373
|
});
|
|
20374
|
+
return;
|
|
19931
20375
|
}
|
|
19932
20376
|
switch (effectiveDecision) {
|
|
19933
20377
|
case "FAIL": {
|
|
@@ -20026,7 +20470,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20026
20470
|
break;
|
|
20027
20471
|
}
|
|
20028
20472
|
case "PASS": {
|
|
20029
|
-
|
|
20473
|
+
resetBlockState(currentCommit);
|
|
20030
20474
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20031
20475
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20032
20476
|
let userSummary = response.user_summary ?? "Verity: PASS";
|
|
@@ -20047,6 +20491,7 @@ ${YELLOW}${grantNudge.trim()}${NC}
|
|
|
20047
20491
|
break;
|
|
20048
20492
|
}
|
|
20049
20493
|
case "WARN": {
|
|
20494
|
+
if (decision !== "FAIL") resetBlockState(currentCommit);
|
|
20050
20495
|
if (watermarkHash) recordPassHash(watermarkHash, baselineSessionId);
|
|
20051
20496
|
if (!watermarkIsPartial && currentCommit && currentCommit !== "no-git") writeBaselineSha(currentCommit);
|
|
20052
20497
|
let userSummary = response.user_summary ?? "Verity: WARN";
|
|
@@ -20145,7 +20590,7 @@ async function runAnalyze(opts, globals) {
|
|
|
20145
20590
|
}
|
|
20146
20591
|
|
|
20147
20592
|
// src/commands/baseline.ts
|
|
20148
|
-
var
|
|
20593
|
+
var import_node_fs31 = require("node:fs");
|
|
20149
20594
|
function registerBaselineCommands(program2) {
|
|
20150
20595
|
const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
|
|
20151
20596
|
baseline.command("capture").description("Snapshot the working tree at task start (used by SessionStart hook)").option("--session-id <id>", "Session id (overrides any value from stdin)").option("--source <source>", "Lifecycle hint: startup|resume|clear|compact").action(async (opts) => {
|
|
@@ -20154,7 +20599,7 @@ function registerBaselineCommands(program2) {
|
|
|
20154
20599
|
process.chdir(repoRoot());
|
|
20155
20600
|
} catch {
|
|
20156
20601
|
}
|
|
20157
|
-
if (!(0,
|
|
20602
|
+
if (!(0, import_node_fs31.existsSync)(VERITY_DIR)) {
|
|
20158
20603
|
process.exit(0);
|
|
20159
20604
|
}
|
|
20160
20605
|
let sessionId = opts.sessionId;
|
|
@@ -20194,7 +20639,7 @@ async function readStdin() {
|
|
|
20194
20639
|
}
|
|
20195
20640
|
|
|
20196
20641
|
// src/commands/review.ts
|
|
20197
|
-
var
|
|
20642
|
+
var import_node_fs32 = require("node:fs");
|
|
20198
20643
|
function registerReviewCommand(program2) {
|
|
20199
20644
|
program2.command("review").description("Run on-demand Verity analysis (advisory, never blocks)").requiredOption("--files <paths>", "Comma-separated file list").option("--changed <paths>", "Subset of --files that were modified").option("--intent <text>", "User intent description (max 2000 chars)").option("--specs <paths>", "Comma-separated spec file paths").option("--json", "Output raw JSON response").action(async (opts) => {
|
|
20200
20645
|
const globals = program2.opts();
|
|
@@ -20213,7 +20658,7 @@ async function runReview(opts, globals) {
|
|
|
20213
20658
|
const securityFiles = filterSecurity(allFiles);
|
|
20214
20659
|
let staticResults;
|
|
20215
20660
|
if (isCodacyAvailable()) {
|
|
20216
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
20661
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs32.existsSync)(f) || resolveFile(f) !== null);
|
|
20217
20662
|
staticResults = runCodacyAnalysis(scannable);
|
|
20218
20663
|
} else {
|
|
20219
20664
|
staticResults = {
|
|
@@ -20239,10 +20684,10 @@ async function runReview(opts, globals) {
|
|
|
20239
20684
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
20240
20685
|
specs = [];
|
|
20241
20686
|
for (const p of specPaths) {
|
|
20242
|
-
if (!(0,
|
|
20687
|
+
if (!(0, import_node_fs32.existsSync)(p)) continue;
|
|
20243
20688
|
try {
|
|
20244
|
-
const { readFileSync:
|
|
20245
|
-
const content =
|
|
20689
|
+
const { readFileSync: readFileSync19 } = await import("node:fs");
|
|
20690
|
+
const content = readFileSync19(p, "utf-8");
|
|
20246
20691
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
20247
20692
|
} catch {
|
|
20248
20693
|
}
|
|
@@ -20299,7 +20744,7 @@ async function runReview(opts, globals) {
|
|
|
20299
20744
|
}
|
|
20300
20745
|
|
|
20301
20746
|
// src/commands/guard.ts
|
|
20302
|
-
var
|
|
20747
|
+
var import_node_fs33 = require("node:fs");
|
|
20303
20748
|
var import_node_path24 = require("node:path");
|
|
20304
20749
|
var GUARD_BLOCK_CAP = 2;
|
|
20305
20750
|
var GUARD_ITER_FILE = (0, import_node_path24.join)(VERITY_DIR, ".guard-iteration");
|
|
@@ -20366,7 +20811,7 @@ function classifyCommand2(command, on) {
|
|
|
20366
20811
|
}
|
|
20367
20812
|
function readIterMap() {
|
|
20368
20813
|
try {
|
|
20369
|
-
const raw = JSON.parse((0,
|
|
20814
|
+
const raw = JSON.parse((0, import_node_fs33.readFileSync)(GUARD_ITER_FILE, "utf-8"));
|
|
20370
20815
|
if (raw && typeof raw === "object") {
|
|
20371
20816
|
if (typeof raw.moment === "string" && typeof raw.count === "number") {
|
|
20372
20817
|
return { [raw.moment]: raw.count };
|
|
@@ -20386,10 +20831,10 @@ function readIter(moment) {
|
|
|
20386
20831
|
}
|
|
20387
20832
|
function writeIter(moment, count) {
|
|
20388
20833
|
try {
|
|
20389
|
-
(0,
|
|
20834
|
+
(0, import_node_fs33.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
20390
20835
|
const map = readIterMap();
|
|
20391
20836
|
map[moment] = count;
|
|
20392
|
-
(0,
|
|
20837
|
+
(0, import_node_fs33.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
20393
20838
|
} catch {
|
|
20394
20839
|
}
|
|
20395
20840
|
}
|
|
@@ -20399,10 +20844,10 @@ function resetIter(moment) {
|
|
|
20399
20844
|
if (!(moment in map)) return;
|
|
20400
20845
|
delete map[moment];
|
|
20401
20846
|
if (Object.keys(map).length === 0) {
|
|
20402
|
-
if ((0,
|
|
20847
|
+
if ((0, import_node_fs33.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs33.unlinkSync)(GUARD_ITER_FILE);
|
|
20403
20848
|
} else {
|
|
20404
|
-
(0,
|
|
20405
|
-
(0,
|
|
20849
|
+
(0, import_node_fs33.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
20850
|
+
(0, import_node_fs33.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
20406
20851
|
}
|
|
20407
20852
|
} catch {
|
|
20408
20853
|
}
|
|
@@ -20466,7 +20911,7 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
20466
20911
|
const securityFiles = filterSecurity(files);
|
|
20467
20912
|
let staticResults;
|
|
20468
20913
|
if (isCodacyAvailable()) {
|
|
20469
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0,
|
|
20914
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs33.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
20470
20915
|
staticResults = runCodacyAnalysis(scannable);
|
|
20471
20916
|
} else {
|
|
20472
20917
|
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
@@ -20511,7 +20956,7 @@ function emitAllowNotice(userMsg, agentMsg) {
|
|
|
20511
20956
|
async function runGuard(opts, globals) {
|
|
20512
20957
|
const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
|
|
20513
20958
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
20514
|
-
if (cwd && (0,
|
|
20959
|
+
if (cwd && (0, import_node_fs33.existsSync)(cwd)) {
|
|
20515
20960
|
try {
|
|
20516
20961
|
process.chdir(cwd);
|
|
20517
20962
|
} catch {
|
|
@@ -20617,14 +21062,14 @@ function writeBlockMessage(moment, response) {
|
|
|
20617
21062
|
}
|
|
20618
21063
|
|
|
20619
21064
|
// src/commands/init.ts
|
|
20620
|
-
var
|
|
21065
|
+
var import_node_fs35 = require("node:fs");
|
|
20621
21066
|
var import_promises13 = require("node:fs/promises");
|
|
20622
21067
|
var import_node_path26 = require("node:path");
|
|
20623
21068
|
var import_node_child_process10 = require("node:child_process");
|
|
20624
21069
|
var readline2 = __toESM(require("node:readline/promises"));
|
|
20625
21070
|
|
|
20626
21071
|
// src/commands/migrate.ts
|
|
20627
|
-
var
|
|
21072
|
+
var import_node_fs34 = require("node:fs");
|
|
20628
21073
|
var import_node_path25 = require("node:path");
|
|
20629
21074
|
var import_node_child_process9 = require("node:child_process");
|
|
20630
21075
|
|
|
@@ -20756,10 +21201,10 @@ async function runMigration(opts = {}) {
|
|
|
20756
21201
|
function migrateProjectDir(root, actions) {
|
|
20757
21202
|
const gateDir = (0, import_node_path25.join)(root, ".gate");
|
|
20758
21203
|
const verityDir = (0, import_node_path25.join)(root, ".verity");
|
|
20759
|
-
if ((0,
|
|
21204
|
+
if ((0, import_node_fs34.existsSync)(gateDir) && !(0, import_node_fs34.existsSync)(verityDir)) {
|
|
20760
21205
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
20761
21206
|
}
|
|
20762
|
-
if ((0,
|
|
21207
|
+
if ((0, import_node_fs34.existsSync)(gateDir) && (0, import_node_fs34.existsSync)(verityDir)) {
|
|
20763
21208
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
20764
21209
|
}
|
|
20765
21210
|
return false;
|
|
@@ -20780,13 +21225,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
20780
21225
|
}
|
|
20781
21226
|
}
|
|
20782
21227
|
if (moved) {
|
|
20783
|
-
if ((0,
|
|
21228
|
+
if ((0, import_node_fs34.existsSync)(gateDir)) {
|
|
20784
21229
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
20785
21230
|
if (carried > 0) {
|
|
20786
21231
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
20787
21232
|
}
|
|
20788
21233
|
try {
|
|
20789
|
-
(0,
|
|
21234
|
+
(0, import_node_fs34.rmSync)(gateDir, { recursive: true, force: true });
|
|
20790
21235
|
} catch {
|
|
20791
21236
|
}
|
|
20792
21237
|
}
|
|
@@ -20802,7 +21247,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
20802
21247
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
20803
21248
|
}
|
|
20804
21249
|
try {
|
|
20805
|
-
(0,
|
|
21250
|
+
(0, import_node_fs34.rmSync)(gateDir, { recursive: true, force: true });
|
|
20806
21251
|
} catch {
|
|
20807
21252
|
}
|
|
20808
21253
|
return carried > 0;
|
|
@@ -20811,9 +21256,9 @@ function migrateGlobalCredentials(home, actions) {
|
|
|
20811
21256
|
if (!home) return;
|
|
20812
21257
|
const gateCreds = (0, import_node_path25.join)(home, ".gate", "credentials");
|
|
20813
21258
|
const verityCreds = (0, import_node_path25.join)(home, ".verity", "credentials");
|
|
20814
|
-
if (!(0,
|
|
20815
|
-
if (!(0,
|
|
20816
|
-
(0,
|
|
21259
|
+
if (!(0, import_node_fs34.existsSync)(gateCreds)) return;
|
|
21260
|
+
if (!(0, import_node_fs34.existsSync)(verityCreds)) {
|
|
21261
|
+
(0, import_node_fs34.mkdirSync)((0, import_node_path25.join)(home, ".verity"), { recursive: true });
|
|
20817
21262
|
moveFile(gateCreds, verityCreds);
|
|
20818
21263
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
20819
21264
|
return;
|
|
@@ -20836,7 +21281,7 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
20836
21281
|
}
|
|
20837
21282
|
async function migrateClaudeMd(root, actions) {
|
|
20838
21283
|
const claudeMd = (0, import_node_path25.join)(root, "CLAUDE.md");
|
|
20839
|
-
const hadLegacyBlock = (0,
|
|
21284
|
+
const hadLegacyBlock = (0, import_node_fs34.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
20840
21285
|
if (!hadLegacyBlock) return;
|
|
20841
21286
|
try {
|
|
20842
21287
|
await ensureClaudeMdPointer(root);
|
|
@@ -20848,7 +21293,7 @@ async function migrateClaudeMd(root, actions) {
|
|
|
20848
21293
|
function migrateStandardFile(root, actions) {
|
|
20849
21294
|
const gateMd = (0, import_node_path25.join)(root, "GATE.md");
|
|
20850
21295
|
const verityMd = (0, import_node_path25.join)(root, "VERITY.md");
|
|
20851
|
-
if (!(0,
|
|
21296
|
+
if (!(0, import_node_fs34.existsSync)(gateMd) || (0, import_node_fs34.existsSync)(verityMd)) return;
|
|
20852
21297
|
let moved = false;
|
|
20853
21298
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
20854
21299
|
try {
|
|
@@ -20860,12 +21305,12 @@ function migrateStandardFile(root, actions) {
|
|
|
20860
21305
|
if (!moved) moveFile(gateMd, verityMd);
|
|
20861
21306
|
const content = readFileSyncSafe(verityMd);
|
|
20862
21307
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
20863
|
-
if (refreshed !== content) (0,
|
|
21308
|
+
if (refreshed !== content) (0, import_node_fs34.writeFileSync)(verityMd, refreshed);
|
|
20864
21309
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
20865
21310
|
}
|
|
20866
21311
|
async function migrateTelemetryHeaders(root, actions) {
|
|
20867
21312
|
const file = (0, import_node_path25.join)(root, ".claude", "settings.local.json");
|
|
20868
|
-
if (!(0,
|
|
21313
|
+
if (!(0, import_node_fs34.existsSync)(file)) return;
|
|
20869
21314
|
let settings;
|
|
20870
21315
|
try {
|
|
20871
21316
|
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
@@ -20913,14 +21358,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
20913
21358
|
}
|
|
20914
21359
|
if (toAppend.length > 0) {
|
|
20915
21360
|
const sep = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
20916
|
-
(0,
|
|
21361
|
+
(0, import_node_fs34.writeFileSync)(verityCreds, verityContent + sep + toAppend.join("\n") + "\n");
|
|
20917
21362
|
}
|
|
20918
|
-
(0,
|
|
21363
|
+
(0, import_node_fs34.rmSync)(gateCreds, { force: true });
|
|
20919
21364
|
return toAppend.length;
|
|
20920
21365
|
}
|
|
20921
21366
|
function readFileSyncSafe(path) {
|
|
20922
21367
|
try {
|
|
20923
|
-
return (0,
|
|
21368
|
+
return (0, import_node_fs34.readFileSync)(path, "utf-8");
|
|
20924
21369
|
} catch {
|
|
20925
21370
|
return "";
|
|
20926
21371
|
}
|
|
@@ -20935,35 +21380,35 @@ function hasStagedChanges(root) {
|
|
|
20935
21380
|
}
|
|
20936
21381
|
function moveDir(from, to) {
|
|
20937
21382
|
try {
|
|
20938
|
-
(0,
|
|
21383
|
+
(0, import_node_fs34.renameSync)(from, to);
|
|
20939
21384
|
} catch (err) {
|
|
20940
21385
|
if (err.code !== "EXDEV") throw err;
|
|
20941
|
-
(0,
|
|
20942
|
-
(0,
|
|
21386
|
+
(0, import_node_fs34.cpSync)(from, to, { recursive: true });
|
|
21387
|
+
(0, import_node_fs34.rmSync)(from, { recursive: true, force: true });
|
|
20943
21388
|
}
|
|
20944
21389
|
}
|
|
20945
21390
|
function moveFile(from, to) {
|
|
20946
21391
|
try {
|
|
20947
|
-
(0,
|
|
21392
|
+
(0, import_node_fs34.renameSync)(from, to);
|
|
20948
21393
|
} catch (err) {
|
|
20949
21394
|
if (err.code !== "EXDEV") throw err;
|
|
20950
|
-
(0,
|
|
20951
|
-
(0,
|
|
21395
|
+
(0, import_node_fs34.cpSync)(from, to);
|
|
21396
|
+
(0, import_node_fs34.rmSync)(from, { force: true });
|
|
20952
21397
|
}
|
|
20953
21398
|
}
|
|
20954
21399
|
function carryLegacyContents(gateDir, verityDir) {
|
|
20955
21400
|
let copied = 0;
|
|
20956
21401
|
const walk = (relDir) => {
|
|
20957
21402
|
const srcDir = (0, import_node_path25.join)(gateDir, relDir);
|
|
20958
|
-
for (const entry of (0,
|
|
21403
|
+
for (const entry of (0, import_node_fs34.readdirSync)(srcDir)) {
|
|
20959
21404
|
const rel = relDir ? (0, import_node_path25.join)(relDir, entry) : entry;
|
|
20960
21405
|
const src = (0, import_node_path25.join)(gateDir, rel);
|
|
20961
21406
|
const dest = (0, import_node_path25.join)(verityDir, rel);
|
|
20962
|
-
if ((0,
|
|
21407
|
+
if ((0, import_node_fs34.statSync)(src).isDirectory()) {
|
|
20963
21408
|
walk(rel);
|
|
20964
|
-
} else if (!(0,
|
|
20965
|
-
(0,
|
|
20966
|
-
(0,
|
|
21409
|
+
} else if (!(0, import_node_fs34.existsSync)(dest)) {
|
|
21410
|
+
(0, import_node_fs34.mkdirSync)((0, import_node_path25.dirname)(dest), { recursive: true });
|
|
21411
|
+
(0, import_node_fs34.cpSync)(src, dest);
|
|
20967
21412
|
copied++;
|
|
20968
21413
|
}
|
|
20969
21414
|
}
|
|
@@ -20974,20 +21419,20 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
20974
21419
|
async function needsMigration(root = repoRoot()) {
|
|
20975
21420
|
const gateDir = (0, import_node_path25.join)(root, ".gate");
|
|
20976
21421
|
const verityDir = (0, import_node_path25.join)(root, ".verity");
|
|
20977
|
-
if ((0,
|
|
20978
|
-
if ((0,
|
|
20979
|
-
if ((0,
|
|
21422
|
+
if ((0, import_node_fs34.existsSync)(gateDir) && !(0, import_node_fs34.existsSync)(verityDir)) return true;
|
|
21423
|
+
if ((0, import_node_fs34.existsSync)(gateDir) && (0, import_node_fs34.existsSync)(verityDir)) {
|
|
21424
|
+
if ((0, import_node_fs34.existsSync)((0, import_node_path25.join)(gateDir, "credentials")) && !(0, import_node_fs34.existsSync)((0, import_node_path25.join)(verityDir, "credentials"))) {
|
|
20980
21425
|
return true;
|
|
20981
21426
|
}
|
|
20982
|
-
if ((0,
|
|
21427
|
+
if ((0, import_node_fs34.existsSync)((0, import_node_path25.join)(gateDir, "memory")) && !(0, import_node_fs34.existsSync)((0, import_node_path25.join)(verityDir, "memory"))) {
|
|
20983
21428
|
return true;
|
|
20984
21429
|
}
|
|
20985
21430
|
}
|
|
20986
21431
|
const claudeMd = (0, import_node_path25.join)(root, "CLAUDE.md");
|
|
20987
|
-
if ((0,
|
|
21432
|
+
if ((0, import_node_fs34.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
20988
21433
|
return true;
|
|
20989
21434
|
}
|
|
20990
|
-
if ((0,
|
|
21435
|
+
if ((0, import_node_fs34.existsSync)((0, import_node_path25.join)(root, "GATE.md")) && !(0, import_node_fs34.existsSync)((0, import_node_path25.join)(root, "VERITY.md"))) {
|
|
20991
21436
|
return true;
|
|
20992
21437
|
}
|
|
20993
21438
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -21129,7 +21574,7 @@ function resolveDataDir() {
|
|
|
21129
21574
|
// local dev: running from repo root
|
|
21130
21575
|
];
|
|
21131
21576
|
for (const candidate of candidates) {
|
|
21132
|
-
if ((0,
|
|
21577
|
+
if ((0, import_node_fs35.existsSync)((0, import_node_path26.join)(candidate, "skills"))) {
|
|
21133
21578
|
return candidate;
|
|
21134
21579
|
}
|
|
21135
21580
|
}
|
|
@@ -21145,7 +21590,7 @@ function registerInitCommand(program2) {
|
|
|
21145
21590
|
program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
21146
21591
|
const force = opts.force ?? false;
|
|
21147
21592
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
21148
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
21593
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs35.existsSync)(m));
|
|
21149
21594
|
if (!isProject) {
|
|
21150
21595
|
printError("No project detected in the current directory.");
|
|
21151
21596
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -21215,14 +21660,14 @@ function registerInitCommand(program2) {
|
|
|
21215
21660
|
for (const skill of skills) {
|
|
21216
21661
|
const src = (0, import_node_path26.join)(skillsSource, skill);
|
|
21217
21662
|
const dest = (0, import_node_path26.join)(skillsDest, skill);
|
|
21218
|
-
if (!(0,
|
|
21663
|
+
if (!(0, import_node_fs35.existsSync)(src)) {
|
|
21219
21664
|
printWarn(` Skill data not found: ${skill}`);
|
|
21220
21665
|
continue;
|
|
21221
21666
|
}
|
|
21222
|
-
if ((0,
|
|
21667
|
+
if ((0, import_node_fs35.existsSync)(dest) && !force) {
|
|
21223
21668
|
const srcSkill = (0, import_node_path26.join)(src, "SKILL.md");
|
|
21224
21669
|
const destSkill = (0, import_node_path26.join)(dest, "SKILL.md");
|
|
21225
|
-
if ((0,
|
|
21670
|
+
if ((0, import_node_fs35.existsSync)(destSkill)) {
|
|
21226
21671
|
try {
|
|
21227
21672
|
const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
|
|
21228
21673
|
const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
|
|
@@ -21294,7 +21739,7 @@ function registerInitCommand(program2) {
|
|
|
21294
21739
|
}
|
|
21295
21740
|
|
|
21296
21741
|
// src/commands/uninstall.ts
|
|
21297
|
-
var
|
|
21742
|
+
var import_node_fs36 = require("node:fs");
|
|
21298
21743
|
var import_node_path27 = require("node:path");
|
|
21299
21744
|
var SKILL_NAMES = [
|
|
21300
21745
|
"verity-setup",
|
|
@@ -21315,10 +21760,10 @@ function registerUninstallCommand(program2) {
|
|
|
21315
21760
|
const skillsRoot = projectPath(".claude/skills");
|
|
21316
21761
|
for (const name of SKILL_NAMES) {
|
|
21317
21762
|
const dir = (0, import_node_path27.join)(skillsRoot, name);
|
|
21318
|
-
if ((0,
|
|
21763
|
+
if ((0, import_node_fs36.existsSync)(dir)) {
|
|
21319
21764
|
actions.push({
|
|
21320
21765
|
label: `Remove .claude/skills/${name}/`,
|
|
21321
|
-
apply: () => (0,
|
|
21766
|
+
apply: () => (0, import_node_fs36.rmSync)(dir, { recursive: true, force: true })
|
|
21322
21767
|
});
|
|
21323
21768
|
}
|
|
21324
21769
|
}
|
|
@@ -21332,24 +21777,24 @@ function registerUninstallCommand(program2) {
|
|
|
21332
21777
|
});
|
|
21333
21778
|
}
|
|
21334
21779
|
const verityDir = projectPath(VERITY_DIR);
|
|
21335
|
-
if ((0,
|
|
21780
|
+
if ((0, import_node_fs36.existsSync)(verityDir)) {
|
|
21336
21781
|
actions.push({
|
|
21337
21782
|
label: `Remove ${VERITY_DIR}/`,
|
|
21338
|
-
apply: () => (0,
|
|
21783
|
+
apply: () => (0, import_node_fs36.rmSync)(verityDir, { recursive: true, force: true })
|
|
21339
21784
|
});
|
|
21340
21785
|
}
|
|
21341
21786
|
if (!keepVerityMd) {
|
|
21342
21787
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
21343
|
-
if ((0,
|
|
21788
|
+
if ((0, import_node_fs36.existsSync)(verityMd)) {
|
|
21344
21789
|
actions.push({
|
|
21345
21790
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
21346
|
-
apply: () => (0,
|
|
21791
|
+
apply: () => (0, import_node_fs36.rmSync)(verityMd, { force: true })
|
|
21347
21792
|
});
|
|
21348
21793
|
}
|
|
21349
21794
|
}
|
|
21350
21795
|
const cleanupEmptyDir = (path) => {
|
|
21351
|
-
if ((0,
|
|
21352
|
-
(0,
|
|
21796
|
+
if ((0, import_node_fs36.existsSync)(path) && (0, import_node_fs36.statSync)(path).isDirectory() && (0, import_node_fs36.readdirSync)(path).length === 0) {
|
|
21797
|
+
(0, import_node_fs36.rmdirSync)(path);
|
|
21353
21798
|
}
|
|
21354
21799
|
};
|
|
21355
21800
|
actions.push({
|
|
@@ -21361,10 +21806,10 @@ function registerUninstallCommand(program2) {
|
|
|
21361
21806
|
});
|
|
21362
21807
|
const home = process.env.HOME ?? "";
|
|
21363
21808
|
const globalVerityDir = (0, import_node_path27.join)(home, ".verity");
|
|
21364
|
-
if (purgeGlobal && (0,
|
|
21809
|
+
if (purgeGlobal && (0, import_node_fs36.existsSync)(globalVerityDir)) {
|
|
21365
21810
|
actions.push({
|
|
21366
21811
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
21367
|
-
apply: () => (0,
|
|
21812
|
+
apply: () => (0, import_node_fs36.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
21368
21813
|
});
|
|
21369
21814
|
}
|
|
21370
21815
|
if (actions.length === 0) {
|
|
@@ -21558,7 +22003,7 @@ function registerTaskCommands(program2) {
|
|
|
21558
22003
|
}
|
|
21559
22004
|
|
|
21560
22005
|
// src/commands/reset.ts
|
|
21561
|
-
var
|
|
22006
|
+
var import_node_fs37 = require("node:fs");
|
|
21562
22007
|
var import_node_path28 = require("node:path");
|
|
21563
22008
|
function registerResetCommand(program2) {
|
|
21564
22009
|
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) => {
|
|
@@ -21596,11 +22041,11 @@ function registerResetCommand(program2) {
|
|
|
21596
22041
|
}
|
|
21597
22042
|
const cacheDir = projectPath(CACHE_DIR);
|
|
21598
22043
|
let purged = 0;
|
|
21599
|
-
if ((0,
|
|
21600
|
-
for (const entry of (0,
|
|
22044
|
+
if ((0, import_node_fs37.existsSync)(cacheDir)) {
|
|
22045
|
+
for (const entry of (0, import_node_fs37.readdirSync)(cacheDir)) {
|
|
21601
22046
|
if (entry.startsWith("pending-")) {
|
|
21602
22047
|
try {
|
|
21603
|
-
(0,
|
|
22048
|
+
(0, import_node_fs37.unlinkSync)((0, import_node_path28.join)(cacheDir, entry));
|
|
21604
22049
|
purged++;
|
|
21605
22050
|
} catch {
|
|
21606
22051
|
}
|
|
@@ -21615,19 +22060,19 @@ function registerResetCommand(program2) {
|
|
|
21615
22060
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
21616
22061
|
];
|
|
21617
22062
|
for (const file of filesToClear) {
|
|
21618
|
-
if ((0,
|
|
22063
|
+
if ((0, import_node_fs37.existsSync)(file)) {
|
|
21619
22064
|
try {
|
|
21620
|
-
(0,
|
|
22065
|
+
(0, import_node_fs37.writeFileSync)(file, "");
|
|
21621
22066
|
} catch {
|
|
21622
22067
|
}
|
|
21623
22068
|
}
|
|
21624
22069
|
}
|
|
21625
22070
|
if (opts.all) {
|
|
21626
22071
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
21627
|
-
if ((0,
|
|
21628
|
-
for (const entry of (0,
|
|
22072
|
+
if ((0, import_node_fs37.existsSync)(logsDir)) {
|
|
22073
|
+
for (const entry of (0, import_node_fs37.readdirSync)(logsDir)) {
|
|
21629
22074
|
try {
|
|
21630
|
-
(0,
|
|
22075
|
+
(0, import_node_fs37.unlinkSync)((0, import_node_path28.join)(logsDir, entry));
|
|
21631
22076
|
} catch {
|
|
21632
22077
|
}
|
|
21633
22078
|
}
|
|
@@ -21935,8 +22380,8 @@ function registerTelemetryCommands(program2) {
|
|
|
21935
22380
|
}
|
|
21936
22381
|
|
|
21937
22382
|
// src/cli.ts
|
|
21938
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.
|
|
21939
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.
|
|
22383
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.29.4-experimental.6f2ede0").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) => {
|
|
22384
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.29.4-experimental.6f2ede0");
|
|
21940
22385
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
21941
22386
|
try {
|
|
21942
22387
|
await foldLegacyLocalCredential();
|