@codacy/verity-cli 0.30.0 → 0.30.1-experimental.e2137b9
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 +868 -473
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10342,6 +10342,10 @@ function repoRoot() {
|
|
|
10342
10342
|
}
|
|
10343
10343
|
return _repoRoot;
|
|
10344
10344
|
}
|
|
10345
|
+
function _resetRepoRoot() {
|
|
10346
|
+
_repoRoot = null;
|
|
10347
|
+
_mainRoot = void 0;
|
|
10348
|
+
}
|
|
10345
10349
|
var _mainRoot;
|
|
10346
10350
|
function mainWorktreeRoot() {
|
|
10347
10351
|
if (_mainRoot !== void 0) return _mainRoot;
|
|
@@ -10493,7 +10497,7 @@ var SECURITY_PATTERNS = [
|
|
|
10493
10497
|
/Dockerfile/
|
|
10494
10498
|
];
|
|
10495
10499
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10496
|
-
var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
|
|
10500
|
+
var DEFAULT_SERVICE_URL = "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1".length > 0 ? "https://wukeddyzpijoegyajtnc.supabase.co/functions/v1" : PROD_SERVICE_URL;
|
|
10497
10501
|
var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
|
|
10498
10502
|
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10499
10503
|
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
@@ -10804,13 +10808,6 @@ function execGit(cmd) {
|
|
|
10804
10808
|
return "";
|
|
10805
10809
|
}
|
|
10806
10810
|
}
|
|
10807
|
-
function execGitArgs(args) {
|
|
10808
|
-
try {
|
|
10809
|
-
return (0, import_node_child_process3.execFileSync)("git", args, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
10810
|
-
} catch {
|
|
10811
|
-
return "";
|
|
10812
|
-
}
|
|
10813
|
-
}
|
|
10814
10811
|
function splitLines(s) {
|
|
10815
10812
|
return s.split("\n").filter((l) => l.length > 0);
|
|
10816
10813
|
}
|
|
@@ -10877,9 +10874,6 @@ function getChangedFiles() {
|
|
|
10877
10874
|
const filtered = Array.from(sets).filter((f) => !isVerityOwnedPath(f));
|
|
10878
10875
|
return { files: filtered, hasRecentCommitFiles };
|
|
10879
10876
|
}
|
|
10880
|
-
function getStagedFiles() {
|
|
10881
|
-
return splitLines(execGit("git diff --cached --name-only")).filter((f) => !isVerityOwnedPath(f));
|
|
10882
|
-
}
|
|
10883
10877
|
function getDirtyFiles() {
|
|
10884
10878
|
const set = /* @__PURE__ */ new Set();
|
|
10885
10879
|
for (const f of splitLines(execGit("git diff --name-only HEAD"))) set.add(f);
|
|
@@ -10900,33 +10894,6 @@ function showContentAtRef(ref, repoRelPath) {
|
|
|
10900
10894
|
return null;
|
|
10901
10895
|
}
|
|
10902
10896
|
}
|
|
10903
|
-
function getPushRangeFiles() {
|
|
10904
|
-
const diff = (range) => splitLines(execGitArgs(["diff", "--name-only", range])).filter((f) => !f.startsWith(".verity/") && !f.startsWith(".verity\\"));
|
|
10905
|
-
const resolvers = [
|
|
10906
|
-
() => execGitArgs(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{push}"]) ? "@{push}..HEAD" : null,
|
|
10907
|
-
() => execGitArgs(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{upstream}"]) ? "@{upstream}..HEAD" : null,
|
|
10908
|
-
() => {
|
|
10909
|
-
const branch = execGitArgs(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
10910
|
-
return branch && branch !== "HEAD" && execGitArgs(["rev-parse", "--verify", "-q", `origin/${branch}`]) ? `origin/${branch}..HEAD` : null;
|
|
10911
|
-
}
|
|
10912
|
-
];
|
|
10913
|
-
for (const resolve3 of resolvers) {
|
|
10914
|
-
const range = resolve3();
|
|
10915
|
-
if (range) return { files: diff(range), range };
|
|
10916
|
-
}
|
|
10917
|
-
const baseline = readBaselineSha();
|
|
10918
|
-
if (baseline) {
|
|
10919
|
-
const files = diff(`${baseline}..HEAD`);
|
|
10920
|
-
if (files.length > 0) return { files, range: `${baseline}..HEAD` };
|
|
10921
|
-
}
|
|
10922
|
-
const last = diff("HEAD~1..HEAD");
|
|
10923
|
-
return { files: last, range: last.length > 0 ? "HEAD~1..HEAD" : null };
|
|
10924
|
-
}
|
|
10925
|
-
function getPushRangeMessages() {
|
|
10926
|
-
const { range } = getPushRangeFiles();
|
|
10927
|
-
if (!range) return "";
|
|
10928
|
-
return execGitArgs(["log", range, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
10929
|
-
}
|
|
10930
10897
|
function filterAnalyzable(files) {
|
|
10931
10898
|
return files.filter((f) => {
|
|
10932
10899
|
const ext = (0, import_node_path3.extname)(f).slice(1);
|
|
@@ -11417,7 +11384,7 @@ var readline = __toESM(require("node:readline/promises"));
|
|
|
11417
11384
|
var import_node_os = require("node:os");
|
|
11418
11385
|
|
|
11419
11386
|
// src/lib/provider-auth.ts
|
|
11420
|
-
var sleep = (ms) => new Promise((
|
|
11387
|
+
var sleep = (ms) => new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
11421
11388
|
var form = (fields) => new URLSearchParams(fields).toString();
|
|
11422
11389
|
async function githubAccountId(owner) {
|
|
11423
11390
|
try {
|
|
@@ -13747,6 +13714,160 @@ var import_node_fs14 = require("node:fs");
|
|
|
13747
13714
|
var import_node_crypto7 = require("node:crypto");
|
|
13748
13715
|
var import_node_path13 = require("node:path");
|
|
13749
13716
|
|
|
13717
|
+
// src/lib/analysis-mode.ts
|
|
13718
|
+
var DEBUG_PHRASES = [
|
|
13719
|
+
"not working",
|
|
13720
|
+
"doesn't work",
|
|
13721
|
+
"doesn't work",
|
|
13722
|
+
"does not work",
|
|
13723
|
+
"isn't working",
|
|
13724
|
+
"is not working",
|
|
13725
|
+
"can't figure out",
|
|
13726
|
+
"stack trace"
|
|
13727
|
+
];
|
|
13728
|
+
var DEBUG_WORDS = [
|
|
13729
|
+
"fix",
|
|
13730
|
+
"bug",
|
|
13731
|
+
"broken",
|
|
13732
|
+
"crash",
|
|
13733
|
+
"crashing",
|
|
13734
|
+
"failing",
|
|
13735
|
+
"debug",
|
|
13736
|
+
"debugging",
|
|
13737
|
+
"investigate",
|
|
13738
|
+
"troubleshoot",
|
|
13739
|
+
"regression",
|
|
13740
|
+
"wrong"
|
|
13741
|
+
];
|
|
13742
|
+
var DEBUG_PATTERN = new RegExp(
|
|
13743
|
+
[
|
|
13744
|
+
...DEBUG_PHRASES.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")),
|
|
13745
|
+
...DEBUG_WORDS.map((w) => `\\b${w}\\b`)
|
|
13746
|
+
].join("|"),
|
|
13747
|
+
"i"
|
|
13748
|
+
);
|
|
13749
|
+
var FALSE_POSITIVE_PATTERNS = [
|
|
13750
|
+
/\b(?:add|create|implement|write|build|design|set\s*up)\b.{0,20}\berror\b/i,
|
|
13751
|
+
/\berror\s+handling\b/i,
|
|
13752
|
+
/\berror\s+boundar(?:y|ies)\b/i,
|
|
13753
|
+
/\berror\s+(?:type|class|page|component|message|code|enum)\b/i,
|
|
13754
|
+
/\b(?:add|create|implement|write|build)\b.{0,20}\b(?:fix|debug|issue)\b/i
|
|
13755
|
+
];
|
|
13756
|
+
function hasDebugIntent(prompt) {
|
|
13757
|
+
if (!DEBUG_PATTERN.test(prompt)) return false;
|
|
13758
|
+
for (const fp of FALSE_POSITIVE_PATTERNS) {
|
|
13759
|
+
if (fp.test(prompt)) return false;
|
|
13760
|
+
}
|
|
13761
|
+
return true;
|
|
13762
|
+
}
|
|
13763
|
+
var GIT_ONLY_PATTERN = /\b(commit|push|deploy|merge|rebase|tag|release|publish|ship)\b/i;
|
|
13764
|
+
var CODE_AUTHORING_PATTERN = /\b(add|create|implement|build|write|fix|update|change|refactor|modify|remove|delete|move|rename)\b.*\b(function|component|feature|endpoint|test|file|module|class|type|interface|hook|page|route|style|migration|code|bug|error|issue)\b/i;
|
|
13765
|
+
function isGitOnlyPrompt(prompt) {
|
|
13766
|
+
if (!GIT_ONLY_PATTERN.test(prompt)) return false;
|
|
13767
|
+
if (CODE_AUTHORING_PATTERN.test(prompt)) return false;
|
|
13768
|
+
return true;
|
|
13769
|
+
}
|
|
13770
|
+
function reconcileAnalysisMode(predictedMode, signals) {
|
|
13771
|
+
const mode2 = resolveAnalysisMode(predictedMode, signals);
|
|
13772
|
+
if (mode2 !== "skip") return mode2;
|
|
13773
|
+
const windowIsOrphaned = signals.actionSummary?.transcript_windowed === "orphaned";
|
|
13774
|
+
if (windowIsOrphaned && !signals.sessionAuthoredCode) return "standard";
|
|
13775
|
+
return mode2;
|
|
13776
|
+
}
|
|
13777
|
+
function resolveAnalysisMode(predictedMode, signals) {
|
|
13778
|
+
if (!predictedMode || !isValidMode(predictedMode)) {
|
|
13779
|
+
return detectAnalysisMode(
|
|
13780
|
+
signals.noFilesChanged,
|
|
13781
|
+
signals.assistantResponse,
|
|
13782
|
+
signals.conversationPrompts,
|
|
13783
|
+
signals.actionSummary,
|
|
13784
|
+
signals.sessionAuthoredCode
|
|
13785
|
+
);
|
|
13786
|
+
}
|
|
13787
|
+
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
|
|
13788
|
+
const agentInvestigated = didAgentInvestigate(signals.actionSummary);
|
|
13789
|
+
switch (predictedMode) {
|
|
13790
|
+
case "skip":
|
|
13791
|
+
if (agentAuthoredCode) return "standard";
|
|
13792
|
+
return "skip";
|
|
13793
|
+
case "plan":
|
|
13794
|
+
if (agentAuthoredCode) return "standard";
|
|
13795
|
+
return "plan";
|
|
13796
|
+
case "debug":
|
|
13797
|
+
return "debug";
|
|
13798
|
+
case "standard":
|
|
13799
|
+
if (!!signals.actionSummary && !agentAuthoredCode && !!signals.assistantResponse) {
|
|
13800
|
+
return agentInvestigated ? "plan" : "skip";
|
|
13801
|
+
}
|
|
13802
|
+
return "standard";
|
|
13803
|
+
}
|
|
13804
|
+
}
|
|
13805
|
+
function didAgentInvestigate(summary) {
|
|
13806
|
+
if (!summary) return false;
|
|
13807
|
+
return summary.files_read.length > 0 || summary.searches > 0 || summary.commands.length > 0 || summary.subagents > 0 || summary.web_fetches > 0;
|
|
13808
|
+
}
|
|
13809
|
+
function isValidMode(mode2) {
|
|
13810
|
+
return mode2 === "standard" || mode2 === "plan" || mode2 === "debug" || mode2 === "skip";
|
|
13811
|
+
}
|
|
13812
|
+
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
|
|
13813
|
+
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
|
|
13814
|
+
if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
|
|
13815
|
+
if (!agentAuthoredCode) return "skip";
|
|
13816
|
+
}
|
|
13817
|
+
if (noFilesChanged && !!assistantResponse && !agentAuthoredCode) {
|
|
13818
|
+
return "plan";
|
|
13819
|
+
}
|
|
13820
|
+
if (!!actionSummary && !agentAuthoredCode && !!assistantResponse) {
|
|
13821
|
+
return didAgentInvestigate(actionSummary) ? "plan" : "skip";
|
|
13822
|
+
}
|
|
13823
|
+
for (const prompt of conversationPrompts) {
|
|
13824
|
+
if (hasDebugIntent(prompt)) {
|
|
13825
|
+
return "debug";
|
|
13826
|
+
}
|
|
13827
|
+
}
|
|
13828
|
+
return "standard";
|
|
13829
|
+
}
|
|
13830
|
+
var FILE_MUTATE_RE = /(?:^|[\s|&;(`])(?:sed\s+-i|perl\s+-i|awk\b|tee\b|dd\b|cp\b|mv\b|ln\b|install\b|touch\b|patch\b|git\s+(?:apply|am)\b|cargo\s+build|go\s+generate|make\b|--write\b|--fix\b|--in-place\b)|>>?(?![&>])/i;
|
|
13831
|
+
var GIT_PLUMBING_RE = /^\s*git\s+(?:merge|rebase|stash|cherry-pick|revert|pull|fetch|checkout|switch|reset|restore|clean)\b/i;
|
|
13832
|
+
var READ_ONLY_RE = /^\s*(?:git\s+(?:status|diff|log|show|branch|remote|config|rev-parse|ls-files|blame|describe)|ls|cat|head|tail|less|grep|rg|find|pwd|echo|printf|wc|which|type|tree|stat|file|env|printenv|date|whoami)\b/i;
|
|
13833
|
+
var CHAIN_RE = /&&|\||;|\$\(|\x60/;
|
|
13834
|
+
function isNonAuthoringCommand(cmd) {
|
|
13835
|
+
if (typeof cmd !== "string" || cmd.trim().length === 0) return false;
|
|
13836
|
+
if (FILE_MUTATE_RE.test(cmd)) return false;
|
|
13837
|
+
if (CHAIN_RE.test(cmd)) return false;
|
|
13838
|
+
return GIT_PLUMBING_RE.test(cmd) || READ_ONLY_RE.test(cmd);
|
|
13839
|
+
}
|
|
13840
|
+
function hasNonEditAuthorship(actionSummary, sessionAuthoredCode) {
|
|
13841
|
+
if (!actionSummary) return sessionAuthoredCode;
|
|
13842
|
+
if ((actionSummary.subagents ?? 0) > 0) return true;
|
|
13843
|
+
if (Object.keys(actionSummary.tool_counts ?? {}).some((t) => t.startsWith("mcp__"))) return true;
|
|
13844
|
+
const commands = actionSummary.commands ?? [];
|
|
13845
|
+
if (commands.some((c) => FILE_MUTATE_RE.test(c))) return true;
|
|
13846
|
+
if (sessionAuthoredCode) {
|
|
13847
|
+
const allSafe = commands.length > 0 && commands.every(isNonAuthoringCommand);
|
|
13848
|
+
if (!allSafe) return true;
|
|
13849
|
+
}
|
|
13850
|
+
return false;
|
|
13851
|
+
}
|
|
13852
|
+
function scopeToAuthored(files, actionSummary) {
|
|
13853
|
+
if (!actionSummary) return { files, signal: "no-transcript" };
|
|
13854
|
+
const touched = [...actionSummary.files_edited ?? [], ...actionSummary.files_created ?? []];
|
|
13855
|
+
if (touched.length === 0) return { files: [], signal: "none-authored" };
|
|
13856
|
+
return { files: narrowToAgentAuthored(files, actionSummary), signal: "authored" };
|
|
13857
|
+
}
|
|
13858
|
+
function narrowToAgentAuthored(files, actionSummary) {
|
|
13859
|
+
if (!actionSummary) return files;
|
|
13860
|
+
const touched = [
|
|
13861
|
+
...actionSummary.files_edited,
|
|
13862
|
+
...actionSummary.files_created
|
|
13863
|
+
];
|
|
13864
|
+
if (touched.length === 0) return files;
|
|
13865
|
+
return files.filter((f) => {
|
|
13866
|
+
const suffix = "/" + f;
|
|
13867
|
+
return touched.some((t) => t === f || t.endsWith(suffix));
|
|
13868
|
+
});
|
|
13869
|
+
}
|
|
13870
|
+
|
|
13750
13871
|
// src/lib/skip-detection.ts
|
|
13751
13872
|
function isBareAckPrompt(prompt) {
|
|
13752
13873
|
if (typeof prompt !== "string") return false;
|
|
@@ -13810,6 +13931,15 @@ function shouldSkipForBareAck(input) {
|
|
|
13810
13931
|
if (input.turnAuthoredCode) return false;
|
|
13811
13932
|
return input.canSeeTurnAuthorship;
|
|
13812
13933
|
}
|
|
13934
|
+
function isCommandOnlyTurn(input) {
|
|
13935
|
+
if (!input.authorshipIsObservable) return false;
|
|
13936
|
+
if (input.userCommandsTruncated) return false;
|
|
13937
|
+
const commands = input.userCommands ?? [];
|
|
13938
|
+
if (commands.length === 0) return false;
|
|
13939
|
+
if (input.agentAuthoredFiles > 0) return false;
|
|
13940
|
+
if (input.agentToolCalls > 0) return false;
|
|
13941
|
+
return commands.every(isNonAuthoringCommand);
|
|
13942
|
+
}
|
|
13813
13943
|
|
|
13814
13944
|
// src/lib/pending-repeat.ts
|
|
13815
13945
|
var STOP = /* @__PURE__ */ new Set([
|
|
@@ -16119,17 +16249,17 @@ async function readHookStdin() {
|
|
|
16119
16249
|
try {
|
|
16120
16250
|
if (process.stdin.isTTY) return {};
|
|
16121
16251
|
const chunks = [];
|
|
16122
|
-
const timeout = new Promise((
|
|
16123
|
-
const read = new Promise((
|
|
16252
|
+
const timeout = new Promise((resolve4) => setTimeout(() => resolve4({}), 500));
|
|
16253
|
+
const read = new Promise((resolve4) => {
|
|
16124
16254
|
process.stdin.on("data", (c) => chunks.push(c));
|
|
16125
16255
|
process.stdin.on("end", () => {
|
|
16126
16256
|
try {
|
|
16127
|
-
|
|
16257
|
+
resolve4(JSON.parse(Buffer.concat(chunks).toString("utf-8").trim() || "{}"));
|
|
16128
16258
|
} catch {
|
|
16129
|
-
|
|
16259
|
+
resolve4({});
|
|
16130
16260
|
}
|
|
16131
16261
|
});
|
|
16132
|
-
process.stdin.on("error", () =>
|
|
16262
|
+
process.stdin.on("error", () => resolve4({}));
|
|
16133
16263
|
process.stdin.resume();
|
|
16134
16264
|
});
|
|
16135
16265
|
return await Promise.race([read, timeout]);
|
|
@@ -16647,6 +16777,7 @@ function createRun(opts, globals) {
|
|
|
16647
16777
|
phaseReached: "",
|
|
16648
16778
|
phasesCompleted: [],
|
|
16649
16779
|
skipReason: null,
|
|
16780
|
+
treeFrame: null,
|
|
16650
16781
|
turnId: "",
|
|
16651
16782
|
// The value `resolveReachability` itself returns when every rung declines.
|
|
16652
16783
|
reachability: { human_reachable: "unknown", human_reachable_source: "inferred", rung: "none" },
|
|
@@ -16786,6 +16917,10 @@ function formatRunEvidence(run, startedAt) {
|
|
|
16786
16917
|
`;
|
|
16787
16918
|
out += row("turn", `${run.turnId || "(unminted)"}${run.sessionId ? ` \xB7 session ${run.sessionId}` : ""}`);
|
|
16788
16919
|
out += row("reached", `${run.phaseReached || "(none)"}${run.skipReason ? ` \xB7 SKIPPED: ${run.skipReason}` : ""} \xB7 ${ms}ms`);
|
|
16920
|
+
if (run.treeFrame) {
|
|
16921
|
+
const f = run.treeFrame;
|
|
16922
|
+
out += row("tree", f.worktreeRoot ? `${f.worktreeRoot}${f.isLinkedWorktree ? " \xB7 linked worktree" : ""}${f.branch ? ` \xB7 branch ${f.branch}` : " \xB7 detached"}` : `(unresolved: ${f.refusal ?? "unknown"})`);
|
|
16923
|
+
}
|
|
16789
16924
|
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}`);
|
|
16790
16925
|
const done = (phase) => run.phasesCompleted.includes(phase);
|
|
16791
16926
|
const ifDone = (phase, value) => done(phase) ? value : "?";
|
|
@@ -16882,6 +17017,301 @@ function installRunEvidence(run) {
|
|
|
16882
17017
|
});
|
|
16883
17018
|
}
|
|
16884
17019
|
|
|
17020
|
+
// src/lib/git-frame.ts
|
|
17021
|
+
var import_node_child_process7 = require("node:child_process");
|
|
17022
|
+
var import_node_fs20 = require("node:fs");
|
|
17023
|
+
var import_node_os3 = require("node:os");
|
|
17024
|
+
var import_node_path18 = require("node:path");
|
|
17025
|
+
var import_node_path19 = require("node:path");
|
|
17026
|
+
var VALUE_TOKEN = `(?:'[^']*'|"[^"]*"|\\S+)`;
|
|
17027
|
+
var GIT_GLOBAL_OPTS = `(?:\\s+(?:-[Cc]\\s+${VALUE_TOKEN}|--?[\\w-]+(?:=\\S+)?))*`;
|
|
17028
|
+
var COMMIT_HEAD = `git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`;
|
|
17029
|
+
var PUSH_HEAD = `git${GIT_GLOBAL_OPTS}\\s+push\\b`;
|
|
17030
|
+
var GH_PR_HEAD = `gh${GIT_GLOBAL_OPTS}\\s+pr\\s+create\\b`;
|
|
17031
|
+
var COMMIT_RE = new RegExp(`(?:^|[\\s;&|(])${COMMIT_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${COMMIT_HEAD}`);
|
|
17032
|
+
var PUSH_RE = new RegExp(`(?:^|[\\s;&|(])${PUSH_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${PUSH_HEAD}`);
|
|
17033
|
+
var GH_PR_RE = new RegExp(`(?:^|[\\s;&|(])${GH_PR_HEAD}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${GH_PR_HEAD}`);
|
|
17034
|
+
function splitSegments(command) {
|
|
17035
|
+
return (command ?? "").split(/&&|\|\||;|\n/);
|
|
17036
|
+
}
|
|
17037
|
+
function findMomentSegment(command, on) {
|
|
17038
|
+
const segments = splitSegments(command);
|
|
17039
|
+
let commitIdx = -1;
|
|
17040
|
+
let pushIdx = -1;
|
|
17041
|
+
for (let i = 0; i < segments.length; i++) {
|
|
17042
|
+
const seg = segments[i];
|
|
17043
|
+
if (/--dry-run\b/.test(seg)) continue;
|
|
17044
|
+
if (commitIdx === -1 && COMMIT_RE.test(seg)) commitIdx = i;
|
|
17045
|
+
if (pushIdx === -1 && (PUSH_RE.test(seg) || GH_PR_RE.test(seg))) pushIdx = i;
|
|
17046
|
+
}
|
|
17047
|
+
if (commitIdx !== -1 && on.includes("commit")) return { moment: "pre-commit", segmentIndex: commitIdx };
|
|
17048
|
+
if (pushIdx !== -1 && on.includes("push")) return { moment: "pre-push", segmentIndex: pushIdx };
|
|
17049
|
+
return null;
|
|
17050
|
+
}
|
|
17051
|
+
function classifyCommand(command, on) {
|
|
17052
|
+
return findMomentSegment(command, on)?.moment ?? null;
|
|
17053
|
+
}
|
|
17054
|
+
function unquote(token) {
|
|
17055
|
+
if (token.length >= 2) {
|
|
17056
|
+
const first = token[0];
|
|
17057
|
+
if ((first === "'" || first === '"') && token.endsWith(first)) return token.slice(1, -1);
|
|
17058
|
+
}
|
|
17059
|
+
return token;
|
|
17060
|
+
}
|
|
17061
|
+
var SHELL_DYNAMIC = /[$`\\]/;
|
|
17062
|
+
function extractCommandTarget(command, segmentIndex, baseDir) {
|
|
17063
|
+
const segments = splitSegments(command);
|
|
17064
|
+
let dir = baseDir;
|
|
17065
|
+
let named = false;
|
|
17066
|
+
for (let i = 0; i < segmentIndex; i++) {
|
|
17067
|
+
const m = segments[i].match(new RegExp(`^\\s*cd(?:\\s+(${VALUE_TOKEN}))?\\s*$`));
|
|
17068
|
+
if (!m) continue;
|
|
17069
|
+
named = true;
|
|
17070
|
+
if (m[1] === void 0) {
|
|
17071
|
+
dir = (0, import_node_os3.homedir)();
|
|
17072
|
+
continue;
|
|
17073
|
+
}
|
|
17074
|
+
const raw = unquote(m[1]);
|
|
17075
|
+
if (SHELL_DYNAMIC.test(raw) || raw === "-") {
|
|
17076
|
+
return { dir: null, named: true, unresolvable: `cd target not statically resolvable: ${raw}` };
|
|
17077
|
+
}
|
|
17078
|
+
const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
|
|
17079
|
+
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17080
|
+
}
|
|
17081
|
+
const seg = segments[segmentIndex];
|
|
17082
|
+
const overrideMatch = /--(?:git-dir|work-tree)(?:=|\s)|\bGIT_(?:DIR|WORK_TREE|INDEX_FILE)=/.exec(seg);
|
|
17083
|
+
if (overrideMatch) {
|
|
17084
|
+
return { dir: null, named: true, unresolvable: `git-dir/work-tree/index override in command: ${overrideMatch[0].trim()}` };
|
|
17085
|
+
}
|
|
17086
|
+
const gitMatch = seg.match(new RegExp(`(?:^|[\\s;&|(])(?:[^\\s;&|()'"]*\\/)?git(${GIT_GLOBAL_OPTS})\\s`));
|
|
17087
|
+
if (gitMatch) {
|
|
17088
|
+
const optsRegion = gitMatch[1] ?? "";
|
|
17089
|
+
const cRe = new RegExp(`-C\\s+(${VALUE_TOKEN})`, "g");
|
|
17090
|
+
let cm;
|
|
17091
|
+
while ((cm = cRe.exec(optsRegion)) !== null) {
|
|
17092
|
+
named = true;
|
|
17093
|
+
const raw = unquote(cm[1]);
|
|
17094
|
+
if (SHELL_DYNAMIC.test(raw)) {
|
|
17095
|
+
return { dir: null, named: true, unresolvable: `-C target not statically resolvable: ${raw}` };
|
|
17096
|
+
}
|
|
17097
|
+
const expanded = raw === "~" ? (0, import_node_os3.homedir)() : raw.startsWith("~/") ? (0, import_node_path19.join)((0, import_node_os3.homedir)(), raw.slice(2)) : raw;
|
|
17098
|
+
dir = (0, import_node_path18.isAbsolute)(expanded) ? expanded : (0, import_node_path18.resolve)(dir, expanded);
|
|
17099
|
+
}
|
|
17100
|
+
}
|
|
17101
|
+
return { dir, named, unresolvable: null };
|
|
17102
|
+
}
|
|
17103
|
+
var PUSH_VALUE_FLAGS = /* @__PURE__ */ new Set(["-o", "--push-option", "--receive-pack", "--exec"]);
|
|
17104
|
+
function parsePushTarget(segment) {
|
|
17105
|
+
const none = { remote: null, srcRef: null, dstRef: null, isDelete: false };
|
|
17106
|
+
const m = PUSH_RE.exec(segment);
|
|
17107
|
+
if (!m) return none;
|
|
17108
|
+
const rest = segment.slice(m.index + m[0].length);
|
|
17109
|
+
const tokens = (rest.match(new RegExp(`'[^']*'|"[^"]*"|\\S+`, "g")) ?? []).map(unquote);
|
|
17110
|
+
let remote = null;
|
|
17111
|
+
let refspec = null;
|
|
17112
|
+
let isDelete = false;
|
|
17113
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
17114
|
+
const t = tokens[i];
|
|
17115
|
+
if (t === "-d" || t === "--delete") {
|
|
17116
|
+
isDelete = true;
|
|
17117
|
+
continue;
|
|
17118
|
+
}
|
|
17119
|
+
if (t.startsWith("--repo=")) {
|
|
17120
|
+
remote = t.slice("--repo=".length);
|
|
17121
|
+
continue;
|
|
17122
|
+
}
|
|
17123
|
+
if (t === "--repo") {
|
|
17124
|
+
remote = tokens[++i] ?? null;
|
|
17125
|
+
continue;
|
|
17126
|
+
}
|
|
17127
|
+
if (PUSH_VALUE_FLAGS.has(t)) {
|
|
17128
|
+
i++;
|
|
17129
|
+
continue;
|
|
17130
|
+
}
|
|
17131
|
+
if (t.startsWith("-")) continue;
|
|
17132
|
+
if (SHELL_DYNAMIC.test(t)) return none;
|
|
17133
|
+
if (remote === null) {
|
|
17134
|
+
remote = t;
|
|
17135
|
+
continue;
|
|
17136
|
+
}
|
|
17137
|
+
if (refspec === null) {
|
|
17138
|
+
refspec = t;
|
|
17139
|
+
continue;
|
|
17140
|
+
}
|
|
17141
|
+
break;
|
|
17142
|
+
}
|
|
17143
|
+
if (refspec === null) return { remote, srcRef: null, dstRef: null, isDelete };
|
|
17144
|
+
const spec = refspec.startsWith("+") ? refspec.slice(1) : refspec;
|
|
17145
|
+
const colon = spec.indexOf(":");
|
|
17146
|
+
if (colon === -1) return { remote, srcRef: spec, dstRef: null, isDelete };
|
|
17147
|
+
const src = spec.slice(0, colon);
|
|
17148
|
+
const dst = spec.slice(colon + 1);
|
|
17149
|
+
if (src === "") return { remote, srcRef: null, dstRef: dst || null, isDelete: true };
|
|
17150
|
+
return { remote, srcRef: src, dstRef: dst || null, isDelete };
|
|
17151
|
+
}
|
|
17152
|
+
function gitAt(dir, args) {
|
|
17153
|
+
try {
|
|
17154
|
+
return (0, import_node_child_process7.execFileSync)("git", args, { cwd: dir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
17155
|
+
} catch {
|
|
17156
|
+
return "";
|
|
17157
|
+
}
|
|
17158
|
+
}
|
|
17159
|
+
function realpathOr(p) {
|
|
17160
|
+
try {
|
|
17161
|
+
return import_node_fs20.realpathSync.native(p);
|
|
17162
|
+
} catch {
|
|
17163
|
+
return (0, import_node_path18.resolve)(p);
|
|
17164
|
+
}
|
|
17165
|
+
}
|
|
17166
|
+
function resolveFrame(input) {
|
|
17167
|
+
const found = findMomentSegment(input.command, input.on);
|
|
17168
|
+
const hookDirUsable = !!input.hookCwd && (0, import_node_fs20.existsSync)(input.hookCwd);
|
|
17169
|
+
const baseDir = hookDirUsable ? input.hookCwd : process.cwd();
|
|
17170
|
+
let anchor = hookDirUsable ? "hook-cwd" : "process-cwd";
|
|
17171
|
+
const refuse = (refusal) => ({
|
|
17172
|
+
moment: found?.moment ?? null,
|
|
17173
|
+
frame: { worktreeRoot: null, gitDir: null, commonDir: null, isLinkedWorktree: false, branch: null, anchor, refusal }
|
|
17174
|
+
});
|
|
17175
|
+
let dir = baseDir;
|
|
17176
|
+
if (found) {
|
|
17177
|
+
const segments = splitSegments(input.command);
|
|
17178
|
+
const kindRes = found.moment === "pre-commit" ? [COMMIT_RE] : [PUSH_RE, GH_PR_RE];
|
|
17179
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
17180
|
+
for (let i = 0; i < segments.length; i++) {
|
|
17181
|
+
if (/--dry-run\b/.test(segments[i])) continue;
|
|
17182
|
+
if (!kindRes.some((re) => re.test(segments[i]))) continue;
|
|
17183
|
+
const target = extractCommandTarget(input.command, i, baseDir);
|
|
17184
|
+
if (target.named) anchor = "command-target";
|
|
17185
|
+
if (target.unresolvable) return refuse(`target:${target.unresolvable}`);
|
|
17186
|
+
dirs.add(target.dir ?? baseDir);
|
|
17187
|
+
}
|
|
17188
|
+
if (dirs.size > 1) return refuse(`target:multiple ${found.moment} targets in one command`);
|
|
17189
|
+
const targetDir = dirs.size === 1 ? [...dirs][0] : baseDir;
|
|
17190
|
+
if (targetDir !== baseDir) {
|
|
17191
|
+
if (!(0, import_node_fs20.existsSync)(targetDir)) return refuse(`target:directory does not exist: ${targetDir}`);
|
|
17192
|
+
dir = targetDir;
|
|
17193
|
+
}
|
|
17194
|
+
}
|
|
17195
|
+
const toplevel = gitAt(dir, ["rev-parse", "--show-toplevel"]);
|
|
17196
|
+
if (!toplevel) {
|
|
17197
|
+
return refuse(anchor === "command-target" ? `target:not a git repository: ${dir}` : `anchor:not a git repository: ${dir}`);
|
|
17198
|
+
}
|
|
17199
|
+
const gitDirRaw = gitAt(dir, ["rev-parse", "--absolute-git-dir"]);
|
|
17200
|
+
const commonRaw = gitAt(dir, ["rev-parse", "--git-common-dir"]);
|
|
17201
|
+
const gitDir = gitDirRaw ? realpathOr(gitDirRaw) : null;
|
|
17202
|
+
const commonDir = commonRaw ? realpathOr((0, import_node_path18.isAbsolute)(commonRaw) ? commonRaw : (0, import_node_path18.resolve)(dir, commonRaw)) : null;
|
|
17203
|
+
const branchRaw = gitAt(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
17204
|
+
return {
|
|
17205
|
+
moment: found?.moment ?? null,
|
|
17206
|
+
frame: {
|
|
17207
|
+
worktreeRoot: realpathOr(toplevel),
|
|
17208
|
+
gitDir,
|
|
17209
|
+
commonDir,
|
|
17210
|
+
// The one honest definition: a linked worktree's own git dir differs from
|
|
17211
|
+
// the shared one. No path convention involved — see the header.
|
|
17212
|
+
isLinkedWorktree: !!gitDir && !!commonDir && gitDir !== commonDir,
|
|
17213
|
+
branch: !branchRaw || branchRaw === "HEAD" ? null : branchRaw,
|
|
17214
|
+
anchor,
|
|
17215
|
+
refusal: null
|
|
17216
|
+
}
|
|
17217
|
+
};
|
|
17218
|
+
}
|
|
17219
|
+
function frameGit(frame, args) {
|
|
17220
|
+
if (!frame.worktreeRoot) return "";
|
|
17221
|
+
return gitAt(frame.worktreeRoot, args);
|
|
17222
|
+
}
|
|
17223
|
+
function refResolves(frame, ref) {
|
|
17224
|
+
return frameGit(frame, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]) !== "";
|
|
17225
|
+
}
|
|
17226
|
+
var SHA_RE2 = /^[0-9a-f]{40}$/;
|
|
17227
|
+
function baselineShaAt(frame) {
|
|
17228
|
+
if (!frame.worktreeRoot) return null;
|
|
17229
|
+
try {
|
|
17230
|
+
const sha = (0, import_node_fs20.readFileSync)((0, import_node_path19.join)(frame.worktreeRoot, BASELINE_SHA_FILE), "utf-8").trim();
|
|
17231
|
+
if (!SHA_RE2.test(sha)) return null;
|
|
17232
|
+
return refResolves(frame, sha) ? sha : null;
|
|
17233
|
+
} catch {
|
|
17234
|
+
return null;
|
|
17235
|
+
}
|
|
17236
|
+
}
|
|
17237
|
+
function stagedRange() {
|
|
17238
|
+
return { kind: "staged", base: "HEAD", head: "INDEX", via: "index" };
|
|
17239
|
+
}
|
|
17240
|
+
function resolvePushRange(frame, command, on) {
|
|
17241
|
+
const nothing = (via) => ({ kind: "nothing", base: null, head: "HEAD", via });
|
|
17242
|
+
if (!frame.worktreeRoot) return nothing("refused");
|
|
17243
|
+
const found = findMomentSegment(command, on);
|
|
17244
|
+
const segment = found ? splitSegments(command)[found.segmentIndex] : "";
|
|
17245
|
+
const target = parsePushTarget(segment);
|
|
17246
|
+
if (target.isDelete) return nothing("deletion");
|
|
17247
|
+
const head = target.srcRef ?? "HEAD";
|
|
17248
|
+
if (!refResolves(frame, head)) return nothing(`src-unresolvable:${head}`);
|
|
17249
|
+
const srcName = target.srcRef;
|
|
17250
|
+
const branchForRemote = srcName ?? frame.branch;
|
|
17251
|
+
const candidates = [];
|
|
17252
|
+
if (target.remote && (target.dstRef ?? srcName)) {
|
|
17253
|
+
const dstName = (target.dstRef ?? srcName).replace(/^refs\/heads\//, "");
|
|
17254
|
+
candidates.push({ ref: `refs/remotes/${target.remote}/${dstName}`, via: `refspec:${target.remote}/${dstName}` });
|
|
17255
|
+
}
|
|
17256
|
+
if (target.remote && !srcName && !target.dstRef && frame.branch) {
|
|
17257
|
+
candidates.push({ ref: `refs/remotes/${target.remote}/${frame.branch}`, via: `remote:${target.remote}/${frame.branch}` });
|
|
17258
|
+
}
|
|
17259
|
+
candidates.push({ ref: srcName ? `${srcName}@{push}` : "@{push}", via: "@{push}" });
|
|
17260
|
+
candidates.push({ ref: srcName ? `${srcName}@{upstream}` : "@{upstream}", via: "@{upstream}" });
|
|
17261
|
+
if (branchForRemote) {
|
|
17262
|
+
candidates.push({
|
|
17263
|
+
ref: `refs/remotes/origin/${branchForRemote.replace(/^refs\/heads\//, "")}`,
|
|
17264
|
+
via: `origin/${branchForRemote.replace(/^refs\/heads\//, "")}`
|
|
17265
|
+
});
|
|
17266
|
+
}
|
|
17267
|
+
for (const c of candidates) {
|
|
17268
|
+
if (!refResolves(frame, c.ref)) continue;
|
|
17269
|
+
const mergeBase = frameGit(frame, ["merge-base", c.ref, head]);
|
|
17270
|
+
if (SHA_RE2.test(mergeBase)) return { kind: "push", base: mergeBase, head, via: c.via };
|
|
17271
|
+
}
|
|
17272
|
+
const baseline = baselineShaAt(frame);
|
|
17273
|
+
if (baseline) return { kind: "baseline", base: baseline, head, via: "review-baseline" };
|
|
17274
|
+
if (refResolves(frame, `${head}~1`)) return { kind: "last-commit", base: `${head}~1`, head, via: `${head}~1` };
|
|
17275
|
+
return nothing("no-parent");
|
|
17276
|
+
}
|
|
17277
|
+
function rangeFiles(frame, range) {
|
|
17278
|
+
let out;
|
|
17279
|
+
switch (range.kind) {
|
|
17280
|
+
case "staged":
|
|
17281
|
+
out = frameGit(frame, ["diff", "--cached", "--name-only"]);
|
|
17282
|
+
break;
|
|
17283
|
+
case "push":
|
|
17284
|
+
case "baseline":
|
|
17285
|
+
case "last-commit":
|
|
17286
|
+
out = frameGit(frame, ["diff", "--name-only", range.base, range.head === "INDEX" ? "HEAD" : range.head]);
|
|
17287
|
+
break;
|
|
17288
|
+
case "nothing":
|
|
17289
|
+
return [];
|
|
17290
|
+
}
|
|
17291
|
+
return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
|
|
17292
|
+
}
|
|
17293
|
+
function rangeMessages(frame, range) {
|
|
17294
|
+
if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
|
|
17295
|
+
return frameGit(frame, ["log", `${range.base}..${range.head === "INDEX" ? "HEAD" : range.head}`, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
17296
|
+
}
|
|
17297
|
+
function frameTelemetry(frame, range, divergence) {
|
|
17298
|
+
const t = {
|
|
17299
|
+
anchor: frame.anchor,
|
|
17300
|
+
linked_worktree: frame.isLinkedWorktree,
|
|
17301
|
+
range_via: range?.via ?? null,
|
|
17302
|
+
refusal: frame.refusal
|
|
17303
|
+
};
|
|
17304
|
+
if (divergence) {
|
|
17305
|
+
t.root_differs = !!frame.worktreeRoot && !!divergence.actualRoot && realpathOr(frame.worktreeRoot) !== realpathOr(divergence.actualRoot);
|
|
17306
|
+
const a = [...divergence.actualFiles].sort().join("\n");
|
|
17307
|
+
const b = [...divergence.frameFiles].sort().join("\n");
|
|
17308
|
+
t.files_differ = a !== b;
|
|
17309
|
+
t.frame_file_count = divergence.frameFiles.length;
|
|
17310
|
+
t.actual_file_count = divergence.actualFiles.length;
|
|
17311
|
+
}
|
|
17312
|
+
return t;
|
|
17313
|
+
}
|
|
17314
|
+
|
|
16885
17315
|
// src/lib/reachability.ts
|
|
16886
17316
|
function resolveReachability(input = {}) {
|
|
16887
17317
|
const env = input.env ?? {};
|
|
@@ -16910,7 +17340,7 @@ function truthy(v) {
|
|
|
16910
17340
|
}
|
|
16911
17341
|
|
|
16912
17342
|
// src/lib/transcript.ts
|
|
16913
|
-
var
|
|
17343
|
+
var import_node_fs21 = require("node:fs");
|
|
16914
17344
|
var MAX_READ_BYTES = 256 * 1024;
|
|
16915
17345
|
var SMALL_FILE_BYTES = 64 * 1024;
|
|
16916
17346
|
var MAX_FILES_LIST = 20;
|
|
@@ -16920,6 +17350,8 @@ var MAX_COMMAND_CHARS = 80;
|
|
|
16920
17350
|
var MAX_TOOL_BLOCKS = 200;
|
|
16921
17351
|
var MAX_SUMMARY_BYTES = 4096;
|
|
16922
17352
|
var HOME = process.env.HOME ?? "";
|
|
17353
|
+
var BASH_INPUT_RE = /^\s*<bash-input>([\s\S]*?)<\/bash-input>/;
|
|
17354
|
+
var BASH_ECHO_RE = /^\s*<bash-(?:stdout|stderr)>/;
|
|
16923
17355
|
async function extractActionSummary(transcriptPath) {
|
|
16924
17356
|
try {
|
|
16925
17357
|
const read = readTurnLines(transcriptPath);
|
|
@@ -16934,7 +17366,7 @@ async function extractActionSummary(transcriptPath) {
|
|
|
16934
17366
|
function readTurnLines(transcriptPath) {
|
|
16935
17367
|
let size;
|
|
16936
17368
|
try {
|
|
16937
|
-
size = (0,
|
|
17369
|
+
size = (0, import_node_fs21.statSync)(transcriptPath).size;
|
|
16938
17370
|
} catch {
|
|
16939
17371
|
return null;
|
|
16940
17372
|
}
|
|
@@ -16942,7 +17374,7 @@ function readTurnLines(transcriptPath) {
|
|
|
16942
17374
|
let raw;
|
|
16943
17375
|
let windowed = false;
|
|
16944
17376
|
if (size <= SMALL_FILE_BYTES) {
|
|
16945
|
-
raw = (0,
|
|
17377
|
+
raw = (0, import_node_fs21.readFileSync)(transcriptPath, "utf-8");
|
|
16946
17378
|
} else {
|
|
16947
17379
|
windowed = true;
|
|
16948
17380
|
const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
|
|
@@ -16983,7 +17415,7 @@ function isRealUserMessage(parsed) {
|
|
|
16983
17415
|
const message = parsed.message;
|
|
16984
17416
|
if (!message) return false;
|
|
16985
17417
|
const content = message.content;
|
|
16986
|
-
if (typeof content === "string") return
|
|
17418
|
+
if (typeof content === "string") return !BASH_ECHO_RE.test(content);
|
|
16987
17419
|
if (Array.isArray(content)) {
|
|
16988
17420
|
return content.some((b) => {
|
|
16989
17421
|
if (typeof b !== "object" || b === null) return false;
|
|
@@ -16998,6 +17430,8 @@ function buildSummary(lines) {
|
|
|
16998
17430
|
const filesEdited = /* @__PURE__ */ new Set();
|
|
16999
17431
|
const filesCreated = /* @__PURE__ */ new Set();
|
|
17000
17432
|
const commands = [];
|
|
17433
|
+
const userCommands = [];
|
|
17434
|
+
let userCommandsTruncated = false;
|
|
17001
17435
|
let searches = 0;
|
|
17002
17436
|
let subagents = 0;
|
|
17003
17437
|
let webFetches = 0;
|
|
@@ -17015,6 +17449,17 @@ function buildSummary(lines) {
|
|
|
17015
17449
|
if (entry.type === "user" && !firstTimestamp) {
|
|
17016
17450
|
firstTimestamp = entry.timestamp ?? null;
|
|
17017
17451
|
}
|
|
17452
|
+
if (entry.type === "user") {
|
|
17453
|
+
const typed = userTypedCommands(entry);
|
|
17454
|
+
if (typed.truncated) userCommandsTruncated = true;
|
|
17455
|
+
for (const cmd of typed.commands) {
|
|
17456
|
+
if (userCommands.length >= MAX_COMMANDS) {
|
|
17457
|
+
userCommandsTruncated = true;
|
|
17458
|
+
break;
|
|
17459
|
+
}
|
|
17460
|
+
userCommands.push(cmd);
|
|
17461
|
+
}
|
|
17462
|
+
}
|
|
17018
17463
|
if (entry.type !== "assistant") continue;
|
|
17019
17464
|
turnMessages++;
|
|
17020
17465
|
lastTimestamp = entry.timestamp ?? lastTimestamp;
|
|
@@ -17092,6 +17537,8 @@ function buildSummary(lines) {
|
|
|
17092
17537
|
],
|
|
17093
17538
|
searches,
|
|
17094
17539
|
commands,
|
|
17540
|
+
user_commands: userCommands,
|
|
17541
|
+
...userCommandsTruncated ? { user_commands_truncated: true } : {},
|
|
17095
17542
|
subagents,
|
|
17096
17543
|
web_fetches: webFetches,
|
|
17097
17544
|
total_tool_calls: totalToolCalls,
|
|
@@ -17100,6 +17547,8 @@ function buildSummary(lines) {
|
|
|
17100
17547
|
};
|
|
17101
17548
|
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
17102
17549
|
summary.commands = [];
|
|
17550
|
+
summary.user_commands = [];
|
|
17551
|
+
summary.user_commands_truncated = true;
|
|
17103
17552
|
if (JSON.stringify(summary).length > MAX_SUMMARY_BYTES) {
|
|
17104
17553
|
summary.files_read = summary.files_read.slice(0, 10);
|
|
17105
17554
|
summary.files_edited = summary.files_edited.slice(0, 10);
|
|
@@ -17117,6 +17566,22 @@ function addPath(set, rawPath) {
|
|
|
17117
17566
|
if (p.length > 200) p = p.slice(0, 200);
|
|
17118
17567
|
set.add(p);
|
|
17119
17568
|
}
|
|
17569
|
+
function userTypedCommands(entry) {
|
|
17570
|
+
const none = { commands: [], truncated: false };
|
|
17571
|
+
const message = entry.message;
|
|
17572
|
+
const content = message?.content;
|
|
17573
|
+
if (typeof content !== "string") return none;
|
|
17574
|
+
const m = BASH_INPUT_RE.exec(content);
|
|
17575
|
+
if (!m) return none;
|
|
17576
|
+
const commands = [];
|
|
17577
|
+
let truncated = false;
|
|
17578
|
+
for (const line of m[1].split("\n")) {
|
|
17579
|
+
if (line.length > MAX_COMMAND_CHARS) truncated = true;
|
|
17580
|
+
const cmd = sanitizeCommand(line);
|
|
17581
|
+
if (cmd) commands.push(cmd);
|
|
17582
|
+
}
|
|
17583
|
+
return { commands, truncated };
|
|
17584
|
+
}
|
|
17120
17585
|
function sanitizeCommand(rawCmd) {
|
|
17121
17586
|
if (typeof rawCmd !== "string" || !rawCmd) return null;
|
|
17122
17587
|
let cmd = rawCmd.split("\n")[0];
|
|
@@ -17150,28 +17615,28 @@ async function readStopHookStdin() {
|
|
|
17150
17615
|
try {
|
|
17151
17616
|
if (process.stdin.isTTY) return empty;
|
|
17152
17617
|
const chunks = [];
|
|
17153
|
-
const timeout = new Promise((
|
|
17154
|
-
const read = new Promise((
|
|
17618
|
+
const timeout = new Promise((resolve4) => setTimeout(() => resolve4(empty), 500));
|
|
17619
|
+
const read = new Promise((resolve4) => {
|
|
17155
17620
|
process.stdin.on("data", (chunk) => chunks.push(chunk));
|
|
17156
17621
|
process.stdin.on("end", () => {
|
|
17157
17622
|
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
17158
17623
|
if (!raw) {
|
|
17159
|
-
|
|
17624
|
+
resolve4(empty);
|
|
17160
17625
|
return;
|
|
17161
17626
|
}
|
|
17162
17627
|
try {
|
|
17163
17628
|
const data = JSON.parse(raw);
|
|
17164
|
-
|
|
17629
|
+
resolve4({
|
|
17165
17630
|
assistantMessage: typeof data.last_assistant_message === "string" ? data.last_assistant_message : null,
|
|
17166
17631
|
stopReason: typeof data.stop_reason === "string" ? data.stop_reason : null,
|
|
17167
17632
|
transcriptPath: typeof data.transcript_path === "string" ? data.transcript_path : null,
|
|
17168
17633
|
sessionId: typeof data.session_id === "string" ? data.session_id : null
|
|
17169
17634
|
});
|
|
17170
17635
|
} catch {
|
|
17171
|
-
|
|
17636
|
+
resolve4(empty);
|
|
17172
17637
|
}
|
|
17173
17638
|
});
|
|
17174
|
-
process.stdin.on("error", () =>
|
|
17639
|
+
process.stdin.on("error", () => resolve4(empty));
|
|
17175
17640
|
process.stdin.resume();
|
|
17176
17641
|
});
|
|
17177
17642
|
return await Promise.race([read, timeout]);
|
|
@@ -17185,6 +17650,7 @@ async function bootstrap(run) {
|
|
|
17185
17650
|
process.chdir(repoRoot());
|
|
17186
17651
|
} catch {
|
|
17187
17652
|
}
|
|
17653
|
+
run.treeFrame = resolveFrame({ command: "", on: [], hookCwd: null }).frame;
|
|
17188
17654
|
const turnId = `t-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
17189
17655
|
let reachability = resolveReachability({
|
|
17190
17656
|
autonomousFlag: process.env.VERITY_AUTONOMOUS === "1" || opts.mode === "autonomous",
|
|
@@ -17350,7 +17816,7 @@ function truncateToCap(text) {
|
|
|
17350
17816
|
function buildHookOutput(gateDecision, systemMessage, agentContext) {
|
|
17351
17817
|
return {
|
|
17352
17818
|
gate_decision: gateDecision,
|
|
17353
|
-
systemMessage,
|
|
17819
|
+
...systemMessage === null ? {} : { systemMessage },
|
|
17354
17820
|
...agentContext ? {
|
|
17355
17821
|
hookSpecificOutput: {
|
|
17356
17822
|
hookEventName: "Stop",
|
|
@@ -17371,7 +17837,7 @@ function channelSilence(input) {
|
|
|
17371
17837
|
// src/lib/cli-version.ts
|
|
17372
17838
|
function cliVersion() {
|
|
17373
17839
|
try {
|
|
17374
|
-
return true ? "0.30.
|
|
17840
|
+
return true ? "0.30.1-experimental.e2137b9" : "dev";
|
|
17375
17841
|
} catch {
|
|
17376
17842
|
return "dev";
|
|
17377
17843
|
}
|
|
@@ -17411,8 +17877,8 @@ async function sendSkipBeacon(ctx, reason) {
|
|
|
17411
17877
|
}
|
|
17412
17878
|
|
|
17413
17879
|
// src/lib/static-analysis.ts
|
|
17414
|
-
var
|
|
17415
|
-
var
|
|
17880
|
+
var import_node_child_process8 = require("node:child_process");
|
|
17881
|
+
var import_node_fs22 = require("node:fs");
|
|
17416
17882
|
var SEVERITY_ORDER = {
|
|
17417
17883
|
Error: 0,
|
|
17418
17884
|
Critical: 0,
|
|
@@ -17424,7 +17890,7 @@ var SEVERITY_ORDER = {
|
|
|
17424
17890
|
};
|
|
17425
17891
|
function isCodacyAvailable() {
|
|
17426
17892
|
try {
|
|
17427
|
-
(0,
|
|
17893
|
+
(0, import_node_child_process8.execSync)("which codacy-analysis", { stdio: "pipe" });
|
|
17428
17894
|
return true;
|
|
17429
17895
|
} catch {
|
|
17430
17896
|
return false;
|
|
@@ -17460,13 +17926,13 @@ function runCodacyAnalysis(files) {
|
|
|
17460
17926
|
if (files.length === 0) return empty;
|
|
17461
17927
|
const existingFiles = files.filter((f) => {
|
|
17462
17928
|
try {
|
|
17463
|
-
return (0,
|
|
17929
|
+
return (0, import_node_fs22.existsSync)(f);
|
|
17464
17930
|
} catch {
|
|
17465
17931
|
return false;
|
|
17466
17932
|
}
|
|
17467
17933
|
});
|
|
17468
17934
|
if (existingFiles.length === 0) return empty;
|
|
17469
|
-
const proc = (0,
|
|
17935
|
+
const proc = (0, import_node_child_process8.spawnSync)("codacy-analysis", buildAnalyzerArgv(existingFiles), {
|
|
17470
17936
|
encoding: "utf-8",
|
|
17471
17937
|
maxBuffer: 10 * 1024 * 1024
|
|
17472
17938
|
});
|
|
@@ -17657,6 +18123,7 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
17657
18123
|
"verity-command",
|
|
17658
18124
|
"bare-acknowledgment",
|
|
17659
18125
|
"reflection-prompt",
|
|
18126
|
+
"command-only-turn",
|
|
17660
18127
|
"skip-mode",
|
|
17661
18128
|
"zero-increment",
|
|
17662
18129
|
"debounce",
|
|
@@ -17675,10 +18142,12 @@ async function passAndExit(run, reason, skip, kindOverride) {
|
|
|
17675
18142
|
}
|
|
17676
18143
|
const AGENT_SILENT_SKIPS = /* @__PURE__ */ new Set([]);
|
|
17677
18144
|
const agentNote = AGENT_SILENT_SKIPS.has(skip) ? null : note;
|
|
18145
|
+
const HUMAN_SILENT_SKIPS = /* @__PURE__ */ new Set(["command-only-turn"]);
|
|
18146
|
+
const humanNote = HUMAN_SILENT_SKIPS.has(skip) ? null : `Verity: ${reason}`;
|
|
17678
18147
|
printJsonCompact(
|
|
17679
18148
|
buildHookOutput(
|
|
17680
18149
|
verdict,
|
|
17681
|
-
|
|
18150
|
+
humanNote,
|
|
17682
18151
|
// `additionalContext` is the agent's ONLY input. Writing only
|
|
17683
18152
|
// `systemMessage` — the human's field — tells the agent nothing at all,
|
|
17684
18153
|
// which is what sixteen of the nineteen terminating paths used to do.
|
|
@@ -17706,8 +18175,8 @@ async function scope(run) {
|
|
|
17706
18175
|
}
|
|
17707
18176
|
|
|
17708
18177
|
// src/lib/specs.ts
|
|
17709
|
-
var
|
|
17710
|
-
var
|
|
18178
|
+
var import_node_fs23 = require("node:fs");
|
|
18179
|
+
var import_node_path20 = require("node:path");
|
|
17711
18180
|
var SPEC_CANDIDATES = [
|
|
17712
18181
|
"CLAUDE.md",
|
|
17713
18182
|
"AGENTS.md",
|
|
@@ -17738,16 +18207,16 @@ function discoverSpecs(consulted = []) {
|
|
|
17738
18207
|
const totalCap = relevant ? MAX_TOTAL_SPEC_BYTES : UNCONSULTED_TOTAL_BYTES;
|
|
17739
18208
|
if (totalBytes >= totalCap) return false;
|
|
17740
18209
|
if (seen.has(specPath)) return true;
|
|
17741
|
-
if (!(0,
|
|
18210
|
+
if (!(0, import_node_fs23.existsSync)(specPath)) return true;
|
|
17742
18211
|
seen.add(specPath);
|
|
17743
18212
|
const remaining = totalCap - totalBytes;
|
|
17744
18213
|
const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
|
|
17745
18214
|
const readBytes = Math.min(fileCap, remaining);
|
|
17746
18215
|
try {
|
|
17747
18216
|
const buf = Buffer.alloc(readBytes);
|
|
17748
|
-
const fd = (0,
|
|
17749
|
-
const bytesRead = (0,
|
|
17750
|
-
(0,
|
|
18217
|
+
const fd = (0, import_node_fs23.openSync)(specPath, "r");
|
|
18218
|
+
const bytesRead = (0, import_node_fs23.readSync)(fd, buf, 0, readBytes, 0);
|
|
18219
|
+
(0, import_node_fs23.closeSync)(fd);
|
|
17751
18220
|
const content = buf.slice(0, bytesRead).toString("utf-8");
|
|
17752
18221
|
if (!content) return true;
|
|
17753
18222
|
result.push({ path: specPath, content });
|
|
@@ -17763,7 +18232,7 @@ function discoverSpecs(consulted = []) {
|
|
|
17763
18232
|
if (!addSpec(candidate)) break;
|
|
17764
18233
|
}
|
|
17765
18234
|
for (const dir of ["spec", "docs"]) {
|
|
17766
|
-
if (!(0,
|
|
18235
|
+
if (!(0, import_node_fs23.existsSync)(dir)) continue;
|
|
17767
18236
|
try {
|
|
17768
18237
|
const mdFiles = findMdFiles(dir, 2).sort();
|
|
17769
18238
|
for (const mdFile of mdFiles) {
|
|
@@ -17778,9 +18247,9 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
17778
18247
|
if (depth >= maxDepth) return [];
|
|
17779
18248
|
const result = [];
|
|
17780
18249
|
try {
|
|
17781
|
-
const entries = (0,
|
|
18250
|
+
const entries = (0, import_node_fs23.readdirSync)(dir, { withFileTypes: true });
|
|
17782
18251
|
for (const entry of entries) {
|
|
17783
|
-
const fullPath = (0,
|
|
18252
|
+
const fullPath = (0, import_node_path20.join)(dir, entry.name);
|
|
17784
18253
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
17785
18254
|
result.push(fullPath);
|
|
17786
18255
|
} else if (entry.isDirectory() && depth < maxDepth - 1) {
|
|
@@ -17792,19 +18261,19 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
17792
18261
|
return result;
|
|
17793
18262
|
}
|
|
17794
18263
|
function discoverPlans() {
|
|
17795
|
-
const homePlansDir = (0,
|
|
18264
|
+
const homePlansDir = (0, import_node_path20.join)(process.env.HOME ?? "", ".claude", "plans");
|
|
17796
18265
|
const localPlansDir = ".claude/plans";
|
|
17797
18266
|
const candidates = [];
|
|
17798
18267
|
const seen = /* @__PURE__ */ new Set();
|
|
17799
18268
|
for (const plansDir of [localPlansDir, homePlansDir]) {
|
|
17800
|
-
if (!(0,
|
|
18269
|
+
if (!(0, import_node_fs23.existsSync)(plansDir)) continue;
|
|
17801
18270
|
try {
|
|
17802
|
-
for (const f of (0,
|
|
18271
|
+
for (const f of (0, import_node_fs23.readdirSync)(plansDir)) {
|
|
17803
18272
|
if (!f.endsWith(".md") || seen.has(f)) continue;
|
|
17804
18273
|
seen.add(f);
|
|
17805
|
-
const fullPath = (0,
|
|
18274
|
+
const fullPath = (0, import_node_path20.join)(plansDir, f);
|
|
17806
18275
|
try {
|
|
17807
|
-
const stat3 = (0,
|
|
18276
|
+
const stat3 = (0, import_node_fs23.statSync)(fullPath);
|
|
17808
18277
|
candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
|
|
17809
18278
|
} catch {
|
|
17810
18279
|
}
|
|
@@ -17817,7 +18286,7 @@ function discoverPlans() {
|
|
|
17817
18286
|
for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
|
|
17818
18287
|
if (entry.size > MAX_PLAN_FILE_BYTES) continue;
|
|
17819
18288
|
try {
|
|
17820
|
-
const content = (0,
|
|
18289
|
+
const content = (0, import_node_fs23.readFileSync)(entry.path, "utf-8");
|
|
17821
18290
|
result.push({ name: entry.name, content });
|
|
17822
18291
|
} catch {
|
|
17823
18292
|
}
|
|
@@ -17828,6 +18297,15 @@ function discoverPlans() {
|
|
|
17828
18297
|
// src/commands/analyze/phases/03-intent-inputs.ts
|
|
17829
18298
|
async function intentInputs(run) {
|
|
17830
18299
|
const { actionSummary, allForReview, assistantResponse, baseline, baselineSessionId } = run;
|
|
18300
|
+
if (isCommandOnlyTurn({
|
|
18301
|
+
userCommands: actionSummary?.user_commands,
|
|
18302
|
+
userCommandsTruncated: actionSummary?.user_commands_truncated,
|
|
18303
|
+
agentAuthoredFiles: (actionSummary?.files_edited.length ?? 0) + (actionSummary?.files_created.length ?? 0),
|
|
18304
|
+
agentToolCalls: actionSummary?.total_tool_calls ?? 0,
|
|
18305
|
+
authorshipIsObservable: !!actionSummary && actionSummary.transcript_windowed !== "orphaned"
|
|
18306
|
+
})) {
|
|
18307
|
+
await passAndExit(run, "User command only \u2014 skipping analysis", "command-only-turn");
|
|
18308
|
+
}
|
|
17831
18309
|
const conversation = await readAndClearConversationBuffer(baselineSessionId);
|
|
17832
18310
|
const specs = discoverSpecs(actionSummary?.files_read ?? []);
|
|
17833
18311
|
const plans = discoverPlans();
|
|
@@ -17868,156 +18346,6 @@ async function connect(run) {
|
|
|
17868
18346
|
Object.assign(run, { urlResult, serviceUrl: urlResult.data, token: tokenResult.data.token });
|
|
17869
18347
|
}
|
|
17870
18348
|
|
|
17871
|
-
// src/lib/analysis-mode.ts
|
|
17872
|
-
var DEBUG_PHRASES = [
|
|
17873
|
-
"not working",
|
|
17874
|
-
"doesn't work",
|
|
17875
|
-
"doesn't work",
|
|
17876
|
-
"does not work",
|
|
17877
|
-
"isn't working",
|
|
17878
|
-
"is not working",
|
|
17879
|
-
"can't figure out",
|
|
17880
|
-
"stack trace"
|
|
17881
|
-
];
|
|
17882
|
-
var DEBUG_WORDS = [
|
|
17883
|
-
"fix",
|
|
17884
|
-
"bug",
|
|
17885
|
-
"broken",
|
|
17886
|
-
"crash",
|
|
17887
|
-
"crashing",
|
|
17888
|
-
"failing",
|
|
17889
|
-
"debug",
|
|
17890
|
-
"debugging",
|
|
17891
|
-
"investigate",
|
|
17892
|
-
"troubleshoot",
|
|
17893
|
-
"regression",
|
|
17894
|
-
"wrong"
|
|
17895
|
-
];
|
|
17896
|
-
var DEBUG_PATTERN = new RegExp(
|
|
17897
|
-
[
|
|
17898
|
-
...DEBUG_PHRASES.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")),
|
|
17899
|
-
...DEBUG_WORDS.map((w) => `\\b${w}\\b`)
|
|
17900
|
-
].join("|"),
|
|
17901
|
-
"i"
|
|
17902
|
-
);
|
|
17903
|
-
var FALSE_POSITIVE_PATTERNS = [
|
|
17904
|
-
/\b(?:add|create|implement|write|build|design|set\s*up)\b.{0,20}\berror\b/i,
|
|
17905
|
-
/\berror\s+handling\b/i,
|
|
17906
|
-
/\berror\s+boundar(?:y|ies)\b/i,
|
|
17907
|
-
/\berror\s+(?:type|class|page|component|message|code|enum)\b/i,
|
|
17908
|
-
/\b(?:add|create|implement|write|build)\b.{0,20}\b(?:fix|debug|issue)\b/i
|
|
17909
|
-
];
|
|
17910
|
-
function hasDebugIntent(prompt) {
|
|
17911
|
-
if (!DEBUG_PATTERN.test(prompt)) return false;
|
|
17912
|
-
for (const fp of FALSE_POSITIVE_PATTERNS) {
|
|
17913
|
-
if (fp.test(prompt)) return false;
|
|
17914
|
-
}
|
|
17915
|
-
return true;
|
|
17916
|
-
}
|
|
17917
|
-
var GIT_ONLY_PATTERN = /\b(commit|push|deploy|merge|rebase|tag|release|publish|ship)\b/i;
|
|
17918
|
-
var CODE_AUTHORING_PATTERN = /\b(add|create|implement|build|write|fix|update|change|refactor|modify|remove|delete|move|rename)\b.*\b(function|component|feature|endpoint|test|file|module|class|type|interface|hook|page|route|style|migration|code|bug|error|issue)\b/i;
|
|
17919
|
-
function isGitOnlyPrompt(prompt) {
|
|
17920
|
-
if (!GIT_ONLY_PATTERN.test(prompt)) return false;
|
|
17921
|
-
if (CODE_AUTHORING_PATTERN.test(prompt)) return false;
|
|
17922
|
-
return true;
|
|
17923
|
-
}
|
|
17924
|
-
function reconcileAnalysisMode(predictedMode, signals) {
|
|
17925
|
-
const mode2 = resolveAnalysisMode(predictedMode, signals);
|
|
17926
|
-
if (mode2 !== "skip") return mode2;
|
|
17927
|
-
const windowIsOrphaned = signals.actionSummary?.transcript_windowed === "orphaned";
|
|
17928
|
-
if (windowIsOrphaned && !signals.sessionAuthoredCode) return "standard";
|
|
17929
|
-
return mode2;
|
|
17930
|
-
}
|
|
17931
|
-
function resolveAnalysisMode(predictedMode, signals) {
|
|
17932
|
-
if (!predictedMode || !isValidMode(predictedMode)) {
|
|
17933
|
-
return detectAnalysisMode(
|
|
17934
|
-
signals.noFilesChanged,
|
|
17935
|
-
signals.assistantResponse,
|
|
17936
|
-
signals.conversationPrompts,
|
|
17937
|
-
signals.actionSummary,
|
|
17938
|
-
signals.sessionAuthoredCode
|
|
17939
|
-
);
|
|
17940
|
-
}
|
|
17941
|
-
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
|
|
17942
|
-
const agentInvestigated = didAgentInvestigate(signals.actionSummary);
|
|
17943
|
-
switch (predictedMode) {
|
|
17944
|
-
case "skip":
|
|
17945
|
-
if (agentAuthoredCode) return "standard";
|
|
17946
|
-
return "skip";
|
|
17947
|
-
case "plan":
|
|
17948
|
-
if (agentAuthoredCode) return "standard";
|
|
17949
|
-
return "plan";
|
|
17950
|
-
case "debug":
|
|
17951
|
-
return "debug";
|
|
17952
|
-
case "standard":
|
|
17953
|
-
if (!!signals.actionSummary && !agentAuthoredCode && !!signals.assistantResponse) {
|
|
17954
|
-
return agentInvestigated ? "plan" : "skip";
|
|
17955
|
-
}
|
|
17956
|
-
return "standard";
|
|
17957
|
-
}
|
|
17958
|
-
}
|
|
17959
|
-
function didAgentInvestigate(summary) {
|
|
17960
|
-
if (!summary) return false;
|
|
17961
|
-
return summary.files_read.length > 0 || summary.searches > 0 || summary.commands.length > 0 || summary.subagents > 0 || summary.web_fetches > 0;
|
|
17962
|
-
}
|
|
17963
|
-
function isValidMode(mode2) {
|
|
17964
|
-
return mode2 === "standard" || mode2 === "plan" || mode2 === "debug" || mode2 === "skip";
|
|
17965
|
-
}
|
|
17966
|
-
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
|
|
17967
|
-
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
|
|
17968
|
-
if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
|
|
17969
|
-
if (!agentAuthoredCode) return "skip";
|
|
17970
|
-
}
|
|
17971
|
-
if (noFilesChanged && !!assistantResponse && !agentAuthoredCode) {
|
|
17972
|
-
return "plan";
|
|
17973
|
-
}
|
|
17974
|
-
if (!!actionSummary && !agentAuthoredCode && !!assistantResponse) {
|
|
17975
|
-
return didAgentInvestigate(actionSummary) ? "plan" : "skip";
|
|
17976
|
-
}
|
|
17977
|
-
for (const prompt of conversationPrompts) {
|
|
17978
|
-
if (hasDebugIntent(prompt)) {
|
|
17979
|
-
return "debug";
|
|
17980
|
-
}
|
|
17981
|
-
}
|
|
17982
|
-
return "standard";
|
|
17983
|
-
}
|
|
17984
|
-
var FILE_MUTATE_RE = /(?:^|[\s|&;(`])(?:sed\s+-i|perl\s+-i|awk\b|tee\b|dd\b|cp\b|mv\b|ln\b|install\b|touch\b|patch\b|git\s+(?:apply|am)\b|cargo\s+build|go\s+generate|make\b|--write\b|--fix\b|--in-place\b)|>>?(?![&>])/i;
|
|
17985
|
-
var GIT_PLUMBING_RE = /^\s*git\s+(?:merge|rebase|stash|cherry-pick|revert|pull|fetch|checkout|switch|reset|restore|clean)\b/i;
|
|
17986
|
-
var READ_ONLY_RE = /^\s*(?:git\s+(?:status|diff|log|show|branch|remote|config|rev-parse|ls-files|blame|describe)|ls|cat|head|tail|less|grep|rg|find|pwd|echo|printf|wc|which|type|tree|stat|file|env|printenv|date|whoami)\b/i;
|
|
17987
|
-
var CHAIN_RE = /&&|\||;|\$\(|\x60/;
|
|
17988
|
-
function hasNonEditAuthorship(actionSummary, sessionAuthoredCode) {
|
|
17989
|
-
if (!actionSummary) return sessionAuthoredCode;
|
|
17990
|
-
if ((actionSummary.subagents ?? 0) > 0) return true;
|
|
17991
|
-
if (Object.keys(actionSummary.tool_counts ?? {}).some((t) => t.startsWith("mcp__"))) return true;
|
|
17992
|
-
const commands = actionSummary.commands ?? [];
|
|
17993
|
-
if (commands.some((c) => FILE_MUTATE_RE.test(c))) return true;
|
|
17994
|
-
if (sessionAuthoredCode) {
|
|
17995
|
-
const allSafe = commands.length > 0 && commands.every(
|
|
17996
|
-
(c) => !CHAIN_RE.test(c) && (GIT_PLUMBING_RE.test(c) || READ_ONLY_RE.test(c))
|
|
17997
|
-
);
|
|
17998
|
-
if (!allSafe) return true;
|
|
17999
|
-
}
|
|
18000
|
-
return false;
|
|
18001
|
-
}
|
|
18002
|
-
function scopeToAuthored(files, actionSummary) {
|
|
18003
|
-
if (!actionSummary) return { files, signal: "no-transcript" };
|
|
18004
|
-
const touched = [...actionSummary.files_edited ?? [], ...actionSummary.files_created ?? []];
|
|
18005
|
-
if (touched.length === 0) return { files: [], signal: "none-authored" };
|
|
18006
|
-
return { files: narrowToAgentAuthored(files, actionSummary), signal: "authored" };
|
|
18007
|
-
}
|
|
18008
|
-
function narrowToAgentAuthored(files, actionSummary) {
|
|
18009
|
-
if (!actionSummary) return files;
|
|
18010
|
-
const touched = [
|
|
18011
|
-
...actionSummary.files_edited,
|
|
18012
|
-
...actionSummary.files_created
|
|
18013
|
-
];
|
|
18014
|
-
if (touched.length === 0) return files;
|
|
18015
|
-
return files.filter((f) => {
|
|
18016
|
-
const suffix = "/" + f;
|
|
18017
|
-
return touched.some((t) => t === f || t.endsWith(suffix));
|
|
18018
|
-
});
|
|
18019
|
-
}
|
|
18020
|
-
|
|
18021
18349
|
// src/commands/analyze/phases/05-mode.ts
|
|
18022
18350
|
async function mode(run) {
|
|
18023
18351
|
const { opts, globals } = run;
|
|
@@ -18092,7 +18420,7 @@ async function mode(run) {
|
|
|
18092
18420
|
}
|
|
18093
18421
|
|
|
18094
18422
|
// src/lib/debounce.ts
|
|
18095
|
-
var
|
|
18423
|
+
var import_node_fs24 = require("node:fs");
|
|
18096
18424
|
var import_node_crypto10 = require("node:crypto");
|
|
18097
18425
|
function scopedFile(base, sessionId) {
|
|
18098
18426
|
if (!sessionId) return base;
|
|
@@ -18100,9 +18428,9 @@ function scopedFile(base, sessionId) {
|
|
|
18100
18428
|
}
|
|
18101
18429
|
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
18102
18430
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
18103
|
-
if (!(0,
|
|
18431
|
+
if (!(0, import_node_fs24.existsSync)(file)) return null;
|
|
18104
18432
|
try {
|
|
18105
|
-
const lastTs = parseInt((0,
|
|
18433
|
+
const lastTs = parseInt((0, import_node_fs24.readFileSync)(file, "utf-8").trim(), 10);
|
|
18106
18434
|
const nowTs = Math.floor(Date.now() / 1e3);
|
|
18107
18435
|
const elapsed = nowTs - lastTs;
|
|
18108
18436
|
if (elapsed < debounceSeconds) {
|
|
@@ -18115,10 +18443,10 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
|
18115
18443
|
function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
18116
18444
|
if (bypassForRecentCommits) return null;
|
|
18117
18445
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
18118
|
-
if (!(0,
|
|
18446
|
+
if (!(0, import_node_fs24.existsSync)(file)) return null;
|
|
18119
18447
|
let debounceTime;
|
|
18120
18448
|
try {
|
|
18121
|
-
debounceTime = (0,
|
|
18449
|
+
debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
|
|
18122
18450
|
} catch {
|
|
18123
18451
|
return null;
|
|
18124
18452
|
}
|
|
@@ -18126,7 +18454,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
|
18126
18454
|
const resolved = resolveFile(f);
|
|
18127
18455
|
if (!resolved) continue;
|
|
18128
18456
|
try {
|
|
18129
|
-
const stat3 = (0,
|
|
18457
|
+
const stat3 = (0, import_node_fs24.statSync)(resolved);
|
|
18130
18458
|
if (stat3.mtimeMs > debounceTime) {
|
|
18131
18459
|
return null;
|
|
18132
18460
|
}
|
|
@@ -18142,8 +18470,8 @@ function computeContentHash(files) {
|
|
|
18142
18470
|
for (const f of sorted) {
|
|
18143
18471
|
const resolved = resolveFile(f) ?? f;
|
|
18144
18472
|
try {
|
|
18145
|
-
if ((0,
|
|
18146
|
-
hash.update((0,
|
|
18473
|
+
if ((0, import_node_fs24.existsSync)(resolved)) {
|
|
18474
|
+
hash.update((0, import_node_fs24.readFileSync)(resolved));
|
|
18147
18475
|
}
|
|
18148
18476
|
} catch {
|
|
18149
18477
|
}
|
|
@@ -18153,9 +18481,9 @@ function computeContentHash(files) {
|
|
|
18153
18481
|
function checkContentHash(files, sessionId) {
|
|
18154
18482
|
const hash = computeContentHash(files);
|
|
18155
18483
|
const file = scopedFile(HASH_FILE, sessionId);
|
|
18156
|
-
if ((0,
|
|
18484
|
+
if ((0, import_node_fs24.existsSync)(file)) {
|
|
18157
18485
|
try {
|
|
18158
|
-
const storedHash = (0,
|
|
18486
|
+
const storedHash = (0, import_node_fs24.readFileSync)(file, "utf-8").trim();
|
|
18159
18487
|
if (hash === storedHash) {
|
|
18160
18488
|
return { skip: "No source changes since last analysis", hash };
|
|
18161
18489
|
}
|
|
@@ -18165,24 +18493,24 @@ function checkContentHash(files, sessionId) {
|
|
|
18165
18493
|
return { skip: null, hash };
|
|
18166
18494
|
}
|
|
18167
18495
|
function recordAnalysisStart(sessionId) {
|
|
18168
|
-
(0,
|
|
18169
|
-
(0,
|
|
18496
|
+
(0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18497
|
+
(0, import_node_fs24.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
|
|
18170
18498
|
}
|
|
18171
18499
|
function recordPassHash(hash, sessionId) {
|
|
18172
|
-
(0,
|
|
18500
|
+
(0, import_node_fs24.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
|
|
18173
18501
|
}
|
|
18174
18502
|
function narrowToRecent(files, sessionId) {
|
|
18175
18503
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
18176
|
-
if (!(0,
|
|
18504
|
+
if (!(0, import_node_fs24.existsSync)(file)) return files;
|
|
18177
18505
|
let debounceTime;
|
|
18178
18506
|
try {
|
|
18179
|
-
debounceTime = (0,
|
|
18507
|
+
debounceTime = (0, import_node_fs24.statSync)(file).mtimeMs;
|
|
18180
18508
|
} catch {
|
|
18181
18509
|
return files;
|
|
18182
18510
|
}
|
|
18183
18511
|
const recent = files.filter((f) => {
|
|
18184
18512
|
try {
|
|
18185
|
-
return (0,
|
|
18513
|
+
return (0, import_node_fs24.existsSync)(f) && (0, import_node_fs24.statSync)(f).mtimeMs > debounceTime;
|
|
18186
18514
|
} catch {
|
|
18187
18515
|
return false;
|
|
18188
18516
|
}
|
|
@@ -18195,9 +18523,9 @@ function readIteration(currentCommit, _contentHash) {
|
|
|
18195
18523
|
var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
|
|
18196
18524
|
function readBlockState(currentCommit, opts) {
|
|
18197
18525
|
if (opts?.newUserPrompt) return NO_BLOCKS;
|
|
18198
|
-
if (!(0,
|
|
18526
|
+
if (!(0, import_node_fs24.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
|
|
18199
18527
|
try {
|
|
18200
|
-
const stored = (0,
|
|
18528
|
+
const stored = (0, import_node_fs24.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
18201
18529
|
const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
|
|
18202
18530
|
if (!parsed) return NO_BLOCKS;
|
|
18203
18531
|
if (parsed.commit !== currentCommit) return NO_BLOCKS;
|
|
@@ -18243,8 +18571,8 @@ function isSameProblem(previous, current) {
|
|
|
18243
18571
|
return current.split(",").some((k) => prev.has(k));
|
|
18244
18572
|
}
|
|
18245
18573
|
function writeBlockState(commit, state) {
|
|
18246
|
-
(0,
|
|
18247
|
-
(0,
|
|
18574
|
+
(0, import_node_fs24.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18575
|
+
(0, import_node_fs24.writeFileSync)(
|
|
18248
18576
|
ITERATION_FILE,
|
|
18249
18577
|
JSON.stringify({
|
|
18250
18578
|
v: 2,
|
|
@@ -18261,8 +18589,8 @@ function resetBlockState(commit) {
|
|
|
18261
18589
|
}
|
|
18262
18590
|
|
|
18263
18591
|
// src/lib/fold.ts
|
|
18264
|
-
var
|
|
18265
|
-
var
|
|
18592
|
+
var import_node_fs25 = require("node:fs");
|
|
18593
|
+
var import_node_path21 = require("node:path");
|
|
18266
18594
|
var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
18267
18595
|
"user",
|
|
18268
18596
|
"assistant",
|
|
@@ -18300,7 +18628,7 @@ var COMMAND_CLASSES = [
|
|
|
18300
18628
|
[/\btsc\b|\bmypy\b|\btypecheck\b/, "typecheck"],
|
|
18301
18629
|
[/^git\s/, "git"]
|
|
18302
18630
|
];
|
|
18303
|
-
function
|
|
18631
|
+
function classifyCommand2(cmd) {
|
|
18304
18632
|
for (const [re, cls] of COMMAND_CLASSES) if (re.test(cmd)) return cls;
|
|
18305
18633
|
return "other";
|
|
18306
18634
|
}
|
|
@@ -18399,7 +18727,7 @@ function candidateRoots(repoRoot2) {
|
|
|
18399
18727
|
const norm = repoRoot2.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
18400
18728
|
const out = [norm];
|
|
18401
18729
|
try {
|
|
18402
|
-
const real =
|
|
18730
|
+
const real = import_node_fs25.realpathSync.native(norm).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
18403
18731
|
if (real !== norm) out.push(real);
|
|
18404
18732
|
} catch {
|
|
18405
18733
|
}
|
|
@@ -18487,31 +18815,31 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18487
18815
|
}
|
|
18488
18816
|
};
|
|
18489
18817
|
try {
|
|
18490
|
-
if (!(0,
|
|
18491
|
-
ingest((0,
|
|
18818
|
+
if (!(0, import_node_fs25.existsSync)(transcriptPath)) return result;
|
|
18819
|
+
ingest((0, import_node_fs25.readFileSync)(transcriptPath, "utf8"), "agent");
|
|
18492
18820
|
result.coverage.complete = true;
|
|
18493
18821
|
} catch {
|
|
18494
18822
|
return result;
|
|
18495
18823
|
}
|
|
18496
18824
|
try {
|
|
18497
|
-
const sidecarDir = (0,
|
|
18498
|
-
(0,
|
|
18499
|
-
(0,
|
|
18825
|
+
const sidecarDir = (0, import_node_path21.join)(
|
|
18826
|
+
(0, import_node_path21.dirname)(transcriptPath),
|
|
18827
|
+
(0, import_node_path21.basename)(transcriptPath).replace(/\.jsonl$/, ""),
|
|
18500
18828
|
"subagents"
|
|
18501
18829
|
);
|
|
18502
|
-
if ((0,
|
|
18830
|
+
if ((0, import_node_fs25.existsSync)(sidecarDir)) {
|
|
18503
18831
|
const maxFiles = opts.maxSidecars ?? 200;
|
|
18504
18832
|
const maxBytes = opts.maxSidecarBytes ?? 16 * 1024 * 1024;
|
|
18505
18833
|
const found = [];
|
|
18506
18834
|
const walk = (d, depth) => {
|
|
18507
18835
|
if (depth > 4) return;
|
|
18508
|
-
for (const e of (0,
|
|
18509
|
-
const p = (0,
|
|
18836
|
+
for (const e of (0, import_node_fs25.readdirSync)(d, { withFileTypes: true })) {
|
|
18837
|
+
const p = (0, import_node_path21.join)(d, e.name);
|
|
18510
18838
|
if (e.isDirectory()) {
|
|
18511
18839
|
walk(p, depth + 1);
|
|
18512
18840
|
} else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
|
|
18513
18841
|
try {
|
|
18514
|
-
const st = (0,
|
|
18842
|
+
const st = (0, import_node_fs25.statSync)(p);
|
|
18515
18843
|
found.push({ path: p, size: st.size, mtimeMs: st.mtimeMs });
|
|
18516
18844
|
} catch {
|
|
18517
18845
|
result.coverage.malformed++;
|
|
@@ -18528,7 +18856,7 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18528
18856
|
continue;
|
|
18529
18857
|
}
|
|
18530
18858
|
try {
|
|
18531
|
-
ingest((0,
|
|
18859
|
+
ingest((0, import_node_fs25.readFileSync)(f.path, "utf8"), "subagent");
|
|
18532
18860
|
bytes += f.size;
|
|
18533
18861
|
result.coverage.subagentFiles++;
|
|
18534
18862
|
} catch {
|
|
@@ -18563,7 +18891,7 @@ function fold(transcriptPath, opts = {}) {
|
|
|
18563
18891
|
}
|
|
18564
18892
|
function classifyUnobserved(path) {
|
|
18565
18893
|
try {
|
|
18566
|
-
const st = (0,
|
|
18894
|
+
const st = (0, import_node_fs25.statSync)(path);
|
|
18567
18895
|
if (!st.isFile()) return "unreadable";
|
|
18568
18896
|
} catch {
|
|
18569
18897
|
return "unreadable";
|
|
@@ -18629,7 +18957,7 @@ function collectFromRecord(record, owner, byPath, commandStats, pendingByToolUse
|
|
|
18629
18957
|
if (name === "Bash") {
|
|
18630
18958
|
const cmd = typeof input.command === "string" ? input.command : "";
|
|
18631
18959
|
if (cmd) {
|
|
18632
|
-
const cls =
|
|
18960
|
+
const cls = classifyCommand2(cmd);
|
|
18633
18961
|
const prev = commandStats.get(cls) ?? { last_status: null, runs: 0, head: "" };
|
|
18634
18962
|
commandStats.set(cls, {
|
|
18635
18963
|
// UNKNOWN until this command's OWN result arrives. Inheriting the
|
|
@@ -18867,20 +19195,20 @@ async function evidence(run) {
|
|
|
18867
19195
|
}
|
|
18868
19196
|
|
|
18869
19197
|
// src/lib/cache-cleanup.ts
|
|
18870
|
-
var
|
|
18871
|
-
var
|
|
19198
|
+
var import_node_fs26 = require("node:fs");
|
|
19199
|
+
var import_node_path22 = require("node:path");
|
|
18872
19200
|
var CACHE_TTL_DAYS = 7;
|
|
18873
19201
|
function pruneStaleCache() {
|
|
18874
19202
|
try {
|
|
18875
19203
|
const dir = projectPath(CACHE_DIR);
|
|
18876
19204
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
18877
|
-
for (const entry of (0,
|
|
19205
|
+
for (const entry of (0, import_node_fs26.readdirSync)(dir)) {
|
|
18878
19206
|
if (!entry.startsWith("pending-")) continue;
|
|
18879
|
-
const path = (0,
|
|
19207
|
+
const path = (0, import_node_path22.join)(dir, entry);
|
|
18880
19208
|
try {
|
|
18881
|
-
const stat3 = (0,
|
|
19209
|
+
const stat3 = (0, import_node_fs26.statSync)(path);
|
|
18882
19210
|
if (stat3.mtimeMs < cutoff) {
|
|
18883
|
-
(0,
|
|
19211
|
+
(0, import_node_fs26.unlinkSync)(path);
|
|
18884
19212
|
logEvent("cache_entry_pruned", {
|
|
18885
19213
|
path: entry,
|
|
18886
19214
|
age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
|
|
@@ -18894,7 +19222,7 @@ function pruneStaleCache() {
|
|
|
18894
19222
|
}
|
|
18895
19223
|
|
|
18896
19224
|
// src/lib/context-files.ts
|
|
18897
|
-
var
|
|
19225
|
+
var import_node_fs27 = require("node:fs");
|
|
18898
19226
|
var MAX_CONTEXT_FILES = 10;
|
|
18899
19227
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
18900
19228
|
var MAX_CONTEXT_TOTAL_BYTES = 51200;
|
|
@@ -18915,7 +19243,7 @@ function gatherContextFiles(contextPaths, deltaFiles) {
|
|
|
18915
19243
|
continue;
|
|
18916
19244
|
}
|
|
18917
19245
|
try {
|
|
18918
|
-
const content = (0,
|
|
19246
|
+
const content = (0, import_node_fs27.readFileSync)(safePath, "utf8");
|
|
18919
19247
|
const bytes = Buffer.byteLength(content);
|
|
18920
19248
|
if (bytes > MAX_CONTEXT_FILE_BYTES) {
|
|
18921
19249
|
logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
|
|
@@ -18977,8 +19305,8 @@ async function contextFiles(run) {
|
|
|
18977
19305
|
|
|
18978
19306
|
// src/lib/seed-runner.ts
|
|
18979
19307
|
var import_promises11 = require("node:fs/promises");
|
|
18980
|
-
var
|
|
18981
|
-
var
|
|
19308
|
+
var import_node_fs28 = require("node:fs");
|
|
19309
|
+
var import_node_path23 = require("node:path");
|
|
18982
19310
|
var import_yaml2 = __toESM(require_dist());
|
|
18983
19311
|
|
|
18984
19312
|
// src/lib/seed.ts
|
|
@@ -19217,7 +19545,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
|
19217
19545
|
return fm;
|
|
19218
19546
|
}
|
|
19219
19547
|
async function runSeed(opts) {
|
|
19220
|
-
if (!(0,
|
|
19548
|
+
if (!(0, import_node_fs28.existsSync)(STANDARD_FILE)) {
|
|
19221
19549
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
19222
19550
|
}
|
|
19223
19551
|
let standardDoc;
|
|
@@ -19229,7 +19557,7 @@ async function runSeed(opts) {
|
|
|
19229
19557
|
}
|
|
19230
19558
|
const knowledgeSpec = standardDoc.knowledge_spec ?? {};
|
|
19231
19559
|
let readmeContent;
|
|
19232
|
-
if ((0,
|
|
19560
|
+
if ((0, import_node_fs28.existsSync)("README.md")) {
|
|
19233
19561
|
try {
|
|
19234
19562
|
readmeContent = await (0, import_promises11.readFile)("README.md", "utf-8");
|
|
19235
19563
|
} catch {
|
|
@@ -19237,7 +19565,7 @@ async function runSeed(opts) {
|
|
|
19237
19565
|
}
|
|
19238
19566
|
let claudeMdContent;
|
|
19239
19567
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
19240
|
-
if ((0,
|
|
19568
|
+
if ((0, import_node_fs28.existsSync)(p)) {
|
|
19241
19569
|
try {
|
|
19242
19570
|
claudeMdContent = await (0, import_promises11.readFile)(p, "utf-8");
|
|
19243
19571
|
break;
|
|
@@ -19260,8 +19588,8 @@ async function runSeed(opts) {
|
|
|
19260
19588
|
if (candidates.length === 0) {
|
|
19261
19589
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
19262
19590
|
}
|
|
19263
|
-
const overviewPath = (0,
|
|
19264
|
-
if ((0,
|
|
19591
|
+
const overviewPath = (0, import_node_path23.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
19592
|
+
if ((0, import_node_fs28.existsSync)(overviewPath) && !opts.force) {
|
|
19265
19593
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates };
|
|
19266
19594
|
}
|
|
19267
19595
|
if (opts.dryRun) {
|
|
@@ -19303,7 +19631,7 @@ async function runSeed(opts) {
|
|
|
19303
19631
|
continue;
|
|
19304
19632
|
}
|
|
19305
19633
|
try {
|
|
19306
|
-
await (0, import_promises11.mkdir)((0,
|
|
19634
|
+
await (0, import_promises11.mkdir)((0, import_node_path23.dirname)(targetPath), { recursive: true });
|
|
19307
19635
|
await (0, import_promises11.writeFile)(targetPath, renderNodeMarkdown(c, nodeId, createdAt));
|
|
19308
19636
|
created++;
|
|
19309
19637
|
opts.onCreated?.(nodeId, filePathRel, c);
|
|
@@ -19316,8 +19644,8 @@ async function runSeed(opts) {
|
|
|
19316
19644
|
}
|
|
19317
19645
|
|
|
19318
19646
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
19319
|
-
var
|
|
19320
|
-
var
|
|
19647
|
+
var import_node_fs29 = require("node:fs");
|
|
19648
|
+
var import_node_path24 = require("node:path");
|
|
19321
19649
|
async function memoryManifest(run) {
|
|
19322
19650
|
const { globals } = run;
|
|
19323
19651
|
const { serviceUrl, token } = run;
|
|
@@ -19327,9 +19655,9 @@ async function memoryManifest(run) {
|
|
|
19327
19655
|
let autoSeedNotice = null;
|
|
19328
19656
|
try {
|
|
19329
19657
|
await ensureMemoryDir();
|
|
19330
|
-
const seedMarker = (0,
|
|
19331
|
-
const hasStandard = (0,
|
|
19332
|
-
const alreadyTried = (0,
|
|
19658
|
+
const seedMarker = (0, import_node_path24.join)(VERITY_DIR, ".seeded");
|
|
19659
|
+
const hasStandard = (0, import_node_fs29.existsSync)(STANDARD_FILE);
|
|
19660
|
+
const alreadyTried = (0, import_node_fs29.existsSync)(seedMarker);
|
|
19333
19661
|
if (hasStandard && !alreadyTried) {
|
|
19334
19662
|
const preManifest = await buildManifest();
|
|
19335
19663
|
if (preManifest.nodes.length === 0) {
|
|
@@ -19342,7 +19670,7 @@ async function memoryManifest(run) {
|
|
|
19342
19670
|
dryRun: false
|
|
19343
19671
|
});
|
|
19344
19672
|
if (seedResult.created > 0) {
|
|
19345
|
-
(0,
|
|
19673
|
+
(0, import_node_fs29.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
|
|
19346
19674
|
`);
|
|
19347
19675
|
autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
|
|
19348
19676
|
logEvent("auto_seed_ran", {
|
|
@@ -19350,7 +19678,7 @@ async function memoryManifest(run) {
|
|
|
19350
19678
|
failed: seedResult.failed
|
|
19351
19679
|
});
|
|
19352
19680
|
} else if (seedResult.skipped === "already_seeded") {
|
|
19353
|
-
(0,
|
|
19681
|
+
(0, import_node_fs29.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
|
|
19354
19682
|
`);
|
|
19355
19683
|
} else {
|
|
19356
19684
|
logEvent("auto_seed_noop", {
|
|
@@ -19445,7 +19773,7 @@ function computeIncrement(reviewedPaths, hashOf, priorAuthored) {
|
|
|
19445
19773
|
}
|
|
19446
19774
|
|
|
19447
19775
|
// src/commands/analyze/phases/10-working-memory.ts
|
|
19448
|
-
var
|
|
19776
|
+
var import_node_path25 = require("node:path");
|
|
19449
19777
|
async function workingMemory(run) {
|
|
19450
19778
|
const { opts } = run;
|
|
19451
19779
|
const { allForReview, baseline, conversation, foldResult, sessionId, token, transcriptPath } = run;
|
|
@@ -19457,7 +19785,7 @@ async function workingMemory(run) {
|
|
|
19457
19785
|
const priorState = foldForMarks(memorySession.d);
|
|
19458
19786
|
incrementReport = computeIncrement(
|
|
19459
19787
|
allForReview,
|
|
19460
|
-
(p) => fileHash((0,
|
|
19788
|
+
(p) => fileHash((0, import_node_path25.join)(repoRoot(), p)),
|
|
19461
19789
|
priorState.authored_all.map((a) => ({
|
|
19462
19790
|
path: a.path,
|
|
19463
19791
|
hash_at_last_verdict: a.hash_at_last_verdict
|
|
@@ -19539,7 +19867,7 @@ async function workingMemory(run) {
|
|
|
19539
19867
|
}
|
|
19540
19868
|
|
|
19541
19869
|
// src/lib/note-budget.ts
|
|
19542
|
-
var
|
|
19870
|
+
var import_node_fs30 = require("node:fs");
|
|
19543
19871
|
var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
|
|
19544
19872
|
var EPISODE_STALE_SECONDS = 30 * 60;
|
|
19545
19873
|
var FRESH = { delivered: 0, tasksCompleted: 0, ts: 0 };
|
|
@@ -19561,9 +19889,9 @@ function advisoryBudgetSpent(episode, rawDecision) {
|
|
|
19561
19889
|
}
|
|
19562
19890
|
function readAdvisoryEpisode(sessionId) {
|
|
19563
19891
|
const file = scopedFile(ADVISORY_EPISODE_FILE, sessionId);
|
|
19564
|
-
if (!(0,
|
|
19892
|
+
if (!(0, import_node_fs30.existsSync)(file)) return null;
|
|
19565
19893
|
try {
|
|
19566
|
-
const o = JSON.parse((0,
|
|
19894
|
+
const o = JSON.parse((0, import_node_fs30.readFileSync)(file, "utf-8")) ?? {};
|
|
19567
19895
|
const delivered = typeof o.delivered === "number" ? o.delivered : NaN;
|
|
19568
19896
|
if (isNaN(delivered)) return null;
|
|
19569
19897
|
return {
|
|
@@ -19577,8 +19905,8 @@ function readAdvisoryEpisode(sessionId) {
|
|
|
19577
19905
|
}
|
|
19578
19906
|
function writeAdvisoryEpisode(episode, sessionId) {
|
|
19579
19907
|
try {
|
|
19580
|
-
(0,
|
|
19581
|
-
(0,
|
|
19908
|
+
(0, import_node_fs30.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
19909
|
+
(0, import_node_fs30.writeFileSync)(
|
|
19582
19910
|
scopedFile(ADVISORY_EPISODE_FILE, sessionId),
|
|
19583
19911
|
JSON.stringify({ v: 1, ...episode })
|
|
19584
19912
|
);
|
|
@@ -19608,7 +19936,7 @@ function isExplicitlyAutonomous(env = process.env) {
|
|
|
19608
19936
|
}
|
|
19609
19937
|
|
|
19610
19938
|
// src/lib/task-context.ts
|
|
19611
|
-
var
|
|
19939
|
+
var import_node_child_process9 = require("node:child_process");
|
|
19612
19940
|
var CLOSING_RE = /\b(close[sd]?|fix(?:e[sd])?|resolve[sd]?)\b[\s:]*#(\d+)/i;
|
|
19613
19941
|
var BRANCH_RE = /(?:^|[/_-])(?:issue|gh|fix)[-_/]?(\d+)\b/i;
|
|
19614
19942
|
function parseLinkedIssue(sources) {
|
|
@@ -19624,7 +19952,7 @@ function parseLinkedIssue(sources) {
|
|
|
19624
19952
|
}
|
|
19625
19953
|
function safeExec(cmd, timeout) {
|
|
19626
19954
|
try {
|
|
19627
|
-
return (0,
|
|
19955
|
+
return (0, import_node_child_process9.execSync)(cmd, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout }).trim();
|
|
19628
19956
|
} catch {
|
|
19629
19957
|
return "";
|
|
19630
19958
|
}
|
|
@@ -19870,7 +20198,10 @@ async function buildRequest(run) {
|
|
|
19870
20198
|
}
|
|
19871
20199
|
if (specs.length > 0) intentContext.specs = specs;
|
|
19872
20200
|
if (plans.length > 0) intentContext.plans = plans;
|
|
19873
|
-
if (actionSummary)
|
|
20201
|
+
if (actionSummary) {
|
|
20202
|
+
const { user_commands: _uc, user_commands_truncated: _uct, ...onTheWire } = actionSummary;
|
|
20203
|
+
intentContext.action_summary = onTheWire;
|
|
20204
|
+
}
|
|
19874
20205
|
for (const key of Object.keys(intentContext)) {
|
|
19875
20206
|
if (intentContext[key] == null) delete intentContext[key];
|
|
19876
20207
|
}
|
|
@@ -19880,14 +20211,14 @@ async function buildRequest(run) {
|
|
|
19880
20211
|
}
|
|
19881
20212
|
|
|
19882
20213
|
// src/lib/offline.ts
|
|
19883
|
-
var
|
|
20214
|
+
var import_node_fs31 = require("node:fs");
|
|
19884
20215
|
var import_node_crypto11 = require("node:crypto");
|
|
19885
20216
|
function cacheRequest(body) {
|
|
19886
20217
|
try {
|
|
19887
|
-
(0,
|
|
20218
|
+
(0, import_node_fs31.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
19888
20219
|
const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
|
|
19889
20220
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
19890
|
-
(0,
|
|
20221
|
+
(0, import_node_fs31.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
|
|
19891
20222
|
} catch {
|
|
19892
20223
|
}
|
|
19893
20224
|
}
|
|
@@ -20006,8 +20337,8 @@ async function transmit(run) {
|
|
|
20006
20337
|
}
|
|
20007
20338
|
|
|
20008
20339
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
20009
|
-
var
|
|
20010
|
-
var
|
|
20340
|
+
var import_node_fs32 = require("node:fs");
|
|
20341
|
+
var import_node_path26 = require("node:path");
|
|
20011
20342
|
async function reconcile(run) {
|
|
20012
20343
|
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run;
|
|
20013
20344
|
const sentPaths = codeDelta.files.map((f) => f.path);
|
|
@@ -20017,7 +20348,7 @@ async function reconcile(run) {
|
|
|
20017
20348
|
const st = foldDossier(memorySession.d);
|
|
20018
20349
|
openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
|
|
20019
20350
|
try {
|
|
20020
|
-
const src = (0,
|
|
20351
|
+
const src = (0, import_node_fs32.readFileSync)((0, import_node_path26.join)(repoRoot(), file), "utf8").split("\n");
|
|
20021
20352
|
const at = src[line - 1];
|
|
20022
20353
|
return at === void 0 ? null : lineSha(at);
|
|
20023
20354
|
} catch {
|
|
@@ -20672,7 +21003,7 @@ async function runAnalyze(opts, globals) {
|
|
|
20672
21003
|
}
|
|
20673
21004
|
|
|
20674
21005
|
// src/commands/baseline.ts
|
|
20675
|
-
var
|
|
21006
|
+
var import_node_fs33 = require("node:fs");
|
|
20676
21007
|
function registerBaselineCommands(program2) {
|
|
20677
21008
|
const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
|
|
20678
21009
|
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) => {
|
|
@@ -20681,7 +21012,7 @@ function registerBaselineCommands(program2) {
|
|
|
20681
21012
|
process.chdir(repoRoot());
|
|
20682
21013
|
} catch {
|
|
20683
21014
|
}
|
|
20684
|
-
if (!(0,
|
|
21015
|
+
if (!(0, import_node_fs33.existsSync)(VERITY_DIR)) {
|
|
20685
21016
|
process.exit(0);
|
|
20686
21017
|
}
|
|
20687
21018
|
let sessionId = opts.sessionId;
|
|
@@ -20721,7 +21052,7 @@ async function readStdin() {
|
|
|
20721
21052
|
}
|
|
20722
21053
|
|
|
20723
21054
|
// src/commands/review.ts
|
|
20724
|
-
var
|
|
21055
|
+
var import_node_fs34 = require("node:fs");
|
|
20725
21056
|
function registerReviewCommand(program2) {
|
|
20726
21057
|
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) => {
|
|
20727
21058
|
const globals = program2.opts();
|
|
@@ -20740,7 +21071,7 @@ async function runReview(opts, globals) {
|
|
|
20740
21071
|
const securityFiles = filterSecurity(allFiles);
|
|
20741
21072
|
let staticResults;
|
|
20742
21073
|
if (isCodacyAvailable()) {
|
|
20743
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
21074
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs34.existsSync)(f) || resolveFile(f) !== null);
|
|
20744
21075
|
staticResults = runCodacyAnalysis(scannable);
|
|
20745
21076
|
} else {
|
|
20746
21077
|
staticResults = {
|
|
@@ -20766,10 +21097,10 @@ async function runReview(opts, globals) {
|
|
|
20766
21097
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
20767
21098
|
specs = [];
|
|
20768
21099
|
for (const p of specPaths) {
|
|
20769
|
-
if (!(0,
|
|
21100
|
+
if (!(0, import_node_fs34.existsSync)(p)) continue;
|
|
20770
21101
|
try {
|
|
20771
|
-
const { readFileSync:
|
|
20772
|
-
const content =
|
|
21102
|
+
const { readFileSync: readFileSync20 } = await import("node:fs");
|
|
21103
|
+
const content = readFileSync20(p, "utf-8");
|
|
20773
21104
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
20774
21105
|
} catch {
|
|
20775
21106
|
}
|
|
@@ -20826,15 +21157,15 @@ async function runReview(opts, globals) {
|
|
|
20826
21157
|
}
|
|
20827
21158
|
|
|
20828
21159
|
// src/commands/guard.ts
|
|
20829
|
-
var
|
|
20830
|
-
var
|
|
21160
|
+
var import_node_fs35 = require("node:fs");
|
|
21161
|
+
var import_node_path27 = require("node:path");
|
|
20831
21162
|
var GUARD_BLOCK_CAP = 2;
|
|
20832
|
-
var GUARD_ITER_FILE = (0,
|
|
21163
|
+
var GUARD_ITER_FILE = (0, import_node_path27.join)(VERITY_DIR, ".guard-iteration");
|
|
20833
21164
|
function readPreToolUseStdin() {
|
|
20834
21165
|
const empty = { command: "", cwd: null, sessionId: null };
|
|
20835
|
-
return new Promise((
|
|
21166
|
+
return new Promise((resolve4) => {
|
|
20836
21167
|
try {
|
|
20837
|
-
if (process.stdin.isTTY) return
|
|
21168
|
+
if (process.stdin.isTTY) return resolve4(empty);
|
|
20838
21169
|
const chunks = [];
|
|
20839
21170
|
let timer;
|
|
20840
21171
|
let settled = false;
|
|
@@ -20847,7 +21178,7 @@ function readPreToolUseStdin() {
|
|
|
20847
21178
|
process.stdin.removeListener("end", onEnd);
|
|
20848
21179
|
process.stdin.removeListener("error", onError);
|
|
20849
21180
|
process.stdin.pause();
|
|
20850
|
-
|
|
21181
|
+
resolve4(value);
|
|
20851
21182
|
};
|
|
20852
21183
|
const onEnd = () => {
|
|
20853
21184
|
try {
|
|
@@ -20868,32 +21199,13 @@ function readPreToolUseStdin() {
|
|
|
20868
21199
|
process.stdin.on("error", onError);
|
|
20869
21200
|
process.stdin.resume();
|
|
20870
21201
|
} catch {
|
|
20871
|
-
|
|
21202
|
+
resolve4(empty);
|
|
20872
21203
|
}
|
|
20873
21204
|
});
|
|
20874
21205
|
}
|
|
20875
|
-
function buildCommandRe(head) {
|
|
20876
|
-
return new RegExp(`(?:^|[\\s;&|(])${head}|(?:^|[;&|(])\\s*[^\\s;&|()'"]*\\/${head}`);
|
|
20877
|
-
}
|
|
20878
|
-
var GIT_GLOBAL_OPTS = "(?:\\s+(?:-[Cc]\\s+\\S+|--?[\\w-]+(?:=\\S+)?))*";
|
|
20879
|
-
var COMMIT_RE = buildCommandRe(`git${GIT_GLOBAL_OPTS}\\s+commit(?![\\w-])`);
|
|
20880
|
-
var PUSH_RE = buildCommandRe(`git${GIT_GLOBAL_OPTS}\\s+push\\b`);
|
|
20881
|
-
var GH_PR_RE = buildCommandRe(`gh${GIT_GLOBAL_OPTS}\\s+pr\\s+create\\b`);
|
|
20882
|
-
function classifyCommand2(command, on) {
|
|
20883
|
-
let commit = false;
|
|
20884
|
-
let push = false;
|
|
20885
|
-
for (const seg of (command ?? "").split(/&&|\|\||;|\n/)) {
|
|
20886
|
-
if (/--dry-run\b/.test(seg)) continue;
|
|
20887
|
-
if (COMMIT_RE.test(seg)) commit = true;
|
|
20888
|
-
if (PUSH_RE.test(seg) || GH_PR_RE.test(seg)) push = true;
|
|
20889
|
-
}
|
|
20890
|
-
if (commit && on.includes("commit")) return "pre-commit";
|
|
20891
|
-
if (push && on.includes("push")) return "pre-push";
|
|
20892
|
-
return null;
|
|
20893
|
-
}
|
|
20894
21206
|
function readIterMap() {
|
|
20895
21207
|
try {
|
|
20896
|
-
const raw = JSON.parse((0,
|
|
21208
|
+
const raw = JSON.parse((0, import_node_fs35.readFileSync)(GUARD_ITER_FILE, "utf-8"));
|
|
20897
21209
|
if (raw && typeof raw === "object") {
|
|
20898
21210
|
if (typeof raw.moment === "string" && typeof raw.count === "number") {
|
|
20899
21211
|
return { [raw.moment]: raw.count };
|
|
@@ -20913,10 +21225,10 @@ function readIter(moment) {
|
|
|
20913
21225
|
}
|
|
20914
21226
|
function writeIter(moment, count) {
|
|
20915
21227
|
try {
|
|
20916
|
-
(0,
|
|
21228
|
+
(0, import_node_fs35.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
20917
21229
|
const map = readIterMap();
|
|
20918
21230
|
map[moment] = count;
|
|
20919
|
-
(0,
|
|
21231
|
+
(0, import_node_fs35.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
20920
21232
|
} catch {
|
|
20921
21233
|
}
|
|
20922
21234
|
}
|
|
@@ -20926,10 +21238,10 @@ function resetIter(moment) {
|
|
|
20926
21238
|
if (!(moment in map)) return;
|
|
20927
21239
|
delete map[moment];
|
|
20928
21240
|
if (Object.keys(map).length === 0) {
|
|
20929
|
-
if ((0,
|
|
21241
|
+
if ((0, import_node_fs35.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs35.unlinkSync)(GUARD_ITER_FILE);
|
|
20930
21242
|
} else {
|
|
20931
|
-
(0,
|
|
20932
|
-
(0,
|
|
21243
|
+
(0, import_node_fs35.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
21244
|
+
(0, import_node_fs35.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
20933
21245
|
}
|
|
20934
21246
|
} catch {
|
|
20935
21247
|
}
|
|
@@ -20944,8 +21256,14 @@ function registerGuardCommand(program2) {
|
|
|
20944
21256
|
}
|
|
20945
21257
|
});
|
|
20946
21258
|
}
|
|
20947
|
-
function
|
|
20948
|
-
return moment === "pre-commit" ?
|
|
21259
|
+
function resolveMomentRange(moment, frame, command, on) {
|
|
21260
|
+
return moment === "pre-commit" ? stagedRange() : resolvePushRange(frame, command, on);
|
|
21261
|
+
}
|
|
21262
|
+
function describeRange(range) {
|
|
21263
|
+
if (range.kind === "staged") return "staged";
|
|
21264
|
+
if (range.kind === "nothing" || !range.base) return null;
|
|
21265
|
+
const base = /^[0-9a-f]{40}$/.test(range.base) ? range.base.slice(0, 7) : range.base;
|
|
21266
|
+
return `${base}..${range.head} via ${range.via}`;
|
|
20949
21267
|
}
|
|
20950
21268
|
function matchFlagValue(command, flags) {
|
|
20951
21269
|
const re = new RegExp(`(?<![\\w-])(?:${flags})(?:=|\\s+)('((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)"|([^\\s'"-][^\\s]*))`);
|
|
@@ -20980,26 +21298,24 @@ function isSubstantiveIntent(text) {
|
|
|
20980
21298
|
if (SHELL_PLUMBING.test(t)) return false;
|
|
20981
21299
|
return true;
|
|
20982
21300
|
}
|
|
20983
|
-
function extractStatedIntent(moment, command) {
|
|
20984
|
-
const text = moment === "pre-commit" ? parseCommitMessage(command) : parsePrIntent(command) ??
|
|
21301
|
+
function extractStatedIntent(moment, command, pushedMessages = null) {
|
|
21302
|
+
const text = moment === "pre-commit" ? parseCommitMessage(command) : parsePrIntent(command) ?? pushedMessages;
|
|
20985
21303
|
return isSubstantiveIntent(text) ? text : null;
|
|
20986
21304
|
}
|
|
20987
21305
|
function hasBlockingFinding(response) {
|
|
20988
21306
|
const findings = response.findings ?? [];
|
|
20989
21307
|
return findings.some((f) => f.scope !== "pre-existing" && ["critical", "high"].includes((f.severity ?? "").toLowerCase()));
|
|
20990
21308
|
}
|
|
20991
|
-
function buildGuardRequest(moment, files, iter, sessionId,
|
|
21309
|
+
function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedIntent, coverageTelemetry) {
|
|
20992
21310
|
const analyzable = filterAnalyzable(files);
|
|
20993
21311
|
const securityFiles = filterSecurity(files);
|
|
20994
21312
|
let staticResults;
|
|
20995
21313
|
if (isCodacyAvailable()) {
|
|
20996
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0,
|
|
21314
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs35.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
20997
21315
|
staticResults = runCodacyAnalysis(scannable);
|
|
20998
21316
|
} else {
|
|
20999
21317
|
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
21000
21318
|
}
|
|
21001
|
-
const codeDelta = collectCodeDelta(files);
|
|
21002
|
-
if (codeDelta.total_files === 0) return null;
|
|
21003
21319
|
const trigger = moment === "pre-commit" ? "hook:pre-commit" : "hook:pre-push";
|
|
21004
21320
|
const requestBody = {
|
|
21005
21321
|
static_results: staticResults,
|
|
@@ -21016,9 +21332,9 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
21016
21332
|
iteration: iter + 1
|
|
21017
21333
|
}
|
|
21018
21334
|
};
|
|
21335
|
+
if (coverageTelemetry) requestBody.coverage_telemetry = coverageTelemetry;
|
|
21019
21336
|
const specs = discoverSpecs();
|
|
21020
21337
|
const plans = discoverPlans();
|
|
21021
|
-
const statedIntent = extractStatedIntent(moment, command);
|
|
21022
21338
|
if (specs.length > 0 || plans.length > 0 || statedIntent) {
|
|
21023
21339
|
const intentContext = {};
|
|
21024
21340
|
if (statedIntent) intentContext.user_prompt = statedIntent;
|
|
@@ -21028,6 +21344,39 @@ function buildGuardRequest(moment, files, iter, sessionId, command) {
|
|
|
21028
21344
|
}
|
|
21029
21345
|
return requestBody;
|
|
21030
21346
|
}
|
|
21347
|
+
function buildGuardCoverage(files, codeDelta, frame, frameRange) {
|
|
21348
|
+
const byReason = {};
|
|
21349
|
+
for (const e of codeDelta.excluded) byReason[e.reason] = (byReason[e.reason] ?? 0) + 1;
|
|
21350
|
+
return {
|
|
21351
|
+
changed_all: files.length,
|
|
21352
|
+
analyzable: filterAnalyzable(files).length,
|
|
21353
|
+
reviewable: filterReviewable(files).length,
|
|
21354
|
+
security: filterSecurity(files).length,
|
|
21355
|
+
for_review: files.length,
|
|
21356
|
+
sent: codeDelta.total_files,
|
|
21357
|
+
capped_out: codeDelta.truncated?.dropped ?? 0,
|
|
21358
|
+
excluded: codeDelta.excluded.length,
|
|
21359
|
+
excluded_by_reason: byReason,
|
|
21360
|
+
transcript_windowed: null,
|
|
21361
|
+
guard_frame: frameTelemetry(frame, frameRange)
|
|
21362
|
+
};
|
|
21363
|
+
}
|
|
21364
|
+
function coverageSummary(c) {
|
|
21365
|
+
const range = c.range ? ` @ ${c.range}` : "";
|
|
21366
|
+
return `reviewed ${c.sent.length} file(s)${range}`;
|
|
21367
|
+
}
|
|
21368
|
+
function coverageBlock(c) {
|
|
21369
|
+
const lines = [];
|
|
21370
|
+
const tree = c.root ? `${c.root}${c.linked ? " (linked worktree)" : ""}${c.branch ? ` \xB7 branch ${c.branch}` : ""}` : "(no tree resolved)";
|
|
21371
|
+
lines.push(`Reviewed (${c.moment}): ${c.sent.length} file(s)${c.range ? ` @ ${c.range}` : ""}`);
|
|
21372
|
+
lines.push(` Tree: ${tree}`);
|
|
21373
|
+
for (const f of c.sent) lines.push(` - ${f}`);
|
|
21374
|
+
if (c.excluded.length > 0) {
|
|
21375
|
+
lines.push(` Excluded (${c.excluded.length}):`);
|
|
21376
|
+
for (const e of c.excluded) lines.push(` - ${e.path} (${e.reason})`);
|
|
21377
|
+
}
|
|
21378
|
+
return lines.join("\n");
|
|
21379
|
+
}
|
|
21031
21380
|
function emitAllowNotice(userMsg, agentMsg) {
|
|
21032
21381
|
process.stdout.write(JSON.stringify({
|
|
21033
21382
|
systemMessage: userMsg,
|
|
@@ -21038,15 +21387,24 @@ function emitAllowNotice(userMsg, agentMsg) {
|
|
|
21038
21387
|
async function runGuard(opts, globals) {
|
|
21039
21388
|
const on = opts.on.split(",").map((s) => s.trim()).filter((s) => s === "commit" || s === "push");
|
|
21040
21389
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
21041
|
-
|
|
21042
|
-
try {
|
|
21043
|
-
process.chdir(cwd);
|
|
21044
|
-
} catch {
|
|
21045
|
-
}
|
|
21046
|
-
}
|
|
21047
|
-
const moment = classifyCommand2(command, on);
|
|
21390
|
+
const moment = classifyCommand(command, on);
|
|
21048
21391
|
if (!moment) process.exit(0);
|
|
21049
21392
|
const verb = moment === "pre-commit" ? "commit" : "push";
|
|
21393
|
+
const { frame } = resolveFrame({ command, on, hookCwd: cwd });
|
|
21394
|
+
if (frame.refusal || !frame.worktreeRoot) {
|
|
21395
|
+
logEvent("guard_frame", { moment, ...frameTelemetry(frame, null) });
|
|
21396
|
+
if ((frame.refusal ?? "").startsWith("anchor:")) process.exit(0);
|
|
21397
|
+
emitAllowNotice(
|
|
21398
|
+
`\u26A0 Verity ${moment}: could not resolve the tree this ${verb} targets \u2014 ${verb}ed WITHOUT review`,
|
|
21399
|
+
`Verity ${moment}: the target tree could not be resolved (${frame.refusal}); the ${verb} was allowed WITHOUT a Verity review.`
|
|
21400
|
+
);
|
|
21401
|
+
}
|
|
21402
|
+
try {
|
|
21403
|
+
process.chdir(frame.worktreeRoot);
|
|
21404
|
+
} catch {
|
|
21405
|
+
process.exit(0);
|
|
21406
|
+
}
|
|
21407
|
+
_resetRepoRoot();
|
|
21050
21408
|
const iter = readIter(moment);
|
|
21051
21409
|
if (iter >= GUARD_BLOCK_CAP) {
|
|
21052
21410
|
resetIter(moment);
|
|
@@ -21055,13 +21413,39 @@ async function runGuard(opts, globals) {
|
|
|
21055
21413
|
`Verity ${moment}: review-cycle cap (${GUARD_BLOCK_CAP}) reached; the ${verb} was allowed without a further block.`
|
|
21056
21414
|
);
|
|
21057
21415
|
}
|
|
21058
|
-
const
|
|
21416
|
+
const range = resolveMomentRange(moment, frame, command, on);
|
|
21417
|
+
const files = rangeFiles(frame, range);
|
|
21059
21418
|
if (files.length === 0) process.exit(0);
|
|
21060
21419
|
const tokenResult = await resolveToken(globals.token);
|
|
21061
21420
|
const urlResult = await resolveServiceUrl(globals.serviceUrl);
|
|
21062
21421
|
if (!tokenResult.ok || !urlResult.ok) process.exit(0);
|
|
21063
|
-
|
|
21064
|
-
|
|
21422
|
+
logEvent("guard_frame", { moment, ...frameTelemetry(frame, range) });
|
|
21423
|
+
const codeDelta = collectCodeDelta(files);
|
|
21424
|
+
if (codeDelta.total_files === 0) process.exit(0);
|
|
21425
|
+
const statedIntent = extractStatedIntent(
|
|
21426
|
+
moment,
|
|
21427
|
+
command,
|
|
21428
|
+
moment === "pre-push" ? rangeMessages(frame, range) || null : null
|
|
21429
|
+
);
|
|
21430
|
+
const requestBody = buildGuardRequest(
|
|
21431
|
+
moment,
|
|
21432
|
+
files,
|
|
21433
|
+
codeDelta,
|
|
21434
|
+
iter,
|
|
21435
|
+
sessionId,
|
|
21436
|
+
statedIntent,
|
|
21437
|
+
buildGuardCoverage(files, codeDelta, frame, range)
|
|
21438
|
+
);
|
|
21439
|
+
const coverage = {
|
|
21440
|
+
moment,
|
|
21441
|
+
root: frame.worktreeRoot,
|
|
21442
|
+
branch: frame.branch,
|
|
21443
|
+
linked: frame.isLinkedWorktree,
|
|
21444
|
+
range: describeRange(range),
|
|
21445
|
+
sent: codeDelta.files.map((f) => f.path),
|
|
21446
|
+
excluded: codeDelta.excluded.map((e) => ({ path: e.path, reason: e.reason }))
|
|
21447
|
+
};
|
|
21448
|
+
logToFileOnly(coverageBlock(coverage));
|
|
21065
21449
|
const result = await analyzeRequest({
|
|
21066
21450
|
serviceUrl: urlResult.data,
|
|
21067
21451
|
token: tokenResult.data.token,
|
|
@@ -21082,31 +21466,39 @@ async function runGuard(opts, globals) {
|
|
|
21082
21466
|
const decision = response.gate_decision ?? "(unrecognised)";
|
|
21083
21467
|
const viewUrl = response.view_url ?? "";
|
|
21084
21468
|
const link = viewUrl ? ` \u2014 ${viewUrl}` : "";
|
|
21469
|
+
const covLine = coverageSummary(coverage);
|
|
21470
|
+
const covDetail = coverageBlock(coverage);
|
|
21085
21471
|
if (decision === "FAIL" && hasBlockingFinding(response)) {
|
|
21086
21472
|
writeIter(moment, iter + 1);
|
|
21087
|
-
writeBlockMessage(moment, response);
|
|
21473
|
+
writeBlockMessage(moment, response, covDetail);
|
|
21088
21474
|
process.exit(2);
|
|
21089
21475
|
}
|
|
21090
21476
|
resetIter(moment);
|
|
21091
21477
|
if (decision === "FAIL") {
|
|
21092
21478
|
const narrative = response.assessment?.narrative ?? "";
|
|
21093
21479
|
emitAllowNotice(
|
|
21094
|
-
`\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding${link}`,
|
|
21095
|
-
`Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
|
|
21480
|
+
`\u26A0 Verity ${moment}: the ${verb} may not match its stated purpose \u2014 proceeding (${covLine})${link}`,
|
|
21481
|
+
`Verity ${moment}: intent-alignment WARNING (not blocked).${narrative ? " " + narrative : ""}
|
|
21482
|
+
${covDetail}${viewUrl ? `
|
|
21483
|
+
Report: ${viewUrl}` : ""}`
|
|
21096
21484
|
);
|
|
21097
21485
|
}
|
|
21098
21486
|
if (decision === "WARN") {
|
|
21099
21487
|
emitAllowNotice(
|
|
21100
|
-
`\u26A0 Verity ${moment}: WARN \u2014 proceeding${link}`,
|
|
21101
|
-
`Verity ${moment} review: WARN (proceeding)
|
|
21488
|
+
`\u26A0 Verity ${moment}: WARN \u2014 proceeding (${covLine})${link}`,
|
|
21489
|
+
`Verity ${moment} review: WARN (proceeding).
|
|
21490
|
+
${covDetail}${viewUrl ? `
|
|
21491
|
+
Report: ${viewUrl}` : ""}`
|
|
21102
21492
|
);
|
|
21103
21493
|
}
|
|
21104
21494
|
emitAllowNotice(
|
|
21105
|
-
`\u2713 Verity ${moment}: PASS${link}`,
|
|
21106
|
-
`Verity ${moment} review: PASS
|
|
21495
|
+
`\u2713 Verity ${moment}: PASS (${covLine})${link}`,
|
|
21496
|
+
`Verity ${moment} review: PASS.
|
|
21497
|
+
${covDetail}${viewUrl ? `
|
|
21498
|
+
Report: ${viewUrl}` : ""}`
|
|
21107
21499
|
);
|
|
21108
21500
|
}
|
|
21109
|
-
function writeBlockMessage(moment, response) {
|
|
21501
|
+
function writeBlockMessage(moment, response, covDetail) {
|
|
21110
21502
|
const label2 = moment === "pre-commit" ? "pre-commit" : "pre-push";
|
|
21111
21503
|
const verb = moment === "pre-commit" ? "commit" : "push";
|
|
21112
21504
|
const assessment = response.assessment;
|
|
@@ -21136,6 +21528,9 @@ function writeBlockMessage(moment, response) {
|
|
|
21136
21528
|
`);
|
|
21137
21529
|
}
|
|
21138
21530
|
}
|
|
21531
|
+
process.stderr.write(`${DIM}${covDetail}${NC}
|
|
21532
|
+
|
|
21533
|
+
`);
|
|
21139
21534
|
if (viewUrl) process.stderr.write(`${CYAN}Full report: ${viewUrl}${NC}
|
|
21140
21535
|
|
|
21141
21536
|
`);
|
|
@@ -21144,16 +21539,16 @@ function writeBlockMessage(moment, response) {
|
|
|
21144
21539
|
}
|
|
21145
21540
|
|
|
21146
21541
|
// src/commands/init.ts
|
|
21147
|
-
var
|
|
21542
|
+
var import_node_fs37 = require("node:fs");
|
|
21148
21543
|
var import_promises13 = require("node:fs/promises");
|
|
21149
|
-
var
|
|
21150
|
-
var
|
|
21544
|
+
var import_node_path29 = require("node:path");
|
|
21545
|
+
var import_node_child_process11 = require("node:child_process");
|
|
21151
21546
|
var readline2 = __toESM(require("node:readline/promises"));
|
|
21152
21547
|
|
|
21153
21548
|
// src/commands/migrate.ts
|
|
21154
|
-
var
|
|
21155
|
-
var
|
|
21156
|
-
var
|
|
21549
|
+
var import_node_fs36 = require("node:fs");
|
|
21550
|
+
var import_node_path28 = require("node:path");
|
|
21551
|
+
var import_node_child_process10 = require("node:child_process");
|
|
21157
21552
|
|
|
21158
21553
|
// src/lib/telemetry.ts
|
|
21159
21554
|
var import_promises12 = require("node:fs/promises");
|
|
@@ -21248,11 +21643,11 @@ async function uninstallTelemetry() {
|
|
|
21248
21643
|
// src/commands/migrate.ts
|
|
21249
21644
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
21250
21645
|
function defaultNpmRemover(pkg) {
|
|
21251
|
-
(0,
|
|
21646
|
+
(0, import_node_child_process10.execSync)(`npm rm -g ${pkg}`, { stdio: "pipe", timeout: 12e4 });
|
|
21252
21647
|
}
|
|
21253
21648
|
function isGitTracked(cwd, relPath) {
|
|
21254
21649
|
try {
|
|
21255
|
-
(0,
|
|
21650
|
+
(0, import_node_child_process10.execSync)(`git ls-files --error-unmatch ${relPath}`, { cwd, stdio: "pipe" });
|
|
21256
21651
|
return true;
|
|
21257
21652
|
} catch {
|
|
21258
21653
|
return false;
|
|
@@ -21260,7 +21655,7 @@ function isGitTracked(cwd, relPath) {
|
|
|
21260
21655
|
}
|
|
21261
21656
|
function isGitRepo(cwd) {
|
|
21262
21657
|
try {
|
|
21263
|
-
(0,
|
|
21658
|
+
(0, import_node_child_process10.execSync)("git rev-parse --is-inside-work-tree", { cwd, stdio: "pipe" });
|
|
21264
21659
|
return true;
|
|
21265
21660
|
} catch {
|
|
21266
21661
|
return false;
|
|
@@ -21281,12 +21676,12 @@ async function runMigration(opts = {}) {
|
|
|
21281
21676
|
return { actions, migrated: actions.length > 0 };
|
|
21282
21677
|
}
|
|
21283
21678
|
function migrateProjectDir(root, actions) {
|
|
21284
|
-
const gateDir = (0,
|
|
21285
|
-
const verityDir = (0,
|
|
21286
|
-
if ((0,
|
|
21679
|
+
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
21680
|
+
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
21681
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && !(0, import_node_fs36.existsSync)(verityDir)) {
|
|
21287
21682
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
21288
21683
|
}
|
|
21289
|
-
if ((0,
|
|
21684
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && (0, import_node_fs36.existsSync)(verityDir)) {
|
|
21290
21685
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
21291
21686
|
}
|
|
21292
21687
|
return false;
|
|
@@ -21300,20 +21695,20 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
21300
21695
|
);
|
|
21301
21696
|
}
|
|
21302
21697
|
try {
|
|
21303
|
-
(0,
|
|
21698
|
+
(0, import_node_child_process10.execSync)("git mv .gate .verity", { cwd: root, stdio: "pipe" });
|
|
21304
21699
|
actions.push("Moved .gate/ \u2192 .verity/ (git mv, staged)");
|
|
21305
21700
|
moved = true;
|
|
21306
21701
|
} catch {
|
|
21307
21702
|
}
|
|
21308
21703
|
}
|
|
21309
21704
|
if (moved) {
|
|
21310
|
-
if ((0,
|
|
21705
|
+
if ((0, import_node_fs36.existsSync)(gateDir)) {
|
|
21311
21706
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
21312
21707
|
if (carried > 0) {
|
|
21313
21708
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
21314
21709
|
}
|
|
21315
21710
|
try {
|
|
21316
|
-
(0,
|
|
21711
|
+
(0, import_node_fs36.rmSync)(gateDir, { recursive: true, force: true });
|
|
21317
21712
|
} catch {
|
|
21318
21713
|
}
|
|
21319
21714
|
}
|
|
@@ -21329,18 +21724,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
21329
21724
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
21330
21725
|
}
|
|
21331
21726
|
try {
|
|
21332
|
-
(0,
|
|
21727
|
+
(0, import_node_fs36.rmSync)(gateDir, { recursive: true, force: true });
|
|
21333
21728
|
} catch {
|
|
21334
21729
|
}
|
|
21335
21730
|
return carried > 0;
|
|
21336
21731
|
}
|
|
21337
21732
|
function migrateGlobalCredentials(home, actions) {
|
|
21338
21733
|
if (!home) return;
|
|
21339
|
-
const gateCreds = (0,
|
|
21340
|
-
const verityCreds = (0,
|
|
21341
|
-
if (!(0,
|
|
21342
|
-
if (!(0,
|
|
21343
|
-
(0,
|
|
21734
|
+
const gateCreds = (0, import_node_path28.join)(home, ".gate", "credentials");
|
|
21735
|
+
const verityCreds = (0, import_node_path28.join)(home, ".verity", "credentials");
|
|
21736
|
+
if (!(0, import_node_fs36.existsSync)(gateCreds)) return;
|
|
21737
|
+
if (!(0, import_node_fs36.existsSync)(verityCreds)) {
|
|
21738
|
+
(0, import_node_fs36.mkdirSync)((0, import_node_path28.join)(home, ".verity"), { recursive: true });
|
|
21344
21739
|
moveFile(gateCreds, verityCreds);
|
|
21345
21740
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
21346
21741
|
return;
|
|
@@ -21362,8 +21757,8 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
21362
21757
|
}
|
|
21363
21758
|
}
|
|
21364
21759
|
async function migrateClaudeMd(root, actions) {
|
|
21365
|
-
const claudeMd = (0,
|
|
21366
|
-
const hadLegacyBlock = (0,
|
|
21760
|
+
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
21761
|
+
const hadLegacyBlock = (0, import_node_fs36.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
21367
21762
|
if (!hadLegacyBlock) return;
|
|
21368
21763
|
try {
|
|
21369
21764
|
await ensureClaudeMdPointer(root);
|
|
@@ -21373,13 +21768,13 @@ async function migrateClaudeMd(root, actions) {
|
|
|
21373
21768
|
}
|
|
21374
21769
|
}
|
|
21375
21770
|
function migrateStandardFile(root, actions) {
|
|
21376
|
-
const gateMd = (0,
|
|
21377
|
-
const verityMd = (0,
|
|
21378
|
-
if (!(0,
|
|
21771
|
+
const gateMd = (0, import_node_path28.join)(root, "GATE.md");
|
|
21772
|
+
const verityMd = (0, import_node_path28.join)(root, "VERITY.md");
|
|
21773
|
+
if (!(0, import_node_fs36.existsSync)(gateMd) || (0, import_node_fs36.existsSync)(verityMd)) return;
|
|
21379
21774
|
let moved = false;
|
|
21380
21775
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
21381
21776
|
try {
|
|
21382
|
-
(0,
|
|
21777
|
+
(0, import_node_child_process10.execSync)("git mv GATE.md VERITY.md", { cwd: root, stdio: "pipe" });
|
|
21383
21778
|
moved = true;
|
|
21384
21779
|
} catch {
|
|
21385
21780
|
}
|
|
@@ -21387,12 +21782,12 @@ function migrateStandardFile(root, actions) {
|
|
|
21387
21782
|
if (!moved) moveFile(gateMd, verityMd);
|
|
21388
21783
|
const content = readFileSyncSafe(verityMd);
|
|
21389
21784
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
21390
|
-
if (refreshed !== content) (0,
|
|
21785
|
+
if (refreshed !== content) (0, import_node_fs36.writeFileSync)(verityMd, refreshed);
|
|
21391
21786
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
21392
21787
|
}
|
|
21393
21788
|
async function migrateTelemetryHeaders(root, actions) {
|
|
21394
|
-
const file = (0,
|
|
21395
|
-
if (!(0,
|
|
21789
|
+
const file = (0, import_node_path28.join)(root, ".claude", "settings.local.json");
|
|
21790
|
+
if (!(0, import_node_fs36.existsSync)(file)) return;
|
|
21396
21791
|
let settings;
|
|
21397
21792
|
try {
|
|
21398
21793
|
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
@@ -21440,21 +21835,21 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
21440
21835
|
}
|
|
21441
21836
|
if (toAppend.length > 0) {
|
|
21442
21837
|
const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
21443
|
-
(0,
|
|
21838
|
+
(0, import_node_fs36.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
|
|
21444
21839
|
}
|
|
21445
|
-
(0,
|
|
21840
|
+
(0, import_node_fs36.rmSync)(gateCreds, { force: true });
|
|
21446
21841
|
return toAppend.length;
|
|
21447
21842
|
}
|
|
21448
21843
|
function readFileSyncSafe(path) {
|
|
21449
21844
|
try {
|
|
21450
|
-
return (0,
|
|
21845
|
+
return (0, import_node_fs36.readFileSync)(path, "utf-8");
|
|
21451
21846
|
} catch {
|
|
21452
21847
|
return "";
|
|
21453
21848
|
}
|
|
21454
21849
|
}
|
|
21455
21850
|
function hasStagedChanges(root) {
|
|
21456
21851
|
try {
|
|
21457
|
-
(0,
|
|
21852
|
+
(0, import_node_child_process10.execSync)("git diff --cached --quiet", { cwd: root, stdio: "pipe" });
|
|
21458
21853
|
return false;
|
|
21459
21854
|
} catch {
|
|
21460
21855
|
return true;
|
|
@@ -21462,35 +21857,35 @@ function hasStagedChanges(root) {
|
|
|
21462
21857
|
}
|
|
21463
21858
|
function moveDir(from, to) {
|
|
21464
21859
|
try {
|
|
21465
|
-
(0,
|
|
21860
|
+
(0, import_node_fs36.renameSync)(from, to);
|
|
21466
21861
|
} catch (err) {
|
|
21467
21862
|
if (err.code !== "EXDEV") throw err;
|
|
21468
|
-
(0,
|
|
21469
|
-
(0,
|
|
21863
|
+
(0, import_node_fs36.cpSync)(from, to, { recursive: true });
|
|
21864
|
+
(0, import_node_fs36.rmSync)(from, { recursive: true, force: true });
|
|
21470
21865
|
}
|
|
21471
21866
|
}
|
|
21472
21867
|
function moveFile(from, to) {
|
|
21473
21868
|
try {
|
|
21474
|
-
(0,
|
|
21869
|
+
(0, import_node_fs36.renameSync)(from, to);
|
|
21475
21870
|
} catch (err) {
|
|
21476
21871
|
if (err.code !== "EXDEV") throw err;
|
|
21477
|
-
(0,
|
|
21478
|
-
(0,
|
|
21872
|
+
(0, import_node_fs36.cpSync)(from, to);
|
|
21873
|
+
(0, import_node_fs36.rmSync)(from, { force: true });
|
|
21479
21874
|
}
|
|
21480
21875
|
}
|
|
21481
21876
|
function carryLegacyContents(gateDir, verityDir) {
|
|
21482
21877
|
let copied = 0;
|
|
21483
21878
|
const walk = (relDir) => {
|
|
21484
|
-
const srcDir = (0,
|
|
21485
|
-
for (const entry of (0,
|
|
21486
|
-
const rel = relDir ? (0,
|
|
21487
|
-
const src = (0,
|
|
21488
|
-
const dest = (0,
|
|
21489
|
-
if ((0,
|
|
21879
|
+
const srcDir = (0, import_node_path28.join)(gateDir, relDir);
|
|
21880
|
+
for (const entry of (0, import_node_fs36.readdirSync)(srcDir)) {
|
|
21881
|
+
const rel = relDir ? (0, import_node_path28.join)(relDir, entry) : entry;
|
|
21882
|
+
const src = (0, import_node_path28.join)(gateDir, rel);
|
|
21883
|
+
const dest = (0, import_node_path28.join)(verityDir, rel);
|
|
21884
|
+
if ((0, import_node_fs36.statSync)(src).isDirectory()) {
|
|
21490
21885
|
walk(rel);
|
|
21491
|
-
} else if (!(0,
|
|
21492
|
-
(0,
|
|
21493
|
-
(0,
|
|
21886
|
+
} else if (!(0, import_node_fs36.existsSync)(dest)) {
|
|
21887
|
+
(0, import_node_fs36.mkdirSync)((0, import_node_path28.dirname)(dest), { recursive: true });
|
|
21888
|
+
(0, import_node_fs36.cpSync)(src, dest);
|
|
21494
21889
|
copied++;
|
|
21495
21890
|
}
|
|
21496
21891
|
}
|
|
@@ -21499,22 +21894,22 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
21499
21894
|
return copied;
|
|
21500
21895
|
}
|
|
21501
21896
|
async function needsMigration(root = repoRoot()) {
|
|
21502
|
-
const gateDir = (0,
|
|
21503
|
-
const verityDir = (0,
|
|
21504
|
-
if ((0,
|
|
21505
|
-
if ((0,
|
|
21506
|
-
if ((0,
|
|
21897
|
+
const gateDir = (0, import_node_path28.join)(root, ".gate");
|
|
21898
|
+
const verityDir = (0, import_node_path28.join)(root, ".verity");
|
|
21899
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && !(0, import_node_fs36.existsSync)(verityDir)) return true;
|
|
21900
|
+
if ((0, import_node_fs36.existsSync)(gateDir) && (0, import_node_fs36.existsSync)(verityDir)) {
|
|
21901
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path28.join)(gateDir, "credentials")) && !(0, import_node_fs36.existsSync)((0, import_node_path28.join)(verityDir, "credentials"))) {
|
|
21507
21902
|
return true;
|
|
21508
21903
|
}
|
|
21509
|
-
if ((0,
|
|
21904
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path28.join)(gateDir, "memory")) && !(0, import_node_fs36.existsSync)((0, import_node_path28.join)(verityDir, "memory"))) {
|
|
21510
21905
|
return true;
|
|
21511
21906
|
}
|
|
21512
21907
|
}
|
|
21513
|
-
const claudeMd = (0,
|
|
21514
|
-
if ((0,
|
|
21908
|
+
const claudeMd = (0, import_node_path28.join)(root, "CLAUDE.md");
|
|
21909
|
+
if ((0, import_node_fs36.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
21515
21910
|
return true;
|
|
21516
21911
|
}
|
|
21517
|
-
if ((0,
|
|
21912
|
+
if ((0, import_node_fs36.existsSync)((0, import_node_path28.join)(root, "GATE.md")) && !(0, import_node_fs36.existsSync)((0, import_node_path28.join)(root, "VERITY.md"))) {
|
|
21518
21913
|
return true;
|
|
21519
21914
|
}
|
|
21520
21915
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -21607,7 +22002,7 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
21607
22002
|
}
|
|
21608
22003
|
let remote = "";
|
|
21609
22004
|
try {
|
|
21610
|
-
remote = (0,
|
|
22005
|
+
remote = (0, import_node_child_process11.execSync)("git remote get-url origin", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
21611
22006
|
} catch {
|
|
21612
22007
|
}
|
|
21613
22008
|
if (!healed) {
|
|
@@ -21650,15 +22045,15 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
21650
22045
|
}
|
|
21651
22046
|
function resolveDataDir() {
|
|
21652
22047
|
const candidates = [
|
|
21653
|
-
(0,
|
|
22048
|
+
(0, import_node_path29.join)(__dirname, "..", "data"),
|
|
21654
22049
|
// installed: node_modules/@codacy/verity-cli/data
|
|
21655
|
-
(0,
|
|
22050
|
+
(0, import_node_path29.join)(__dirname, "..", "..", "data"),
|
|
21656
22051
|
// edge case: nested resolution
|
|
21657
|
-
(0,
|
|
22052
|
+
(0, import_node_path29.join)(process.cwd(), "cli", "data")
|
|
21658
22053
|
// local dev: running from repo root
|
|
21659
22054
|
];
|
|
21660
22055
|
for (const candidate of candidates) {
|
|
21661
|
-
if ((0,
|
|
22056
|
+
if ((0, import_node_fs37.existsSync)((0, import_node_path29.join)(candidate, "skills"))) {
|
|
21662
22057
|
return candidate;
|
|
21663
22058
|
}
|
|
21664
22059
|
}
|
|
@@ -21674,7 +22069,7 @@ function registerInitCommand(program2) {
|
|
|
21674
22069
|
program2.command("init").description("Initialize Verity in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
21675
22070
|
const force = opts.force ?? false;
|
|
21676
22071
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
21677
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
22072
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs37.existsSync)(m));
|
|
21678
22073
|
if (!isProject) {
|
|
21679
22074
|
printError("No project detected in the current directory.");
|
|
21680
22075
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -21702,30 +22097,30 @@ function registerInitCommand(program2) {
|
|
|
21702
22097
|
}
|
|
21703
22098
|
printInfo(` Node.js ${nodeVersion} \u2713`);
|
|
21704
22099
|
try {
|
|
21705
|
-
const gitVersion = (0,
|
|
22100
|
+
const gitVersion = (0, import_node_child_process11.execSync)("git --version", { encoding: "utf-8" }).trim();
|
|
21706
22101
|
printInfo(` ${gitVersion} \u2713`);
|
|
21707
22102
|
} catch {
|
|
21708
22103
|
printError("git is required but not installed. Install from https://git-scm.com");
|
|
21709
22104
|
process.exit(1);
|
|
21710
22105
|
}
|
|
21711
22106
|
try {
|
|
21712
|
-
(0,
|
|
22107
|
+
(0, import_node_child_process11.execSync)("which claude", { encoding: "utf-8" });
|
|
21713
22108
|
printInfo(" Claude Code \u2713");
|
|
21714
22109
|
} catch {
|
|
21715
22110
|
printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
|
|
21716
22111
|
}
|
|
21717
22112
|
try {
|
|
21718
|
-
(0,
|
|
22113
|
+
(0, import_node_child_process11.execSync)("which codacy-analysis", { encoding: "utf-8", stdio: "pipe" });
|
|
21719
22114
|
printInfo(" @codacy/analysis-cli \u2713");
|
|
21720
22115
|
} catch {
|
|
21721
22116
|
printInfo(" Installing @codacy/analysis-cli...");
|
|
21722
22117
|
try {
|
|
21723
|
-
(0,
|
|
22118
|
+
(0, import_node_child_process11.execSync)("npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "pipe", timeout: 12e4 });
|
|
21724
22119
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
21725
22120
|
} catch {
|
|
21726
22121
|
try {
|
|
21727
22122
|
printWarn(" Retrying with sudo...");
|
|
21728
|
-
(0,
|
|
22123
|
+
(0, import_node_child_process11.execSync)("sudo npm install -g @codacy/analysis-cli", { encoding: "utf-8", stdio: "inherit", timeout: 12e4 });
|
|
21729
22124
|
printInfo(" @codacy/analysis-cli installed \u2713");
|
|
21730
22125
|
} catch {
|
|
21731
22126
|
printWarn(" Could not install @codacy/analysis-cli automatically.");
|
|
@@ -21737,21 +22132,21 @@ function registerInitCommand(program2) {
|
|
|
21737
22132
|
console.log("");
|
|
21738
22133
|
printInfo("Installing skills...");
|
|
21739
22134
|
const dataDir = resolveDataDir();
|
|
21740
|
-
const skillsSource = (0,
|
|
22135
|
+
const skillsSource = (0, import_node_path29.join)(dataDir, "skills");
|
|
21741
22136
|
const skillsDest = ".claude/skills";
|
|
21742
22137
|
const skills = ["verity-setup", "verity-analyze", "verity-status", "verity-feedback", "verity-learn", "verity-memory", "verity-insights", "verity-reflect"];
|
|
21743
22138
|
let skillsInstalled = 0;
|
|
21744
22139
|
for (const skill of skills) {
|
|
21745
|
-
const src = (0,
|
|
21746
|
-
const dest = (0,
|
|
21747
|
-
if (!(0,
|
|
22140
|
+
const src = (0, import_node_path29.join)(skillsSource, skill);
|
|
22141
|
+
const dest = (0, import_node_path29.join)(skillsDest, skill);
|
|
22142
|
+
if (!(0, import_node_fs37.existsSync)(src)) {
|
|
21748
22143
|
printWarn(` Skill data not found: ${skill}`);
|
|
21749
22144
|
continue;
|
|
21750
22145
|
}
|
|
21751
|
-
if ((0,
|
|
21752
|
-
const srcSkill = (0,
|
|
21753
|
-
const destSkill = (0,
|
|
21754
|
-
if ((0,
|
|
22146
|
+
if ((0, import_node_fs37.existsSync)(dest) && !force) {
|
|
22147
|
+
const srcSkill = (0, import_node_path29.join)(src, "SKILL.md");
|
|
22148
|
+
const destSkill = (0, import_node_path29.join)(dest, "SKILL.md");
|
|
22149
|
+
if ((0, import_node_fs37.existsSync)(destSkill)) {
|
|
21755
22150
|
try {
|
|
21756
22151
|
const srcContent = await (0, import_promises13.readFile)(srcSkill, "utf-8");
|
|
21757
22152
|
const destContent = await (0, import_promises13.readFile)(destSkill, "utf-8");
|
|
@@ -21794,7 +22189,7 @@ function registerInitCommand(program2) {
|
|
|
21794
22189
|
} catch (err) {
|
|
21795
22190
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
21796
22191
|
}
|
|
21797
|
-
const globalVerityDir = (0,
|
|
22192
|
+
const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
|
|
21798
22193
|
await (0, import_promises13.mkdir)(globalVerityDir, { recursive: true });
|
|
21799
22194
|
console.log("");
|
|
21800
22195
|
try {
|
|
@@ -21829,8 +22224,8 @@ function registerInitCommand(program2) {
|
|
|
21829
22224
|
}
|
|
21830
22225
|
|
|
21831
22226
|
// src/commands/uninstall.ts
|
|
21832
|
-
var
|
|
21833
|
-
var
|
|
22227
|
+
var import_node_fs38 = require("node:fs");
|
|
22228
|
+
var import_node_path30 = require("node:path");
|
|
21834
22229
|
var SKILL_NAMES = [
|
|
21835
22230
|
"verity-setup",
|
|
21836
22231
|
"verity-analyze",
|
|
@@ -21849,11 +22244,11 @@ function registerUninstallCommand(program2) {
|
|
|
21849
22244
|
const actions = [];
|
|
21850
22245
|
const skillsRoot = projectPath(".claude/skills");
|
|
21851
22246
|
for (const name of SKILL_NAMES) {
|
|
21852
|
-
const dir = (0,
|
|
21853
|
-
if ((0,
|
|
22247
|
+
const dir = (0, import_node_path30.join)(skillsRoot, name);
|
|
22248
|
+
if ((0, import_node_fs38.existsSync)(dir)) {
|
|
21854
22249
|
actions.push({
|
|
21855
22250
|
label: `Remove .claude/skills/${name}/`,
|
|
21856
|
-
apply: () => (0,
|
|
22251
|
+
apply: () => (0, import_node_fs38.rmSync)(dir, { recursive: true, force: true })
|
|
21857
22252
|
});
|
|
21858
22253
|
}
|
|
21859
22254
|
}
|
|
@@ -21867,24 +22262,24 @@ function registerUninstallCommand(program2) {
|
|
|
21867
22262
|
});
|
|
21868
22263
|
}
|
|
21869
22264
|
const verityDir = projectPath(VERITY_DIR);
|
|
21870
|
-
if ((0,
|
|
22265
|
+
if ((0, import_node_fs38.existsSync)(verityDir)) {
|
|
21871
22266
|
actions.push({
|
|
21872
22267
|
label: `Remove ${VERITY_DIR}/`,
|
|
21873
|
-
apply: () => (0,
|
|
22268
|
+
apply: () => (0, import_node_fs38.rmSync)(verityDir, { recursive: true, force: true })
|
|
21874
22269
|
});
|
|
21875
22270
|
}
|
|
21876
22271
|
if (!keepVerityMd) {
|
|
21877
22272
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
21878
|
-
if ((0,
|
|
22273
|
+
if ((0, import_node_fs38.existsSync)(verityMd)) {
|
|
21879
22274
|
actions.push({
|
|
21880
22275
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
21881
|
-
apply: () => (0,
|
|
22276
|
+
apply: () => (0, import_node_fs38.rmSync)(verityMd, { force: true })
|
|
21882
22277
|
});
|
|
21883
22278
|
}
|
|
21884
22279
|
}
|
|
21885
22280
|
const cleanupEmptyDir = (path) => {
|
|
21886
|
-
if ((0,
|
|
21887
|
-
(0,
|
|
22281
|
+
if ((0, import_node_fs38.existsSync)(path) && (0, import_node_fs38.statSync)(path).isDirectory() && (0, import_node_fs38.readdirSync)(path).length === 0) {
|
|
22282
|
+
(0, import_node_fs38.rmdirSync)(path);
|
|
21888
22283
|
}
|
|
21889
22284
|
};
|
|
21890
22285
|
actions.push({
|
|
@@ -21895,11 +22290,11 @@ function registerUninstallCommand(program2) {
|
|
|
21895
22290
|
}
|
|
21896
22291
|
});
|
|
21897
22292
|
const home = process.env.HOME ?? "";
|
|
21898
|
-
const globalVerityDir = (0,
|
|
21899
|
-
if (purgeGlobal && (0,
|
|
22293
|
+
const globalVerityDir = (0, import_node_path30.join)(home, ".verity");
|
|
22294
|
+
if (purgeGlobal && (0, import_node_fs38.existsSync)(globalVerityDir)) {
|
|
21900
22295
|
actions.push({
|
|
21901
22296
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
21902
|
-
apply: () => (0,
|
|
22297
|
+
apply: () => (0, import_node_fs38.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
21903
22298
|
});
|
|
21904
22299
|
}
|
|
21905
22300
|
if (actions.length === 0) {
|
|
@@ -22093,8 +22488,8 @@ function registerTaskCommands(program2) {
|
|
|
22093
22488
|
}
|
|
22094
22489
|
|
|
22095
22490
|
// src/commands/reset.ts
|
|
22096
|
-
var
|
|
22097
|
-
var
|
|
22491
|
+
var import_node_fs39 = require("node:fs");
|
|
22492
|
+
var import_node_path31 = require("node:path");
|
|
22098
22493
|
function registerResetCommand(program2) {
|
|
22099
22494
|
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) => {
|
|
22100
22495
|
const globals = program2.opts();
|
|
@@ -22131,11 +22526,11 @@ function registerResetCommand(program2) {
|
|
|
22131
22526
|
}
|
|
22132
22527
|
const cacheDir = projectPath(CACHE_DIR);
|
|
22133
22528
|
let purged = 0;
|
|
22134
|
-
if ((0,
|
|
22135
|
-
for (const entry of (0,
|
|
22529
|
+
if ((0, import_node_fs39.existsSync)(cacheDir)) {
|
|
22530
|
+
for (const entry of (0, import_node_fs39.readdirSync)(cacheDir)) {
|
|
22136
22531
|
if (entry.startsWith("pending-")) {
|
|
22137
22532
|
try {
|
|
22138
|
-
(0,
|
|
22533
|
+
(0, import_node_fs39.unlinkSync)((0, import_node_path31.join)(cacheDir, entry));
|
|
22139
22534
|
purged++;
|
|
22140
22535
|
} catch {
|
|
22141
22536
|
}
|
|
@@ -22150,19 +22545,19 @@ function registerResetCommand(program2) {
|
|
|
22150
22545
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
22151
22546
|
];
|
|
22152
22547
|
for (const file of filesToClear) {
|
|
22153
|
-
if ((0,
|
|
22548
|
+
if ((0, import_node_fs39.existsSync)(file)) {
|
|
22154
22549
|
try {
|
|
22155
|
-
(0,
|
|
22550
|
+
(0, import_node_fs39.writeFileSync)(file, "");
|
|
22156
22551
|
} catch {
|
|
22157
22552
|
}
|
|
22158
22553
|
}
|
|
22159
22554
|
}
|
|
22160
22555
|
if (opts.all) {
|
|
22161
22556
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
22162
|
-
if ((0,
|
|
22163
|
-
for (const entry of (0,
|
|
22557
|
+
if ((0, import_node_fs39.existsSync)(logsDir)) {
|
|
22558
|
+
for (const entry of (0, import_node_fs39.readdirSync)(logsDir)) {
|
|
22164
22559
|
try {
|
|
22165
|
-
(0,
|
|
22560
|
+
(0, import_node_fs39.unlinkSync)((0, import_node_path31.join)(logsDir, entry));
|
|
22166
22561
|
} catch {
|
|
22167
22562
|
}
|
|
22168
22563
|
}
|
|
@@ -22470,8 +22865,8 @@ function registerTelemetryCommands(program2) {
|
|
|
22470
22865
|
}
|
|
22471
22866
|
|
|
22472
22867
|
// src/cli.ts
|
|
22473
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.30.
|
|
22474
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.30.
|
|
22868
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.30.1-experimental.e2137b9").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) => {
|
|
22869
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.30.1-experimental.e2137b9");
|
|
22475
22870
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
22476
22871
|
try {
|
|
22477
22872
|
await foldLegacyLocalCredential();
|