@codacy/verity-cli 0.32.0-experimental.f0746f7 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -2
- package/bin/verity.js +1142 -876
- package/data/skills/verity-analyze/SKILL.md +7 -0
- package/data/skills/verity-feedback/SKILL.md +6 -0
- package/data/skills/verity-insights/SKILL.md +7 -0
- package/data/skills/verity-learn/SKILL.md +6 -0
- package/data/skills/verity-memory/SKILL.md +7 -0
- package/data/skills/verity-reflect/SKILL.md +6 -0
- package/data/skills/verity-setup/SKILL.md +12 -5
- package/data/skills/verity-status/SKILL.md +6 -0
- package/package.json +1 -1
package/bin/verity.js
CHANGED
|
@@ -10405,6 +10405,8 @@ var MAX_INTENT_CHARS = 2e3;
|
|
|
10405
10405
|
var SNAPSHOT_DIR = `${VERITY_DIR}/.snapshot`;
|
|
10406
10406
|
var BASELINE_DIR = `${VERITY_DIR}/.baseline`;
|
|
10407
10407
|
var CONVERSATION_BUFFER_FILE = `${VERITY_DIR}/.conversation-buffer`;
|
|
10408
|
+
var PLUGIN_MARKER_FILE = `${VERITY_DIR}/.plugin-active`;
|
|
10409
|
+
var PROJECT_CONFIG_FILE = `${VERITY_DIR}/config.json`;
|
|
10408
10410
|
var CONVERSATION_MAX_ENTRIES = 10;
|
|
10409
10411
|
var CONVERSATION_WINDOW_MINUTES = 15;
|
|
10410
10412
|
var MAX_FINDINGS = 25;
|
|
@@ -10507,7 +10509,7 @@ var SECURITY_PATTERNS = [
|
|
|
10507
10509
|
/Dockerfile/
|
|
10508
10510
|
];
|
|
10509
10511
|
var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
|
|
10510
|
-
var DEFAULT_SERVICE_URL = "
|
|
10512
|
+
var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
|
|
10511
10513
|
var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
|
|
10512
10514
|
var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
|
|
10513
10515
|
var GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
|
|
@@ -10831,16 +10833,9 @@ function splitLines(s) {
|
|
|
10831
10833
|
}
|
|
10832
10834
|
var SHA_RE = /^[0-9a-f]{40}$/;
|
|
10833
10835
|
function readBaselineSha() {
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
sha = (0, import_node_fs3.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
10838
|
-
} catch {
|
|
10839
|
-
return null;
|
|
10840
|
-
}
|
|
10841
|
-
if (!SHA_RE.test(sha)) return null;
|
|
10842
|
-
const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
10843
|
-
if (!reachable) {
|
|
10836
|
+
const sha = readRawBaselineSha();
|
|
10837
|
+
if (sha === null) return null;
|
|
10838
|
+
if (!commitResolves(sha)) {
|
|
10844
10839
|
try {
|
|
10845
10840
|
(0, import_node_fs3.unlinkSync)(BASELINE_SHA_FILE);
|
|
10846
10841
|
} catch {
|
|
@@ -10849,6 +10844,16 @@ function readBaselineSha() {
|
|
|
10849
10844
|
}
|
|
10850
10845
|
return sha;
|
|
10851
10846
|
}
|
|
10847
|
+
function readRawBaselineSha() {
|
|
10848
|
+
if (!(0, import_node_fs3.existsSync)(BASELINE_SHA_FILE)) return null;
|
|
10849
|
+
let sha;
|
|
10850
|
+
try {
|
|
10851
|
+
sha = (0, import_node_fs3.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
|
|
10852
|
+
} catch {
|
|
10853
|
+
return null;
|
|
10854
|
+
}
|
|
10855
|
+
return SHA_RE.test(sha) ? sha : null;
|
|
10856
|
+
}
|
|
10852
10857
|
function writeBaselineSha(sha) {
|
|
10853
10858
|
if (!SHA_RE.test(sha)) return;
|
|
10854
10859
|
try {
|
|
@@ -10857,6 +10862,25 @@ function writeBaselineSha(sha) {
|
|
|
10857
10862
|
} catch {
|
|
10858
10863
|
}
|
|
10859
10864
|
}
|
|
10865
|
+
function commitObjectExists(sha) {
|
|
10866
|
+
if (!SHA_RE.test(sha)) return false;
|
|
10867
|
+
return execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
|
|
10868
|
+
}
|
|
10869
|
+
function committedSinceRewrite(oldSha) {
|
|
10870
|
+
if (!SHA_RE.test(oldSha)) return [];
|
|
10871
|
+
const newLocal = new Set(splitLines(execGit(`git rev-list HEAD --not ${oldSha} --remotes`)));
|
|
10872
|
+
if (newLocal.size === 0) return [];
|
|
10873
|
+
const files = /* @__PURE__ */ new Set();
|
|
10874
|
+
for (const line of splitLines(execGit(`git cherry ${oldSha} HEAD`))) {
|
|
10875
|
+
const sp = line.indexOf(" ");
|
|
10876
|
+
if (sp < 0) continue;
|
|
10877
|
+
const mark = line.slice(0, sp);
|
|
10878
|
+
const sha = line.slice(sp + 1).trim();
|
|
10879
|
+
if (mark !== "+" || !newLocal.has(sha)) continue;
|
|
10880
|
+
for (const f of splitLines(execGit(`git diff-tree --no-commit-id --name-only -r ${sha}`))) files.add(f);
|
|
10881
|
+
}
|
|
10882
|
+
return [...files].filter((f) => !isVerityOwnedPath(f));
|
|
10883
|
+
}
|
|
10860
10884
|
var VERITY_OWNED_PREFIXES = [".verity/", ".gate/", ".codacy/"];
|
|
10861
10885
|
var VERITY_OWNED_FILES = ["VERITY.md"];
|
|
10862
10886
|
function isVerityOwnedPath(file) {
|
|
@@ -10869,6 +10893,7 @@ function getChangedFiles() {
|
|
|
10869
10893
|
for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
|
|
10870
10894
|
for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
|
|
10871
10895
|
for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
|
|
10896
|
+
const rawBaseline = readRawBaselineSha();
|
|
10872
10897
|
const baseline = readBaselineSha();
|
|
10873
10898
|
if (baseline) {
|
|
10874
10899
|
const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
|
|
@@ -10876,6 +10901,13 @@ function getChangedFiles() {
|
|
|
10876
10901
|
hasRecentCommitFiles = true;
|
|
10877
10902
|
for (const f of committed) sets.add(f);
|
|
10878
10903
|
}
|
|
10904
|
+
} else if (rawBaseline && commitObjectExists(rawBaseline)) {
|
|
10905
|
+
const committed = committedSinceRewrite(rawBaseline);
|
|
10906
|
+
logEvent("baseline_rewritten", { recovered: committed.length });
|
|
10907
|
+
if (committed.length > 0) {
|
|
10908
|
+
hasRecentCommitFiles = true;
|
|
10909
|
+
for (const f of committed) sets.add(f);
|
|
10910
|
+
}
|
|
10879
10911
|
} else {
|
|
10880
10912
|
const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
|
|
10881
10913
|
const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
|
|
@@ -12701,9 +12733,155 @@ async function applyMomentSelection(moments) {
|
|
|
12701
12733
|
return settings;
|
|
12702
12734
|
}
|
|
12703
12735
|
|
|
12736
|
+
// src/lib/project-config.ts
|
|
12737
|
+
var import_node_fs4 = require("node:fs");
|
|
12738
|
+
var DEFAULTS = { git_moments: [] };
|
|
12739
|
+
function isMoment(value) {
|
|
12740
|
+
return value === "commit" || value === "push";
|
|
12741
|
+
}
|
|
12742
|
+
function parseMoments(raw) {
|
|
12743
|
+
return [...new Set(raw.split(",").map((s) => s.trim()).filter(isMoment))];
|
|
12744
|
+
}
|
|
12745
|
+
function readProjectConfig() {
|
|
12746
|
+
try {
|
|
12747
|
+
if (!(0, import_node_fs4.existsSync)(PROJECT_CONFIG_FILE)) return DEFAULTS;
|
|
12748
|
+
const raw = JSON.parse((0, import_node_fs4.readFileSync)(PROJECT_CONFIG_FILE, "utf-8"));
|
|
12749
|
+
const moments = Array.isArray(raw.git_moments) ? raw.git_moments.filter(isMoment) : [];
|
|
12750
|
+
return { git_moments: [...new Set(moments)] };
|
|
12751
|
+
} catch {
|
|
12752
|
+
return DEFAULTS;
|
|
12753
|
+
}
|
|
12754
|
+
}
|
|
12755
|
+
function writeProjectConfig(patch) {
|
|
12756
|
+
const next = { ...readProjectConfig(), ...patch };
|
|
12757
|
+
(0, import_node_fs4.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
12758
|
+
(0, import_node_fs4.writeFileSync)(PROJECT_CONFIG_FILE, JSON.stringify(next, null, 2) + "\n");
|
|
12759
|
+
return next;
|
|
12760
|
+
}
|
|
12761
|
+
function resolveGuardMoments(explicit) {
|
|
12762
|
+
if (explicit !== void 0) return parseMoments(explicit);
|
|
12763
|
+
return readProjectConfig().git_moments;
|
|
12764
|
+
}
|
|
12765
|
+
|
|
12766
|
+
// src/lib/plugin-ownership.ts
|
|
12767
|
+
var import_node_fs6 = require("node:fs");
|
|
12768
|
+
|
|
12769
|
+
// src/lib/stderr-log.ts
|
|
12770
|
+
var import_node_fs5 = require("node:fs");
|
|
12771
|
+
var TOKEN_RE2 = /verity_[0-9a-f]{16,}/g;
|
|
12772
|
+
var ANSI_RE = /\u001b\[[0-?]*[ -/]*[@-~]/g;
|
|
12773
|
+
function scrub(s) {
|
|
12774
|
+
return s.replace(TOKEN_RE2, "verity_***REDACTED***").replace(ANSI_RE, "");
|
|
12775
|
+
}
|
|
12776
|
+
var installed = false;
|
|
12777
|
+
var wroteBanner = false;
|
|
12778
|
+
var banner = "";
|
|
12779
|
+
function append(text) {
|
|
12780
|
+
try {
|
|
12781
|
+
const dir = projectPath(DEBUG_LOG_DIR);
|
|
12782
|
+
const file = projectPath(STDERR_LOG_FILE);
|
|
12783
|
+
(0, import_node_fs5.mkdirSync)(dir, { recursive: true });
|
|
12784
|
+
rotateIfNeeded(file);
|
|
12785
|
+
(0, import_node_fs5.appendFileSync)(file, text);
|
|
12786
|
+
} catch {
|
|
12787
|
+
}
|
|
12788
|
+
}
|
|
12789
|
+
function ensureBanner() {
|
|
12790
|
+
if (wroteBanner) return;
|
|
12791
|
+
wroteBanner = true;
|
|
12792
|
+
append(banner);
|
|
12793
|
+
}
|
|
12794
|
+
function installStderrLog(cmd, argv, version) {
|
|
12795
|
+
if (installed || !isDebugEnabled()) return;
|
|
12796
|
+
installed = true;
|
|
12797
|
+
banner = `
|
|
12798
|
+
\u2501\u2501 verity ${cmd} \xB7 ${(/* @__PURE__ */ new Date()).toISOString()} \xB7 pid ${process.pid}
|
|
12799
|
+
v${version} \xB7 ${process.cwd()}
|
|
12800
|
+
argv: ${scrub(argv.join(" "))}
|
|
12801
|
+
`;
|
|
12802
|
+
const original = process.stderr.write.bind(process.stderr);
|
|
12803
|
+
const tee = (...args) => {
|
|
12804
|
+
const result = original(...args);
|
|
12805
|
+
try {
|
|
12806
|
+
const chunk = args[0];
|
|
12807
|
+
const text = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : String(chunk);
|
|
12808
|
+
ensureBanner();
|
|
12809
|
+
append(scrub(text));
|
|
12810
|
+
} catch {
|
|
12811
|
+
}
|
|
12812
|
+
return result;
|
|
12813
|
+
};
|
|
12814
|
+
process.stderr.write = tee;
|
|
12815
|
+
}
|
|
12816
|
+
function logToFileOnly(text) {
|
|
12817
|
+
if (!installed) return;
|
|
12818
|
+
ensureBanner();
|
|
12819
|
+
append(text.endsWith("\n") ? text : `${text}
|
|
12820
|
+
`);
|
|
12821
|
+
}
|
|
12822
|
+
|
|
12823
|
+
// src/lib/plugin-ownership.ts
|
|
12824
|
+
var MARKER_TTL_SECONDS = 24 * 60 * 60;
|
|
12825
|
+
function isPluginInvocation() {
|
|
12826
|
+
return !!process.env.VERITY_PLUGIN_ROOT;
|
|
12827
|
+
}
|
|
12828
|
+
function readMarker() {
|
|
12829
|
+
try {
|
|
12830
|
+
if (!(0, import_node_fs6.existsSync)(PLUGIN_MARKER_FILE)) return null;
|
|
12831
|
+
const raw = JSON.parse((0, import_node_fs6.readFileSync)(PLUGIN_MARKER_FILE, "utf-8"));
|
|
12832
|
+
const pluginRoot = typeof raw.plugin_root === "string" ? raw.plugin_root : "";
|
|
12833
|
+
if (!pluginRoot) return null;
|
|
12834
|
+
return {
|
|
12835
|
+
session_id: typeof raw.session_id === "string" ? raw.session_id : null,
|
|
12836
|
+
plugin_root: pluginRoot,
|
|
12837
|
+
version: typeof raw.version === "string" ? raw.version : null,
|
|
12838
|
+
ts: typeof raw.ts === "number" ? raw.ts : 0
|
|
12839
|
+
};
|
|
12840
|
+
} catch {
|
|
12841
|
+
return null;
|
|
12842
|
+
}
|
|
12843
|
+
}
|
|
12844
|
+
function recordPluginOwnership(sessionId) {
|
|
12845
|
+
const pluginRoot = process.env.VERITY_PLUGIN_ROOT;
|
|
12846
|
+
if (!pluginRoot) return;
|
|
12847
|
+
try {
|
|
12848
|
+
(0, import_node_fs6.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
12849
|
+
const marker = {
|
|
12850
|
+
session_id: sessionId,
|
|
12851
|
+
plugin_root: pluginRoot,
|
|
12852
|
+
version: process.env.VERITY_PLUGIN_VERSION || null,
|
|
12853
|
+
ts: Math.floor(Date.now() / 1e3)
|
|
12854
|
+
};
|
|
12855
|
+
(0, import_node_fs6.writeFileSync)(PLUGIN_MARKER_FILE, JSON.stringify(marker));
|
|
12856
|
+
} catch {
|
|
12857
|
+
}
|
|
12858
|
+
}
|
|
12859
|
+
function shouldDeferToPlugin(sessionId) {
|
|
12860
|
+
if (isPluginInvocation()) {
|
|
12861
|
+
recordPluginOwnership(sessionId);
|
|
12862
|
+
return false;
|
|
12863
|
+
}
|
|
12864
|
+
const marker = readMarker();
|
|
12865
|
+
if (!marker) return false;
|
|
12866
|
+
if (!(0, import_node_fs6.existsSync)(marker.plugin_root)) return false;
|
|
12867
|
+
if (sessionId && marker.session_id) return sessionId === marker.session_id;
|
|
12868
|
+
return Math.floor(Date.now() / 1e3) - marker.ts < MARKER_TTL_SECONDS;
|
|
12869
|
+
}
|
|
12870
|
+
function pluginActiveHere() {
|
|
12871
|
+
const marker = readMarker();
|
|
12872
|
+
return !!marker && (0, import_node_fs6.existsSync)(marker.plugin_root);
|
|
12873
|
+
}
|
|
12874
|
+
function deferredToPlugin(command, sessionId) {
|
|
12875
|
+
if (!shouldDeferToPlugin(sessionId)) return false;
|
|
12876
|
+
logToFileOnly(
|
|
12877
|
+
`${command}: the Verity Claude Code plugin owns this session's hooks \u2014 standing down so the turn is not gated twice. Remove the duplicate settings.json hooks with \`verity init --plugin-mode\`.`
|
|
12878
|
+
);
|
|
12879
|
+
return true;
|
|
12880
|
+
}
|
|
12881
|
+
|
|
12704
12882
|
// src/commands/hooks.ts
|
|
12705
12883
|
var ALL_MOMENTS = ["stop", "pre-commit", "pre-push"];
|
|
12706
|
-
function
|
|
12884
|
+
function parseMoments2(raw) {
|
|
12707
12885
|
const seen = /* @__PURE__ */ new Set();
|
|
12708
12886
|
for (const part of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
12709
12887
|
if (ALL_MOMENTS.includes(part)) seen.add(part);
|
|
@@ -12715,7 +12893,22 @@ function registerHooksCommands(program2) {
|
|
|
12715
12893
|
hooks.command("install").description("Install Verity hooks into Claude Code settings").option("--force", "Overwrite existing Verity hooks").option("--moments <list>", "Reconcile to exactly these moments: stop,pre-commit,pre-push").action(async (opts) => {
|
|
12716
12894
|
const force = opts.force ?? false;
|
|
12717
12895
|
if (opts.moments != null) {
|
|
12718
|
-
const moments =
|
|
12896
|
+
const moments = parseMoments2(opts.moments);
|
|
12897
|
+
const gitMoments = [
|
|
12898
|
+
...moments.includes("pre-commit") ? ["commit"] : [],
|
|
12899
|
+
...moments.includes("pre-push") ? ["push"] : []
|
|
12900
|
+
];
|
|
12901
|
+
writeProjectConfig({ git_moments: gitMoments });
|
|
12902
|
+
if (pluginActiveHere()) {
|
|
12903
|
+
printInfo("The Verity plugin wires the hooks; recorded your selection in .verity/config.json:");
|
|
12904
|
+
printInfo(` Stop (analysis on every turn): ${moments.includes("stop") ? "on" : "off \u2014 the plugin still wires it; see below"}`);
|
|
12905
|
+
printInfo(` Pre-commit gate: ${gitMoments.includes("commit") ? "on" : "off"}`);
|
|
12906
|
+
printInfo(` Pre-push/PR gate: ${gitMoments.includes("push") ? "on" : "off"}`);
|
|
12907
|
+
if (!moments.includes("stop")) {
|
|
12908
|
+
printWarn(" Turning the Stop review off is not yet supported under the plugin \u2014 it stays on.");
|
|
12909
|
+
}
|
|
12910
|
+
return;
|
|
12911
|
+
}
|
|
12719
12912
|
await applyMomentSelection(moments);
|
|
12720
12913
|
const status = await checkAllVerityHooks();
|
|
12721
12914
|
printInfo("Verity hooks reconciled in .claude/settings.json:");
|
|
@@ -12762,6 +12955,24 @@ function registerHooksCommands(program2) {
|
|
|
12762
12955
|
});
|
|
12763
12956
|
hooks.command("check").description("Check if Verity hooks are installed").action(async () => {
|
|
12764
12957
|
const status = await checkAllVerityHooks();
|
|
12958
|
+
if (pluginActiveHere()) {
|
|
12959
|
+
const moments = readProjectConfig().git_moments;
|
|
12960
|
+
printInfo("Wired by the Verity Claude Code plugin (not .claude/settings.json):");
|
|
12961
|
+
printInfo(" Stop hook (verity analyze): installed");
|
|
12962
|
+
printInfo(" Intent hook (verity intent capture): installed");
|
|
12963
|
+
printInfo(" Baseline hook (verity baseline capture): installed");
|
|
12964
|
+
printInfo(
|
|
12965
|
+
` Git-moment gate (verity guard): ${moments.length ? `installed [${moments.join(", ")}]` : "wired but gating nothing"}`
|
|
12966
|
+
);
|
|
12967
|
+
if (!moments.length) {
|
|
12968
|
+
printInfo(' Enable it with "verity config git-moments commit,push".');
|
|
12969
|
+
}
|
|
12970
|
+
if (status.stop || status.intent || status.baseline || status.guard) {
|
|
12971
|
+
printWarn(" Duplicate hooks also exist in .claude/settings.json. They stand down at run time,");
|
|
12972
|
+
printWarn(' but remove them with "verity init --plugin-mode" so the wiring says what it does.');
|
|
12973
|
+
}
|
|
12974
|
+
return;
|
|
12975
|
+
}
|
|
12765
12976
|
printInfo(`Stop hook (verity analyze): ${status.stop ? "installed" : "not installed"}`);
|
|
12766
12977
|
printInfo(`Intent hook (verity intent capture): ${status.intent ? "installed" : "not installed"}`);
|
|
12767
12978
|
printInfo(`Baseline hook (verity baseline capture): ${status.baseline ? "installed" : "not installed"}`);
|
|
@@ -12787,7 +12998,7 @@ var import_node_crypto8 = require("node:crypto");
|
|
|
12787
12998
|
|
|
12788
12999
|
// src/lib/conversation-buffer.ts
|
|
12789
13000
|
var import_promises5 = require("node:fs/promises");
|
|
12790
|
-
var
|
|
13001
|
+
var import_node_fs7 = require("node:fs");
|
|
12791
13002
|
var import_node_child_process5 = require("node:child_process");
|
|
12792
13003
|
var import_node_crypto = require("node:crypto");
|
|
12793
13004
|
function stripImageReferences(text) {
|
|
@@ -12823,7 +13034,7 @@ async function appendToConversationBuffer(prompt, sessionId) {
|
|
|
12823
13034
|
}
|
|
12824
13035
|
async function readAndClearConversationBuffer(currentSessionId) {
|
|
12825
13036
|
try {
|
|
12826
|
-
if ((0,
|
|
13037
|
+
if ((0, import_node_fs7.existsSync)(CONVERSATION_BUFFER_FILE)) {
|
|
12827
13038
|
const entries = await readBufferEntries();
|
|
12828
13039
|
let mine = entries;
|
|
12829
13040
|
let others = [];
|
|
@@ -12847,7 +13058,7 @@ async function readAndClearConversationBuffer(currentSessionId) {
|
|
|
12847
13058
|
};
|
|
12848
13059
|
}
|
|
12849
13060
|
}
|
|
12850
|
-
if ((0,
|
|
13061
|
+
if ((0, import_node_fs7.existsSync)(INTENT_FILE)) {
|
|
12851
13062
|
try {
|
|
12852
13063
|
const content = await (0, import_promises5.readFile)(INTENT_FILE, "utf-8");
|
|
12853
13064
|
await (0, import_promises5.unlink)(INTENT_FILE).catch(() => {
|
|
@@ -12902,88 +13113,324 @@ function getRecentCommitMessages() {
|
|
|
12902
13113
|
}
|
|
12903
13114
|
}
|
|
12904
13115
|
|
|
12905
|
-
// src/lib/
|
|
12906
|
-
var
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
"",
|
|
12912
|
-
"
|
|
12913
|
-
"
|
|
12914
|
-
"
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
"
|
|
12918
|
-
"
|
|
12919
|
-
"
|
|
12920
|
-
"
|
|
12921
|
-
"
|
|
12922
|
-
"
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
} catch {
|
|
12949
|
-
}
|
|
12950
|
-
treeKey = (0, import_node_crypto2.createHash)("sha256").update(resolved).digest("hex").slice(0, 12);
|
|
13116
|
+
// src/lib/analysis-mode.ts
|
|
13117
|
+
var DEBUG_PHRASES = [
|
|
13118
|
+
"not working",
|
|
13119
|
+
"doesn't work",
|
|
13120
|
+
"doesn't work",
|
|
13121
|
+
"does not work",
|
|
13122
|
+
"isn't working",
|
|
13123
|
+
"is not working",
|
|
13124
|
+
"can't figure out",
|
|
13125
|
+
"stack trace"
|
|
13126
|
+
];
|
|
13127
|
+
var DEBUG_WORDS = [
|
|
13128
|
+
"fix",
|
|
13129
|
+
"bug",
|
|
13130
|
+
"broken",
|
|
13131
|
+
"crash",
|
|
13132
|
+
"crashing",
|
|
13133
|
+
"failing",
|
|
13134
|
+
"debug",
|
|
13135
|
+
"debugging",
|
|
13136
|
+
"investigate",
|
|
13137
|
+
"troubleshoot",
|
|
13138
|
+
"regression",
|
|
13139
|
+
"wrong"
|
|
13140
|
+
];
|
|
13141
|
+
var DEBUG_PATTERN = new RegExp(
|
|
13142
|
+
[
|
|
13143
|
+
...DEBUG_PHRASES.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")),
|
|
13144
|
+
...DEBUG_WORDS.map((w) => `\\b${w}\\b`)
|
|
13145
|
+
].join("|"),
|
|
13146
|
+
"i"
|
|
13147
|
+
);
|
|
13148
|
+
var FALSE_POSITIVE_PATTERNS = [
|
|
13149
|
+
/\b(?:add|create|implement|write|build|design|set\s*up)\b.{0,20}\berror\b/i,
|
|
13150
|
+
/\berror\s+handling\b/i,
|
|
13151
|
+
/\berror\s+boundar(?:y|ies)\b/i,
|
|
13152
|
+
/\berror\s+(?:type|class|page|component|message|code|enum)\b/i,
|
|
13153
|
+
/\b(?:add|create|implement|write|build)\b.{0,20}\b(?:fix|debug|issue)\b/i
|
|
13154
|
+
];
|
|
13155
|
+
function hasDebugIntent(prompt) {
|
|
13156
|
+
if (!DEBUG_PATTERN.test(prompt)) return false;
|
|
13157
|
+
for (const fp of FALSE_POSITIVE_PATTERNS) {
|
|
13158
|
+
if (fp.test(prompt)) return false;
|
|
12951
13159
|
}
|
|
12952
|
-
return
|
|
12953
|
-
userKey,
|
|
12954
|
-
treeKey,
|
|
12955
|
-
sessionKey: sessionKey2,
|
|
12956
|
-
userKeySource: tokenUsable ? "token" : "ephemeral",
|
|
12957
|
-
sessionKeySource: sessionUsable ? "session_id" : "ephemeral",
|
|
12958
|
-
ephemeral: !tokenUsable || !sessionUsable
|
|
12959
|
-
};
|
|
12960
|
-
}
|
|
12961
|
-
function verityHome() {
|
|
12962
|
-
const override = process.env.VERITY_HOME;
|
|
12963
|
-
return override && override.trim() ? (0, import_node_path6.resolve)(override) : (0, import_node_path6.join)((0, import_node_os2.homedir)(), ".verity");
|
|
12964
|
-
}
|
|
12965
|
-
function dossierDir(identity) {
|
|
12966
|
-
return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
|
|
12967
|
-
}
|
|
12968
|
-
function treeDir(identity) {
|
|
12969
|
-
return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
|
|
13160
|
+
return true;
|
|
12970
13161
|
}
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
|
|
12976
|
-
return
|
|
13162
|
+
var GIT_ONLY_PATTERN = /\b(commit|push|deploy|merge|rebase|tag|release|publish|ship)\b/i;
|
|
13163
|
+
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;
|
|
13164
|
+
function isGitOnlyPrompt(prompt) {
|
|
13165
|
+
if (!GIT_ONLY_PATTERN.test(prompt)) return false;
|
|
13166
|
+
if (CODE_AUTHORING_PATTERN.test(prompt)) return false;
|
|
13167
|
+
return true;
|
|
12977
13168
|
}
|
|
12978
|
-
function
|
|
12979
|
-
|
|
13169
|
+
function reconcileAnalysisMode(predictedMode, signals) {
|
|
13170
|
+
const mode2 = resolveAnalysisMode(predictedMode, signals);
|
|
13171
|
+
if (mode2 !== "skip") return mode2;
|
|
13172
|
+
const windowIsOrphaned = signals.actionSummary?.transcript_windowed === "orphaned";
|
|
13173
|
+
if (windowIsOrphaned && !signals.sessionAuthoredCode) return "standard";
|
|
13174
|
+
return mode2;
|
|
12980
13175
|
}
|
|
12981
|
-
|
|
12982
|
-
|
|
12983
|
-
|
|
12984
|
-
|
|
12985
|
-
|
|
12986
|
-
|
|
13176
|
+
function resolveAnalysisMode(predictedMode, signals) {
|
|
13177
|
+
if (!predictedMode || !isValidMode(predictedMode)) {
|
|
13178
|
+
return detectAnalysisMode(
|
|
13179
|
+
signals.noFilesChanged,
|
|
13180
|
+
signals.assistantResponse,
|
|
13181
|
+
signals.conversationPrompts,
|
|
13182
|
+
signals.actionSummary,
|
|
13183
|
+
signals.sessionAuthoredCode
|
|
13184
|
+
);
|
|
13185
|
+
}
|
|
13186
|
+
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
|
|
13187
|
+
const agentInvestigated = didAgentInvestigate(signals.actionSummary);
|
|
13188
|
+
switch (predictedMode) {
|
|
13189
|
+
case "skip":
|
|
13190
|
+
if (agentAuthoredCode) return "standard";
|
|
13191
|
+
return "skip";
|
|
13192
|
+
case "plan":
|
|
13193
|
+
if (agentAuthoredCode) return "standard";
|
|
13194
|
+
return "plan";
|
|
13195
|
+
case "debug":
|
|
13196
|
+
return "debug";
|
|
13197
|
+
case "standard":
|
|
13198
|
+
if (!!signals.actionSummary && !agentAuthoredCode && !!signals.assistantResponse) {
|
|
13199
|
+
return agentInvestigated ? "plan" : "skip";
|
|
13200
|
+
}
|
|
13201
|
+
return "standard";
|
|
13202
|
+
}
|
|
13203
|
+
}
|
|
13204
|
+
function didAgentInvestigate(summary) {
|
|
13205
|
+
if (!summary) return false;
|
|
13206
|
+
return summary.files_read.length > 0 || summary.searches > 0 || summary.commands.length > 0 || summary.subagents > 0 || summary.web_fetches > 0;
|
|
13207
|
+
}
|
|
13208
|
+
function isValidMode(mode2) {
|
|
13209
|
+
return mode2 === "standard" || mode2 === "plan" || mode2 === "debug" || mode2 === "skip";
|
|
13210
|
+
}
|
|
13211
|
+
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
|
|
13212
|
+
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
|
|
13213
|
+
if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
|
|
13214
|
+
if (!agentAuthoredCode) return "skip";
|
|
13215
|
+
}
|
|
13216
|
+
if (noFilesChanged && !!assistantResponse && !agentAuthoredCode) {
|
|
13217
|
+
return "plan";
|
|
13218
|
+
}
|
|
13219
|
+
if (!!actionSummary && !agentAuthoredCode && !!assistantResponse) {
|
|
13220
|
+
return didAgentInvestigate(actionSummary) ? "plan" : "skip";
|
|
13221
|
+
}
|
|
13222
|
+
for (const prompt of conversationPrompts) {
|
|
13223
|
+
if (hasDebugIntent(prompt)) {
|
|
13224
|
+
return "debug";
|
|
13225
|
+
}
|
|
13226
|
+
}
|
|
13227
|
+
return "standard";
|
|
13228
|
+
}
|
|
13229
|
+
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;
|
|
13230
|
+
var GIT_PLUMBING_RE = /^\s*git\s+(?:merge|rebase|stash|cherry-pick|revert|pull|fetch|checkout|switch|reset|restore|clean)\b/i;
|
|
13231
|
+
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;
|
|
13232
|
+
var CHAIN_RE = /&&|\||;|\$\(|\x60/;
|
|
13233
|
+
function isNonAuthoringCommand(cmd) {
|
|
13234
|
+
if (typeof cmd !== "string" || cmd.trim().length === 0) return false;
|
|
13235
|
+
if (FILE_MUTATE_RE.test(cmd)) return false;
|
|
13236
|
+
if (CHAIN_RE.test(cmd)) return false;
|
|
13237
|
+
return GIT_PLUMBING_RE.test(cmd) || READ_ONLY_RE.test(cmd);
|
|
13238
|
+
}
|
|
13239
|
+
function hasNonEditAuthorship(actionSummary, sessionAuthoredCode) {
|
|
13240
|
+
if (!actionSummary) return sessionAuthoredCode;
|
|
13241
|
+
if ((actionSummary.subagents ?? 0) > 0) return true;
|
|
13242
|
+
if (Object.keys(actionSummary.tool_counts ?? {}).some((t) => t.startsWith("mcp__"))) return true;
|
|
13243
|
+
const commands = actionSummary.commands ?? [];
|
|
13244
|
+
if (commands.some((c) => FILE_MUTATE_RE.test(c))) return true;
|
|
13245
|
+
if (sessionAuthoredCode) {
|
|
13246
|
+
const allSafe = commands.length > 0 && commands.every(isNonAuthoringCommand);
|
|
13247
|
+
if (!allSafe) return true;
|
|
13248
|
+
}
|
|
13249
|
+
return false;
|
|
13250
|
+
}
|
|
13251
|
+
function scopeToAuthored(files, actionSummary) {
|
|
13252
|
+
if (!actionSummary) return { files, signal: "no-transcript" };
|
|
13253
|
+
const touched = [...actionSummary.files_edited ?? [], ...actionSummary.files_created ?? []];
|
|
13254
|
+
if (touched.length === 0) return { files: [], signal: "none-authored" };
|
|
13255
|
+
return { files: narrowToAgentAuthored(files, actionSummary), signal: "authored" };
|
|
13256
|
+
}
|
|
13257
|
+
function chooseReviewScope(input) {
|
|
13258
|
+
if (input.narrowingIsTrustworthy) return input.scopedFiles;
|
|
13259
|
+
if (input.recoveredScope.length > 0) return input.recoveredScope;
|
|
13260
|
+
return input.authorshipWasObservable ? [] : input.allForReview;
|
|
13261
|
+
}
|
|
13262
|
+
function narrowToAgentAuthored(files, actionSummary) {
|
|
13263
|
+
if (!actionSummary) return files;
|
|
13264
|
+
const touched = [
|
|
13265
|
+
...actionSummary.files_edited,
|
|
13266
|
+
...actionSummary.files_created
|
|
13267
|
+
];
|
|
13268
|
+
if (touched.length === 0) return files;
|
|
13269
|
+
return files.filter((f) => {
|
|
13270
|
+
const suffix = "/" + f;
|
|
13271
|
+
return touched.some((t) => t === f || t.endsWith(suffix));
|
|
13272
|
+
});
|
|
13273
|
+
}
|
|
13274
|
+
|
|
13275
|
+
// src/lib/skip-detection.ts
|
|
13276
|
+
function isBareAckPrompt(prompt) {
|
|
13277
|
+
if (typeof prompt !== "string") return false;
|
|
13278
|
+
const trimmed = prompt.trim();
|
|
13279
|
+
if (trimmed.length === 0) return false;
|
|
13280
|
+
if (trimmed.length > 20) return false;
|
|
13281
|
+
const bareAckPattern = /^(\d{1,2}|y|n|yes|no|yep|nope|ok(ay)?|sure|skip|cancel|stop|done|noted|got\s+it|sounds\s+good|thanks|thank\s+you|thx)[.!?]*$/i;
|
|
13282
|
+
return bareAckPattern.test(trimmed);
|
|
13283
|
+
}
|
|
13284
|
+
function isContinuationPrompt(prompt) {
|
|
13285
|
+
if (typeof prompt !== "string") return false;
|
|
13286
|
+
const trimmed = prompt.trim();
|
|
13287
|
+
if (trimmed.length === 0) return false;
|
|
13288
|
+
if (trimmed.length > 24) return false;
|
|
13289
|
+
const continuation = /^(let['’]?s\s+(go|do\s+it|start|continue)|go|go\s+ahead|go\s+on|proceed|continue|carry\s+on|keep\s+going|do\s+it|make\s+it\s+so|next|start|begin|ship\s+it|yes\s+please|please\s+continue|perfect|great|nice|excellent|agreed)[.!]*$/i;
|
|
13290
|
+
return continuation.test(trimmed) || isBareAckPrompt(trimmed);
|
|
13291
|
+
}
|
|
13292
|
+
function isSlashCommand(prompt) {
|
|
13293
|
+
if (typeof prompt !== "string") return false;
|
|
13294
|
+
return /^\s*\/[A-Za-z][\w-]*(\s|$)/.test(prompt);
|
|
13295
|
+
}
|
|
13296
|
+
function resolveGoalPrompt(prompts) {
|
|
13297
|
+
if (prompts.length === 0) return null;
|
|
13298
|
+
const latest = prompts[prompts.length - 1];
|
|
13299
|
+
if (!isContinuationPrompt(latest.prompt)) return { entry: latest, turnsBack: 0 };
|
|
13300
|
+
for (let i = prompts.length - 2; i >= 0; i--) {
|
|
13301
|
+
if (!isContinuationPrompt(prompts[i].prompt)) {
|
|
13302
|
+
return { entry: prompts[i], turnsBack: prompts.length - 1 - i };
|
|
13303
|
+
}
|
|
13304
|
+
}
|
|
13305
|
+
return { entry: latest, turnsBack: 0 };
|
|
13306
|
+
}
|
|
13307
|
+
function isReflectionQuestion(response) {
|
|
13308
|
+
if (!response || typeof response !== "string") return false;
|
|
13309
|
+
const markers = [
|
|
13310
|
+
/reflection\s+for\s+future\s+agents/i,
|
|
13311
|
+
/what(?:'s|\s+is)\s+one\s+thing\s+you\s+learned/i,
|
|
13312
|
+
/say\s+['"]?skip['"]?\s+to\s+skip/i,
|
|
13313
|
+
/quick\s+reflection\s+question/i,
|
|
13314
|
+
// Post-flip (VRT-21): the agent drafts the reflection itself and, when
|
|
13315
|
+
// interactive, asks the user to confirm/correct before recording. That
|
|
13316
|
+
// turn authors no code either, so it's still a reflection turn.
|
|
13317
|
+
/reflection\s+draft/i,
|
|
13318
|
+
/confirm,?\s+correct,?\s+or\s+add/i
|
|
13319
|
+
];
|
|
13320
|
+
return markers.some((m) => m.test(response));
|
|
13321
|
+
}
|
|
13322
|
+
function isMetaTaskLabel(label2) {
|
|
13323
|
+
if (label2 === null || label2 === void 0) return false;
|
|
13324
|
+
if (typeof label2 !== "string") return false;
|
|
13325
|
+
const trimmed = label2.trim();
|
|
13326
|
+
if (trimmed.length === 0) return true;
|
|
13327
|
+
const metaPatterns = [
|
|
13328
|
+
/^verity\s+[\w-]+\s+response$/i,
|
|
13329
|
+
// "Verity reflect response"
|
|
13330
|
+
/^simple user response$/i,
|
|
13331
|
+
/^verity\s+command$/i,
|
|
13332
|
+
// "Verity command"
|
|
13333
|
+
/^user\s+(question|reply|response|ack)$/i
|
|
13334
|
+
];
|
|
13335
|
+
return metaPatterns.some((p) => p.test(trimmed));
|
|
13336
|
+
}
|
|
13337
|
+
function shouldSkipForBareAck(input) {
|
|
13338
|
+
if (!isBareAckPrompt(input.prompt)) return false;
|
|
13339
|
+
if (input.turnAuthoredCode) return false;
|
|
13340
|
+
return input.canSeeTurnAuthorship;
|
|
13341
|
+
}
|
|
13342
|
+
function isCommandOnlyTurn(input) {
|
|
13343
|
+
if (!input.authorshipIsObservable) return false;
|
|
13344
|
+
if (input.userCommandsTruncated) return false;
|
|
13345
|
+
const commands = input.userCommands ?? [];
|
|
13346
|
+
if (commands.length === 0) return false;
|
|
13347
|
+
if (input.agentAuthoredFiles > 0) return false;
|
|
13348
|
+
if (input.agentToolCalls > 0) return false;
|
|
13349
|
+
return commands.every(isNonAuthoringCommand);
|
|
13350
|
+
}
|
|
13351
|
+
|
|
13352
|
+
// src/lib/context-identity.ts
|
|
13353
|
+
var import_node_crypto2 = require("node:crypto");
|
|
13354
|
+
var import_node_fs8 = require("node:fs");
|
|
13355
|
+
var import_node_os2 = require("node:os");
|
|
13356
|
+
var import_node_path6 = require("node:path");
|
|
13357
|
+
var SHARED_SENTINELS = /* @__PURE__ */ new Set([
|
|
13358
|
+
"",
|
|
13359
|
+
"-",
|
|
13360
|
+
"n/a",
|
|
13361
|
+
"na",
|
|
13362
|
+
"none",
|
|
13363
|
+
"null",
|
|
13364
|
+
"undefined",
|
|
13365
|
+
"unknown",
|
|
13366
|
+
"anon",
|
|
13367
|
+
"anonymous",
|
|
13368
|
+
"default",
|
|
13369
|
+
"_default"
|
|
13370
|
+
]);
|
|
13371
|
+
function isSharedSentinel(value) {
|
|
13372
|
+
if (value == null) return true;
|
|
13373
|
+
return SHARED_SENTINELS.has(value.trim().toLowerCase());
|
|
13374
|
+
}
|
|
13375
|
+
var ephemeral = /* @__PURE__ */ new Map();
|
|
13376
|
+
function ephemeralId(slot, width) {
|
|
13377
|
+
let id = ephemeral.get(slot);
|
|
13378
|
+
if (!id) {
|
|
13379
|
+
id = `eph-${(0, import_node_crypto2.randomBytes)(Math.ceil(width / 2)).toString("hex").slice(0, width - 4)}`;
|
|
13380
|
+
ephemeral.set(slot, id);
|
|
13381
|
+
}
|
|
13382
|
+
return id;
|
|
13383
|
+
}
|
|
13384
|
+
function contextIdentity(input) {
|
|
13385
|
+
const tokenUsable = !isSharedSentinel(input.token ?? null);
|
|
13386
|
+
const sessionUsable = !isSharedSentinel(input.sessionId ?? null);
|
|
13387
|
+
const userKey = tokenUsable ? (0, import_node_crypto2.createHash)("sha256").update(String(input.token)).digest("hex").slice(0, 12) : ephemeralId("user", 12);
|
|
13388
|
+
const sessionKey2 = sessionUsable ? (0, import_node_crypto2.createHash)("sha256").update(String(input.sessionId)).digest("hex").slice(0, 16) : ephemeralId("session", 16);
|
|
13389
|
+
let treeKey = "no-tree";
|
|
13390
|
+
const rawTree = input.treeRoot;
|
|
13391
|
+
if (rawTree && !isSharedSentinel(rawTree)) {
|
|
13392
|
+
let resolved = rawTree;
|
|
13393
|
+
try {
|
|
13394
|
+
resolved = import_node_fs8.realpathSync.native(rawTree);
|
|
13395
|
+
} catch {
|
|
13396
|
+
}
|
|
13397
|
+
treeKey = (0, import_node_crypto2.createHash)("sha256").update(resolved).digest("hex").slice(0, 12);
|
|
13398
|
+
}
|
|
13399
|
+
return {
|
|
13400
|
+
userKey,
|
|
13401
|
+
treeKey,
|
|
13402
|
+
sessionKey: sessionKey2,
|
|
13403
|
+
userKeySource: tokenUsable ? "token" : "ephemeral",
|
|
13404
|
+
sessionKeySource: sessionUsable ? "session_id" : "ephemeral",
|
|
13405
|
+
ephemeral: !tokenUsable || !sessionUsable
|
|
13406
|
+
};
|
|
13407
|
+
}
|
|
13408
|
+
function verityHome() {
|
|
13409
|
+
const override = process.env.VERITY_HOME;
|
|
13410
|
+
return override && override.trim() ? (0, import_node_path6.resolve)(override) : (0, import_node_path6.join)((0, import_node_os2.homedir)(), ".verity");
|
|
13411
|
+
}
|
|
13412
|
+
function dossierDir(identity) {
|
|
13413
|
+
return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey, identity.sessionKey);
|
|
13414
|
+
}
|
|
13415
|
+
function treeDir(identity) {
|
|
13416
|
+
return (0, import_node_path6.join)(verityHome(), "sessions", identity.userKey, identity.treeKey);
|
|
13417
|
+
}
|
|
13418
|
+
function scopeIdentity(token, sessionId) {
|
|
13419
|
+
const t = (token ?? "").trim();
|
|
13420
|
+
const s = (sessionId ?? "").trim();
|
|
13421
|
+
const userKey = t.length > 0 ? (0, import_node_crypto2.createHash)("sha256").update(t).digest("hex").slice(0, 12) : "anon";
|
|
13422
|
+
const sessionKey2 = s.length > 0 ? (0, import_node_crypto2.createHash)("sha256").update(s).digest("hex").slice(0, 16) : "_default";
|
|
13423
|
+
return { userKey, sessionKey: sessionKey2, bucket: `${userKey}/${sessionKey2}` };
|
|
13424
|
+
}
|
|
13425
|
+
function sessionScopeKey(token, sessionId) {
|
|
13426
|
+
return scopeIdentity(token, sessionId).bucket;
|
|
13427
|
+
}
|
|
13428
|
+
|
|
13429
|
+
// src/lib/task-context-buffer.ts
|
|
13430
|
+
var import_promises6 = require("node:fs/promises");
|
|
13431
|
+
var import_node_fs9 = require("node:fs");
|
|
13432
|
+
var import_node_path7 = require("node:path");
|
|
13433
|
+
var TASK_CONTEXT_DIR = `${VERITY_DIR}/.task-context`;
|
|
12987
13434
|
var MAX_BUFFER_BYTES = 500 * 1024;
|
|
12988
13435
|
var MAX_PROMPT_CHARS = 2e3;
|
|
12989
13436
|
var MAX_RESPONSE_CHARS = 4e3;
|
|
@@ -13021,7 +13468,7 @@ async function appendResponseToTaskBuffer(taskId, assistantResponse, actionSumma
|
|
|
13021
13468
|
}
|
|
13022
13469
|
async function readTaskContextBuffer(taskId) {
|
|
13023
13470
|
const filePath = bufferPath(taskId);
|
|
13024
|
-
if (!(0,
|
|
13471
|
+
if (!(0, import_node_fs9.existsSync)(filePath)) return null;
|
|
13025
13472
|
try {
|
|
13026
13473
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
13027
13474
|
if (!content.trim()) return null;
|
|
@@ -13055,7 +13502,7 @@ async function readTaskContextBuffer(taskId) {
|
|
|
13055
13502
|
}
|
|
13056
13503
|
async function cleanupTaskContextBuffers() {
|
|
13057
13504
|
try {
|
|
13058
|
-
if (!(0,
|
|
13505
|
+
if (!(0, import_node_fs9.existsSync)(TASK_CONTEXT_DIR)) return;
|
|
13059
13506
|
const files = await (0, import_promises6.readdir)(TASK_CONTEXT_DIR);
|
|
13060
13507
|
const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
13061
13508
|
for (const file of files) {
|
|
@@ -13080,7 +13527,7 @@ async function appendEntry(taskId, entry) {
|
|
|
13080
13527
|
try {
|
|
13081
13528
|
await (0, import_promises6.mkdir)(TASK_CONTEXT_DIR, { recursive: true });
|
|
13082
13529
|
const filePath = bufferPath(taskId);
|
|
13083
|
-
if ((0,
|
|
13530
|
+
if ((0, import_node_fs9.existsSync)(filePath)) {
|
|
13084
13531
|
const stats = await (0, import_promises6.stat)(filePath);
|
|
13085
13532
|
if (stats.size >= MAX_BUFFER_BYTES) {
|
|
13086
13533
|
const content = await (0, import_promises6.readFile)(filePath, "utf-8");
|
|
@@ -13091,7 +13538,7 @@ async function appendEntry(taskId, entry) {
|
|
|
13091
13538
|
}
|
|
13092
13539
|
}
|
|
13093
13540
|
const line = JSON.stringify(entry) + "\n";
|
|
13094
|
-
const existing = (0,
|
|
13541
|
+
const existing = (0, import_node_fs9.existsSync)(filePath) ? await (0, import_promises6.readFile)(filePath, "utf-8") : "";
|
|
13095
13542
|
await (0, import_promises6.writeFile)(filePath, existing + line);
|
|
13096
13543
|
} catch {
|
|
13097
13544
|
}
|
|
@@ -13099,7 +13546,7 @@ async function appendEntry(taskId, entry) {
|
|
|
13099
13546
|
|
|
13100
13547
|
// src/lib/memory-retrieval.ts
|
|
13101
13548
|
var import_promises7 = require("node:fs/promises");
|
|
13102
|
-
var
|
|
13549
|
+
var import_node_fs10 = require("node:fs");
|
|
13103
13550
|
var import_node_path8 = require("node:path");
|
|
13104
13551
|
var memoryDir = () => projectPath(`${VERITY_DIR}/memory`);
|
|
13105
13552
|
var DOMAINS = ["decisions", "quality", "security", "intent", "gotchas", "patterns", "domain", "integrations"];
|
|
@@ -13193,13 +13640,13 @@ function parseFrontmatter(content) {
|
|
|
13193
13640
|
return { fm, body: match[2].trim() };
|
|
13194
13641
|
}
|
|
13195
13642
|
async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = DEFAULT_BUDGET_TOKENS) {
|
|
13196
|
-
if (!(0,
|
|
13643
|
+
if (!(0, import_node_fs10.existsSync)(memoryDir())) return null;
|
|
13197
13644
|
const budget = Math.min(budgetTokens, MAX_BUDGET_TOKENS);
|
|
13198
13645
|
const promptTokens = tokenize(promptText);
|
|
13199
13646
|
const nodes = [];
|
|
13200
13647
|
for (const domain of DOMAINS) {
|
|
13201
13648
|
const domainDir = (0, import_node_path8.join)(memoryDir(), domain);
|
|
13202
|
-
if (!(0,
|
|
13649
|
+
if (!(0, import_node_fs10.existsSync)(domainDir)) continue;
|
|
13203
13650
|
try {
|
|
13204
13651
|
const files = await (0, import_promises7.readdir)(domainDir);
|
|
13205
13652
|
for (const file of files) {
|
|
@@ -13264,12 +13711,12 @@ async function retrieveForInjection(promptText, taskFiles = [], budgetTokens = D
|
|
|
13264
13711
|
|
|
13265
13712
|
// src/lib/memory-sync.ts
|
|
13266
13713
|
var import_promises8 = require("node:fs/promises");
|
|
13267
|
-
var
|
|
13714
|
+
var import_node_fs12 = require("node:fs");
|
|
13268
13715
|
var import_node_path10 = require("node:path");
|
|
13269
13716
|
var import_node_crypto3 = require("node:crypto");
|
|
13270
13717
|
|
|
13271
13718
|
// src/lib/safe-path.ts
|
|
13272
|
-
var
|
|
13719
|
+
var import_node_fs11 = require("node:fs");
|
|
13273
13720
|
var import_node_path9 = require("node:path");
|
|
13274
13721
|
function resolveInside(baseDir, candidate) {
|
|
13275
13722
|
if (typeof candidate !== "string" || candidate.length === 0) return null;
|
|
@@ -13279,16 +13726,16 @@ function resolveInside(baseDir, candidate) {
|
|
|
13279
13726
|
const baseSep = baseAbs.endsWith(import_node_path9.sep) ? baseAbs : baseAbs + import_node_path9.sep;
|
|
13280
13727
|
if (full !== baseAbs && !full.startsWith(baseSep)) return null;
|
|
13281
13728
|
try {
|
|
13282
|
-
if ((0,
|
|
13283
|
-
const realBase = (0,
|
|
13729
|
+
if ((0, import_node_fs11.existsSync)(baseAbs)) {
|
|
13730
|
+
const realBase = (0, import_node_fs11.realpathSync)(baseAbs);
|
|
13284
13731
|
const realBaseSep = realBase.endsWith(import_node_path9.sep) ? realBase : realBase + import_node_path9.sep;
|
|
13285
13732
|
let probe = full;
|
|
13286
|
-
while (!(0,
|
|
13733
|
+
while (!(0, import_node_fs11.existsSync)(probe)) {
|
|
13287
13734
|
const parent = (0, import_node_path9.dirname)(probe);
|
|
13288
13735
|
if (parent === probe) break;
|
|
13289
13736
|
probe = parent;
|
|
13290
13737
|
}
|
|
13291
|
-
const realProbe = (0,
|
|
13738
|
+
const realProbe = (0, import_node_fs11.realpathSync)(probe);
|
|
13292
13739
|
if (realProbe !== realBase && !realProbe.startsWith(realBaseSep)) return null;
|
|
13293
13740
|
}
|
|
13294
13741
|
} catch {
|
|
@@ -13367,24 +13814,24 @@ async function ensureMemoryDir() {
|
|
|
13367
13814
|
for (const domain of DOMAINS2) {
|
|
13368
13815
|
await (0, import_promises8.mkdir)((0, import_node_path10.join)(memoryDir2(), domain), { recursive: true });
|
|
13369
13816
|
}
|
|
13370
|
-
if (!(0,
|
|
13817
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"))) {
|
|
13371
13818
|
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "SCHEMA.md"), SCHEMA_TEMPLATE);
|
|
13372
13819
|
}
|
|
13373
|
-
if (!(0,
|
|
13820
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "index.md"))) {
|
|
13374
13821
|
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "index.md"), "# Project Memory Index\n\nNo nodes yet. Run an analysis to start building the knowledge graph.\n");
|
|
13375
13822
|
}
|
|
13376
|
-
if (!(0,
|
|
13823
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md"))) {
|
|
13377
13824
|
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "# Memory Log\n\n");
|
|
13378
13825
|
}
|
|
13379
13826
|
}
|
|
13380
13827
|
async function buildManifest() {
|
|
13381
|
-
if (!(0,
|
|
13828
|
+
if (!(0, import_node_fs12.existsSync)(memoryDir2())) {
|
|
13382
13829
|
return { schema_version: 1, nodes: [], index_hash: null, log_length: 0 };
|
|
13383
13830
|
}
|
|
13384
13831
|
const nodes = [];
|
|
13385
13832
|
for (const domain of DOMAINS2) {
|
|
13386
13833
|
const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
|
|
13387
|
-
if (!(0,
|
|
13834
|
+
if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
|
|
13388
13835
|
try {
|
|
13389
13836
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
13390
13837
|
for (const file of files) {
|
|
@@ -13420,10 +13867,10 @@ function hashContent(content) {
|
|
|
13420
13867
|
}
|
|
13421
13868
|
async function readOnDiskNodes() {
|
|
13422
13869
|
const out = /* @__PURE__ */ new Map();
|
|
13423
|
-
if (!(0,
|
|
13870
|
+
if (!(0, import_node_fs12.existsSync)(memoryDir2())) return out;
|
|
13424
13871
|
for (const domain of DOMAINS2) {
|
|
13425
13872
|
const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
|
|
13426
|
-
if (!(0,
|
|
13873
|
+
if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
|
|
13427
13874
|
try {
|
|
13428
13875
|
for (const file of await (0, import_promises8.readdir)(domainDir)) {
|
|
13429
13876
|
if (!file.endsWith(".md")) continue;
|
|
@@ -13475,7 +13922,7 @@ async function computeEditedNodeUploads() {
|
|
|
13475
13922
|
for (const [path, prevHash] of prev) {
|
|
13476
13923
|
if (prevHash == null) continue;
|
|
13477
13924
|
const full = (0, import_node_path10.join)(memoryDir2(), path);
|
|
13478
|
-
if (!(0,
|
|
13925
|
+
if (!(0, import_node_fs12.existsSync)(full)) continue;
|
|
13479
13926
|
let content;
|
|
13480
13927
|
try {
|
|
13481
13928
|
content = await (0, import_promises8.readFile)(full, "utf-8");
|
|
@@ -13511,7 +13958,7 @@ async function applyMemoryWrites(writes, opts = {}) {
|
|
|
13511
13958
|
const logLines = [`- ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)} \u2014 Applied ${count} write(s) from server`];
|
|
13512
13959
|
for (const n of notes) logLines.push(` - ${n}`);
|
|
13513
13960
|
try {
|
|
13514
|
-
const existing = (0,
|
|
13961
|
+
const existing = (0, import_node_fs12.existsSync)((0, import_node_path10.join)(memoryDir2(), "log.md")) ? await (0, import_promises8.readFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), "utf-8") : "# Memory Log\n\n";
|
|
13515
13962
|
await (0, import_promises8.writeFile)((0, import_node_path10.join)(memoryDir2(), "log.md"), existing + logLines.join("\n") + "\n");
|
|
13516
13963
|
} catch {
|
|
13517
13964
|
}
|
|
@@ -13532,7 +13979,7 @@ async function applyOneWrite(write, treePaths) {
|
|
|
13532
13979
|
notes.push(`${write.path}: dropped unmatched file_globs [${grounded.dropped.join(", ")}]`);
|
|
13533
13980
|
}
|
|
13534
13981
|
}
|
|
13535
|
-
if ((0,
|
|
13982
|
+
if ((0, import_node_fs12.existsSync)(fullPath)) {
|
|
13536
13983
|
let existing = "";
|
|
13537
13984
|
try {
|
|
13538
13985
|
existing = await (0, import_promises8.readFile)(fullPath, "utf-8");
|
|
@@ -13586,7 +14033,7 @@ async function regenerateIndex() {
|
|
|
13586
14033
|
let totalNodes = 0;
|
|
13587
14034
|
for (const domain of DOMAINS2.filter((d) => d !== "_archive")) {
|
|
13588
14035
|
const domainDir = (0, import_node_path10.join)(memoryDir2(), domain);
|
|
13589
|
-
if (!(0,
|
|
14036
|
+
if (!(0, import_node_fs12.existsSync)(domainDir)) continue;
|
|
13590
14037
|
try {
|
|
13591
14038
|
const files = await (0, import_promises8.readdir)(domainDir);
|
|
13592
14039
|
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
@@ -13856,7 +14303,7 @@ function hasLegacyMemoryBlock(text) {
|
|
|
13856
14303
|
async function ensureClaudeMdPointer(cwd = repoRoot()) {
|
|
13857
14304
|
const claudeMdPath = (0, import_node_path10.join)(cwd, "CLAUDE.md");
|
|
13858
14305
|
let existing = "";
|
|
13859
|
-
if ((0,
|
|
14306
|
+
if ((0, import_node_fs12.existsSync)(claudeMdPath)) {
|
|
13860
14307
|
existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
|
|
13861
14308
|
}
|
|
13862
14309
|
let startTag = CLAUDE_MD_START;
|
|
@@ -13963,266 +14410,39 @@ source: extractor | user | imported
|
|
|
13963
14410
|
|
|
13964
14411
|
# Title
|
|
13965
14412
|
|
|
13966
|
-
Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
13967
|
-
\`\`\`
|
|
13968
|
-
|
|
13969
|
-
## Edge types
|
|
13970
|
-
|
|
13971
|
-
| Edge | Meaning |
|
|
13972
|
-
|------|---------|
|
|
13973
|
-
| related | Loose association |
|
|
13974
|
-
| supersedes | A replaces B |
|
|
13975
|
-
| contradicts | A and B disagree |
|
|
13976
|
-
| caused_by | Something in B led to A |
|
|
13977
|
-
| example_of | A is an instance of B |
|
|
13978
|
-
|
|
13979
|
-
## Domains
|
|
13980
|
-
|
|
13981
|
-
| Directory | Purpose |
|
|
13982
|
-
|-----------|---------|
|
|
13983
|
-
| decisions/ | Architectural choices (ADR-style) |
|
|
13984
|
-
| quality/ | Quality patterns |
|
|
13985
|
-
| security/ | Security constraints |
|
|
13986
|
-
| intent/ | Intent templates |
|
|
13987
|
-
| gotchas/ | Footguns and surprises |
|
|
13988
|
-
| patterns/ | Code conventions |
|
|
13989
|
-
| domain/ | Business logic concepts |
|
|
13990
|
-
| integrations/ | External system knowledge |
|
|
13991
|
-
| _archive/ | Superseded nodes |
|
|
13992
|
-
`;
|
|
13993
|
-
|
|
13994
|
-
// src/lib/dossier-session.ts
|
|
13995
|
-
var
|
|
13996
|
-
var import_node_crypto7 = require("node:crypto");
|
|
13997
|
-
var import_node_path13 = require("node:path");
|
|
13998
|
-
|
|
13999
|
-
// src/lib/analysis-mode.ts
|
|
14000
|
-
var DEBUG_PHRASES = [
|
|
14001
|
-
"not working",
|
|
14002
|
-
"doesn't work",
|
|
14003
|
-
"doesn't work",
|
|
14004
|
-
"does not work",
|
|
14005
|
-
"isn't working",
|
|
14006
|
-
"is not working",
|
|
14007
|
-
"can't figure out",
|
|
14008
|
-
"stack trace"
|
|
14009
|
-
];
|
|
14010
|
-
var DEBUG_WORDS = [
|
|
14011
|
-
"fix",
|
|
14012
|
-
"bug",
|
|
14013
|
-
"broken",
|
|
14014
|
-
"crash",
|
|
14015
|
-
"crashing",
|
|
14016
|
-
"failing",
|
|
14017
|
-
"debug",
|
|
14018
|
-
"debugging",
|
|
14019
|
-
"investigate",
|
|
14020
|
-
"troubleshoot",
|
|
14021
|
-
"regression",
|
|
14022
|
-
"wrong"
|
|
14023
|
-
];
|
|
14024
|
-
var DEBUG_PATTERN = new RegExp(
|
|
14025
|
-
[
|
|
14026
|
-
...DEBUG_PHRASES.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")),
|
|
14027
|
-
...DEBUG_WORDS.map((w) => `\\b${w}\\b`)
|
|
14028
|
-
].join("|"),
|
|
14029
|
-
"i"
|
|
14030
|
-
);
|
|
14031
|
-
var FALSE_POSITIVE_PATTERNS = [
|
|
14032
|
-
/\b(?:add|create|implement|write|build|design|set\s*up)\b.{0,20}\berror\b/i,
|
|
14033
|
-
/\berror\s+handling\b/i,
|
|
14034
|
-
/\berror\s+boundar(?:y|ies)\b/i,
|
|
14035
|
-
/\berror\s+(?:type|class|page|component|message|code|enum)\b/i,
|
|
14036
|
-
/\b(?:add|create|implement|write|build)\b.{0,20}\b(?:fix|debug|issue)\b/i
|
|
14037
|
-
];
|
|
14038
|
-
function hasDebugIntent(prompt) {
|
|
14039
|
-
if (!DEBUG_PATTERN.test(prompt)) return false;
|
|
14040
|
-
for (const fp of FALSE_POSITIVE_PATTERNS) {
|
|
14041
|
-
if (fp.test(prompt)) return false;
|
|
14042
|
-
}
|
|
14043
|
-
return true;
|
|
14044
|
-
}
|
|
14045
|
-
var GIT_ONLY_PATTERN = /\b(commit|push|deploy|merge|rebase|tag|release|publish|ship)\b/i;
|
|
14046
|
-
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;
|
|
14047
|
-
function isGitOnlyPrompt(prompt) {
|
|
14048
|
-
if (!GIT_ONLY_PATTERN.test(prompt)) return false;
|
|
14049
|
-
if (CODE_AUTHORING_PATTERN.test(prompt)) return false;
|
|
14050
|
-
return true;
|
|
14051
|
-
}
|
|
14052
|
-
function reconcileAnalysisMode(predictedMode, signals) {
|
|
14053
|
-
const mode2 = resolveAnalysisMode(predictedMode, signals);
|
|
14054
|
-
if (mode2 !== "skip") return mode2;
|
|
14055
|
-
const windowIsOrphaned = signals.actionSummary?.transcript_windowed === "orphaned";
|
|
14056
|
-
if (windowIsOrphaned && !signals.sessionAuthoredCode) return "standard";
|
|
14057
|
-
return mode2;
|
|
14058
|
-
}
|
|
14059
|
-
function resolveAnalysisMode(predictedMode, signals) {
|
|
14060
|
-
if (!predictedMode || !isValidMode(predictedMode)) {
|
|
14061
|
-
return detectAnalysisMode(
|
|
14062
|
-
signals.noFilesChanged,
|
|
14063
|
-
signals.assistantResponse,
|
|
14064
|
-
signals.conversationPrompts,
|
|
14065
|
-
signals.actionSummary,
|
|
14066
|
-
signals.sessionAuthoredCode
|
|
14067
|
-
);
|
|
14068
|
-
}
|
|
14069
|
-
const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
|
|
14070
|
-
const agentInvestigated = didAgentInvestigate(signals.actionSummary);
|
|
14071
|
-
switch (predictedMode) {
|
|
14072
|
-
case "skip":
|
|
14073
|
-
if (agentAuthoredCode) return "standard";
|
|
14074
|
-
return "skip";
|
|
14075
|
-
case "plan":
|
|
14076
|
-
if (agentAuthoredCode) return "standard";
|
|
14077
|
-
return "plan";
|
|
14078
|
-
case "debug":
|
|
14079
|
-
return "debug";
|
|
14080
|
-
case "standard":
|
|
14081
|
-
if (!!signals.actionSummary && !agentAuthoredCode && !!signals.assistantResponse) {
|
|
14082
|
-
return agentInvestigated ? "plan" : "skip";
|
|
14083
|
-
}
|
|
14084
|
-
return "standard";
|
|
14085
|
-
}
|
|
14086
|
-
}
|
|
14087
|
-
function didAgentInvestigate(summary) {
|
|
14088
|
-
if (!summary) return false;
|
|
14089
|
-
return summary.files_read.length > 0 || summary.searches > 0 || summary.commands.length > 0 || summary.subagents > 0 || summary.web_fetches > 0;
|
|
14090
|
-
}
|
|
14091
|
-
function isValidMode(mode2) {
|
|
14092
|
-
return mode2 === "standard" || mode2 === "plan" || mode2 === "debug" || mode2 === "skip";
|
|
14093
|
-
}
|
|
14094
|
-
function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
|
|
14095
|
-
const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
|
|
14096
|
-
if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
|
|
14097
|
-
if (!agentAuthoredCode) return "skip";
|
|
14098
|
-
}
|
|
14099
|
-
if (noFilesChanged && !!assistantResponse && !agentAuthoredCode) {
|
|
14100
|
-
return "plan";
|
|
14101
|
-
}
|
|
14102
|
-
if (!!actionSummary && !agentAuthoredCode && !!assistantResponse) {
|
|
14103
|
-
return didAgentInvestigate(actionSummary) ? "plan" : "skip";
|
|
14104
|
-
}
|
|
14105
|
-
for (const prompt of conversationPrompts) {
|
|
14106
|
-
if (hasDebugIntent(prompt)) {
|
|
14107
|
-
return "debug";
|
|
14108
|
-
}
|
|
14109
|
-
}
|
|
14110
|
-
return "standard";
|
|
14111
|
-
}
|
|
14112
|
-
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;
|
|
14113
|
-
var GIT_PLUMBING_RE = /^\s*git\s+(?:merge|rebase|stash|cherry-pick|revert|pull|fetch|checkout|switch|reset|restore|clean)\b/i;
|
|
14114
|
-
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;
|
|
14115
|
-
var CHAIN_RE = /&&|\||;|\$\(|\x60/;
|
|
14116
|
-
function isNonAuthoringCommand(cmd) {
|
|
14117
|
-
if (typeof cmd !== "string" || cmd.trim().length === 0) return false;
|
|
14118
|
-
if (FILE_MUTATE_RE.test(cmd)) return false;
|
|
14119
|
-
if (CHAIN_RE.test(cmd)) return false;
|
|
14120
|
-
return GIT_PLUMBING_RE.test(cmd) || READ_ONLY_RE.test(cmd);
|
|
14121
|
-
}
|
|
14122
|
-
function hasNonEditAuthorship(actionSummary, sessionAuthoredCode) {
|
|
14123
|
-
if (!actionSummary) return sessionAuthoredCode;
|
|
14124
|
-
if ((actionSummary.subagents ?? 0) > 0) return true;
|
|
14125
|
-
if (Object.keys(actionSummary.tool_counts ?? {}).some((t) => t.startsWith("mcp__"))) return true;
|
|
14126
|
-
const commands = actionSummary.commands ?? [];
|
|
14127
|
-
if (commands.some((c) => FILE_MUTATE_RE.test(c))) return true;
|
|
14128
|
-
if (sessionAuthoredCode) {
|
|
14129
|
-
const allSafe = commands.length > 0 && commands.every(isNonAuthoringCommand);
|
|
14130
|
-
if (!allSafe) return true;
|
|
14131
|
-
}
|
|
14132
|
-
return false;
|
|
14133
|
-
}
|
|
14134
|
-
function scopeToAuthored(files, actionSummary) {
|
|
14135
|
-
if (!actionSummary) return { files, signal: "no-transcript" };
|
|
14136
|
-
const touched = [...actionSummary.files_edited ?? [], ...actionSummary.files_created ?? []];
|
|
14137
|
-
if (touched.length === 0) return { files: [], signal: "none-authored" };
|
|
14138
|
-
return { files: narrowToAgentAuthored(files, actionSummary), signal: "authored" };
|
|
14139
|
-
}
|
|
14140
|
-
function narrowToAgentAuthored(files, actionSummary) {
|
|
14141
|
-
if (!actionSummary) return files;
|
|
14142
|
-
const touched = [
|
|
14143
|
-
...actionSummary.files_edited,
|
|
14144
|
-
...actionSummary.files_created
|
|
14145
|
-
];
|
|
14146
|
-
if (touched.length === 0) return files;
|
|
14147
|
-
return files.filter((f) => {
|
|
14148
|
-
const suffix = "/" + f;
|
|
14149
|
-
return touched.some((t) => t === f || t.endsWith(suffix));
|
|
14150
|
-
});
|
|
14151
|
-
}
|
|
14152
|
-
|
|
14153
|
-
// src/lib/skip-detection.ts
|
|
14154
|
-
function isBareAckPrompt(prompt) {
|
|
14155
|
-
if (typeof prompt !== "string") return false;
|
|
14156
|
-
const trimmed = prompt.trim();
|
|
14157
|
-
if (trimmed.length === 0) return false;
|
|
14158
|
-
if (trimmed.length > 20) return false;
|
|
14159
|
-
const bareAckPattern = /^(\d{1,2}|y|n|yes|no|yep|nope|ok(ay)?|sure|skip|cancel|stop|done|noted|got\s+it|sounds\s+good|thanks|thank\s+you|thx)[.!?]*$/i;
|
|
14160
|
-
return bareAckPattern.test(trimmed);
|
|
14161
|
-
}
|
|
14162
|
-
function isContinuationPrompt(prompt) {
|
|
14163
|
-
if (typeof prompt !== "string") return false;
|
|
14164
|
-
const trimmed = prompt.trim();
|
|
14165
|
-
if (trimmed.length === 0) return false;
|
|
14166
|
-
if (trimmed.length > 24) return false;
|
|
14167
|
-
const continuation = /^(let['’]?s\s+(go|do\s+it|start|continue)|go|go\s+ahead|go\s+on|proceed|continue|carry\s+on|keep\s+going|do\s+it|make\s+it\s+so|next|start|begin|ship\s+it|yes\s+please|please\s+continue|perfect|great|nice|excellent|agreed)[.!]*$/i;
|
|
14168
|
-
return continuation.test(trimmed) || isBareAckPrompt(trimmed);
|
|
14169
|
-
}
|
|
14170
|
-
function resolveGoalPrompt(prompts) {
|
|
14171
|
-
if (prompts.length === 0) return null;
|
|
14172
|
-
const latest = prompts[prompts.length - 1];
|
|
14173
|
-
if (!isContinuationPrompt(latest.prompt)) return { entry: latest, turnsBack: 0 };
|
|
14174
|
-
for (let i = prompts.length - 2; i >= 0; i--) {
|
|
14175
|
-
if (!isContinuationPrompt(prompts[i].prompt)) {
|
|
14176
|
-
return { entry: prompts[i], turnsBack: prompts.length - 1 - i };
|
|
14177
|
-
}
|
|
14178
|
-
}
|
|
14179
|
-
return { entry: latest, turnsBack: 0 };
|
|
14180
|
-
}
|
|
14181
|
-
function isReflectionQuestion(response) {
|
|
14182
|
-
if (!response || typeof response !== "string") return false;
|
|
14183
|
-
const markers = [
|
|
14184
|
-
/reflection\s+for\s+future\s+agents/i,
|
|
14185
|
-
/what(?:'s|\s+is)\s+one\s+thing\s+you\s+learned/i,
|
|
14186
|
-
/say\s+['"]?skip['"]?\s+to\s+skip/i,
|
|
14187
|
-
/quick\s+reflection\s+question/i,
|
|
14188
|
-
// Post-flip (VRT-21): the agent drafts the reflection itself and, when
|
|
14189
|
-
// interactive, asks the user to confirm/correct before recording. That
|
|
14190
|
-
// turn authors no code either, so it's still a reflection turn.
|
|
14191
|
-
/reflection\s+draft/i,
|
|
14192
|
-
/confirm,?\s+correct,?\s+or\s+add/i
|
|
14193
|
-
];
|
|
14194
|
-
return markers.some((m) => m.test(response));
|
|
14195
|
-
}
|
|
14196
|
-
function isMetaTaskLabel(label2) {
|
|
14197
|
-
if (label2 === null || label2 === void 0) return false;
|
|
14198
|
-
if (typeof label2 !== "string") return false;
|
|
14199
|
-
const trimmed = label2.trim();
|
|
14200
|
-
if (trimmed.length === 0) return true;
|
|
14201
|
-
const metaPatterns = [
|
|
14202
|
-
/^verity\s+[\w-]+\s+response$/i,
|
|
14203
|
-
// "Verity reflect response"
|
|
14204
|
-
/^simple user response$/i,
|
|
14205
|
-
/^verity\s+command$/i,
|
|
14206
|
-
// "Verity command"
|
|
14207
|
-
/^user\s+(question|reply|response|ack)$/i
|
|
14208
|
-
];
|
|
14209
|
-
return metaPatterns.some((p) => p.test(trimmed));
|
|
14210
|
-
}
|
|
14211
|
-
function shouldSkipForBareAck(input) {
|
|
14212
|
-
if (!isBareAckPrompt(input.prompt)) return false;
|
|
14213
|
-
if (input.turnAuthoredCode) return false;
|
|
14214
|
-
return input.canSeeTurnAuthorship;
|
|
14215
|
-
}
|
|
14216
|
-
function isCommandOnlyTurn(input) {
|
|
14217
|
-
if (!input.authorshipIsObservable) return false;
|
|
14218
|
-
if (input.userCommandsTruncated) return false;
|
|
14219
|
-
const commands = input.userCommands ?? [];
|
|
14220
|
-
if (commands.length === 0) return false;
|
|
14221
|
-
if (input.agentAuthoredFiles > 0) return false;
|
|
14222
|
-
if (input.agentToolCalls > 0) return false;
|
|
14223
|
-
return commands.every(isNonAuthoringCommand);
|
|
14224
|
-
}
|
|
14225
|
-
|
|
14413
|
+
Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
|
|
14414
|
+
\`\`\`
|
|
14415
|
+
|
|
14416
|
+
## Edge types
|
|
14417
|
+
|
|
14418
|
+
| Edge | Meaning |
|
|
14419
|
+
|------|---------|
|
|
14420
|
+
| related | Loose association |
|
|
14421
|
+
| supersedes | A replaces B |
|
|
14422
|
+
| contradicts | A and B disagree |
|
|
14423
|
+
| caused_by | Something in B led to A |
|
|
14424
|
+
| example_of | A is an instance of B |
|
|
14425
|
+
|
|
14426
|
+
## Domains
|
|
14427
|
+
|
|
14428
|
+
| Directory | Purpose |
|
|
14429
|
+
|-----------|---------|
|
|
14430
|
+
| decisions/ | Architectural choices (ADR-style) |
|
|
14431
|
+
| quality/ | Quality patterns |
|
|
14432
|
+
| security/ | Security constraints |
|
|
14433
|
+
| intent/ | Intent templates |
|
|
14434
|
+
| gotchas/ | Footguns and surprises |
|
|
14435
|
+
| patterns/ | Code conventions |
|
|
14436
|
+
| domain/ | Business logic concepts |
|
|
14437
|
+
| integrations/ | External system knowledge |
|
|
14438
|
+
| _archive/ | Superseded nodes |
|
|
14439
|
+
`;
|
|
14440
|
+
|
|
14441
|
+
// src/lib/dossier-session.ts
|
|
14442
|
+
var import_node_fs17 = require("node:fs");
|
|
14443
|
+
var import_node_crypto7 = require("node:crypto");
|
|
14444
|
+
var import_node_path13 = require("node:path");
|
|
14445
|
+
|
|
14226
14446
|
// src/lib/pending-repeat.ts
|
|
14227
14447
|
var STOP = /* @__PURE__ */ new Set([
|
|
14228
14448
|
"the",
|
|
@@ -14326,7 +14546,7 @@ function statementAnchorKey(file, patternId) {
|
|
|
14326
14546
|
|
|
14327
14547
|
// src/lib/dossier/log.ts
|
|
14328
14548
|
var import_node_crypto4 = require("node:crypto");
|
|
14329
|
-
var
|
|
14549
|
+
var import_node_fs13 = require("node:fs");
|
|
14330
14550
|
var import_node_path11 = require("node:path");
|
|
14331
14551
|
var CRC_TABLE = (() => {
|
|
14332
14552
|
const t = new Int32Array(256);
|
|
@@ -14346,7 +14566,7 @@ function crc32(s) {
|
|
|
14346
14566
|
function openDossier(identity) {
|
|
14347
14567
|
try {
|
|
14348
14568
|
const dir = dossierDir(identity);
|
|
14349
|
-
(0,
|
|
14569
|
+
(0, import_node_fs13.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
14350
14570
|
return {
|
|
14351
14571
|
dir,
|
|
14352
14572
|
identity,
|
|
@@ -14414,7 +14634,7 @@ function appendEvent(d, ev) {
|
|
|
14414
14634
|
at: ev.at ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
14415
14635
|
...ev
|
|
14416
14636
|
});
|
|
14417
|
-
(0,
|
|
14637
|
+
(0, import_node_fs13.appendFileSync)(d.eventsPath, line, { mode: 384 });
|
|
14418
14638
|
return true;
|
|
14419
14639
|
} catch {
|
|
14420
14640
|
return false;
|
|
@@ -14422,14 +14642,14 @@ function appendEvent(d, ev) {
|
|
|
14422
14642
|
}
|
|
14423
14643
|
function rotateIfNeeded2(d) {
|
|
14424
14644
|
try {
|
|
14425
|
-
if (!(0,
|
|
14426
|
-
if ((0,
|
|
14427
|
-
(0,
|
|
14428
|
-
(0,
|
|
14429
|
-
const kept = (0,
|
|
14645
|
+
if (!(0, import_node_fs13.existsSync)(d.eventsPath)) return;
|
|
14646
|
+
if ((0, import_node_fs13.statSync)(d.eventsPath).size < ROTATE_BYTES) return;
|
|
14647
|
+
(0, import_node_fs13.mkdirSync)(d.rotatedDir, { recursive: true, mode: 448 });
|
|
14648
|
+
(0, import_node_fs13.renameSync)(d.eventsPath, (0, import_node_path11.join)(d.rotatedDir, `events.${Date.now()}.jsonl`));
|
|
14649
|
+
const kept = (0, import_node_fs13.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
14430
14650
|
for (const stale of kept.slice(0, Math.max(0, kept.length - ROTATE_KEEP))) {
|
|
14431
14651
|
try {
|
|
14432
|
-
(0,
|
|
14652
|
+
(0, import_node_fs13.renameSync)((0, import_node_path11.join)(d.rotatedDir, stale), (0, import_node_path11.join)(d.rotatedDir, `${stale}.pruned`));
|
|
14433
14653
|
} catch {
|
|
14434
14654
|
}
|
|
14435
14655
|
}
|
|
@@ -14439,7 +14659,7 @@ function rotateIfNeeded2(d) {
|
|
|
14439
14659
|
|
|
14440
14660
|
// src/lib/dossier/fold-dossier.ts
|
|
14441
14661
|
var import_node_crypto5 = require("node:crypto");
|
|
14442
|
-
var
|
|
14662
|
+
var import_node_fs14 = require("node:fs");
|
|
14443
14663
|
var import_node_path12 = require("node:path");
|
|
14444
14664
|
var EMPTY_CAPABILITIES = () => ({
|
|
14445
14665
|
human_reachable: { value: "unknown", tier: "unknown" },
|
|
@@ -14491,12 +14711,12 @@ function foldDossier(d, opts = {}) {
|
|
|
14491
14711
|
}
|
|
14492
14712
|
};
|
|
14493
14713
|
try {
|
|
14494
|
-
if ((0,
|
|
14495
|
-
const files = (0,
|
|
14714
|
+
if ((0, import_node_fs14.existsSync)(d.rotatedDir)) {
|
|
14715
|
+
const files = (0, import_node_fs14.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).sort();
|
|
14496
14716
|
state.meta.rotations = files.length;
|
|
14497
14717
|
for (const f of files) {
|
|
14498
14718
|
try {
|
|
14499
|
-
ingest((0,
|
|
14719
|
+
ingest((0, import_node_fs14.readFileSync)((0, import_node_path12.join)(d.rotatedDir, f), "utf8"));
|
|
14500
14720
|
} catch {
|
|
14501
14721
|
state.meta.dropped_lines++;
|
|
14502
14722
|
}
|
|
@@ -14505,9 +14725,9 @@ function foldDossier(d, opts = {}) {
|
|
|
14505
14725
|
} catch {
|
|
14506
14726
|
}
|
|
14507
14727
|
try {
|
|
14508
|
-
if ((0,
|
|
14509
|
-
state.meta.upto_offset = (0,
|
|
14510
|
-
ingest((0,
|
|
14728
|
+
if ((0, import_node_fs14.existsSync)(d.eventsPath)) {
|
|
14729
|
+
state.meta.upto_offset = (0, import_node_fs14.statSync)(d.eventsPath).size;
|
|
14730
|
+
ingest((0, import_node_fs14.readFileSync)(d.eventsPath, "utf8"));
|
|
14511
14731
|
}
|
|
14512
14732
|
} catch {
|
|
14513
14733
|
}
|
|
@@ -14778,7 +14998,7 @@ function applyBounds(state, input) {
|
|
|
14778
14998
|
}
|
|
14779
14999
|
|
|
14780
15000
|
// src/lib/dossier/cache.ts
|
|
14781
|
-
var
|
|
15001
|
+
var import_node_fs15 = require("node:fs");
|
|
14782
15002
|
function compactState(s) {
|
|
14783
15003
|
const ms = (iso) => Date.parse(iso) || 0;
|
|
14784
15004
|
return {
|
|
@@ -14907,20 +15127,20 @@ function encodeState(s) {
|
|
|
14907
15127
|
function writeFoldCache(d, state) {
|
|
14908
15128
|
try {
|
|
14909
15129
|
const tmp = `${d.foldPath}.${process.pid}.tmp`;
|
|
14910
|
-
(0,
|
|
14911
|
-
(0,
|
|
15130
|
+
(0, import_node_fs15.writeFileSync)(tmp, encodeState(state), { mode: 384 });
|
|
15131
|
+
(0, import_node_fs15.renameSync)(tmp, d.foldPath);
|
|
14912
15132
|
} catch {
|
|
14913
15133
|
}
|
|
14914
15134
|
}
|
|
14915
15135
|
function readFoldCache(d) {
|
|
14916
15136
|
try {
|
|
14917
|
-
if (!(0,
|
|
14918
|
-
const raw = JSON.parse((0,
|
|
15137
|
+
if (!(0, import_node_fs15.existsSync)(d.foldPath)) return null;
|
|
15138
|
+
const raw = JSON.parse((0, import_node_fs15.readFileSync)(d.foldPath, "utf8"));
|
|
14919
15139
|
if (raw?.v !== 1) return null;
|
|
14920
15140
|
const cached2 = expandState(raw);
|
|
14921
15141
|
if (!cached2?.meta) return null;
|
|
14922
|
-
const size = (0,
|
|
14923
|
-
const rotations = (0,
|
|
15142
|
+
const size = (0, import_node_fs15.existsSync)(d.eventsPath) ? (0, import_node_fs15.statSync)(d.eventsPath).size : 0;
|
|
15143
|
+
const rotations = (0, import_node_fs15.existsSync)(d.rotatedDir) ? (0, import_node_fs15.readdirSync)(d.rotatedDir).filter((f) => f.endsWith(".jsonl")).length : 0;
|
|
14924
15144
|
if (cached2.meta.upto_offset !== size || cached2.meta.rotations !== rotations) return null;
|
|
14925
15145
|
return cached2;
|
|
14926
15146
|
} catch {
|
|
@@ -14971,13 +15191,13 @@ function assessContinuity(i) {
|
|
|
14971
15191
|
|
|
14972
15192
|
// src/lib/dossier/reanchor.ts
|
|
14973
15193
|
var import_node_crypto6 = require("node:crypto");
|
|
14974
|
-
var
|
|
15194
|
+
var import_node_fs16 = require("node:fs");
|
|
14975
15195
|
function lineSha(text) {
|
|
14976
15196
|
return (0, import_node_crypto6.createHash)("sha256").update(text.trim()).digest("hex").slice(0, HASH_WIDTH);
|
|
14977
15197
|
}
|
|
14978
15198
|
function fileHash(path) {
|
|
14979
15199
|
try {
|
|
14980
|
-
return (0, import_node_crypto6.createHash)("sha256").update((0,
|
|
15200
|
+
return (0, import_node_crypto6.createHash)("sha256").update((0, import_node_fs16.readFileSync)(path)).digest("hex").slice(0, HASH_WIDTH);
|
|
14981
15201
|
} catch {
|
|
14982
15202
|
return null;
|
|
14983
15203
|
}
|
|
@@ -15349,14 +15569,14 @@ function foreignAuthoredPaths(identity, opts = {}) {
|
|
|
15349
15569
|
let sessions = 0;
|
|
15350
15570
|
try {
|
|
15351
15571
|
const dir = treeDir(identity);
|
|
15352
|
-
if (!(0,
|
|
15353
|
-
for (const entry of (0,
|
|
15572
|
+
if (!(0, import_node_fs17.existsSync)(dir)) return { paths: [], sessions: 0 };
|
|
15573
|
+
for (const entry of (0, import_node_fs17.readdirSync)(dir, { withFileTypes: true })) {
|
|
15354
15574
|
if (!entry.isDirectory()) continue;
|
|
15355
15575
|
if (entry.name === identity.sessionKey) continue;
|
|
15356
15576
|
const log = (0, import_node_path13.join)(dir, entry.name, "events.jsonl");
|
|
15357
15577
|
try {
|
|
15358
|
-
if (!(0,
|
|
15359
|
-
if (now - (0,
|
|
15578
|
+
if (!(0, import_node_fs17.existsSync)(log)) continue;
|
|
15579
|
+
if (now - (0, import_node_fs17.statSync)(log).mtimeMs > windowMs) continue;
|
|
15360
15580
|
const sib = {
|
|
15361
15581
|
dir: (0, import_node_path13.join)(dir, entry.name),
|
|
15362
15582
|
identity,
|
|
@@ -15391,13 +15611,13 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
|
|
|
15391
15611
|
try {
|
|
15392
15612
|
const mine = dossierDir(identity);
|
|
15393
15613
|
const userDir = (0, import_node_path13.dirname)((0, import_node_path13.dirname)(mine));
|
|
15394
|
-
if (!(0,
|
|
15614
|
+
if (!(0, import_node_fs17.existsSync)(userDir)) return 0;
|
|
15395
15615
|
const cutoff = Date.now() - maxAgeMs;
|
|
15396
|
-
for (const tree of (0,
|
|
15616
|
+
for (const tree of (0, import_node_fs17.readdirSync)(userDir, { withFileTypes: true })) {
|
|
15397
15617
|
if (!tree.isDirectory()) continue;
|
|
15398
15618
|
const treePath = (0, import_node_path13.join)(userDir, tree.name);
|
|
15399
15619
|
let live = 0;
|
|
15400
|
-
for (const entry of (0,
|
|
15620
|
+
for (const entry of (0, import_node_fs17.readdirSync)(treePath, { withFileTypes: true })) {
|
|
15401
15621
|
if (!entry.isDirectory()) continue;
|
|
15402
15622
|
const dir = (0, import_node_path13.join)(treePath, entry.name);
|
|
15403
15623
|
if (dir === mine) {
|
|
@@ -15406,9 +15626,9 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
|
|
|
15406
15626
|
}
|
|
15407
15627
|
try {
|
|
15408
15628
|
const log = (0, import_node_path13.join)(dir, "events.jsonl");
|
|
15409
|
-
const at = (0,
|
|
15629
|
+
const at = (0, import_node_fs17.existsSync)(log) ? (0, import_node_fs17.statSync)(log).mtimeMs : (0, import_node_fs17.statSync)(dir).mtimeMs;
|
|
15410
15630
|
if (at < cutoff) {
|
|
15411
|
-
(0,
|
|
15631
|
+
(0, import_node_fs17.rmSync)(dir, { recursive: true, force: true });
|
|
15412
15632
|
removed++;
|
|
15413
15633
|
} else {
|
|
15414
15634
|
live++;
|
|
@@ -15418,7 +15638,7 @@ function pruneOldDossiers(identity, maxAgeMs = 7 * 24 * 60 * 60 * 1e3) {
|
|
|
15418
15638
|
}
|
|
15419
15639
|
if (live === 0) {
|
|
15420
15640
|
try {
|
|
15421
|
-
(0,
|
|
15641
|
+
(0, import_node_fs17.rmSync)(treePath, { recursive: false, force: false });
|
|
15422
15642
|
} catch {
|
|
15423
15643
|
}
|
|
15424
15644
|
}
|
|
@@ -15435,8 +15655,8 @@ function sessionDossier(token, sessionId) {
|
|
|
15435
15655
|
}
|
|
15436
15656
|
function hasActiveGoal(d) {
|
|
15437
15657
|
try {
|
|
15438
|
-
if (!(0,
|
|
15439
|
-
return (0,
|
|
15658
|
+
if (!(0, import_node_fs17.existsSync)(d.eventsPath)) return false;
|
|
15659
|
+
return (0, import_node_fs17.readFileSync)(d.eventsPath, "utf8").includes('"k":"goal"');
|
|
15440
15660
|
} catch {
|
|
15441
15661
|
return false;
|
|
15442
15662
|
}
|
|
@@ -15529,7 +15749,7 @@ function recordVerdict(d, v) {
|
|
|
15529
15749
|
if (!lines.has(f.file)) {
|
|
15530
15750
|
try {
|
|
15531
15751
|
const abs = (0, import_node_path13.join)(root, f.file);
|
|
15532
|
-
lines.set(f.file, (0,
|
|
15752
|
+
lines.set(f.file, (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null);
|
|
15533
15753
|
} catch {
|
|
15534
15754
|
lines.set(f.file, null);
|
|
15535
15755
|
}
|
|
@@ -15630,7 +15850,7 @@ function recallMemory(d, identity, opts) {
|
|
|
15630
15850
|
readFileLines: (file) => {
|
|
15631
15851
|
try {
|
|
15632
15852
|
const abs = (0, import_node_path13.join)(root, file);
|
|
15633
|
-
return (0,
|
|
15853
|
+
return (0, import_node_fs17.existsSync)(abs) ? (0, import_node_fs17.readFileSync)(abs, "utf8").split("\n") : null;
|
|
15634
15854
|
} catch {
|
|
15635
15855
|
return null;
|
|
15636
15856
|
}
|
|
@@ -15679,15 +15899,21 @@ function registerIntentCommands(program2) {
|
|
|
15679
15899
|
if (!prompt) {
|
|
15680
15900
|
process.exit(0);
|
|
15681
15901
|
}
|
|
15902
|
+
if (deferredToPlugin("intent capture", event.session_id ?? process.env.CLAUDE_SESSION_ID ?? null)) {
|
|
15903
|
+
process.exit(0);
|
|
15904
|
+
}
|
|
15905
|
+
const isPrimitive = isSlashCommand(prompt);
|
|
15682
15906
|
const authForScope = await resolveToken(program2.opts().token);
|
|
15683
15907
|
const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
|
|
15684
15908
|
const scopeSession = event.session_id || process.env.CLAUDE_SESSION_ID || "";
|
|
15685
|
-
await appendToConversationBuffer(prompt, sessionScopeKey(scopeToken, scopeSession));
|
|
15686
|
-
|
|
15687
|
-
|
|
15688
|
-
|
|
15689
|
-
|
|
15690
|
-
|
|
15909
|
+
if (!isPrimitive) await appendToConversationBuffer(prompt, sessionScopeKey(scopeToken, scopeSession));
|
|
15910
|
+
if (!isPrimitive) {
|
|
15911
|
+
try {
|
|
15912
|
+
const tok = await resolveToken(globals.token);
|
|
15913
|
+
const session = sessionDossier(tok.ok ? tok.data.token : null, event.session_id ?? null);
|
|
15914
|
+
if (session) recordGoal(session.d, prompt);
|
|
15915
|
+
} catch {
|
|
15916
|
+
}
|
|
15691
15917
|
}
|
|
15692
15918
|
try {
|
|
15693
15919
|
await ensureMemoryDir();
|
|
@@ -15768,21 +15994,21 @@ async function fireClassify(prompt, sessionId, globals) {
|
|
|
15768
15994
|
}
|
|
15769
15995
|
|
|
15770
15996
|
// src/commands/lifecycle.ts
|
|
15771
|
-
var
|
|
15997
|
+
var import_node_fs21 = require("node:fs");
|
|
15772
15998
|
var import_node_path17 = require("node:path");
|
|
15773
15999
|
|
|
15774
16000
|
// src/lib/baseline.ts
|
|
15775
|
-
var
|
|
16001
|
+
var import_node_fs20 = require("node:fs");
|
|
15776
16002
|
var import_node_path16 = require("node:path");
|
|
15777
16003
|
var import_node_crypto9 = require("node:crypto");
|
|
15778
16004
|
|
|
15779
16005
|
// src/lib/snapshot.ts
|
|
15780
|
-
var
|
|
16006
|
+
var import_node_fs19 = require("node:fs");
|
|
15781
16007
|
var import_node_path15 = require("node:path");
|
|
15782
16008
|
var import_node_child_process6 = require("node:child_process");
|
|
15783
16009
|
|
|
15784
16010
|
// src/lib/files.ts
|
|
15785
|
-
var
|
|
16011
|
+
var import_node_fs18 = require("node:fs");
|
|
15786
16012
|
var import_node_path14 = require("node:path");
|
|
15787
16013
|
var LANG_MAP = {
|
|
15788
16014
|
// Analyzable (static analysis + Gemini)
|
|
@@ -15859,7 +16085,7 @@ function sortByMtime(files) {
|
|
|
15859
16085
|
const resolved = resolveFile(f);
|
|
15860
16086
|
if (!resolved) return null;
|
|
15861
16087
|
try {
|
|
15862
|
-
const stat3 = (0,
|
|
16088
|
+
const stat3 = (0, import_node_fs18.statSync)(resolved);
|
|
15863
16089
|
return { path: f, resolved, mtime: stat3.mtimeMs };
|
|
15864
16090
|
} catch {
|
|
15865
16091
|
return null;
|
|
@@ -15892,7 +16118,7 @@ function collectCodeDelta(files, opts) {
|
|
|
15892
16118
|
}
|
|
15893
16119
|
let size;
|
|
15894
16120
|
try {
|
|
15895
|
-
size = (0,
|
|
16121
|
+
size = (0, import_node_fs18.statSync)(resolved).size;
|
|
15896
16122
|
} catch {
|
|
15897
16123
|
exclude(filepath, "not-stattable");
|
|
15898
16124
|
continue;
|
|
@@ -15909,7 +16135,7 @@ function collectCodeDelta(files, opts) {
|
|
|
15909
16135
|
}
|
|
15910
16136
|
let content;
|
|
15911
16137
|
try {
|
|
15912
|
-
content = (0,
|
|
16138
|
+
content = (0, import_node_fs18.readFileSync)(resolved, "utf-8");
|
|
15913
16139
|
} catch {
|
|
15914
16140
|
exclude(filepath, "not-readable");
|
|
15915
16141
|
continue;
|
|
@@ -15948,7 +16174,7 @@ function collectCodeDelta(files, opts) {
|
|
|
15948
16174
|
|
|
15949
16175
|
// src/lib/snapshot.ts
|
|
15950
16176
|
function generateSnapshotDiffs(files) {
|
|
15951
|
-
if (!(0,
|
|
16177
|
+
if (!(0, import_node_fs19.existsSync)(SNAPSHOT_DIR)) {
|
|
15952
16178
|
return { diffs: [], has_snapshots: false };
|
|
15953
16179
|
}
|
|
15954
16180
|
const diffs = [];
|
|
@@ -15956,8 +16182,8 @@ function generateSnapshotDiffs(files) {
|
|
|
15956
16182
|
if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
|
|
15957
16183
|
const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
|
|
15958
16184
|
const language = file.language ?? detectLanguage(file.path);
|
|
15959
|
-
if ((0,
|
|
15960
|
-
const oldContent = (0,
|
|
16185
|
+
if ((0, import_node_fs19.existsSync)(snapshotPath)) {
|
|
16186
|
+
const oldContent = (0, import_node_fs19.readFileSync)(snapshotPath, "utf-8");
|
|
15961
16187
|
if (oldContent === file.content) continue;
|
|
15962
16188
|
const diff = computeDiff(oldContent, file.content, file.path);
|
|
15963
16189
|
if (diff) {
|
|
@@ -15984,8 +16210,8 @@ function saveSnapshots(files) {
|
|
|
15984
16210
|
if (!resolveInside(SNAPSHOT_DIR, file.path)) continue;
|
|
15985
16211
|
const snapshotPath = (0, import_node_path15.join)(SNAPSHOT_DIR, file.path);
|
|
15986
16212
|
snapshotPaths.add(snapshotPath);
|
|
15987
|
-
(0,
|
|
15988
|
-
(0,
|
|
16213
|
+
(0, import_node_fs19.mkdirSync)((0, import_node_path15.dirname)(snapshotPath), { recursive: true });
|
|
16214
|
+
(0, import_node_fs19.writeFileSync)(snapshotPath, file.content);
|
|
15989
16215
|
}
|
|
15990
16216
|
cleanStaleSnapshots(SNAPSHOT_DIR, snapshotPaths);
|
|
15991
16217
|
}
|
|
@@ -15993,9 +16219,9 @@ function computeDiff(oldContent, newContent, filePath) {
|
|
|
15993
16219
|
const tmpOld = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-old.tmp");
|
|
15994
16220
|
const tmpNew = (0, import_node_path15.join)(SNAPSHOT_DIR, ".diff-new.tmp");
|
|
15995
16221
|
try {
|
|
15996
|
-
(0,
|
|
15997
|
-
(0,
|
|
15998
|
-
(0,
|
|
16222
|
+
(0, import_node_fs19.mkdirSync)(SNAPSHOT_DIR, { recursive: true });
|
|
16223
|
+
(0, import_node_fs19.writeFileSync)(tmpOld, oldContent);
|
|
16224
|
+
(0, import_node_fs19.writeFileSync)(tmpNew, newContent);
|
|
15999
16225
|
const result = (0, import_node_child_process6.execSync)(
|
|
16000
16226
|
`git diff --no-index --unified=10 -- "${tmpOld}" "${tmpNew}"`,
|
|
16001
16227
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -16009,32 +16235,32 @@ function computeDiff(oldContent, newContent, filePath) {
|
|
|
16009
16235
|
return null;
|
|
16010
16236
|
} finally {
|
|
16011
16237
|
try {
|
|
16012
|
-
(0,
|
|
16238
|
+
(0, import_node_fs19.unlinkSync)(tmpOld);
|
|
16013
16239
|
} catch {
|
|
16014
16240
|
}
|
|
16015
16241
|
try {
|
|
16016
|
-
(0,
|
|
16242
|
+
(0, import_node_fs19.unlinkSync)(tmpNew);
|
|
16017
16243
|
} catch {
|
|
16018
16244
|
}
|
|
16019
16245
|
}
|
|
16020
16246
|
}
|
|
16021
16247
|
function cleanStaleSnapshots(dir, keepSet) {
|
|
16022
|
-
if (!(0,
|
|
16248
|
+
if (!(0, import_node_fs19.existsSync)(dir)) return;
|
|
16023
16249
|
try {
|
|
16024
|
-
const entries = (0,
|
|
16250
|
+
const entries = (0, import_node_fs19.readdirSync)(dir, { withFileTypes: true });
|
|
16025
16251
|
for (const entry of entries) {
|
|
16026
16252
|
if (dir === SNAPSHOT_DIR && (entry.name === ".diff-old.tmp" || entry.name === ".diff-new.tmp")) continue;
|
|
16027
16253
|
const fullPath = (0, import_node_path15.join)(dir, entry.name);
|
|
16028
16254
|
if (entry.isDirectory()) {
|
|
16029
16255
|
cleanStaleSnapshots(fullPath, keepSet);
|
|
16030
16256
|
try {
|
|
16031
|
-
const remaining = (0,
|
|
16032
|
-
if (remaining.length === 0) (0,
|
|
16257
|
+
const remaining = (0, import_node_fs19.readdirSync)(fullPath);
|
|
16258
|
+
if (remaining.length === 0) (0, import_node_fs19.rmdirSync)(fullPath);
|
|
16033
16259
|
} catch {
|
|
16034
16260
|
}
|
|
16035
16261
|
} else if (!keepSet.has(fullPath)) {
|
|
16036
16262
|
try {
|
|
16037
|
-
(0,
|
|
16263
|
+
(0, import_node_fs19.unlinkSync)(fullPath);
|
|
16038
16264
|
} catch {
|
|
16039
16265
|
}
|
|
16040
16266
|
}
|
|
@@ -16065,8 +16291,8 @@ var CARRY_FILE = `${BASELINE_DIR}/.carry`;
|
|
|
16065
16291
|
var CARRY_WINDOW_MS = 12e4;
|
|
16066
16292
|
function writeCarry(sessionId, headSha) {
|
|
16067
16293
|
try {
|
|
16068
|
-
(0,
|
|
16069
|
-
(0,
|
|
16294
|
+
(0, import_node_fs20.mkdirSync)(projectPath(BASELINE_DIR), { recursive: true });
|
|
16295
|
+
(0, import_node_fs20.writeFileSync)(
|
|
16070
16296
|
projectPath(CARRY_FILE),
|
|
16071
16297
|
JSON.stringify({ from_key: sessionKey(sessionId), head_sha: headSha, ts: Date.now() })
|
|
16072
16298
|
);
|
|
@@ -16076,10 +16302,10 @@ function writeCarry(sessionId, headSha) {
|
|
|
16076
16302
|
function claimCarry(newKey) {
|
|
16077
16303
|
const carryPath = projectPath(CARRY_FILE);
|
|
16078
16304
|
try {
|
|
16079
|
-
if (!(0,
|
|
16080
|
-
const carry = JSON.parse((0,
|
|
16305
|
+
if (!(0, import_node_fs20.existsSync)(carryPath)) return null;
|
|
16306
|
+
const carry = JSON.parse((0, import_node_fs20.readFileSync)(carryPath, "utf-8"));
|
|
16081
16307
|
try {
|
|
16082
|
-
(0,
|
|
16308
|
+
(0, import_node_fs20.rmSync)(carryPath, { force: true });
|
|
16083
16309
|
} catch {
|
|
16084
16310
|
}
|
|
16085
16311
|
if (!carry?.from_key || typeof carry.ts !== "number") return null;
|
|
@@ -16090,11 +16316,11 @@ function claimCarry(newKey) {
|
|
|
16090
16316
|
if (!prior) return null;
|
|
16091
16317
|
const toDir = sessionDir(newKey);
|
|
16092
16318
|
try {
|
|
16093
|
-
(0,
|
|
16319
|
+
(0, import_node_fs20.rmSync)(toDir, { recursive: true, force: true });
|
|
16094
16320
|
} catch {
|
|
16095
16321
|
}
|
|
16096
|
-
(0,
|
|
16097
|
-
(0,
|
|
16322
|
+
(0, import_node_fs20.renameSync)(fromDir, toDir);
|
|
16323
|
+
(0, import_node_fs20.writeFileSync)(manifestPath(toDir), JSON.stringify({ ...prior, session_id: newKey }) + "\n");
|
|
16098
16324
|
return readManifest(toDir);
|
|
16099
16325
|
} catch {
|
|
16100
16326
|
return null;
|
|
@@ -16118,21 +16344,21 @@ function captureBaseline(opts = {}) {
|
|
|
16118
16344
|
const head_sha = getCurrentCommit();
|
|
16119
16345
|
const dirty = getDirtyFiles();
|
|
16120
16346
|
try {
|
|
16121
|
-
(0,
|
|
16347
|
+
(0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
|
|
16122
16348
|
} catch {
|
|
16123
16349
|
}
|
|
16124
16350
|
const filesDir = (0, import_node_path16.join)(dir, "files");
|
|
16125
16351
|
const mirrored = [];
|
|
16126
16352
|
try {
|
|
16127
|
-
(0,
|
|
16353
|
+
(0, import_node_fs20.mkdirSync)(filesDir, { recursive: true });
|
|
16128
16354
|
for (const p of dirty) {
|
|
16129
16355
|
if (p.includes("..")) continue;
|
|
16130
16356
|
const content = safeReadForMirror(projectPath(p));
|
|
16131
16357
|
if (content === null) continue;
|
|
16132
16358
|
const dest = mirrorPath(dir, p);
|
|
16133
16359
|
try {
|
|
16134
|
-
(0,
|
|
16135
|
-
(0,
|
|
16360
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
16361
|
+
(0, import_node_fs20.writeFileSync)(dest, content);
|
|
16136
16362
|
mirrored.push(p);
|
|
16137
16363
|
} catch {
|
|
16138
16364
|
}
|
|
@@ -16147,8 +16373,8 @@ function captureBaseline(opts = {}) {
|
|
|
16147
16373
|
version: BASELINE_VERSION
|
|
16148
16374
|
};
|
|
16149
16375
|
try {
|
|
16150
|
-
(0,
|
|
16151
|
-
(0,
|
|
16376
|
+
(0, import_node_fs20.mkdirSync)(dir, { recursive: true });
|
|
16377
|
+
(0, import_node_fs20.writeFileSync)(manifestPath(dir), JSON.stringify(baseline));
|
|
16152
16378
|
} catch {
|
|
16153
16379
|
}
|
|
16154
16380
|
pruneOldBaselines();
|
|
@@ -16159,9 +16385,9 @@ function readBaseline(sessionId) {
|
|
|
16159
16385
|
}
|
|
16160
16386
|
function readManifest(dir) {
|
|
16161
16387
|
const mp = manifestPath(dir);
|
|
16162
|
-
if (!(0,
|
|
16388
|
+
if (!(0, import_node_fs20.existsSync)(mp)) return null;
|
|
16163
16389
|
try {
|
|
16164
|
-
const parsed = JSON.parse((0,
|
|
16390
|
+
const parsed = JSON.parse((0, import_node_fs20.readFileSync)(mp, "utf-8"));
|
|
16165
16391
|
if (typeof parsed.head_sha !== "string" || typeof parsed.captured_at !== "number" || !Array.isArray(parsed.dirty_paths) || parsed.version !== BASELINE_VERSION) {
|
|
16166
16392
|
return null;
|
|
16167
16393
|
}
|
|
@@ -16192,9 +16418,9 @@ function preImage(repoRelPath, baseline) {
|
|
|
16192
16418
|
function resolvePreImage(repoRelPath, baseline) {
|
|
16193
16419
|
if (baseline.dirty_paths.includes(repoRelPath)) {
|
|
16194
16420
|
const mp = mirrorPath(sessionDir(sessionKey(baseline.session_id)), repoRelPath);
|
|
16195
|
-
if ((0,
|
|
16421
|
+
if ((0, import_node_fs20.existsSync)(mp)) {
|
|
16196
16422
|
try {
|
|
16197
|
-
return { content: (0,
|
|
16423
|
+
return { content: (0, import_node_fs20.readFileSync)(mp, "utf-8"), existed: true };
|
|
16198
16424
|
} catch {
|
|
16199
16425
|
}
|
|
16200
16426
|
}
|
|
@@ -16239,8 +16465,8 @@ function absorbIntoBaseline(paths, sessionId) {
|
|
|
16239
16465
|
const content = safeReadForMirror(projectPath(p));
|
|
16240
16466
|
if (content === null) continue;
|
|
16241
16467
|
const dest = mirrorPath(dir, p);
|
|
16242
|
-
(0,
|
|
16243
|
-
(0,
|
|
16468
|
+
(0, import_node_fs20.mkdirSync)((0, import_node_path16.dirname)(dest), { recursive: true });
|
|
16469
|
+
(0, import_node_fs20.writeFileSync)(dest, content);
|
|
16244
16470
|
dirty.add(p);
|
|
16245
16471
|
adopted++;
|
|
16246
16472
|
} catch {
|
|
@@ -16249,7 +16475,7 @@ function absorbIntoBaseline(paths, sessionId) {
|
|
|
16249
16475
|
if (adopted === 0) return 0;
|
|
16250
16476
|
try {
|
|
16251
16477
|
const updated = { ...baseline, dirty_paths: [...dirty] };
|
|
16252
|
-
(0,
|
|
16478
|
+
(0, import_node_fs20.writeFileSync)(manifestPath(dir), JSON.stringify(updated));
|
|
16253
16479
|
preImageCache.delete(baseline);
|
|
16254
16480
|
} catch {
|
|
16255
16481
|
return 0;
|
|
@@ -16260,7 +16486,7 @@ function changedSinceBaseline(repoRelPath, baseline) {
|
|
|
16260
16486
|
const pre = preImage(repoRelPath, baseline);
|
|
16261
16487
|
let current;
|
|
16262
16488
|
try {
|
|
16263
|
-
current = (0,
|
|
16489
|
+
current = (0, import_node_fs20.readFileSync)(projectPath(repoRelPath), "utf-8");
|
|
16264
16490
|
} catch {
|
|
16265
16491
|
return pre.existed;
|
|
16266
16492
|
}
|
|
@@ -16269,8 +16495,8 @@ function changedSinceBaseline(repoRelPath, baseline) {
|
|
|
16269
16495
|
}
|
|
16270
16496
|
function safeReadForMirror(absPath) {
|
|
16271
16497
|
try {
|
|
16272
|
-
if ((0,
|
|
16273
|
-
const buf = (0,
|
|
16498
|
+
if ((0, import_node_fs20.statSync)(absPath).size > MIRROR_MAX_BYTES) return null;
|
|
16499
|
+
const buf = (0, import_node_fs20.readFileSync)(absPath);
|
|
16274
16500
|
if (buf.includes(0)) return null;
|
|
16275
16501
|
return buf.toString("utf-8");
|
|
16276
16502
|
} catch {
|
|
@@ -16281,7 +16507,7 @@ function pruneOldBaselines() {
|
|
|
16281
16507
|
const root = projectPath(BASELINE_DIR);
|
|
16282
16508
|
let entries;
|
|
16283
16509
|
try {
|
|
16284
|
-
entries = (0,
|
|
16510
|
+
entries = (0, import_node_fs20.readdirSync)(root);
|
|
16285
16511
|
} catch {
|
|
16286
16512
|
return;
|
|
16287
16513
|
}
|
|
@@ -16291,8 +16517,8 @@ function pruneOldBaselines() {
|
|
|
16291
16517
|
const manifest = readManifest(dir);
|
|
16292
16518
|
if (!manifest) {
|
|
16293
16519
|
try {
|
|
16294
|
-
if (now - (0,
|
|
16295
|
-
(0,
|
|
16520
|
+
if (now - (0, import_node_fs20.statSync)(dir).mtimeMs > BASELINE_TTL_MS) {
|
|
16521
|
+
(0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
|
|
16296
16522
|
}
|
|
16297
16523
|
} catch {
|
|
16298
16524
|
}
|
|
@@ -16300,7 +16526,7 @@ function pruneOldBaselines() {
|
|
|
16300
16526
|
}
|
|
16301
16527
|
if (now - manifest.captured_at <= BASELINE_TTL_MS) continue;
|
|
16302
16528
|
try {
|
|
16303
|
-
(0,
|
|
16529
|
+
(0, import_node_fs20.rmSync)(dir, { recursive: true, force: true });
|
|
16304
16530
|
} catch {
|
|
16305
16531
|
}
|
|
16306
16532
|
}
|
|
@@ -16394,6 +16620,7 @@ function registerLifecycleCommands(program2) {
|
|
|
16394
16620
|
if (!verityConfigured()) process.exit(0);
|
|
16395
16621
|
const event = await readHookStdin();
|
|
16396
16622
|
const sessionId = opts.sessionId ?? event.session_id ?? process.env.CLAUDE_SESSION_ID ?? null;
|
|
16623
|
+
if (deferredToPlugin("compact", sessionId)) process.exit(0);
|
|
16397
16624
|
const globals = program2.opts();
|
|
16398
16625
|
const tok = await resolveToken(globals.token);
|
|
16399
16626
|
const session2 = sessionDossier(tok.ok ? tok.data.token : null, sessionId);
|
|
@@ -16422,6 +16649,7 @@ function registerLifecycleCommands(program2) {
|
|
|
16422
16649
|
if (!verityConfigured()) process.exit(0);
|
|
16423
16650
|
const event = await readHookStdin();
|
|
16424
16651
|
const sessionId = opts.sessionId ?? event.session_id ?? process.env.CLAUDE_SESSION_ID ?? null;
|
|
16652
|
+
if (deferredToPlugin("session end", sessionId)) process.exit(0);
|
|
16425
16653
|
const reason = opts.reason ?? event.session_end_reason ?? event.reason ?? "other";
|
|
16426
16654
|
if (reason === "clear") {
|
|
16427
16655
|
writeCarry(sessionId ?? void 0, getCurrentCommit());
|
|
@@ -16474,7 +16702,7 @@ function buildCompactionContext(session) {
|
|
|
16474
16702
|
readFileLines: (file) => {
|
|
16475
16703
|
try {
|
|
16476
16704
|
const abs = (0, import_node_path17.join)(root, file);
|
|
16477
|
-
return (0,
|
|
16705
|
+
return (0, import_node_fs21.existsSync)(abs) ? (0, import_node_fs21.readFileSync)(abs, "utf8").split("\n") : null;
|
|
16478
16706
|
} catch {
|
|
16479
16707
|
return null;
|
|
16480
16708
|
}
|
|
@@ -16531,18 +16759,18 @@ async function readHookStdin() {
|
|
|
16531
16759
|
|
|
16532
16760
|
// src/commands/standard.ts
|
|
16533
16761
|
var import_promises12 = require("node:fs/promises");
|
|
16534
|
-
var
|
|
16762
|
+
var import_node_fs28 = require("node:fs");
|
|
16535
16763
|
var import_yaml3 = __toESM(require_dist());
|
|
16536
16764
|
|
|
16537
16765
|
// src/lib/synthesize.ts
|
|
16538
16766
|
var import_node_child_process8 = require("node:child_process");
|
|
16539
|
-
var
|
|
16767
|
+
var import_node_fs24 = require("node:fs");
|
|
16540
16768
|
var import_promises9 = require("node:fs/promises");
|
|
16541
16769
|
var import_node_path20 = require("node:path");
|
|
16542
16770
|
var import_yaml = __toESM(require_dist());
|
|
16543
16771
|
|
|
16544
16772
|
// src/lib/data-dir.ts
|
|
16545
|
-
var
|
|
16773
|
+
var import_node_fs22 = require("node:fs");
|
|
16546
16774
|
var import_node_path18 = require("node:path");
|
|
16547
16775
|
function resolveDataDir() {
|
|
16548
16776
|
const candidates = [
|
|
@@ -16550,10 +16778,21 @@ function resolveDataDir() {
|
|
|
16550
16778
|
// installed: node_modules/@codacy/verity-cli/data
|
|
16551
16779
|
(0, import_node_path18.join)(__dirname, "..", "..", "data"),
|
|
16552
16780
|
// edge case: nested resolution
|
|
16781
|
+
// THE COMMITTED SOURCE, for a source checkout that has not been built.
|
|
16782
|
+
// cli/data/skills/ is a BUILD ARTIFACT (scripts/build.js copies client/skills
|
|
16783
|
+
// into it) and is gitignored, because skills have one committed source — so
|
|
16784
|
+
// in a fresh clone the packaged candidates above do not exist at all, and
|
|
16785
|
+
// without this the synthesizer throws "Could not find Verity skill data"
|
|
16786
|
+
// for every test and every `verity` run from source. Resolved from this
|
|
16787
|
+
// module's own location, never the cwd: see the warning below.
|
|
16788
|
+
(0, import_node_path18.join)(__dirname, "..", "..", "client"),
|
|
16789
|
+
// bundled: cli/bin/ → ../../client
|
|
16790
|
+
(0, import_node_path18.join)(__dirname, "..", "..", "..", "client"),
|
|
16791
|
+
// tsx: cli/src/lib/ → ../../../client
|
|
16553
16792
|
...process.env.VERITY_DEV_DATA_DIR ? [process.env.VERITY_DEV_DATA_DIR] : []
|
|
16554
16793
|
];
|
|
16555
16794
|
for (const candidate of candidates) {
|
|
16556
|
-
if ((0,
|
|
16795
|
+
if ((0, import_node_fs22.existsSync)((0, import_node_path18.join)(candidate, "skills"))) {
|
|
16557
16796
|
return candidate;
|
|
16558
16797
|
}
|
|
16559
16798
|
}
|
|
@@ -16567,7 +16806,7 @@ function setupDataPath(file) {
|
|
|
16567
16806
|
|
|
16568
16807
|
// src/lib/detect.ts
|
|
16569
16808
|
var import_node_child_process7 = require("node:child_process");
|
|
16570
|
-
var
|
|
16809
|
+
var import_node_fs23 = require("node:fs");
|
|
16571
16810
|
var import_node_path19 = require("node:path");
|
|
16572
16811
|
var TOOLED_LANGUAGES = /* @__PURE__ */ new Set([
|
|
16573
16812
|
"typescript",
|
|
@@ -16624,7 +16863,7 @@ function walk(root) {
|
|
|
16624
16863
|
if (depth > WALK_MAX_DEPTH || found.length >= WALK_MAX_FILES) return;
|
|
16625
16864
|
let entries;
|
|
16626
16865
|
try {
|
|
16627
|
-
entries = (0,
|
|
16866
|
+
entries = (0, import_node_fs23.readdirSync)(dir, { withFileTypes: true });
|
|
16628
16867
|
} catch {
|
|
16629
16868
|
return;
|
|
16630
16869
|
}
|
|
@@ -16699,7 +16938,7 @@ var TOOL_CONFIG_MARKERS = [
|
|
|
16699
16938
|
];
|
|
16700
16939
|
function readJson(path) {
|
|
16701
16940
|
try {
|
|
16702
|
-
return JSON.parse((0,
|
|
16941
|
+
return JSON.parse((0, import_node_fs23.readFileSync)(path, "utf-8"));
|
|
16703
16942
|
} catch {
|
|
16704
16943
|
return null;
|
|
16705
16944
|
}
|
|
@@ -16732,9 +16971,9 @@ function declaredDependencies(root, files) {
|
|
|
16732
16971
|
...files.filter((f) => f.includes("/") && /(^|\/)(pyproject\.toml|requirements\.txt)$/.test(f)).slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path19.join)(root, f))
|
|
16733
16972
|
];
|
|
16734
16973
|
for (const path of pythonManifests) {
|
|
16735
|
-
if (!(0,
|
|
16974
|
+
if (!(0, import_node_fs23.existsSync)(path)) continue;
|
|
16736
16975
|
try {
|
|
16737
|
-
const text = (0,
|
|
16976
|
+
const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
|
|
16738
16977
|
for (const m of text.matchAll(/^\s*["']?([A-Za-z][A-Za-z0-9._-]+)/gm)) names2.push(m[1]);
|
|
16739
16978
|
for (const line of text.split("\n")) {
|
|
16740
16979
|
if (!/dependencies\s*=/.test(line)) continue;
|
|
@@ -16748,9 +16987,9 @@ function declaredDependencies(root, files) {
|
|
|
16748
16987
|
...files.filter((f) => f.includes("/") && (0, import_node_path19.basename)(f) === "go.mod").slice(0, NESTED_MANIFEST_LIMIT).map((f) => (0, import_node_path19.join)(root, f))
|
|
16749
16988
|
];
|
|
16750
16989
|
for (const path of goMods) {
|
|
16751
|
-
if (!(0,
|
|
16990
|
+
if (!(0, import_node_fs23.existsSync)(path)) continue;
|
|
16752
16991
|
try {
|
|
16753
|
-
const text = (0,
|
|
16992
|
+
const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
|
|
16754
16993
|
for (const m of text.matchAll(/^\s+([\w.-]+\/[\w./-]+)\s+v/gm)) {
|
|
16755
16994
|
names2.push(m[1].replace(/^github\.com\//, ""));
|
|
16756
16995
|
}
|
|
@@ -16759,9 +16998,9 @@ function declaredDependencies(root, files) {
|
|
|
16759
16998
|
}
|
|
16760
16999
|
for (const file of ["pom.xml", "build.gradle", "build.gradle.kts", "Gemfile", "Cargo.toml"]) {
|
|
16761
17000
|
const path = (0, import_node_path19.join)(root, file);
|
|
16762
|
-
if (!(0,
|
|
17001
|
+
if (!(0, import_node_fs23.existsSync)(path)) continue;
|
|
16763
17002
|
try {
|
|
16764
|
-
const text = (0,
|
|
17003
|
+
const text = (0, import_node_fs23.readFileSync)(path, "utf-8");
|
|
16765
17004
|
for (const m of text.matchAll(/["'<]([A-Za-z][A-Za-z0-9._-]{2,})["'>]/g)) names2.push(m[1]);
|
|
16766
17005
|
} catch {
|
|
16767
17006
|
}
|
|
@@ -16769,7 +17008,7 @@ function declaredDependencies(root, files) {
|
|
|
16769
17008
|
return names2;
|
|
16770
17009
|
}
|
|
16771
17010
|
function detectBuildSystem(root, files) {
|
|
16772
|
-
const has = (f) => (0,
|
|
17011
|
+
const has = (f) => (0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, f)) || files.some((p) => (0, import_node_path19.basename)(p) === f);
|
|
16773
17012
|
if (has("pnpm-lock.yaml")) return "pnpm";
|
|
16774
17013
|
if (has("yarn.lock")) return "yarn";
|
|
16775
17014
|
if (has("bun.lock") || has("bun.lockb")) return "bun";
|
|
@@ -16786,7 +17025,7 @@ function detectBuildSystem(root, files) {
|
|
|
16786
17025
|
}
|
|
16787
17026
|
function detectArchitecture(root, files) {
|
|
16788
17027
|
const workspaceMarkers = ["lerna.json", "pnpm-workspace.yaml", "nx.json", "turbo.json", "rush.json"];
|
|
16789
|
-
if (workspaceMarkers.some((m) => (0,
|
|
17028
|
+
if (workspaceMarkers.some((m) => (0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, m)))) return "monorepo";
|
|
16790
17029
|
const pkg = readJson((0, import_node_path19.join)(root, "package.json"));
|
|
16791
17030
|
if (pkg && "workspaces" in pkg) return "monorepo";
|
|
16792
17031
|
const manifests = files.filter((f) => /(^|\/)(package\.json|go\.mod|pyproject\.toml|Cargo\.toml|pom\.xml)$/.test(f));
|
|
@@ -16811,8 +17050,8 @@ function measureAvgFileLength(root, files, languages) {
|
|
|
16811
17050
|
for (let i = 0; i < candidates.length; i += stride) {
|
|
16812
17051
|
const path = (0, import_node_path19.join)(root, candidates[i]);
|
|
16813
17052
|
try {
|
|
16814
|
-
if ((0,
|
|
16815
|
-
total += (0,
|
|
17053
|
+
if ((0, import_node_fs23.statSync)(path).size > 2 * 1024 * 1024) continue;
|
|
17054
|
+
total += (0, import_node_fs23.readFileSync)(path, "utf-8").split("\n").length;
|
|
16816
17055
|
counted++;
|
|
16817
17056
|
} catch {
|
|
16818
17057
|
}
|
|
@@ -16837,7 +17076,7 @@ function detectProject(root = repoRoot()) {
|
|
|
16837
17076
|
const existingToolConfigs = [];
|
|
16838
17077
|
for (const [tool, markers] of TOOL_CONFIG_MARKERS) {
|
|
16839
17078
|
for (const marker of markers) {
|
|
16840
|
-
if ((0,
|
|
17079
|
+
if ((0, import_node_fs23.existsSync)((0, import_node_path19.join)(root, marker))) {
|
|
16841
17080
|
existingToolConfigs.push({ tool, path: `./${marker}` });
|
|
16842
17081
|
break;
|
|
16843
17082
|
}
|
|
@@ -16942,8 +17181,8 @@ ${closingNote(input.origin)}
|
|
|
16942
17181
|
|
|
16943
17182
|
// src/lib/synthesize.ts
|
|
16944
17183
|
function loadCatalog() {
|
|
16945
|
-
const catalog = (0, import_yaml.parse)((0,
|
|
16946
|
-
const template = (0, import_yaml.parse)((0,
|
|
17184
|
+
const catalog = (0, import_yaml.parse)((0, import_node_fs24.readFileSync)(setupDataPath("patterns-reference.yaml"), "utf-8"));
|
|
17185
|
+
const template = (0, import_yaml.parse)((0, import_node_fs24.readFileSync)(setupDataPath("standard-template.yaml"), "utf-8"));
|
|
16947
17186
|
return { catalog, template };
|
|
16948
17187
|
}
|
|
16949
17188
|
function selectTools(languages, intensity, catalog) {
|
|
@@ -17221,7 +17460,7 @@ function validatePatternIds() {
|
|
|
17221
17460
|
}
|
|
17222
17461
|
async function runSynthesis(opts) {
|
|
17223
17462
|
const standardPath = projectPath(STANDARD_FILE);
|
|
17224
|
-
if ((0,
|
|
17463
|
+
if ((0, import_node_fs24.existsSync)(standardPath) && !opts.force) {
|
|
17225
17464
|
return { refused: `${STANDARD_FILE} already exists \u2014 pass --force to replace it.` };
|
|
17226
17465
|
}
|
|
17227
17466
|
const detected = opts.detected ?? detectProject();
|
|
@@ -17349,11 +17588,11 @@ ${validation.detail}`);
|
|
|
17349
17588
|
|
|
17350
17589
|
// src/lib/setup-state.ts
|
|
17351
17590
|
var import_promises10 = require("node:fs/promises");
|
|
17352
|
-
var
|
|
17591
|
+
var import_node_fs25 = require("node:fs");
|
|
17353
17592
|
var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
|
|
17354
17593
|
async function readSetupState() {
|
|
17355
17594
|
const path = projectPath(SETUP_STATE_FILE);
|
|
17356
|
-
if (!(0,
|
|
17595
|
+
if (!(0, import_node_fs25.existsSync)(path)) return null;
|
|
17357
17596
|
try {
|
|
17358
17597
|
const parsed = JSON.parse(await (0, import_promises10.readFile)(path, "utf-8"));
|
|
17359
17598
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
@@ -17369,12 +17608,12 @@ async function writeSetupState(patch) {
|
|
|
17369
17608
|
}
|
|
17370
17609
|
|
|
17371
17610
|
// src/lib/push-setup.ts
|
|
17372
|
-
var
|
|
17611
|
+
var import_node_fs27 = require("node:fs");
|
|
17373
17612
|
var import_promises11 = require("node:fs/promises");
|
|
17374
17613
|
var import_yaml2 = __toESM(require_dist());
|
|
17375
17614
|
|
|
17376
17615
|
// src/lib/verityignore.ts
|
|
17377
|
-
var
|
|
17616
|
+
var import_node_fs26 = require("node:fs");
|
|
17378
17617
|
var EMPTY = { rules: [], securityOverlap: [], problems: [] };
|
|
17379
17618
|
var SECURITY_PROBES = [
|
|
17380
17619
|
".env",
|
|
@@ -17467,9 +17706,9 @@ function isIgnored2(ig, path) {
|
|
|
17467
17706
|
}
|
|
17468
17707
|
function loadVerityIgnore() {
|
|
17469
17708
|
const file = projectPath(VERITYIGNORE_FILE);
|
|
17470
|
-
if (!(0,
|
|
17709
|
+
if (!(0, import_node_fs26.existsSync)(file)) return EMPTY;
|
|
17471
17710
|
try {
|
|
17472
|
-
return parseVerityIgnore((0,
|
|
17711
|
+
return parseVerityIgnore((0, import_node_fs26.readFileSync)(file, "utf-8"));
|
|
17473
17712
|
} catch {
|
|
17474
17713
|
return EMPTY;
|
|
17475
17714
|
}
|
|
@@ -17507,9 +17746,9 @@ function buildStandardUpload(standard, ignoreRaw) {
|
|
|
17507
17746
|
}
|
|
17508
17747
|
function readVerityIgnoreRaw() {
|
|
17509
17748
|
const file = projectPath(VERITYIGNORE_FILE);
|
|
17510
|
-
if (!(0,
|
|
17749
|
+
if (!(0, import_node_fs26.existsSync)(file)) return null;
|
|
17511
17750
|
try {
|
|
17512
|
-
return (0,
|
|
17751
|
+
return (0, import_node_fs26.readFileSync)(file, "utf-8");
|
|
17513
17752
|
} catch {
|
|
17514
17753
|
return null;
|
|
17515
17754
|
}
|
|
@@ -17537,7 +17776,7 @@ async function pushStandardAndConfig(globals, what = {}) {
|
|
|
17537
17776
|
}
|
|
17538
17777
|
let standardVersion = null;
|
|
17539
17778
|
const standardPath = projectPath(STANDARD_FILE);
|
|
17540
|
-
if (pushStandard && (0,
|
|
17779
|
+
if (pushStandard && (0, import_node_fs27.existsSync)(standardPath)) {
|
|
17541
17780
|
try {
|
|
17542
17781
|
const content = (0, import_yaml2.parse)(await (0, import_promises11.readFile)(standardPath, "utf-8"));
|
|
17543
17782
|
const upload = buildStandardUpload(content, readVerityIgnoreRaw());
|
|
@@ -17562,7 +17801,7 @@ async function pushStandardAndConfig(globals, what = {}) {
|
|
|
17562
17801
|
}
|
|
17563
17802
|
let configPushed = false;
|
|
17564
17803
|
const configPath = projectPath(CODACY_CONFIG_FILE);
|
|
17565
|
-
if (pushConfig && (0,
|
|
17804
|
+
if (pushConfig && (0, import_node_fs27.existsSync)(configPath)) {
|
|
17566
17805
|
try {
|
|
17567
17806
|
const content = JSON.parse(await (0, import_promises11.readFile)(configPath, "utf-8"));
|
|
17568
17807
|
const result = await apiRequest({
|
|
@@ -17594,7 +17833,7 @@ function registerStandardCommands(program2) {
|
|
|
17594
17833
|
const state = await readSetupState();
|
|
17595
17834
|
if (opts.configOnly) {
|
|
17596
17835
|
const standardPath = projectPath(STANDARD_FILE);
|
|
17597
|
-
if (!(0,
|
|
17836
|
+
if (!(0, import_node_fs28.existsSync)(standardPath)) {
|
|
17598
17837
|
printError(`No ${STANDARD_FILE} here \u2014 run "verity standard synthesize" to create one.`);
|
|
17599
17838
|
process.exit(1);
|
|
17600
17839
|
}
|
|
@@ -17763,6 +18002,22 @@ function registerConfigCommands(program2) {
|
|
|
17763
18002
|
}
|
|
17764
18003
|
process.stdout.write(urlResult.data + "\n");
|
|
17765
18004
|
});
|
|
18005
|
+
config.command("git-moments [moments]").description('Get or set the git moments the guard reviews: commit,push \u2014 or "none"').action((moments) => {
|
|
18006
|
+
if (moments === void 0) {
|
|
18007
|
+
const current = readProjectConfig().git_moments;
|
|
18008
|
+
process.stdout.write((current.length ? current.join(",") : "none") + "\n");
|
|
18009
|
+
return;
|
|
18010
|
+
}
|
|
18011
|
+
const next = moments === "none" ? [] : parseMoments(moments);
|
|
18012
|
+
if (moments !== "none" && next.length === 0) {
|
|
18013
|
+
printError(`Unrecognised moments: ${moments}. Use "commit", "push", "commit,push", or "none".`);
|
|
18014
|
+
process.exit(1);
|
|
18015
|
+
}
|
|
18016
|
+
writeProjectConfig({ git_moments: next });
|
|
18017
|
+
printInfo(
|
|
18018
|
+
next.length ? `Git-moment review enabled for: ${next.join(", ")}` : "Git-moment review disabled \u2014 commits and pushes are no longer gated."
|
|
18019
|
+
);
|
|
18020
|
+
});
|
|
17766
18021
|
config.command("push").description("Upload the analysis config to the service").option("--file <path>", "Path to config file", CODACY_CONFIG_FILE).action(async (opts) => {
|
|
17767
18022
|
const globals = program2.opts();
|
|
17768
18023
|
const tokenResult = await resolveToken(globals.token);
|
|
@@ -17905,10 +18160,10 @@ function formatRunDetail(run2) {
|
|
|
17905
18160
|
}
|
|
17906
18161
|
|
|
17907
18162
|
// src/lib/ignore-declaration.ts
|
|
17908
|
-
var
|
|
18163
|
+
var import_node_fs30 = require("node:fs");
|
|
17909
18164
|
|
|
17910
18165
|
// src/lib/debounce.ts
|
|
17911
|
-
var
|
|
18166
|
+
var import_node_fs29 = require("node:fs");
|
|
17912
18167
|
var import_node_crypto10 = require("node:crypto");
|
|
17913
18168
|
function scopedFile(base, sessionId) {
|
|
17914
18169
|
if (!sessionId) return base;
|
|
@@ -17916,9 +18171,9 @@ function scopedFile(base, sessionId) {
|
|
|
17916
18171
|
}
|
|
17917
18172
|
function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
17918
18173
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
17919
|
-
if (!(0,
|
|
18174
|
+
if (!(0, import_node_fs29.existsSync)(file)) return null;
|
|
17920
18175
|
try {
|
|
17921
|
-
const lastTs = parseInt((0,
|
|
18176
|
+
const lastTs = parseInt((0, import_node_fs29.readFileSync)(file, "utf-8").trim(), 10);
|
|
17922
18177
|
const nowTs = Math.floor(Date.now() / 1e3);
|
|
17923
18178
|
const elapsed = nowTs - lastTs;
|
|
17924
18179
|
if (elapsed < debounceSeconds) {
|
|
@@ -17931,10 +18186,10 @@ function checkDebounce(debounceSeconds = DEBOUNCE_SECONDS, sessionId) {
|
|
|
17931
18186
|
function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
17932
18187
|
if (bypassForRecentCommits) return null;
|
|
17933
18188
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
17934
|
-
if (!(0,
|
|
18189
|
+
if (!(0, import_node_fs29.existsSync)(file)) return null;
|
|
17935
18190
|
let debounceTime;
|
|
17936
18191
|
try {
|
|
17937
|
-
debounceTime = (0,
|
|
18192
|
+
debounceTime = (0, import_node_fs29.statSync)(file).mtimeMs;
|
|
17938
18193
|
} catch {
|
|
17939
18194
|
return null;
|
|
17940
18195
|
}
|
|
@@ -17942,7 +18197,7 @@ function checkMtime(files, bypassForRecentCommits, sessionId) {
|
|
|
17942
18197
|
const resolved = resolveFile(f);
|
|
17943
18198
|
if (!resolved) continue;
|
|
17944
18199
|
try {
|
|
17945
|
-
const stat3 = (0,
|
|
18200
|
+
const stat3 = (0, import_node_fs29.statSync)(resolved);
|
|
17946
18201
|
if (stat3.mtimeMs > debounceTime) {
|
|
17947
18202
|
return null;
|
|
17948
18203
|
}
|
|
@@ -17958,8 +18213,8 @@ function computeContentHash(files) {
|
|
|
17958
18213
|
for (const f of sorted) {
|
|
17959
18214
|
const resolved = resolveFile(f) ?? f;
|
|
17960
18215
|
try {
|
|
17961
|
-
if ((0,
|
|
17962
|
-
hash.update((0,
|
|
18216
|
+
if ((0, import_node_fs29.existsSync)(resolved)) {
|
|
18217
|
+
hash.update((0, import_node_fs29.readFileSync)(resolved));
|
|
17963
18218
|
}
|
|
17964
18219
|
} catch {
|
|
17965
18220
|
}
|
|
@@ -17969,9 +18224,9 @@ function computeContentHash(files) {
|
|
|
17969
18224
|
function checkContentHash(files, sessionId) {
|
|
17970
18225
|
const hash = computeContentHash(files);
|
|
17971
18226
|
const file = scopedFile(HASH_FILE, sessionId);
|
|
17972
|
-
if ((0,
|
|
18227
|
+
if ((0, import_node_fs29.existsSync)(file)) {
|
|
17973
18228
|
try {
|
|
17974
|
-
const storedHash = (0,
|
|
18229
|
+
const storedHash = (0, import_node_fs29.readFileSync)(file, "utf-8").trim();
|
|
17975
18230
|
if (hash === storedHash) {
|
|
17976
18231
|
return { skip: "No source changes since last analysis", hash };
|
|
17977
18232
|
}
|
|
@@ -17981,24 +18236,24 @@ function checkContentHash(files, sessionId) {
|
|
|
17981
18236
|
return { skip: null, hash };
|
|
17982
18237
|
}
|
|
17983
18238
|
function recordAnalysisStart(sessionId) {
|
|
17984
|
-
(0,
|
|
17985
|
-
(0,
|
|
18239
|
+
(0, import_node_fs29.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18240
|
+
(0, import_node_fs29.writeFileSync)(scopedFile(DEBOUNCE_FILE, sessionId), String(Math.floor(Date.now() / 1e3)));
|
|
17986
18241
|
}
|
|
17987
18242
|
function recordPassHash(hash, sessionId) {
|
|
17988
|
-
(0,
|
|
18243
|
+
(0, import_node_fs29.writeFileSync)(scopedFile(HASH_FILE, sessionId), hash);
|
|
17989
18244
|
}
|
|
17990
18245
|
function narrowToRecent(files, sessionId) {
|
|
17991
18246
|
const file = scopedFile(DEBOUNCE_FILE, sessionId);
|
|
17992
|
-
if (!(0,
|
|
18247
|
+
if (!(0, import_node_fs29.existsSync)(file)) return files;
|
|
17993
18248
|
let debounceTime;
|
|
17994
18249
|
try {
|
|
17995
|
-
debounceTime = (0,
|
|
18250
|
+
debounceTime = (0, import_node_fs29.statSync)(file).mtimeMs;
|
|
17996
18251
|
} catch {
|
|
17997
18252
|
return files;
|
|
17998
18253
|
}
|
|
17999
18254
|
const recent = files.filter((f) => {
|
|
18000
18255
|
try {
|
|
18001
|
-
return (0,
|
|
18256
|
+
return (0, import_node_fs29.existsSync)(f) && (0, import_node_fs29.statSync)(f).mtimeMs > debounceTime;
|
|
18002
18257
|
} catch {
|
|
18003
18258
|
return false;
|
|
18004
18259
|
}
|
|
@@ -18011,9 +18266,9 @@ function readIteration(currentCommit, _contentHash) {
|
|
|
18011
18266
|
var NO_BLOCKS = { attempts: 0, blocks: 0, fingerprint: null };
|
|
18012
18267
|
function readBlockState(currentCommit, opts) {
|
|
18013
18268
|
if (opts?.newUserPrompt) return NO_BLOCKS;
|
|
18014
|
-
if (!(0,
|
|
18269
|
+
if (!(0, import_node_fs29.existsSync)(ITERATION_FILE)) return NO_BLOCKS;
|
|
18015
18270
|
try {
|
|
18016
|
-
const stored = (0,
|
|
18271
|
+
const stored = (0, import_node_fs29.readFileSync)(ITERATION_FILE, "utf-8").trim();
|
|
18017
18272
|
const parsed = stored.startsWith("{") ? parseJsonState(stored) : parseLegacyState(stored);
|
|
18018
18273
|
if (!parsed) return NO_BLOCKS;
|
|
18019
18274
|
if (parsed.commit !== currentCommit) return NO_BLOCKS;
|
|
@@ -18059,8 +18314,8 @@ function isSameProblem(previous, current) {
|
|
|
18059
18314
|
return current.split(",").some((k) => prev.has(k));
|
|
18060
18315
|
}
|
|
18061
18316
|
function writeBlockState(commit, state) {
|
|
18062
|
-
(0,
|
|
18063
|
-
(0,
|
|
18317
|
+
(0, import_node_fs29.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
18318
|
+
(0, import_node_fs29.writeFileSync)(
|
|
18064
18319
|
ITERATION_FILE,
|
|
18065
18320
|
JSON.stringify({
|
|
18066
18321
|
v: 2,
|
|
@@ -18165,9 +18420,9 @@ function resolveIgnoreState(keys) {
|
|
|
18165
18420
|
}
|
|
18166
18421
|
function readIgnoreState(sessionId) {
|
|
18167
18422
|
const file = stateFile(sessionId);
|
|
18168
|
-
if (!(0,
|
|
18423
|
+
if (!(0, import_node_fs30.existsSync)(file)) return null;
|
|
18169
18424
|
try {
|
|
18170
|
-
const o = JSON.parse((0,
|
|
18425
|
+
const o = JSON.parse((0, import_node_fs30.readFileSync)(file, "utf-8")) ?? {};
|
|
18171
18426
|
const spent = typeof o.spent === "number" ? o.spent : 0;
|
|
18172
18427
|
const raw = o.active;
|
|
18173
18428
|
let active = null;
|
|
@@ -18191,8 +18446,8 @@ function readIgnoreState(sessionId) {
|
|
|
18191
18446
|
}
|
|
18192
18447
|
function writeIgnoreState(state, sessionId) {
|
|
18193
18448
|
try {
|
|
18194
|
-
(0,
|
|
18195
|
-
(0,
|
|
18449
|
+
(0, import_node_fs30.mkdirSync)(projectPath(VERITY_DIR), { recursive: true });
|
|
18450
|
+
(0, import_node_fs30.writeFileSync)(stateFile(sessionId), JSON.stringify({ v: 1, active: state.active, spent: state.spent }));
|
|
18196
18451
|
} catch {
|
|
18197
18452
|
}
|
|
18198
18453
|
}
|
|
@@ -18566,59 +18821,8 @@ function createRun(opts, globals) {
|
|
|
18566
18821
|
};
|
|
18567
18822
|
}
|
|
18568
18823
|
|
|
18569
|
-
// src/
|
|
18570
|
-
var
|
|
18571
|
-
var TOKEN_RE2 = /verity_[0-9a-f]{16,}/g;
|
|
18572
|
-
var ANSI_RE = /\u001b\[[0-?]*[ -/]*[@-~]/g;
|
|
18573
|
-
function scrub(s) {
|
|
18574
|
-
return s.replace(TOKEN_RE2, "verity_***REDACTED***").replace(ANSI_RE, "");
|
|
18575
|
-
}
|
|
18576
|
-
var installed = false;
|
|
18577
|
-
var wroteBanner = false;
|
|
18578
|
-
var banner = "";
|
|
18579
|
-
function append(text) {
|
|
18580
|
-
try {
|
|
18581
|
-
const dir = projectPath(DEBUG_LOG_DIR);
|
|
18582
|
-
const file = projectPath(STDERR_LOG_FILE);
|
|
18583
|
-
(0, import_node_fs28.mkdirSync)(dir, { recursive: true });
|
|
18584
|
-
rotateIfNeeded(file);
|
|
18585
|
-
(0, import_node_fs28.appendFileSync)(file, text);
|
|
18586
|
-
} catch {
|
|
18587
|
-
}
|
|
18588
|
-
}
|
|
18589
|
-
function ensureBanner() {
|
|
18590
|
-
if (wroteBanner) return;
|
|
18591
|
-
wroteBanner = true;
|
|
18592
|
-
append(banner);
|
|
18593
|
-
}
|
|
18594
|
-
function installStderrLog(cmd, argv, version) {
|
|
18595
|
-
if (installed || !isDebugEnabled()) return;
|
|
18596
|
-
installed = true;
|
|
18597
|
-
banner = `
|
|
18598
|
-
\u2501\u2501 verity ${cmd} \xB7 ${(/* @__PURE__ */ new Date()).toISOString()} \xB7 pid ${process.pid}
|
|
18599
|
-
v${version} \xB7 ${process.cwd()}
|
|
18600
|
-
argv: ${scrub(argv.join(" "))}
|
|
18601
|
-
`;
|
|
18602
|
-
const original = process.stderr.write.bind(process.stderr);
|
|
18603
|
-
const tee = (...args) => {
|
|
18604
|
-
const result = original(...args);
|
|
18605
|
-
try {
|
|
18606
|
-
const chunk = args[0];
|
|
18607
|
-
const text = typeof chunk === "string" ? chunk : Buffer.isBuffer(chunk) ? chunk.toString("utf-8") : String(chunk);
|
|
18608
|
-
ensureBanner();
|
|
18609
|
-
append(scrub(text));
|
|
18610
|
-
} catch {
|
|
18611
|
-
}
|
|
18612
|
-
return result;
|
|
18613
|
-
};
|
|
18614
|
-
process.stderr.write = tee;
|
|
18615
|
-
}
|
|
18616
|
-
function logToFileOnly(text) {
|
|
18617
|
-
if (!installed) return;
|
|
18618
|
-
ensureBanner();
|
|
18619
|
-
append(text.endsWith("\n") ? text : `${text}
|
|
18620
|
-
`);
|
|
18621
|
-
}
|
|
18824
|
+
// src/commands/analyze/index.ts
|
|
18825
|
+
var import_node_fs43 = require("node:fs");
|
|
18622
18826
|
|
|
18623
18827
|
// src/lib/repo-context.ts
|
|
18624
18828
|
var import_node_child_process9 = require("node:child_process");
|
|
@@ -19439,7 +19643,7 @@ function installRunEvidence(run2) {
|
|
|
19439
19643
|
|
|
19440
19644
|
// src/lib/git-frame.ts
|
|
19441
19645
|
var import_node_child_process10 = require("node:child_process");
|
|
19442
|
-
var
|
|
19646
|
+
var import_node_fs31 = require("node:fs");
|
|
19443
19647
|
var import_node_os4 = require("node:os");
|
|
19444
19648
|
var import_node_path21 = require("node:path");
|
|
19445
19649
|
var import_node_path22 = require("node:path");
|
|
@@ -19578,14 +19782,14 @@ function gitAt(dir, args) {
|
|
|
19578
19782
|
}
|
|
19579
19783
|
function realpathOr(p) {
|
|
19580
19784
|
try {
|
|
19581
|
-
return
|
|
19785
|
+
return import_node_fs31.realpathSync.native(p);
|
|
19582
19786
|
} catch {
|
|
19583
19787
|
return (0, import_node_path21.resolve)(p);
|
|
19584
19788
|
}
|
|
19585
19789
|
}
|
|
19586
19790
|
function resolveFrame(input) {
|
|
19587
19791
|
const found = findMomentSegment(input.command, input.on);
|
|
19588
|
-
const hookDirUsable = !!input.hookCwd && (0,
|
|
19792
|
+
const hookDirUsable = !!input.hookCwd && (0, import_node_fs31.existsSync)(input.hookCwd);
|
|
19589
19793
|
const baseDir = hookDirUsable ? input.hookCwd : process.cwd();
|
|
19590
19794
|
let anchor = hookDirUsable ? "hook-cwd" : "process-cwd";
|
|
19591
19795
|
const refuse = (refusal) => ({
|
|
@@ -19608,7 +19812,7 @@ function resolveFrame(input) {
|
|
|
19608
19812
|
if (dirs.size > 1) return refuse(`target:multiple ${found.moment} targets in one command`);
|
|
19609
19813
|
const targetDir = dirs.size === 1 ? [...dirs][0] : baseDir;
|
|
19610
19814
|
if (targetDir !== baseDir) {
|
|
19611
|
-
if (!(0,
|
|
19815
|
+
if (!(0, import_node_fs31.existsSync)(targetDir)) return refuse(`target:directory does not exist: ${targetDir}`);
|
|
19612
19816
|
dir = targetDir;
|
|
19613
19817
|
}
|
|
19614
19818
|
}
|
|
@@ -19644,21 +19848,19 @@ function refResolves(frame, ref) {
|
|
|
19644
19848
|
return frameGit(frame, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]) !== "";
|
|
19645
19849
|
}
|
|
19646
19850
|
var SHA_RE2 = /^[0-9a-f]{40}$/;
|
|
19647
|
-
function
|
|
19648
|
-
if (!frame.worktreeRoot) return null;
|
|
19649
|
-
|
|
19650
|
-
|
|
19651
|
-
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
return
|
|
19851
|
+
function stagedRange(frame) {
|
|
19852
|
+
if (!frame.worktreeRoot) return { kind: "nothing", base: null, head: "INDEX", via: "refused" };
|
|
19853
|
+
const mergeHead = frame.gitDir ? (0, import_node_path22.join)(frame.gitDir, "MERGE_HEAD") : null;
|
|
19854
|
+
if (mergeHead && (0, import_node_fs31.existsSync)(mergeHead)) {
|
|
19855
|
+
const vsHead = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "HEAD"]).split("\n").filter(Boolean));
|
|
19856
|
+
const vsMerge = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
|
|
19857
|
+
const resolutions = [...vsHead].filter((f) => vsMerge.has(f) && !isVerityOwnedPath(f));
|
|
19858
|
+
return { kind: "merge", base: "HEAD", head: "INDEX", via: "merge-resolutions", files: resolutions };
|
|
19655
19859
|
}
|
|
19656
|
-
}
|
|
19657
|
-
function stagedRange() {
|
|
19658
19860
|
return { kind: "staged", base: "HEAD", head: "INDEX", via: "index" };
|
|
19659
19861
|
}
|
|
19660
19862
|
function resolvePushRange(frame, command, on) {
|
|
19661
|
-
const nothing = (
|
|
19863
|
+
const nothing = (via2) => ({ kind: "nothing", base: null, head: "HEAD", via: via2 });
|
|
19662
19864
|
if (!frame.worktreeRoot) return nothing("refused");
|
|
19663
19865
|
const found = findMomentSegment(command, on);
|
|
19664
19866
|
const segment = found ? splitSegments(command)[found.segmentIndex] : "";
|
|
@@ -19666,54 +19868,32 @@ function resolvePushRange(frame, command, on) {
|
|
|
19666
19868
|
if (target.isDelete) return nothing("deletion");
|
|
19667
19869
|
const head = target.srcRef ?? "HEAD";
|
|
19668
19870
|
if (!refResolves(frame, head)) return nothing(`src-unresolvable:${head}`);
|
|
19669
|
-
const
|
|
19670
|
-
const
|
|
19671
|
-
const
|
|
19672
|
-
if (
|
|
19673
|
-
|
|
19674
|
-
|
|
19675
|
-
|
|
19676
|
-
|
|
19677
|
-
|
|
19678
|
-
|
|
19679
|
-
|
|
19680
|
-
candidates.push({ ref: srcName ? `${srcName}@{upstream}` : "@{upstream}", via: "@{upstream}" });
|
|
19681
|
-
if (branchForRemote) {
|
|
19682
|
-
candidates.push({
|
|
19683
|
-
ref: `refs/remotes/origin/${branchForRemote.replace(/^refs\/heads\//, "")}`,
|
|
19684
|
-
via: `origin/${branchForRemote.replace(/^refs\/heads\//, "")}`
|
|
19685
|
-
});
|
|
19686
|
-
}
|
|
19687
|
-
for (const c of candidates) {
|
|
19688
|
-
if (!refResolves(frame, c.ref)) continue;
|
|
19689
|
-
const mergeBase = frameGit(frame, ["merge-base", c.ref, head]);
|
|
19690
|
-
if (SHA_RE2.test(mergeBase)) return { kind: "push", base: mergeBase, head, via: c.via };
|
|
19871
|
+
const remotePattern = target.remote ? `--remotes=${target.remote}` : "--remotes";
|
|
19872
|
+
const via = target.remote ? `publication:${target.remote}` : "publication";
|
|
19873
|
+
const commits = frameGit(frame, ["rev-list", head, "--not", remotePattern]).split("\n").filter(Boolean);
|
|
19874
|
+
if (commits.length === 0) return { kind: "nothing", base: null, head, via: "already-published" };
|
|
19875
|
+
const boundary = frameGit(frame, ["rev-list", head, "--not", remotePattern, "--boundary"]).split("\n").filter((l) => l.startsWith("-")).map((l) => l.slice(1));
|
|
19876
|
+
const base = boundary.find((s) => SHA_RE2.test(s)) ?? null;
|
|
19877
|
+
const files = /* @__PURE__ */ new Set();
|
|
19878
|
+
for (const sha of commits) {
|
|
19879
|
+
for (const f of frameGit(frame, ["diff-tree", "--no-commit-id", "--name-only", "-r", sha]).split("\n")) {
|
|
19880
|
+
if (f && !isVerityOwnedPath(f)) files.add(f);
|
|
19881
|
+
}
|
|
19691
19882
|
}
|
|
19692
|
-
|
|
19693
|
-
if (baseline) return { kind: "baseline", base: baseline, head, via: "review-baseline" };
|
|
19694
|
-
if (refResolves(frame, `${head}~1`)) return { kind: "last-commit", base: `${head}~1`, head, via: `${head}~1` };
|
|
19695
|
-
return nothing("no-parent");
|
|
19883
|
+
return { kind: "push", base, head, via, files: [...files], commits };
|
|
19696
19884
|
}
|
|
19697
19885
|
function rangeFiles(frame, range) {
|
|
19698
|
-
|
|
19699
|
-
|
|
19700
|
-
|
|
19701
|
-
out = frameGit(frame, ["diff", "--cached", "--name-only"]);
|
|
19702
|
-
break;
|
|
19703
|
-
case "push":
|
|
19704
|
-
case "baseline":
|
|
19705
|
-
case "last-commit":
|
|
19706
|
-
out = frameGit(frame, ["diff", "--name-only", range.base, range.head === "INDEX" ? "HEAD" : range.head]);
|
|
19707
|
-
break;
|
|
19708
|
-
case "nothing":
|
|
19709
|
-
return [];
|
|
19886
|
+
if (range.files) return range.files.filter((f) => !isVerityOwnedPath(f));
|
|
19887
|
+
if (range.kind === "staged") {
|
|
19888
|
+
return frameGit(frame, ["diff", "--cached", "--name-only"]).split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
|
|
19710
19889
|
}
|
|
19711
|
-
return
|
|
19890
|
+
return [];
|
|
19712
19891
|
}
|
|
19713
19892
|
function rangeChangeSignals(frame, range, paths) {
|
|
19714
19893
|
const out = /* @__PURE__ */ new Map();
|
|
19715
19894
|
if (range.kind === "nothing" || paths.length === 0) return out;
|
|
19716
|
-
|
|
19895
|
+
if (range.kind === "push" && !range.base) return out;
|
|
19896
|
+
const args = range.kind === "staged" || range.kind === "merge" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
|
|
19717
19897
|
const diff = frameGit(frame, [...args, "--", ...paths]);
|
|
19718
19898
|
let current = null;
|
|
19719
19899
|
let oldSide = null;
|
|
@@ -19745,8 +19925,9 @@ function rangeChangeSignals(frame, range, paths) {
|
|
|
19745
19925
|
return out;
|
|
19746
19926
|
}
|
|
19747
19927
|
function rangeMessages(frame, range) {
|
|
19748
|
-
if (range.kind
|
|
19749
|
-
|
|
19928
|
+
if (range.kind !== "push" || !range.commits || range.commits.length === 0) return "";
|
|
19929
|
+
const commits = range.commits.slice(0, 100);
|
|
19930
|
+
return frameGit(frame, ["show", "-s", "--format=%B%x00", ...commits]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
|
|
19750
19931
|
}
|
|
19751
19932
|
function frameTelemetry(frame, range, divergence) {
|
|
19752
19933
|
const t = {
|
|
@@ -19794,7 +19975,7 @@ function truthy(v) {
|
|
|
19794
19975
|
}
|
|
19795
19976
|
|
|
19796
19977
|
// src/lib/transcript.ts
|
|
19797
|
-
var
|
|
19978
|
+
var import_node_fs32 = require("node:fs");
|
|
19798
19979
|
var MAX_READ_BYTES = 256 * 1024;
|
|
19799
19980
|
var SMALL_FILE_BYTES = 64 * 1024;
|
|
19800
19981
|
var MAX_FILES_LIST = 20;
|
|
@@ -19821,7 +20002,7 @@ async function extractActionSummary(transcriptPath) {
|
|
|
19821
20002
|
function readTurnLines(transcriptPath) {
|
|
19822
20003
|
let size;
|
|
19823
20004
|
try {
|
|
19824
|
-
size = (0,
|
|
20005
|
+
size = (0, import_node_fs32.statSync)(transcriptPath).size;
|
|
19825
20006
|
} catch {
|
|
19826
20007
|
return null;
|
|
19827
20008
|
}
|
|
@@ -19829,7 +20010,7 @@ function readTurnLines(transcriptPath) {
|
|
|
19829
20010
|
let raw;
|
|
19830
20011
|
let windowed = false;
|
|
19831
20012
|
if (size <= SMALL_FILE_BYTES) {
|
|
19832
|
-
raw = (0,
|
|
20013
|
+
raw = (0, import_node_fs32.readFileSync)(transcriptPath, "utf-8");
|
|
19833
20014
|
} else {
|
|
19834
20015
|
windowed = true;
|
|
19835
20016
|
const buf = Buffer.alloc(Math.min(MAX_READ_BYTES, size));
|
|
@@ -20146,6 +20327,9 @@ async function bootstrap(run2) {
|
|
|
20146
20327
|
isTTY: process.stdout.isTTY === true
|
|
20147
20328
|
});
|
|
20148
20329
|
const { assistantMessage: assistantResponse, stopReason, transcriptPath, sessionId } = await readStopHookStdin();
|
|
20330
|
+
if (deferredToPlugin("analyze", sessionId ?? process.env.CLAUDE_SESSION_ID ?? null)) {
|
|
20331
|
+
process.exit(0);
|
|
20332
|
+
}
|
|
20149
20333
|
const actionSummary = transcriptPath ? await extractActionSummary(transcriptPath) : null;
|
|
20150
20334
|
const tokenResult = await resolveToken(globals.token);
|
|
20151
20335
|
const scopeToken = tokenResult.ok ? tokenResult.data.token : void 0;
|
|
@@ -20325,7 +20509,7 @@ function channelSilence(input) {
|
|
|
20325
20509
|
// src/lib/cli-version.ts
|
|
20326
20510
|
function cliVersion() {
|
|
20327
20511
|
try {
|
|
20328
|
-
return true ? "0.32.0
|
|
20512
|
+
return true ? "0.32.0" : "dev";
|
|
20329
20513
|
} catch {
|
|
20330
20514
|
return "dev";
|
|
20331
20515
|
}
|
|
@@ -20366,7 +20550,7 @@ async function sendSkipBeacon(ctx, reason) {
|
|
|
20366
20550
|
|
|
20367
20551
|
// src/lib/static-analysis.ts
|
|
20368
20552
|
var import_node_child_process11 = require("node:child_process");
|
|
20369
|
-
var
|
|
20553
|
+
var import_node_fs33 = require("node:fs");
|
|
20370
20554
|
var SEVERITY_ORDER = {
|
|
20371
20555
|
Error: 0,
|
|
20372
20556
|
Critical: 0,
|
|
@@ -20414,7 +20598,7 @@ function runCodacyAnalysis(files) {
|
|
|
20414
20598
|
if (files.length === 0) return empty;
|
|
20415
20599
|
const existingFiles = files.filter((f) => {
|
|
20416
20600
|
try {
|
|
20417
|
-
return (0,
|
|
20601
|
+
return (0, import_node_fs33.existsSync)(f);
|
|
20418
20602
|
} catch {
|
|
20419
20603
|
return false;
|
|
20420
20604
|
}
|
|
@@ -20683,7 +20867,7 @@ async function scope(run2) {
|
|
|
20683
20867
|
}
|
|
20684
20868
|
|
|
20685
20869
|
// src/lib/specs.ts
|
|
20686
|
-
var
|
|
20870
|
+
var import_node_fs34 = require("node:fs");
|
|
20687
20871
|
var import_node_path23 = require("node:path");
|
|
20688
20872
|
var SPEC_CANDIDATES = [
|
|
20689
20873
|
"CLAUDE.md",
|
|
@@ -20715,16 +20899,16 @@ function discoverSpecs(consulted = []) {
|
|
|
20715
20899
|
const totalCap = relevant ? MAX_TOTAL_SPEC_BYTES : UNCONSULTED_TOTAL_BYTES;
|
|
20716
20900
|
if (totalBytes >= totalCap) return false;
|
|
20717
20901
|
if (seen.has(specPath)) return true;
|
|
20718
|
-
if (!(0,
|
|
20902
|
+
if (!(0, import_node_fs34.existsSync)(specPath)) return true;
|
|
20719
20903
|
seen.add(specPath);
|
|
20720
20904
|
const remaining = totalCap - totalBytes;
|
|
20721
20905
|
const fileCap = relevant ? MAX_SPEC_FILE_BYTES : UNCONSULTED_FILE_BYTES;
|
|
20722
20906
|
const readBytes = Math.min(fileCap, remaining);
|
|
20723
20907
|
try {
|
|
20724
20908
|
const buf = Buffer.alloc(readBytes);
|
|
20725
|
-
const fd = (0,
|
|
20726
|
-
const bytesRead = (0,
|
|
20727
|
-
(0,
|
|
20909
|
+
const fd = (0, import_node_fs34.openSync)(specPath, "r");
|
|
20910
|
+
const bytesRead = (0, import_node_fs34.readSync)(fd, buf, 0, readBytes, 0);
|
|
20911
|
+
(0, import_node_fs34.closeSync)(fd);
|
|
20728
20912
|
const content = buf.slice(0, bytesRead).toString("utf-8");
|
|
20729
20913
|
if (!content) return true;
|
|
20730
20914
|
result.push({ path: specPath, content });
|
|
@@ -20740,7 +20924,7 @@ function discoverSpecs(consulted = []) {
|
|
|
20740
20924
|
if (!addSpec(candidate)) break;
|
|
20741
20925
|
}
|
|
20742
20926
|
for (const dir of ["spec", "docs"]) {
|
|
20743
|
-
if (!(0,
|
|
20927
|
+
if (!(0, import_node_fs34.existsSync)(dir)) continue;
|
|
20744
20928
|
try {
|
|
20745
20929
|
const mdFiles = findMdFiles(dir, 2).sort();
|
|
20746
20930
|
for (const mdFile of mdFiles) {
|
|
@@ -20755,7 +20939,7 @@ function findMdFiles(dir, maxDepth, depth = 0) {
|
|
|
20755
20939
|
if (depth >= maxDepth) return [];
|
|
20756
20940
|
const result = [];
|
|
20757
20941
|
try {
|
|
20758
|
-
const entries = (0,
|
|
20942
|
+
const entries = (0, import_node_fs34.readdirSync)(dir, { withFileTypes: true });
|
|
20759
20943
|
for (const entry of entries) {
|
|
20760
20944
|
const fullPath = (0, import_node_path23.join)(dir, entry.name);
|
|
20761
20945
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -20774,14 +20958,14 @@ function discoverPlans() {
|
|
|
20774
20958
|
const candidates = [];
|
|
20775
20959
|
const seen = /* @__PURE__ */ new Set();
|
|
20776
20960
|
for (const plansDir of [localPlansDir, homePlansDir]) {
|
|
20777
|
-
if (!(0,
|
|
20961
|
+
if (!(0, import_node_fs34.existsSync)(plansDir)) continue;
|
|
20778
20962
|
try {
|
|
20779
|
-
for (const f of (0,
|
|
20963
|
+
for (const f of (0, import_node_fs34.readdirSync)(plansDir)) {
|
|
20780
20964
|
if (!f.endsWith(".md") || seen.has(f)) continue;
|
|
20781
20965
|
seen.add(f);
|
|
20782
20966
|
const fullPath = (0, import_node_path23.join)(plansDir, f);
|
|
20783
20967
|
try {
|
|
20784
|
-
const stat3 = (0,
|
|
20968
|
+
const stat3 = (0, import_node_fs34.statSync)(fullPath);
|
|
20785
20969
|
candidates.push({ name: f, path: fullPath, mtime: stat3.mtimeMs, size: stat3.size });
|
|
20786
20970
|
} catch {
|
|
20787
20971
|
}
|
|
@@ -20794,7 +20978,7 @@ function discoverPlans() {
|
|
|
20794
20978
|
for (const entry of candidates.slice(0, MAX_PLAN_FILES)) {
|
|
20795
20979
|
if (entry.size > MAX_PLAN_FILE_BYTES) continue;
|
|
20796
20980
|
try {
|
|
20797
|
-
const content = (0,
|
|
20981
|
+
const content = (0, import_node_fs34.readFileSync)(entry.path, "utf-8");
|
|
20798
20982
|
result.push({ name: entry.name, content });
|
|
20799
20983
|
} catch {
|
|
20800
20984
|
}
|
|
@@ -20809,12 +20993,12 @@ function discoverGuardDocs(rangeFiles2) {
|
|
|
20809
20993
|
if (result.length >= MAX_SPEC_FILES) break;
|
|
20810
20994
|
if (!GUARD_DOC_EXT.test(path)) continue;
|
|
20811
20995
|
if (path.startsWith("/") || path.includes("..")) continue;
|
|
20812
|
-
if (!(0,
|
|
20996
|
+
if (!(0, import_node_fs34.existsSync)(path)) continue;
|
|
20813
20997
|
try {
|
|
20814
|
-
const stat3 = (0,
|
|
20998
|
+
const stat3 = (0, import_node_fs34.statSync)(path);
|
|
20815
20999
|
if (stat3.size > MAX_PLAN_FILE_BYTES) continue;
|
|
20816
21000
|
if (totalBytes + stat3.size > MAX_TOTAL_SPEC_BYTES) continue;
|
|
20817
|
-
const content = (0,
|
|
21001
|
+
const content = (0, import_node_fs34.readFileSync)(path, "utf-8");
|
|
20818
21002
|
if (!content) continue;
|
|
20819
21003
|
result.push({ name: path, content });
|
|
20820
21004
|
totalBytes += content.length;
|
|
@@ -20990,7 +21174,7 @@ async function mode(run2) {
|
|
|
20990
21174
|
}
|
|
20991
21175
|
|
|
20992
21176
|
// src/lib/fold.ts
|
|
20993
|
-
var
|
|
21177
|
+
var import_node_fs35 = require("node:fs");
|
|
20994
21178
|
var import_node_path24 = require("node:path");
|
|
20995
21179
|
var KNOWN_RECORD_TYPES = /* @__PURE__ */ new Set([
|
|
20996
21180
|
"user",
|
|
@@ -21128,7 +21312,7 @@ function candidateRoots(repoRoot2) {
|
|
|
21128
21312
|
const norm = repoRoot2.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
21129
21313
|
const out = [norm];
|
|
21130
21314
|
try {
|
|
21131
|
-
const real =
|
|
21315
|
+
const real = import_node_fs35.realpathSync.native(norm).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
21132
21316
|
if (real !== norm) out.push(real);
|
|
21133
21317
|
} catch {
|
|
21134
21318
|
}
|
|
@@ -21216,8 +21400,8 @@ function fold(transcriptPath, opts = {}) {
|
|
|
21216
21400
|
}
|
|
21217
21401
|
};
|
|
21218
21402
|
try {
|
|
21219
|
-
if (!(0,
|
|
21220
|
-
ingest((0,
|
|
21403
|
+
if (!(0, import_node_fs35.existsSync)(transcriptPath)) return result;
|
|
21404
|
+
ingest((0, import_node_fs35.readFileSync)(transcriptPath, "utf8"), "agent");
|
|
21221
21405
|
result.coverage.complete = true;
|
|
21222
21406
|
} catch {
|
|
21223
21407
|
return result;
|
|
@@ -21228,19 +21412,19 @@ function fold(transcriptPath, opts = {}) {
|
|
|
21228
21412
|
(0, import_node_path24.basename)(transcriptPath).replace(/\.jsonl$/, ""),
|
|
21229
21413
|
"subagents"
|
|
21230
21414
|
);
|
|
21231
|
-
if ((0,
|
|
21415
|
+
if ((0, import_node_fs35.existsSync)(sidecarDir)) {
|
|
21232
21416
|
const maxFiles = opts.maxSidecars ?? 200;
|
|
21233
21417
|
const maxBytes = opts.maxSidecarBytes ?? 16 * 1024 * 1024;
|
|
21234
21418
|
const found = [];
|
|
21235
21419
|
const walk2 = (d, depth) => {
|
|
21236
21420
|
if (depth > 4) return;
|
|
21237
|
-
for (const e of (0,
|
|
21421
|
+
for (const e of (0, import_node_fs35.readdirSync)(d, { withFileTypes: true })) {
|
|
21238
21422
|
const p = (0, import_node_path24.join)(d, e.name);
|
|
21239
21423
|
if (e.isDirectory()) {
|
|
21240
21424
|
walk2(p, depth + 1);
|
|
21241
21425
|
} else if (e.name.startsWith("agent-") && e.name.endsWith(".jsonl")) {
|
|
21242
21426
|
try {
|
|
21243
|
-
const st = (0,
|
|
21427
|
+
const st = (0, import_node_fs35.statSync)(p);
|
|
21244
21428
|
found.push({ path: p, size: st.size, mtimeMs: st.mtimeMs });
|
|
21245
21429
|
} catch {
|
|
21246
21430
|
result.coverage.malformed++;
|
|
@@ -21257,7 +21441,7 @@ function fold(transcriptPath, opts = {}) {
|
|
|
21257
21441
|
continue;
|
|
21258
21442
|
}
|
|
21259
21443
|
try {
|
|
21260
|
-
ingest((0,
|
|
21444
|
+
ingest((0, import_node_fs35.readFileSync)(f.path, "utf8"), "subagent");
|
|
21261
21445
|
bytes += f.size;
|
|
21262
21446
|
result.coverage.subagentFiles++;
|
|
21263
21447
|
} catch {
|
|
@@ -21292,7 +21476,7 @@ function fold(transcriptPath, opts = {}) {
|
|
|
21292
21476
|
}
|
|
21293
21477
|
function classifyUnobserved(path) {
|
|
21294
21478
|
try {
|
|
21295
|
-
const st = (0,
|
|
21479
|
+
const st = (0, import_node_fs35.statSync)(path);
|
|
21296
21480
|
if (!st.isFile()) return "unreadable";
|
|
21297
21481
|
} catch {
|
|
21298
21482
|
return "unreadable";
|
|
@@ -21544,7 +21728,20 @@ async function evidence(run2) {
|
|
|
21544
21728
|
widened_to: allForReview.length
|
|
21545
21729
|
});
|
|
21546
21730
|
}
|
|
21547
|
-
const
|
|
21731
|
+
const authorshipWasObservable = scoped.signal === "authored" && actionSummary?.transcript_windowed !== "orphaned";
|
|
21732
|
+
if (!narrowingIsTrustworthy && recoveredScope.length === 0 && authorshipWasObservable) {
|
|
21733
|
+
logEvent("authored_nothing_reviewable", {
|
|
21734
|
+
touched: (actionSummary?.files_edited?.length ?? 0) + (actionSummary?.files_created?.length ?? 0),
|
|
21735
|
+
candidates: allForReview.length
|
|
21736
|
+
});
|
|
21737
|
+
}
|
|
21738
|
+
const baseForReview = chooseReviewScope({
|
|
21739
|
+
narrowingIsTrustworthy,
|
|
21740
|
+
scopedFiles: scoped.files,
|
|
21741
|
+
recoveredScope,
|
|
21742
|
+
allForReview,
|
|
21743
|
+
authorshipWasObservable
|
|
21744
|
+
});
|
|
21548
21745
|
const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
|
|
21549
21746
|
if (!opts.skipStatic && isCodacyAvailable()) {
|
|
21550
21747
|
let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
|
|
@@ -21596,20 +21793,20 @@ async function evidence(run2) {
|
|
|
21596
21793
|
}
|
|
21597
21794
|
|
|
21598
21795
|
// src/lib/cache-cleanup.ts
|
|
21599
|
-
var
|
|
21796
|
+
var import_node_fs36 = require("node:fs");
|
|
21600
21797
|
var import_node_path25 = require("node:path");
|
|
21601
21798
|
var CACHE_TTL_DAYS = 7;
|
|
21602
21799
|
function pruneStaleCache() {
|
|
21603
21800
|
try {
|
|
21604
21801
|
const dir = projectPath(CACHE_DIR);
|
|
21605
21802
|
const cutoff = Date.now() - CACHE_TTL_DAYS * 24 * 3600 * 1e3;
|
|
21606
|
-
for (const entry of (0,
|
|
21803
|
+
for (const entry of (0, import_node_fs36.readdirSync)(dir)) {
|
|
21607
21804
|
if (!entry.startsWith("pending-")) continue;
|
|
21608
21805
|
const path = (0, import_node_path25.join)(dir, entry);
|
|
21609
21806
|
try {
|
|
21610
|
-
const stat3 = (0,
|
|
21807
|
+
const stat3 = (0, import_node_fs36.statSync)(path);
|
|
21611
21808
|
if (stat3.mtimeMs < cutoff) {
|
|
21612
|
-
(0,
|
|
21809
|
+
(0, import_node_fs36.unlinkSync)(path);
|
|
21613
21810
|
logEvent("cache_entry_pruned", {
|
|
21614
21811
|
path: entry,
|
|
21615
21812
|
age_days: Math.round((Date.now() - stat3.mtimeMs) / 864e5)
|
|
@@ -21623,7 +21820,7 @@ function pruneStaleCache() {
|
|
|
21623
21820
|
}
|
|
21624
21821
|
|
|
21625
21822
|
// src/lib/context-files.ts
|
|
21626
|
-
var
|
|
21823
|
+
var import_node_fs37 = require("node:fs");
|
|
21627
21824
|
var import_node_os5 = require("node:os");
|
|
21628
21825
|
var MAX_CONTEXT_FILES = 10;
|
|
21629
21826
|
var MAX_CONTEXT_FILE_BYTES = 10240;
|
|
@@ -21681,7 +21878,7 @@ function gatherContextFiles(contextPaths, deltaFiles, opts) {
|
|
|
21681
21878
|
continue;
|
|
21682
21879
|
}
|
|
21683
21880
|
try {
|
|
21684
|
-
const content = (0,
|
|
21881
|
+
const content = (0, import_node_fs37.readFileSync)(safePath, "utf8");
|
|
21685
21882
|
const bytes = Buffer.byteLength(content);
|
|
21686
21883
|
if (bytes > MAX_CONTEXT_FILE_BYTES) {
|
|
21687
21884
|
logEvent("context_file_skipped", { path: filePath, reason: "too_large", bytes });
|
|
@@ -21772,7 +21969,7 @@ async function repoContext(run2) {
|
|
|
21772
21969
|
|
|
21773
21970
|
// src/lib/seed-runner.ts
|
|
21774
21971
|
var import_promises14 = require("node:fs/promises");
|
|
21775
|
-
var
|
|
21972
|
+
var import_node_fs38 = require("node:fs");
|
|
21776
21973
|
var import_node_path26 = require("node:path");
|
|
21777
21974
|
var import_yaml4 = __toESM(require_dist());
|
|
21778
21975
|
|
|
@@ -22012,7 +22209,7 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
|
|
|
22012
22209
|
return fm;
|
|
22013
22210
|
}
|
|
22014
22211
|
async function runSeed(opts) {
|
|
22015
|
-
if (!(0,
|
|
22212
|
+
if (!(0, import_node_fs38.existsSync)(STANDARD_FILE)) {
|
|
22016
22213
|
return { created: 0, failed: 0, skipped: "no_standard", candidates: [] };
|
|
22017
22214
|
}
|
|
22018
22215
|
let standardDoc;
|
|
@@ -22024,7 +22221,7 @@ async function runSeed(opts) {
|
|
|
22024
22221
|
}
|
|
22025
22222
|
const knowledgeSpec = standardDoc.knowledge_spec ?? {};
|
|
22026
22223
|
let readmeContent;
|
|
22027
|
-
if ((0,
|
|
22224
|
+
if ((0, import_node_fs38.existsSync)("README.md")) {
|
|
22028
22225
|
try {
|
|
22029
22226
|
readmeContent = await (0, import_promises14.readFile)("README.md", "utf-8");
|
|
22030
22227
|
} catch {
|
|
@@ -22032,7 +22229,7 @@ async function runSeed(opts) {
|
|
|
22032
22229
|
}
|
|
22033
22230
|
let claudeMdContent;
|
|
22034
22231
|
for (const p of ["CLAUDE.md", ".claude/CLAUDE.md"]) {
|
|
22035
|
-
if ((0,
|
|
22232
|
+
if ((0, import_node_fs38.existsSync)(p)) {
|
|
22036
22233
|
try {
|
|
22037
22234
|
claudeMdContent = await (0, import_promises14.readFile)(p, "utf-8");
|
|
22038
22235
|
break;
|
|
@@ -22056,7 +22253,7 @@ async function runSeed(opts) {
|
|
|
22056
22253
|
return { created: 0, failed: 0, skipped: "no_candidates", candidates: [] };
|
|
22057
22254
|
}
|
|
22058
22255
|
const overviewPath = (0, import_node_path26.join)(MEMORY_DIR, "domain", "project-overview.md");
|
|
22059
|
-
if ((0,
|
|
22256
|
+
if ((0, import_node_fs38.existsSync)(overviewPath) && !opts.force) {
|
|
22060
22257
|
return { created: 0, failed: 0, skipped: "already_seeded", candidates };
|
|
22061
22258
|
}
|
|
22062
22259
|
if (opts.dryRun) {
|
|
@@ -22111,7 +22308,7 @@ async function runSeed(opts) {
|
|
|
22111
22308
|
}
|
|
22112
22309
|
|
|
22113
22310
|
// src/commands/analyze/phases/08-memory-manifest.ts
|
|
22114
|
-
var
|
|
22311
|
+
var import_node_fs39 = require("node:fs");
|
|
22115
22312
|
var import_node_path27 = require("node:path");
|
|
22116
22313
|
async function memoryManifest(run2) {
|
|
22117
22314
|
const { globals } = run2;
|
|
@@ -22123,8 +22320,8 @@ async function memoryManifest(run2) {
|
|
|
22123
22320
|
try {
|
|
22124
22321
|
await ensureMemoryDir();
|
|
22125
22322
|
const seedMarker = (0, import_node_path27.join)(VERITY_DIR, ".seeded");
|
|
22126
|
-
const hasStandard = (0,
|
|
22127
|
-
const alreadyTried = (0,
|
|
22323
|
+
const hasStandard = (0, import_node_fs39.existsSync)(STANDARD_FILE);
|
|
22324
|
+
const alreadyTried = (0, import_node_fs39.existsSync)(seedMarker);
|
|
22128
22325
|
if (hasStandard && !alreadyTried) {
|
|
22129
22326
|
const preManifest = await buildManifest();
|
|
22130
22327
|
if (preManifest.nodes.length === 0) {
|
|
@@ -22137,7 +22334,7 @@ async function memoryManifest(run2) {
|
|
|
22137
22334
|
dryRun: false
|
|
22138
22335
|
});
|
|
22139
22336
|
if (seedResult.created > 0) {
|
|
22140
|
-
(0,
|
|
22337
|
+
(0, import_node_fs39.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} created=${seedResult.created}
|
|
22141
22338
|
`);
|
|
22142
22339
|
autoSeedNotice = `Seeded ${seedResult.created} knowledge node(s) from your existing Standard (one-time).`;
|
|
22143
22340
|
logEvent("auto_seed_ran", {
|
|
@@ -22145,7 +22342,7 @@ async function memoryManifest(run2) {
|
|
|
22145
22342
|
failed: seedResult.failed
|
|
22146
22343
|
});
|
|
22147
22344
|
} else if (seedResult.skipped === "already_seeded") {
|
|
22148
|
-
(0,
|
|
22345
|
+
(0, import_node_fs39.writeFileSync)(seedMarker, `${(/* @__PURE__ */ new Date()).toISOString()} skipped=already_seeded
|
|
22149
22346
|
`);
|
|
22150
22347
|
} else {
|
|
22151
22348
|
logEvent("auto_seed_noop", {
|
|
@@ -22334,7 +22531,7 @@ async function workingMemory(run2) {
|
|
|
22334
22531
|
}
|
|
22335
22532
|
|
|
22336
22533
|
// src/lib/note-budget.ts
|
|
22337
|
-
var
|
|
22534
|
+
var import_node_fs40 = require("node:fs");
|
|
22338
22535
|
var ADVISORY_BUDGET = { PASS: 1, WARN: 2 };
|
|
22339
22536
|
var EPISODE_STALE_SECONDS = 30 * 60;
|
|
22340
22537
|
var FRESH = { delivered: 0, tasksCompleted: 0, ts: 0 };
|
|
@@ -22356,9 +22553,9 @@ function advisoryBudgetSpent(episode, rawDecision) {
|
|
|
22356
22553
|
}
|
|
22357
22554
|
function readAdvisoryEpisode(sessionId) {
|
|
22358
22555
|
const file = scopedFile(ADVISORY_EPISODE_FILE, sessionId);
|
|
22359
|
-
if (!(0,
|
|
22556
|
+
if (!(0, import_node_fs40.existsSync)(file)) return null;
|
|
22360
22557
|
try {
|
|
22361
|
-
const o = JSON.parse((0,
|
|
22558
|
+
const o = JSON.parse((0, import_node_fs40.readFileSync)(file, "utf-8")) ?? {};
|
|
22362
22559
|
const delivered = typeof o.delivered === "number" ? o.delivered : NaN;
|
|
22363
22560
|
if (isNaN(delivered)) return null;
|
|
22364
22561
|
return {
|
|
@@ -22372,8 +22569,8 @@ function readAdvisoryEpisode(sessionId) {
|
|
|
22372
22569
|
}
|
|
22373
22570
|
function writeAdvisoryEpisode(episode, sessionId) {
|
|
22374
22571
|
try {
|
|
22375
|
-
(0,
|
|
22376
|
-
(0,
|
|
22572
|
+
(0, import_node_fs40.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
22573
|
+
(0, import_node_fs40.writeFileSync)(
|
|
22377
22574
|
scopedFile(ADVISORY_EPISODE_FILE, sessionId),
|
|
22378
22575
|
JSON.stringify({ v: 1, ...episode })
|
|
22379
22576
|
);
|
|
@@ -22690,14 +22887,14 @@ async function buildRequest(run2) {
|
|
|
22690
22887
|
}
|
|
22691
22888
|
|
|
22692
22889
|
// src/lib/offline.ts
|
|
22693
|
-
var
|
|
22890
|
+
var import_node_fs41 = require("node:fs");
|
|
22694
22891
|
var import_node_crypto11 = require("node:crypto");
|
|
22695
22892
|
function cacheRequest(body) {
|
|
22696
22893
|
try {
|
|
22697
|
-
(0,
|
|
22894
|
+
(0, import_node_fs41.mkdirSync)(CACHE_DIR, { recursive: true });
|
|
22698
22895
|
const suffix = (0, import_node_crypto11.randomBytes)(4).toString("hex");
|
|
22699
22896
|
const filename = `pending-${Math.floor(Date.now() / 1e3)}-${suffix}.json`;
|
|
22700
|
-
(0,
|
|
22897
|
+
(0, import_node_fs41.writeFileSync)(`${CACHE_DIR}/${filename}`, JSON.stringify(redactRequest(body)));
|
|
22701
22898
|
} catch {
|
|
22702
22899
|
}
|
|
22703
22900
|
}
|
|
@@ -22816,7 +23013,7 @@ async function transmit(run2) {
|
|
|
22816
23013
|
}
|
|
22817
23014
|
|
|
22818
23015
|
// src/commands/analyze/phases/13-reconcile.ts
|
|
22819
|
-
var
|
|
23016
|
+
var import_node_fs42 = require("node:fs");
|
|
22820
23017
|
var import_node_path29 = require("node:path");
|
|
22821
23018
|
async function reconcile(run2) {
|
|
22822
23019
|
const { actionSummary, allChanged, analyzable, baseline, baselineSessionId, codeDelta, contentHash, conversation, decision, foldResult, memory, memorySession, response, reviewable, securityFiles, turnId } = run2;
|
|
@@ -22846,7 +23043,7 @@ async function reconcile(run2) {
|
|
|
22846
23043
|
const st = foldDossier(memorySession.d);
|
|
22847
23044
|
openElsewhere = openBlockingElsewhere(st.statements, sentPaths, (file, line) => {
|
|
22848
23045
|
try {
|
|
22849
|
-
const src = (0,
|
|
23046
|
+
const src = (0, import_node_fs42.readFileSync)((0, import_node_path29.join)(repoRoot(), file), "utf8").split("\n");
|
|
22850
23047
|
const at = src[line - 1];
|
|
22851
23048
|
return at === void 0 ? null : lineSha(at);
|
|
22852
23049
|
} catch {
|
|
@@ -23510,6 +23707,10 @@ function registerAnalyzeCommand(program2) {
|
|
|
23510
23707
|
}
|
|
23511
23708
|
var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
|
|
23512
23709
|
async function runAnalyze(opts, globals) {
|
|
23710
|
+
if (!verityConfigured()) {
|
|
23711
|
+
(0, import_node_fs43.writeSync)(2, '[verity] not set up in this project \u2014 run "verity init" first.\n');
|
|
23712
|
+
process.exit(0);
|
|
23713
|
+
}
|
|
23513
23714
|
const run2 = createRun(opts, globals);
|
|
23514
23715
|
installRunEvidence(run2);
|
|
23515
23716
|
for (const [name, phase] of PIPELINE) {
|
|
@@ -23528,7 +23729,6 @@ async function runAnalyze(opts, globals) {
|
|
|
23528
23729
|
}
|
|
23529
23730
|
|
|
23530
23731
|
// src/commands/baseline.ts
|
|
23531
|
-
var import_node_fs41 = require("node:fs");
|
|
23532
23732
|
function registerBaselineCommands(program2) {
|
|
23533
23733
|
const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
|
|
23534
23734
|
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) => {
|
|
@@ -23537,7 +23737,16 @@ function registerBaselineCommands(program2) {
|
|
|
23537
23737
|
process.chdir(repoRoot());
|
|
23538
23738
|
} catch {
|
|
23539
23739
|
}
|
|
23540
|
-
if (!(
|
|
23740
|
+
if (!verityConfigured()) {
|
|
23741
|
+
const how = isPluginInvocation() ? "Offer to run /verity:setup for the user." : "Offer to run `verity init` (or /verity-setup) for the user.";
|
|
23742
|
+
process.stdout.write(
|
|
23743
|
+
JSON.stringify({
|
|
23744
|
+
hookSpecificOutput: {
|
|
23745
|
+
hookEventName: "SessionStart",
|
|
23746
|
+
additionalContext: `Verity is installed but this project is not set up yet, so the quality gate will not review anything here. ${how}`
|
|
23747
|
+
}
|
|
23748
|
+
}) + "\n"
|
|
23749
|
+
);
|
|
23541
23750
|
process.exit(0);
|
|
23542
23751
|
}
|
|
23543
23752
|
let sessionId = opts.sessionId;
|
|
@@ -23553,6 +23762,9 @@ function registerBaselineCommands(program2) {
|
|
|
23553
23762
|
}
|
|
23554
23763
|
}
|
|
23555
23764
|
}
|
|
23765
|
+
if (deferredToPlugin("baseline capture", sessionId ?? process.env.CLAUDE_SESSION_ID ?? null)) {
|
|
23766
|
+
process.exit(0);
|
|
23767
|
+
}
|
|
23556
23768
|
const authForScope = await resolveToken(program2.opts().token);
|
|
23557
23769
|
const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
|
|
23558
23770
|
const scopeSession = sessionId || process.env.CLAUDE_SESSION_ID || void 0;
|
|
@@ -23577,7 +23789,7 @@ async function readStdin() {
|
|
|
23577
23789
|
}
|
|
23578
23790
|
|
|
23579
23791
|
// src/commands/review.ts
|
|
23580
|
-
var
|
|
23792
|
+
var import_node_fs44 = require("node:fs");
|
|
23581
23793
|
function registerReviewCommand(program2) {
|
|
23582
23794
|
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) => {
|
|
23583
23795
|
const globals = program2.opts();
|
|
@@ -23596,7 +23808,7 @@ async function runReview(opts, globals) {
|
|
|
23596
23808
|
const securityFiles = filterSecurity(allFiles);
|
|
23597
23809
|
let staticResults;
|
|
23598
23810
|
if (isCodacyAvailable()) {
|
|
23599
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0,
|
|
23811
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs44.existsSync)(f) || resolveFile(f) !== null);
|
|
23600
23812
|
staticResults = runCodacyAnalysis(scannable);
|
|
23601
23813
|
} else {
|
|
23602
23814
|
staticResults = {
|
|
@@ -23622,10 +23834,10 @@ async function runReview(opts, globals) {
|
|
|
23622
23834
|
const specPaths = opts.specs.split(",").map((f) => f.trim()).filter(Boolean);
|
|
23623
23835
|
specs = [];
|
|
23624
23836
|
for (const p of specPaths) {
|
|
23625
|
-
if (!(0,
|
|
23837
|
+
if (!(0, import_node_fs44.existsSync)(p)) continue;
|
|
23626
23838
|
try {
|
|
23627
|
-
const { readFileSync:
|
|
23628
|
-
const content =
|
|
23839
|
+
const { readFileSync: readFileSync27 } = await import("node:fs");
|
|
23840
|
+
const content = readFileSync27(p, "utf-8");
|
|
23629
23841
|
specs.push({ path: p, content: content.slice(0, 10240) });
|
|
23630
23842
|
} catch {
|
|
23631
23843
|
}
|
|
@@ -23682,7 +23894,7 @@ async function runReview(opts, globals) {
|
|
|
23682
23894
|
}
|
|
23683
23895
|
|
|
23684
23896
|
// src/commands/guard.ts
|
|
23685
|
-
var
|
|
23897
|
+
var import_node_fs45 = require("node:fs");
|
|
23686
23898
|
var import_node_path30 = require("node:path");
|
|
23687
23899
|
var GUARD_BLOCK_CAP = 2;
|
|
23688
23900
|
var GUARD_ITER_FILE = (0, import_node_path30.join)(VERITY_DIR, ".guard-iteration");
|
|
@@ -23730,7 +23942,7 @@ function readPreToolUseStdin() {
|
|
|
23730
23942
|
}
|
|
23731
23943
|
function readIterMap() {
|
|
23732
23944
|
try {
|
|
23733
|
-
const raw = JSON.parse((0,
|
|
23945
|
+
const raw = JSON.parse((0, import_node_fs45.readFileSync)(GUARD_ITER_FILE, "utf-8"));
|
|
23734
23946
|
if (raw && typeof raw === "object") {
|
|
23735
23947
|
if (typeof raw.moment === "string" && typeof raw.count === "number") {
|
|
23736
23948
|
return { [raw.moment]: raw.count };
|
|
@@ -23750,10 +23962,10 @@ function readIter(moment) {
|
|
|
23750
23962
|
}
|
|
23751
23963
|
function writeIter(moment, count) {
|
|
23752
23964
|
try {
|
|
23753
|
-
(0,
|
|
23965
|
+
(0, import_node_fs45.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
23754
23966
|
const map = readIterMap();
|
|
23755
23967
|
map[moment] = count;
|
|
23756
|
-
(0,
|
|
23968
|
+
(0, import_node_fs45.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
23757
23969
|
} catch {
|
|
23758
23970
|
}
|
|
23759
23971
|
}
|
|
@@ -23763,16 +23975,16 @@ function resetIter(moment) {
|
|
|
23763
23975
|
if (!(moment in map)) return;
|
|
23764
23976
|
delete map[moment];
|
|
23765
23977
|
if (Object.keys(map).length === 0) {
|
|
23766
|
-
if ((0,
|
|
23978
|
+
if ((0, import_node_fs45.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs45.unlinkSync)(GUARD_ITER_FILE);
|
|
23767
23979
|
} else {
|
|
23768
|
-
(0,
|
|
23769
|
-
(0,
|
|
23980
|
+
(0, import_node_fs45.mkdirSync)(VERITY_DIR, { recursive: true });
|
|
23981
|
+
(0, import_node_fs45.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
|
|
23770
23982
|
}
|
|
23771
23983
|
} catch {
|
|
23772
23984
|
}
|
|
23773
23985
|
}
|
|
23774
23986
|
function registerGuardCommand(program2) {
|
|
23775
|
-
program2.command("guard").description("Git-moment gate: review staged/to-push changes before a commit/push (PreToolUse hook)").option("--on <moments>", "Which git moments to gate: commit,push
|
|
23987
|
+
program2.command("guard").description("Git-moment gate: review staged/to-push changes before a commit/push (PreToolUse hook)").option("--on <moments>", "Which git moments to gate: commit,push (default: the project config)").option("--json", "Output raw JSON response (debug)").action(async (opts) => {
|
|
23776
23988
|
const globals = program2.opts();
|
|
23777
23989
|
try {
|
|
23778
23990
|
await runGuard(opts, globals);
|
|
@@ -23782,13 +23994,14 @@ function registerGuardCommand(program2) {
|
|
|
23782
23994
|
});
|
|
23783
23995
|
}
|
|
23784
23996
|
function resolveMomentRange(moment, frame, command, on) {
|
|
23785
|
-
return moment === "pre-commit" ? stagedRange() : resolvePushRange(frame, command, on);
|
|
23997
|
+
return moment === "pre-commit" ? stagedRange(frame) : resolvePushRange(frame, command, on);
|
|
23786
23998
|
}
|
|
23787
23999
|
function describeRange(range) {
|
|
23788
24000
|
if (range.kind === "staged") return "staged";
|
|
23789
|
-
if (range.kind === "
|
|
23790
|
-
|
|
23791
|
-
|
|
24001
|
+
if (range.kind === "merge") return `merge (${range.via})`;
|
|
24002
|
+
if (range.kind === "nothing") return range.via === "already-published" ? "already published" : null;
|
|
24003
|
+
const base = range.base && /^[0-9a-f]{40}$/.test(range.base) ? range.base.slice(0, 7) : range.base;
|
|
24004
|
+
return base ? `${base}..${range.head} via ${range.via}` : `via ${range.via}`;
|
|
23792
24005
|
}
|
|
23793
24006
|
function matchFlagValue(command, flags) {
|
|
23794
24007
|
const re = new RegExp(`(?<![\\w-])(?:${flags})(?:=|\\s+)('((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)"|([^\\s'"-][^\\s]*))`);
|
|
@@ -23837,7 +24050,7 @@ function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedInte
|
|
|
23837
24050
|
const securityFiles = filterSecurity(files);
|
|
23838
24051
|
let staticResults;
|
|
23839
24052
|
if (isCodacyAvailable()) {
|
|
23840
|
-
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0,
|
|
24053
|
+
const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs45.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
|
|
23841
24054
|
staticResults = runCodacyAnalysis(scannable);
|
|
23842
24055
|
} else {
|
|
23843
24056
|
staticResults = { tool: "@codacy/analysis-cli", findings: [], summary: { total_findings: 0, by_severity: {}, tools_run: [] } };
|
|
@@ -23910,8 +24123,10 @@ function emitAllowNotice(userMsg, agentMsg) {
|
|
|
23910
24123
|
process.exit(0);
|
|
23911
24124
|
}
|
|
23912
24125
|
async function runGuard(opts, globals) {
|
|
23913
|
-
const on = opts.on
|
|
24126
|
+
const on = resolveGuardMoments(opts.on);
|
|
24127
|
+
if (on.length === 0) process.exit(0);
|
|
23914
24128
|
const { command, cwd, sessionId } = await readPreToolUseStdin();
|
|
24129
|
+
if (deferredToPlugin("guard", sessionId)) process.exit(0);
|
|
23915
24130
|
const moment = classifyCommand(command, on);
|
|
23916
24131
|
if (!moment) process.exit(0);
|
|
23917
24132
|
const verb = moment === "pre-commit" ? "commit" : "push";
|
|
@@ -23974,7 +24189,7 @@ async function runGuard(opts, globals) {
|
|
|
23974
24189
|
upgradeToExcerpts(repoContext2, {
|
|
23975
24190
|
readFile: (rel) => {
|
|
23976
24191
|
try {
|
|
23977
|
-
return (0,
|
|
24192
|
+
return (0, import_node_fs45.readFileSync)((0, import_node_path30.join)(frame.worktreeRoot ?? process.cwd(), rel), "utf8");
|
|
23978
24193
|
} catch {
|
|
23979
24194
|
return null;
|
|
23980
24195
|
}
|
|
@@ -24195,7 +24410,7 @@ function registerIgnoreCommand(program2) {
|
|
|
24195
24410
|
|
|
24196
24411
|
// src/commands/waive.ts
|
|
24197
24412
|
var import_node_crypto12 = require("node:crypto");
|
|
24198
|
-
var
|
|
24413
|
+
var import_node_fs46 = require("node:fs");
|
|
24199
24414
|
function registerWaiveCommand(program2) {
|
|
24200
24415
|
program2.command("waive <pattern-id>").description("Record an accepted-risk disposition for an open finding (voids when the file changes)").option("--file <path>", "File the finding is anchored to, REPO-RELATIVE (recommended \u2014 narrows the waive)").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action(async (patternId, opts) => {
|
|
24201
24416
|
const globals = program2.opts();
|
|
@@ -24224,7 +24439,7 @@ function registerWaiveCommand(program2) {
|
|
|
24224
24439
|
if (opts.file) {
|
|
24225
24440
|
body.file = opts.file;
|
|
24226
24441
|
try {
|
|
24227
|
-
body.file_sha256 = (0, import_node_crypto12.createHash)("sha256").update((0,
|
|
24442
|
+
body.file_sha256 = (0, import_node_crypto12.createHash)("sha256").update((0, import_node_fs46.readFileSync)(opts.file)).digest("hex");
|
|
24228
24443
|
} catch {
|
|
24229
24444
|
printError(`Cannot read ${opts.file} \u2014 run from the repo root, or omit --file to waive by pattern.`);
|
|
24230
24445
|
process.exit(1);
|
|
@@ -24249,7 +24464,7 @@ function registerWaiveCommand(program2) {
|
|
|
24249
24464
|
}
|
|
24250
24465
|
|
|
24251
24466
|
// src/commands/init.ts
|
|
24252
|
-
var
|
|
24467
|
+
var import_node_fs51 = require("node:fs");
|
|
24253
24468
|
var import_promises17 = require("node:fs/promises");
|
|
24254
24469
|
var import_yaml6 = __toESM(require_dist());
|
|
24255
24470
|
var import_node_path33 = require("node:path");
|
|
@@ -24331,7 +24546,7 @@ function printPhase(n, of, title, subtitle) {
|
|
|
24331
24546
|
}
|
|
24332
24547
|
|
|
24333
24548
|
// src/commands/doctor.ts
|
|
24334
|
-
var
|
|
24549
|
+
var import_node_fs48 = require("node:fs");
|
|
24335
24550
|
|
|
24336
24551
|
// src/lib/prereqs.ts
|
|
24337
24552
|
var import_node_child_process13 = require("node:child_process");
|
|
@@ -24451,7 +24666,7 @@ var import_promises15 = require("node:fs/promises");
|
|
|
24451
24666
|
|
|
24452
24667
|
// src/lib/gitignore.ts
|
|
24453
24668
|
var import_node_child_process14 = require("node:child_process");
|
|
24454
|
-
var
|
|
24669
|
+
var import_node_fs47 = require("node:fs");
|
|
24455
24670
|
var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
|
|
24456
24671
|
var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
|
|
24457
24672
|
var VERITY_GITIGNORE_BLOCK = [
|
|
@@ -24484,7 +24699,7 @@ function semanticsHold() {
|
|
|
24484
24699
|
function ensureVerityGitignore() {
|
|
24485
24700
|
let content = "";
|
|
24486
24701
|
try {
|
|
24487
|
-
content = (0,
|
|
24702
|
+
content = (0, import_node_fs47.readFileSync)(".gitignore", "utf-8");
|
|
24488
24703
|
} catch {
|
|
24489
24704
|
}
|
|
24490
24705
|
const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
|
|
@@ -24505,7 +24720,7 @@ function ensureVerityGitignore() {
|
|
|
24505
24720
|
const sep2 = next === "" ? "" : next.endsWith("\n") ? "\n" : "\n\n";
|
|
24506
24721
|
next = next + sep2 + VERITY_GITIGNORE_BLOCK;
|
|
24507
24722
|
}
|
|
24508
|
-
(0,
|
|
24723
|
+
(0, import_node_fs47.writeFileSync)(".gitignore", next);
|
|
24509
24724
|
return verified(needsRepair ? "repaired" : "added");
|
|
24510
24725
|
} catch {
|
|
24511
24726
|
return "failed";
|
|
@@ -24632,11 +24847,11 @@ async function buildReport() {
|
|
|
24632
24847
|
const state = await readSetupState();
|
|
24633
24848
|
const hooks = await checkAllVerityHooks();
|
|
24634
24849
|
const telemetry = await checkTelemetry();
|
|
24635
|
-
const hasConfig = (0,
|
|
24850
|
+
const hasConfig = (0, import_node_fs48.existsSync)(projectPath(CODACY_CONFIG_FILE));
|
|
24636
24851
|
const artifacts = {
|
|
24637
|
-
standard: (0,
|
|
24852
|
+
standard: (0, import_node_fs48.existsSync)(projectPath(STANDARD_FILE)),
|
|
24638
24853
|
analysisConfig: hasConfig,
|
|
24639
|
-
verityMd: (0,
|
|
24854
|
+
verityMd: (0, import_node_fs48.existsSync)(projectPath(VERITY_MD_FILE)),
|
|
24640
24855
|
analysisConfigIds: hasConfig ? validatePatternIds().status : "absent"
|
|
24641
24856
|
};
|
|
24642
24857
|
const next = [];
|
|
@@ -24730,7 +24945,7 @@ function registerDoctorCommand(program2) {
|
|
|
24730
24945
|
}
|
|
24731
24946
|
|
|
24732
24947
|
// src/commands/migrate.ts
|
|
24733
|
-
var
|
|
24948
|
+
var import_node_fs49 = require("node:fs");
|
|
24734
24949
|
var import_node_path31 = require("node:path");
|
|
24735
24950
|
var import_node_child_process15 = require("node:child_process");
|
|
24736
24951
|
var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
|
|
@@ -24770,10 +24985,10 @@ async function runMigration(opts = {}) {
|
|
|
24770
24985
|
function migrateProjectDir(root, actions) {
|
|
24771
24986
|
const gateDir = (0, import_node_path31.join)(root, ".gate");
|
|
24772
24987
|
const verityDir = (0, import_node_path31.join)(root, ".verity");
|
|
24773
|
-
if ((0,
|
|
24988
|
+
if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) {
|
|
24774
24989
|
return migrateProjectDirRename(root, gateDir, verityDir, actions);
|
|
24775
24990
|
}
|
|
24776
|
-
if ((0,
|
|
24991
|
+
if ((0, import_node_fs49.existsSync)(gateDir) && (0, import_node_fs49.existsSync)(verityDir)) {
|
|
24777
24992
|
return migrateProjectDirCarry(gateDir, verityDir, actions);
|
|
24778
24993
|
}
|
|
24779
24994
|
return false;
|
|
@@ -24794,13 +25009,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
|
|
|
24794
25009
|
}
|
|
24795
25010
|
}
|
|
24796
25011
|
if (moved) {
|
|
24797
|
-
if ((0,
|
|
25012
|
+
if ((0, import_node_fs49.existsSync)(gateDir)) {
|
|
24798
25013
|
const carried = carryLegacyContents(gateDir, verityDir);
|
|
24799
25014
|
if (carried > 0) {
|
|
24800
25015
|
actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
|
|
24801
25016
|
}
|
|
24802
25017
|
try {
|
|
24803
|
-
(0,
|
|
25018
|
+
(0, import_node_fs49.rmSync)(gateDir, { recursive: true, force: true });
|
|
24804
25019
|
} catch {
|
|
24805
25020
|
}
|
|
24806
25021
|
}
|
|
@@ -24816,7 +25031,7 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
|
|
|
24816
25031
|
actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
|
|
24817
25032
|
}
|
|
24818
25033
|
try {
|
|
24819
|
-
(0,
|
|
25034
|
+
(0, import_node_fs49.rmSync)(gateDir, { recursive: true, force: true });
|
|
24820
25035
|
} catch {
|
|
24821
25036
|
}
|
|
24822
25037
|
return carried > 0;
|
|
@@ -24825,9 +25040,9 @@ function migrateGlobalCredentials(home, actions) {
|
|
|
24825
25040
|
if (!home) return;
|
|
24826
25041
|
const gateCreds = (0, import_node_path31.join)(home, ".gate", "credentials");
|
|
24827
25042
|
const verityCreds = (0, import_node_path31.join)(home, ".verity", "credentials");
|
|
24828
|
-
if (!(0,
|
|
24829
|
-
if (!(0,
|
|
24830
|
-
(0,
|
|
25043
|
+
if (!(0, import_node_fs49.existsSync)(gateCreds)) return;
|
|
25044
|
+
if (!(0, import_node_fs49.existsSync)(verityCreds)) {
|
|
25045
|
+
(0, import_node_fs49.mkdirSync)((0, import_node_path31.join)(home, ".verity"), { recursive: true });
|
|
24831
25046
|
moveFile(gateCreds, verityCreds);
|
|
24832
25047
|
actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
|
|
24833
25048
|
return;
|
|
@@ -24850,7 +25065,7 @@ async function migrateLegacyHooks(root, actions) {
|
|
|
24850
25065
|
}
|
|
24851
25066
|
async function migrateClaudeMd(root, actions) {
|
|
24852
25067
|
const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
|
|
24853
|
-
const hadLegacyBlock = (0,
|
|
25068
|
+
const hadLegacyBlock = (0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
|
|
24854
25069
|
if (!hadLegacyBlock) return;
|
|
24855
25070
|
try {
|
|
24856
25071
|
await ensureClaudeMdPointer(root);
|
|
@@ -24862,7 +25077,7 @@ async function migrateClaudeMd(root, actions) {
|
|
|
24862
25077
|
function migrateStandardFile(root, actions) {
|
|
24863
25078
|
const gateMd = (0, import_node_path31.join)(root, "GATE.md");
|
|
24864
25079
|
const verityMd = (0, import_node_path31.join)(root, "VERITY.md");
|
|
24865
|
-
if (!(0,
|
|
25080
|
+
if (!(0, import_node_fs49.existsSync)(gateMd) || (0, import_node_fs49.existsSync)(verityMd)) return;
|
|
24866
25081
|
let moved = false;
|
|
24867
25082
|
if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
|
|
24868
25083
|
try {
|
|
@@ -24874,12 +25089,12 @@ function migrateStandardFile(root, actions) {
|
|
|
24874
25089
|
if (!moved) moveFile(gateMd, verityMd);
|
|
24875
25090
|
const content = readFileSyncSafe(verityMd);
|
|
24876
25091
|
const refreshed = content.split("GATE.md").join("VERITY.md");
|
|
24877
|
-
if (refreshed !== content) (0,
|
|
25092
|
+
if (refreshed !== content) (0, import_node_fs49.writeFileSync)(verityMd, refreshed);
|
|
24878
25093
|
actions.push("Renamed GATE.md \u2192 VERITY.md");
|
|
24879
25094
|
}
|
|
24880
25095
|
async function migrateTelemetryHeaders(root, actions) {
|
|
24881
25096
|
const file = (0, import_node_path31.join)(root, ".claude", "settings.local.json");
|
|
24882
|
-
if (!(0,
|
|
25097
|
+
if (!(0, import_node_fs49.existsSync)(file)) return;
|
|
24883
25098
|
let settings;
|
|
24884
25099
|
try {
|
|
24885
25100
|
settings = JSON.parse(readFileSyncSafe(file) || "{}");
|
|
@@ -24927,14 +25142,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
|
|
|
24927
25142
|
}
|
|
24928
25143
|
if (toAppend.length > 0) {
|
|
24929
25144
|
const sep2 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
|
|
24930
|
-
(0,
|
|
25145
|
+
(0, import_node_fs49.writeFileSync)(verityCreds, verityContent + sep2 + toAppend.join("\n") + "\n");
|
|
24931
25146
|
}
|
|
24932
|
-
(0,
|
|
25147
|
+
(0, import_node_fs49.rmSync)(gateCreds, { force: true });
|
|
24933
25148
|
return toAppend.length;
|
|
24934
25149
|
}
|
|
24935
25150
|
function readFileSyncSafe(path) {
|
|
24936
25151
|
try {
|
|
24937
|
-
return (0,
|
|
25152
|
+
return (0, import_node_fs49.readFileSync)(path, "utf-8");
|
|
24938
25153
|
} catch {
|
|
24939
25154
|
return "";
|
|
24940
25155
|
}
|
|
@@ -24949,35 +25164,35 @@ function hasStagedChanges(root) {
|
|
|
24949
25164
|
}
|
|
24950
25165
|
function moveDir(from, to) {
|
|
24951
25166
|
try {
|
|
24952
|
-
(0,
|
|
25167
|
+
(0, import_node_fs49.renameSync)(from, to);
|
|
24953
25168
|
} catch (err) {
|
|
24954
25169
|
if (err.code !== "EXDEV") throw err;
|
|
24955
|
-
(0,
|
|
24956
|
-
(0,
|
|
25170
|
+
(0, import_node_fs49.cpSync)(from, to, { recursive: true });
|
|
25171
|
+
(0, import_node_fs49.rmSync)(from, { recursive: true, force: true });
|
|
24957
25172
|
}
|
|
24958
25173
|
}
|
|
24959
25174
|
function moveFile(from, to) {
|
|
24960
25175
|
try {
|
|
24961
|
-
(0,
|
|
25176
|
+
(0, import_node_fs49.renameSync)(from, to);
|
|
24962
25177
|
} catch (err) {
|
|
24963
25178
|
if (err.code !== "EXDEV") throw err;
|
|
24964
|
-
(0,
|
|
24965
|
-
(0,
|
|
25179
|
+
(0, import_node_fs49.cpSync)(from, to);
|
|
25180
|
+
(0, import_node_fs49.rmSync)(from, { force: true });
|
|
24966
25181
|
}
|
|
24967
25182
|
}
|
|
24968
25183
|
function carryLegacyContents(gateDir, verityDir) {
|
|
24969
25184
|
let copied = 0;
|
|
24970
25185
|
const walk2 = (relDir) => {
|
|
24971
25186
|
const srcDir = (0, import_node_path31.join)(gateDir, relDir);
|
|
24972
|
-
for (const entry of (0,
|
|
25187
|
+
for (const entry of (0, import_node_fs49.readdirSync)(srcDir)) {
|
|
24973
25188
|
const rel = relDir ? (0, import_node_path31.join)(relDir, entry) : entry;
|
|
24974
25189
|
const src = (0, import_node_path31.join)(gateDir, rel);
|
|
24975
25190
|
const dest = (0, import_node_path31.join)(verityDir, rel);
|
|
24976
|
-
if ((0,
|
|
25191
|
+
if ((0, import_node_fs49.statSync)(src).isDirectory()) {
|
|
24977
25192
|
walk2(rel);
|
|
24978
|
-
} else if (!(0,
|
|
24979
|
-
(0,
|
|
24980
|
-
(0,
|
|
25193
|
+
} else if (!(0, import_node_fs49.existsSync)(dest)) {
|
|
25194
|
+
(0, import_node_fs49.mkdirSync)((0, import_node_path31.dirname)(dest), { recursive: true });
|
|
25195
|
+
(0, import_node_fs49.cpSync)(src, dest);
|
|
24981
25196
|
copied++;
|
|
24982
25197
|
}
|
|
24983
25198
|
}
|
|
@@ -24988,20 +25203,20 @@ function carryLegacyContents(gateDir, verityDir) {
|
|
|
24988
25203
|
async function needsMigration(root = repoRoot()) {
|
|
24989
25204
|
const gateDir = (0, import_node_path31.join)(root, ".gate");
|
|
24990
25205
|
const verityDir = (0, import_node_path31.join)(root, ".verity");
|
|
24991
|
-
if ((0,
|
|
24992
|
-
if ((0,
|
|
24993
|
-
if ((0,
|
|
25206
|
+
if ((0, import_node_fs49.existsSync)(gateDir) && !(0, import_node_fs49.existsSync)(verityDir)) return true;
|
|
25207
|
+
if ((0, import_node_fs49.existsSync)(gateDir) && (0, import_node_fs49.existsSync)(verityDir)) {
|
|
25208
|
+
if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(gateDir, "credentials")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(verityDir, "credentials"))) {
|
|
24994
25209
|
return true;
|
|
24995
25210
|
}
|
|
24996
|
-
if ((0,
|
|
25211
|
+
if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(gateDir, "memory")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(verityDir, "memory"))) {
|
|
24997
25212
|
return true;
|
|
24998
25213
|
}
|
|
24999
25214
|
}
|
|
25000
25215
|
const claudeMd = (0, import_node_path31.join)(root, "CLAUDE.md");
|
|
25001
|
-
if ((0,
|
|
25216
|
+
if ((0, import_node_fs49.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
|
|
25002
25217
|
return true;
|
|
25003
25218
|
}
|
|
25004
|
-
if ((0,
|
|
25219
|
+
if ((0, import_node_fs49.existsSync)((0, import_node_path31.join)(root, "GATE.md")) && !(0, import_node_fs49.existsSync)((0, import_node_path31.join)(root, "VERITY.md"))) {
|
|
25005
25220
|
return true;
|
|
25006
25221
|
}
|
|
25007
25222
|
if (await hasLegacyHooksAt(root)) return true;
|
|
@@ -25276,7 +25491,7 @@ async function promptMultiSelect(question, choices, fallback) {
|
|
|
25276
25491
|
}
|
|
25277
25492
|
|
|
25278
25493
|
// src/lib/remote-config.ts
|
|
25279
|
-
var
|
|
25494
|
+
var import_node_fs50 = require("node:fs");
|
|
25280
25495
|
var import_promises16 = require("node:fs/promises");
|
|
25281
25496
|
var import_node_path32 = require("node:path");
|
|
25282
25497
|
var import_yaml5 = __toESM(require_dist());
|
|
@@ -25323,7 +25538,7 @@ async function adoptRemoteSetup(found, opts) {
|
|
|
25323
25538
|
written.push(STANDARD_FILE);
|
|
25324
25539
|
if (rider !== null) {
|
|
25325
25540
|
const localIgnore = projectPath(VERITYIGNORE_FILE);
|
|
25326
|
-
if (!(0,
|
|
25541
|
+
if (!(0, import_node_fs50.existsSync)(localIgnore)) {
|
|
25327
25542
|
await writeOut(VERITYIGNORE_FILE, rider);
|
|
25328
25543
|
written.push(VERITYIGNORE_FILE);
|
|
25329
25544
|
} else {
|
|
@@ -25481,7 +25696,7 @@ function resolveDataDir2() {
|
|
|
25481
25696
|
// local dev: running from repo root
|
|
25482
25697
|
];
|
|
25483
25698
|
for (const candidate of candidates) {
|
|
25484
|
-
if ((0,
|
|
25699
|
+
if ((0, import_node_fs51.existsSync)((0, import_node_path33.join)(candidate, "skills"))) {
|
|
25485
25700
|
return candidate;
|
|
25486
25701
|
}
|
|
25487
25702
|
}
|
|
@@ -25497,7 +25712,7 @@ async function skillIsCurrent(src, dest) {
|
|
|
25497
25712
|
const list2 = (dir) => {
|
|
25498
25713
|
const out = [];
|
|
25499
25714
|
const walk2 = (d, prefix) => {
|
|
25500
|
-
for (const e of (0,
|
|
25715
|
+
for (const e of (0, import_node_fs51.readdirSync)(d, { withFileTypes: true })) {
|
|
25501
25716
|
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
25502
25717
|
if (e.isDirectory()) walk2((0, import_node_path33.join)(d, e.name), rel);
|
|
25503
25718
|
else if (e.isFile()) out.push(rel);
|
|
@@ -25689,7 +25904,7 @@ async function synthesizeLocally(opts) {
|
|
|
25689
25904
|
async function healStaleAnalysisConfig(globals) {
|
|
25690
25905
|
const configPath = projectPath(CODACY_CONFIG_FILE);
|
|
25691
25906
|
const standardPath = projectPath(STANDARD_FILE);
|
|
25692
|
-
if (!(0,
|
|
25907
|
+
if (!(0, import_node_fs51.existsSync)(configPath) || !(0, import_node_fs51.existsSync)(standardPath)) return;
|
|
25693
25908
|
const validation = validatePatternIds();
|
|
25694
25909
|
if (validation.status !== "invalid") return;
|
|
25695
25910
|
printWarn(" Your analysis config names pattern ids that no longer resolve \u2014 those tools were");
|
|
@@ -25783,14 +25998,125 @@ async function handoffToSetup(enabled, claudeInstalled) {
|
|
|
25783
25998
|
}
|
|
25784
25999
|
await reportPhaseTwo(startedAt);
|
|
25785
26000
|
}
|
|
26001
|
+
async function installSkills(force, step) {
|
|
26002
|
+
step("Installing skills");
|
|
26003
|
+
const dataDir = resolveDataDir2();
|
|
26004
|
+
const skillsSource = (0, import_node_path33.join)(dataDir, "skills");
|
|
26005
|
+
const skillsDest = ".claude/skills";
|
|
26006
|
+
let skillsInstalled = 0;
|
|
26007
|
+
for (const skill of SKILLS) {
|
|
26008
|
+
const src = (0, import_node_path33.join)(skillsSource, skill);
|
|
26009
|
+
const dest = (0, import_node_path33.join)(skillsDest, skill);
|
|
26010
|
+
if (!(0, import_node_fs51.existsSync)(src)) {
|
|
26011
|
+
printWarn(` Skill data not found: ${skill}`);
|
|
26012
|
+
continue;
|
|
26013
|
+
}
|
|
26014
|
+
if ((0, import_node_fs51.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
|
|
26015
|
+
skillsInstalled++;
|
|
26016
|
+
continue;
|
|
26017
|
+
}
|
|
26018
|
+
await copyDir(src, dest);
|
|
26019
|
+
skillsInstalled++;
|
|
26020
|
+
}
|
|
26021
|
+
printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
|
|
26022
|
+
}
|
|
26023
|
+
async function adoptPluginWiring(gitMoments, moments) {
|
|
26024
|
+
const settings = await readSettings();
|
|
26025
|
+
const stripped = removeVerityHooks(settings);
|
|
26026
|
+
const hadAny = JSON.stringify(settings.hooks ?? {}) !== JSON.stringify(stripped.hooks ?? {});
|
|
26027
|
+
if (hadAny) {
|
|
26028
|
+
await writeSettings(stripped);
|
|
26029
|
+
printInfo(" Removed this project's Verity hooks from .claude/settings.json \u2713");
|
|
26030
|
+
printInfo(" The plugin wires them now, so each turn is reviewed once.");
|
|
26031
|
+
} else {
|
|
26032
|
+
printInfo(" Wired by the Verity plugin \u2014 nothing to reconcile here \u2713");
|
|
26033
|
+
}
|
|
26034
|
+
printInfo(` Pre-commit gate: ${gitMoments.includes("commit") ? "on" : "off"}`);
|
|
26035
|
+
printInfo(` Pre-push/PR gate: ${gitMoments.includes("push") ? "on" : "off"}`);
|
|
26036
|
+
printInfo(" Stop + intent + baseline + compact + session-end: always on \u2713");
|
|
26037
|
+
if (!moments.includes("stop")) {
|
|
26038
|
+
printWarn(" Turning the Stop review off is not yet supported under the plugin \u2014 it stays on.");
|
|
26039
|
+
}
|
|
26040
|
+
}
|
|
26041
|
+
async function reconcileOwnWiring(moments) {
|
|
26042
|
+
await applyMomentSelection(moments);
|
|
26043
|
+
const hookStatus = await checkAllVerityHooks();
|
|
26044
|
+
printInfo(` Stop (verity analyze): ${hookStatus.stop ? "on" : "off"}`);
|
|
26045
|
+
printInfo(` Pre-commit gate: ${hookStatus.guardOn.includes("commit") ? "on" : "off"}`);
|
|
26046
|
+
printInfo(` Pre-push/PR gate: ${hookStatus.guardOn.includes("push") ? "on" : "off"}`);
|
|
26047
|
+
printInfo(" Intent + baseline + compact + session-end: always on \u2713");
|
|
26048
|
+
if (!hookStatus.stop && hookStatus.guardOn.length === 0) {
|
|
26049
|
+
printWarn(" No analysis moment is active \u2014 code changes will NOT be reviewed.");
|
|
26050
|
+
printWarn(" Enable one: verity hooks install --moments stop");
|
|
26051
|
+
}
|
|
26052
|
+
}
|
|
26053
|
+
async function checkPrerequisites(step) {
|
|
26054
|
+
step("Checking prerequisites");
|
|
26055
|
+
const prereqs = await checkPrereqs({ install: true });
|
|
26056
|
+
for (const c of prereqs.checks) {
|
|
26057
|
+
if (c.status === "ok") {
|
|
26058
|
+
if (c.justInstalled) continue;
|
|
26059
|
+
printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
26060
|
+
} else {
|
|
26061
|
+
printWarn(` ${c.label}: ${c.detail}`);
|
|
26062
|
+
if (c.remedy) printWarn(` ${c.remedy}`);
|
|
26063
|
+
}
|
|
26064
|
+
}
|
|
26065
|
+
if (prereqs.blocked) {
|
|
26066
|
+
printError("A required prerequisite is missing \u2014 cannot continue.");
|
|
26067
|
+
process.exit(1);
|
|
26068
|
+
}
|
|
26069
|
+
return prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
|
|
26070
|
+
}
|
|
26071
|
+
async function scaffoldProject(step, defaultsOnly) {
|
|
26072
|
+
step("Knowledge base, .gitignore and CLAUDE.md");
|
|
26073
|
+
await (0, import_promises17.mkdir)(VERITY_DIR, { recursive: true });
|
|
26074
|
+
await ensureMemoryDir();
|
|
26075
|
+
const ignoreResult = ensureVerityGitignore();
|
|
26076
|
+
if (ignoreResult === "failed") {
|
|
26077
|
+
printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
|
|
26078
|
+
printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
|
|
26079
|
+
} else if (ignoreResult === "conflict") {
|
|
26080
|
+
printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
|
|
26081
|
+
printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
|
|
26082
|
+
printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
|
|
26083
|
+
printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
|
|
26084
|
+
} else if (ignoreResult === "repaired") {
|
|
26085
|
+
printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
|
|
26086
|
+
} else {
|
|
26087
|
+
printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
|
|
26088
|
+
}
|
|
26089
|
+
const tracked = committedVerityState();
|
|
26090
|
+
if (tracked.length > 0) {
|
|
26091
|
+
printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
|
|
26092
|
+
const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
|
|
26093
|
+
if (untrack) {
|
|
26094
|
+
const result = untrackVerityState();
|
|
26095
|
+
if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
|
|
26096
|
+
else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
|
|
26097
|
+
} else {
|
|
26098
|
+
printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
|
|
26099
|
+
}
|
|
26100
|
+
}
|
|
26101
|
+
try {
|
|
26102
|
+
await ensureClaudeMdPointer();
|
|
26103
|
+
printInfo(" CLAUDE.md instructions \u2713");
|
|
26104
|
+
} catch (err) {
|
|
26105
|
+
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
26106
|
+
}
|
|
26107
|
+
}
|
|
25786
26108
|
function registerInitCommand(program2) {
|
|
25787
|
-
program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").option(
|
|
26109
|
+
program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").option(
|
|
26110
|
+
"--plugin-mode",
|
|
26111
|
+
"The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has"
|
|
26112
|
+
).option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
|
|
25788
26113
|
const force = opts.force ?? false;
|
|
25789
26114
|
const wantsHandoff = opts.setup !== false;
|
|
25790
26115
|
const wantsAdopt = opts.adopt !== false;
|
|
25791
26116
|
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
26117
|
+
const pluginMode = opts.pluginMode ?? pluginActiveHere();
|
|
25792
26118
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
25793
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
26119
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs51.existsSync)(m));
|
|
25794
26120
|
if (!isProject) {
|
|
25795
26121
|
printError("No project detected in the current directory.");
|
|
25796
26122
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -25821,43 +26147,13 @@ function registerInitCommand(program2) {
|
|
|
25821
26147
|
}
|
|
25822
26148
|
console.log("");
|
|
25823
26149
|
}
|
|
25824
|
-
step
|
|
25825
|
-
const prereqs = await checkPrereqs({ install: true });
|
|
25826
|
-
for (const c of prereqs.checks) {
|
|
25827
|
-
if (c.status === "ok") {
|
|
25828
|
-
if (c.justInstalled) continue;
|
|
25829
|
-
printInfo(` ${c.label} ${c.detail} \u2713`);
|
|
25830
|
-
} else {
|
|
25831
|
-
printWarn(` ${c.label}: ${c.detail}`);
|
|
25832
|
-
if (c.remedy) printWarn(` ${c.remedy}`);
|
|
25833
|
-
}
|
|
25834
|
-
}
|
|
25835
|
-
if (prereqs.blocked) {
|
|
25836
|
-
printError("A required prerequisite is missing \u2014 cannot continue.");
|
|
25837
|
-
process.exit(1);
|
|
25838
|
-
}
|
|
25839
|
-
const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
|
|
26150
|
+
const claudeInstalled = await checkPrerequisites(step);
|
|
25840
26151
|
console.log("");
|
|
25841
|
-
|
|
25842
|
-
|
|
25843
|
-
|
|
25844
|
-
|
|
25845
|
-
let skillsInstalled = 0;
|
|
25846
|
-
for (const skill of SKILLS) {
|
|
25847
|
-
const src = (0, import_node_path33.join)(skillsSource, skill);
|
|
25848
|
-
const dest = (0, import_node_path33.join)(skillsDest, skill);
|
|
25849
|
-
if (!(0, import_node_fs49.existsSync)(src)) {
|
|
25850
|
-
printWarn(` Skill data not found: ${skill}`);
|
|
25851
|
-
continue;
|
|
25852
|
-
}
|
|
25853
|
-
if ((0, import_node_fs49.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
|
|
25854
|
-
skillsInstalled++;
|
|
25855
|
-
continue;
|
|
25856
|
-
}
|
|
25857
|
-
await copyDir(src, dest);
|
|
25858
|
-
skillsInstalled++;
|
|
26152
|
+
if (pluginMode) {
|
|
26153
|
+
printInfo("Skipping skills \u2014 the Verity plugin provides them, namespaced as /verity:<name>.");
|
|
26154
|
+
} else {
|
|
26155
|
+
await installSkills(force, step);
|
|
25859
26156
|
}
|
|
25860
|
-
printInfo(` ${skillsInstalled}/${SKILLS.length} skills installed to .claude/skills/ \u2713`);
|
|
25861
26157
|
step(defaultsOnly ? "Setup answers (defaults)" : "Your setup answers");
|
|
25862
26158
|
const previous = await readSetupState();
|
|
25863
26159
|
const answers = await askSetupQuestions(defaultsOnly, previous);
|
|
@@ -25865,54 +26161,20 @@ function registerInitCommand(program2) {
|
|
|
25865
26161
|
if (defaultsOnly) {
|
|
25866
26162
|
printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
|
|
25867
26163
|
}
|
|
25868
|
-
step
|
|
25869
|
-
await (0, import_promises17.mkdir)(VERITY_DIR, { recursive: true });
|
|
25870
|
-
await ensureMemoryDir();
|
|
25871
|
-
const ignoreResult = ensureVerityGitignore();
|
|
25872
|
-
if (ignoreResult === "failed") {
|
|
25873
|
-
printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
|
|
25874
|
-
printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
|
|
25875
|
-
} else if (ignoreResult === "conflict") {
|
|
25876
|
-
printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
|
|
25877
|
-
printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
|
|
25878
|
-
printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
|
|
25879
|
-
printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
|
|
25880
|
-
} else if (ignoreResult === "repaired") {
|
|
25881
|
-
printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
|
|
25882
|
-
} else {
|
|
25883
|
-
printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
|
|
25884
|
-
}
|
|
25885
|
-
const tracked = committedVerityState();
|
|
25886
|
-
if (tracked.length > 0) {
|
|
25887
|
-
printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
|
|
25888
|
-
const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
|
|
25889
|
-
if (untrack) {
|
|
25890
|
-
const result = untrackVerityState();
|
|
25891
|
-
if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
|
|
25892
|
-
else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
|
|
25893
|
-
} else {
|
|
25894
|
-
printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
|
|
25895
|
-
}
|
|
25896
|
-
}
|
|
25897
|
-
try {
|
|
25898
|
-
await ensureClaudeMdPointer();
|
|
25899
|
-
printInfo(" CLAUDE.md instructions \u2713");
|
|
25900
|
-
} catch (err) {
|
|
25901
|
-
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
25902
|
-
}
|
|
26164
|
+
await scaffoldProject(step, defaultsOnly);
|
|
25903
26165
|
const globalVerityDir = (0, import_node_path33.join)(process.env.HOME ?? "", ".verity");
|
|
25904
26166
|
await (0, import_promises17.mkdir)(globalVerityDir, { recursive: true });
|
|
25905
26167
|
console.log("");
|
|
25906
26168
|
step("Wiring Claude Code hooks");
|
|
25907
|
-
|
|
25908
|
-
|
|
25909
|
-
|
|
25910
|
-
|
|
25911
|
-
|
|
25912
|
-
|
|
25913
|
-
|
|
25914
|
-
|
|
25915
|
-
|
|
26169
|
+
const gitMoments = [
|
|
26170
|
+
...moments.includes("pre-commit") ? ["commit"] : [],
|
|
26171
|
+
...moments.includes("pre-push") ? ["push"] : []
|
|
26172
|
+
];
|
|
26173
|
+
writeProjectConfig({ git_moments: gitMoments });
|
|
26174
|
+
if (pluginMode) {
|
|
26175
|
+
await adoptPluginWiring(gitMoments, moments);
|
|
26176
|
+
} else {
|
|
26177
|
+
await reconcileOwnWiring(moments);
|
|
25916
26178
|
}
|
|
25917
26179
|
console.log("");
|
|
25918
26180
|
step("Sign in to Verity (optional)");
|
|
@@ -25955,7 +26217,7 @@ function registerInitCommand(program2) {
|
|
|
25955
26217
|
}
|
|
25956
26218
|
step("Your project's Standard");
|
|
25957
26219
|
let haveStandard = false;
|
|
25958
|
-
if ((0,
|
|
26220
|
+
if ((0, import_node_fs51.existsSync)(projectPath(STANDARD_FILE))) {
|
|
25959
26221
|
printInfo(" This project already has .verity/standard.yaml \u2014 keeping it.");
|
|
25960
26222
|
haveStandard = true;
|
|
25961
26223
|
} else {
|
|
@@ -25982,7 +26244,7 @@ function registerInitCommand(program2) {
|
|
|
25982
26244
|
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
25983
26245
|
init: {
|
|
25984
26246
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25985
|
-
cli_version: true ? "0.32.0
|
|
26247
|
+
cli_version: true ? "0.32.0" : "dev"
|
|
25986
26248
|
}
|
|
25987
26249
|
});
|
|
25988
26250
|
} catch (err) {
|
|
@@ -25991,9 +26253,13 @@ function registerInitCommand(program2) {
|
|
|
25991
26253
|
console.log("");
|
|
25992
26254
|
printInfo("This machine is set up.");
|
|
25993
26255
|
console.log("");
|
|
25994
|
-
|
|
25995
|
-
|
|
25996
|
-
|
|
26256
|
+
if (pluginMode) {
|
|
26257
|
+
console.log(" (skills and hooks come from the Verity plugin, not this project)");
|
|
26258
|
+
} else {
|
|
26259
|
+
console.log(" .claude/skills/verity-*/ 8 skills (setup, analyze, status, feedback,");
|
|
26260
|
+
console.log(" learn, memory, insights, reflect)");
|
|
26261
|
+
console.log(" .claude/settings.json hooks, reconciled to your chosen moments");
|
|
26262
|
+
}
|
|
25997
26263
|
console.log(" .verity/memory/ knowledge base (commit to git)");
|
|
25998
26264
|
console.log(" .verity/standard.yaml the Standard the gate enforces");
|
|
25999
26265
|
console.log(" .codacy/codacy.config.json static-analysis patterns (validated)");
|
|
@@ -26017,7 +26283,7 @@ function registerInitCommand(program2) {
|
|
|
26017
26283
|
}
|
|
26018
26284
|
|
|
26019
26285
|
// src/commands/uninstall.ts
|
|
26020
|
-
var
|
|
26286
|
+
var import_node_fs52 = require("node:fs");
|
|
26021
26287
|
var import_node_path34 = require("node:path");
|
|
26022
26288
|
var SKILL_NAMES = [
|
|
26023
26289
|
"verity-setup",
|
|
@@ -26038,10 +26304,10 @@ function registerUninstallCommand(program2) {
|
|
|
26038
26304
|
const skillsRoot = projectPath(".claude/skills");
|
|
26039
26305
|
for (const name of SKILL_NAMES) {
|
|
26040
26306
|
const dir = (0, import_node_path34.join)(skillsRoot, name);
|
|
26041
|
-
if ((0,
|
|
26307
|
+
if ((0, import_node_fs52.existsSync)(dir)) {
|
|
26042
26308
|
actions.push({
|
|
26043
26309
|
label: `Remove .claude/skills/${name}/`,
|
|
26044
|
-
apply: () => (0,
|
|
26310
|
+
apply: () => (0, import_node_fs52.rmSync)(dir, { recursive: true, force: true })
|
|
26045
26311
|
});
|
|
26046
26312
|
}
|
|
26047
26313
|
}
|
|
@@ -26055,24 +26321,24 @@ function registerUninstallCommand(program2) {
|
|
|
26055
26321
|
});
|
|
26056
26322
|
}
|
|
26057
26323
|
const verityDir = projectPath(VERITY_DIR);
|
|
26058
|
-
if ((0,
|
|
26324
|
+
if ((0, import_node_fs52.existsSync)(verityDir)) {
|
|
26059
26325
|
actions.push({
|
|
26060
26326
|
label: `Remove ${VERITY_DIR}/`,
|
|
26061
|
-
apply: () => (0,
|
|
26327
|
+
apply: () => (0, import_node_fs52.rmSync)(verityDir, { recursive: true, force: true })
|
|
26062
26328
|
});
|
|
26063
26329
|
}
|
|
26064
26330
|
if (!keepVerityMd) {
|
|
26065
26331
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
26066
|
-
if ((0,
|
|
26332
|
+
if ((0, import_node_fs52.existsSync)(verityMd)) {
|
|
26067
26333
|
actions.push({
|
|
26068
26334
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
26069
|
-
apply: () => (0,
|
|
26335
|
+
apply: () => (0, import_node_fs52.rmSync)(verityMd, { force: true })
|
|
26070
26336
|
});
|
|
26071
26337
|
}
|
|
26072
26338
|
}
|
|
26073
26339
|
const cleanupEmptyDir = (path) => {
|
|
26074
|
-
if ((0,
|
|
26075
|
-
(0,
|
|
26340
|
+
if ((0, import_node_fs52.existsSync)(path) && (0, import_node_fs52.statSync)(path).isDirectory() && (0, import_node_fs52.readdirSync)(path).length === 0) {
|
|
26341
|
+
(0, import_node_fs52.rmdirSync)(path);
|
|
26076
26342
|
}
|
|
26077
26343
|
};
|
|
26078
26344
|
actions.push({
|
|
@@ -26084,10 +26350,10 @@ function registerUninstallCommand(program2) {
|
|
|
26084
26350
|
});
|
|
26085
26351
|
const home = process.env.HOME ?? "";
|
|
26086
26352
|
const globalVerityDir = (0, import_node_path34.join)(home, ".verity");
|
|
26087
|
-
if (purgeGlobal && (0,
|
|
26353
|
+
if (purgeGlobal && (0, import_node_fs52.existsSync)(globalVerityDir)) {
|
|
26088
26354
|
actions.push({
|
|
26089
26355
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
26090
|
-
apply: () => (0,
|
|
26356
|
+
apply: () => (0, import_node_fs52.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
26091
26357
|
});
|
|
26092
26358
|
}
|
|
26093
26359
|
if (actions.length === 0) {
|
|
@@ -26281,7 +26547,7 @@ function registerTaskCommands(program2) {
|
|
|
26281
26547
|
}
|
|
26282
26548
|
|
|
26283
26549
|
// src/commands/reset.ts
|
|
26284
|
-
var
|
|
26550
|
+
var import_node_fs53 = require("node:fs");
|
|
26285
26551
|
var import_node_path35 = require("node:path");
|
|
26286
26552
|
function registerResetCommand(program2) {
|
|
26287
26553
|
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) => {
|
|
@@ -26319,11 +26585,11 @@ function registerResetCommand(program2) {
|
|
|
26319
26585
|
}
|
|
26320
26586
|
const cacheDir = projectPath(CACHE_DIR);
|
|
26321
26587
|
let purged = 0;
|
|
26322
|
-
if ((0,
|
|
26323
|
-
for (const entry of (0,
|
|
26588
|
+
if ((0, import_node_fs53.existsSync)(cacheDir)) {
|
|
26589
|
+
for (const entry of (0, import_node_fs53.readdirSync)(cacheDir)) {
|
|
26324
26590
|
if (entry.startsWith("pending-")) {
|
|
26325
26591
|
try {
|
|
26326
|
-
(0,
|
|
26592
|
+
(0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(cacheDir, entry));
|
|
26327
26593
|
purged++;
|
|
26328
26594
|
} catch {
|
|
26329
26595
|
}
|
|
@@ -26338,19 +26604,19 @@ function registerResetCommand(program2) {
|
|
|
26338
26604
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
26339
26605
|
];
|
|
26340
26606
|
for (const file of filesToClear) {
|
|
26341
|
-
if ((0,
|
|
26607
|
+
if ((0, import_node_fs53.existsSync)(file)) {
|
|
26342
26608
|
try {
|
|
26343
|
-
(0,
|
|
26609
|
+
(0, import_node_fs53.writeFileSync)(file, "");
|
|
26344
26610
|
} catch {
|
|
26345
26611
|
}
|
|
26346
26612
|
}
|
|
26347
26613
|
}
|
|
26348
26614
|
if (opts.all) {
|
|
26349
26615
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
26350
|
-
if ((0,
|
|
26351
|
-
for (const entry of (0,
|
|
26616
|
+
if ((0, import_node_fs53.existsSync)(logsDir)) {
|
|
26617
|
+
for (const entry of (0, import_node_fs53.readdirSync)(logsDir)) {
|
|
26352
26618
|
try {
|
|
26353
|
-
(0,
|
|
26619
|
+
(0, import_node_fs53.unlinkSync)((0, import_node_path35.join)(logsDir, entry));
|
|
26354
26620
|
} catch {
|
|
26355
26621
|
}
|
|
26356
26622
|
}
|
|
@@ -26658,8 +26924,8 @@ function registerTelemetryCommands(program2) {
|
|
|
26658
26924
|
}
|
|
26659
26925
|
|
|
26660
26926
|
// src/cli.ts
|
|
26661
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.32.0
|
|
26662
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.0
|
|
26927
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.32.0").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) => {
|
|
26928
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.32.0");
|
|
26663
26929
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
26664
26930
|
try {
|
|
26665
26931
|
await foldLegacyLocalCredential();
|