@codacy/verity-cli 0.31.2 → 0.31.4

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/bin/verity.js +560 -506
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -24,6 +24,16 @@ installs and updates it for you.
24
24
  plugin is present it owns the hooks, and the settings.json copy stands down.
25
25
  `verity init` detects the plugin and removes the duplicate wiring.
26
26
 
27
+ ### 🚪 A project that was never set up is left alone
28
+
29
+ Every hook now asks the same question the same way. `verity analyze` used to run
30
+ in a project that had never seen `verity init`, create `.verity/` there, and then —
31
+ with no baseline to compare against — review nothing. It now stands down like the
32
+ other hooks always have, so installing Verity no longer scatters state through
33
+ directories you merely opened. On the first session in such a project, Claude is
34
+ told once that the gate is off here and can offer `/verity:setup` (or `verity init`
35
+ on an npm install). Works correctly from linked git worktrees.
36
+
27
37
  ### ⚙️ Git-moment gating moved into the project
28
38
 
29
39
  `verity guard` now reads `.verity/config.json` instead of taking its moments from
@@ -107,6 +117,26 @@ with one entry point.
107
117
  terminal (Ctrl+D, a pty that ends) falls back to the default instead of
108
118
  crashing init between copying the skills and wiring the hooks.
109
119
 
120
+ ## [0.31.3] — 2026-09-03
121
+
122
+ **After a rebase, Verity reviewed the wrong code.** When a branch was rebased
123
+ onto an updated main, the gate could treat all of the upstream changes as the
124
+ session's own work and block the push on findings in code the session never
125
+ touched. The cause was that the review baseline was trusted by whether its commit
126
+ still existed rather than whether it was still part of the current history, so a
127
+ rewritten baseline kept anchoring every diff.
128
+
129
+ - **The push gate reviews exactly what the push publishes** — the commits the
130
+ remote does not yet have — so a rebase, a force-push, a first push, or
131
+ re-pushing an already-published commit each review only their real new work.
132
+ - **The stop-hook baseline is trusted only while it is an ancestor of HEAD.** A
133
+ rewritten baseline is discarded and re-derived from the commits actually made
134
+ since, so upstream changes are never attributed to the session.
135
+ - **A commit made during a merge reviews only the files you resolved**, not the
136
+ whole incoming branch.
137
+ - **A slash command such as `/login` is no longer treated as a task or a goal**,
138
+ so the review is graded against what you were actually working on.
139
+
110
140
  ## [0.31.1] — 2026-08-27
111
141
 
112
142
  **Verity stops repeating itself.** A finding it had already closed could keep
package/bin/verity.js CHANGED
@@ -10833,16 +10833,9 @@ function splitLines(s) {
10833
10833
  }
10834
10834
  var SHA_RE = /^[0-9a-f]{40}$/;
10835
10835
  function readBaselineSha() {
10836
- if (!(0, import_node_fs3.existsSync)(BASELINE_SHA_FILE)) return null;
10837
- let sha;
10838
- try {
10839
- sha = (0, import_node_fs3.readFileSync)(BASELINE_SHA_FILE, "utf-8").trim();
10840
- } catch {
10841
- return null;
10842
- }
10843
- if (!SHA_RE.test(sha)) return null;
10844
- const reachable = execGit(`git cat-file -e ${sha}^{commit} 2>/dev/null && echo ok`) === "ok";
10845
- if (!reachable) {
10836
+ const sha = readRawBaselineSha();
10837
+ if (sha === null) return null;
10838
+ if (!commitResolves(sha)) {
10846
10839
  try {
10847
10840
  (0, import_node_fs3.unlinkSync)(BASELINE_SHA_FILE);
10848
10841
  } catch {
@@ -10851,6 +10844,16 @@ function readBaselineSha() {
10851
10844
  }
10852
10845
  return sha;
10853
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
+ }
10854
10857
  function writeBaselineSha(sha) {
10855
10858
  if (!SHA_RE.test(sha)) return;
10856
10859
  try {
@@ -10859,6 +10862,25 @@ function writeBaselineSha(sha) {
10859
10862
  } catch {
10860
10863
  }
10861
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
+ }
10862
10884
  var VERITY_OWNED_PREFIXES = [".verity/", ".gate/", ".codacy/"];
10863
10885
  var VERITY_OWNED_FILES = ["VERITY.md"];
10864
10886
  function isVerityOwnedPath(file) {
@@ -10871,6 +10893,7 @@ function getChangedFiles() {
10871
10893
  for (const f of splitLines(execGit("git diff --name-only HEAD"))) sets.add(f);
10872
10894
  for (const f of splitLines(execGit("git diff --name-only --cached"))) sets.add(f);
10873
10895
  for (const f of splitLines(execGit("git ls-files --others --exclude-standard"))) sets.add(f);
10896
+ const rawBaseline = readRawBaselineSha();
10874
10897
  const baseline = readBaselineSha();
10875
10898
  if (baseline) {
10876
10899
  const committed = splitLines(execGit(`git diff --name-only ${baseline}..HEAD`));
@@ -10878,6 +10901,13 @@ function getChangedFiles() {
10878
10901
  hasRecentCommitFiles = true;
10879
10902
  for (const f of committed) sets.add(f);
10880
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
+ }
10881
10911
  } else {
10882
10912
  const headTimestamp = parseInt(execGit("git log -1 --format=%ct HEAD"), 10) || 0;
10883
10913
  const commitAge = Math.floor(Date.now() / 1e3) - headTimestamp;
@@ -13083,6 +13113,242 @@ function getRecentCommitMessages() {
13083
13113
  }
13084
13114
  }
13085
13115
 
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;
13159
+ }
13160
+ return true;
13161
+ }
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;
13168
+ }
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;
13175
+ }
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
+
13086
13352
  // src/lib/context-identity.ts
13087
13353
  var import_node_crypto2 = require("node:crypto");
13088
13354
  var import_node_fs8 = require("node:fs");
@@ -14023,386 +14289,159 @@ function* scanLines(text) {
14023
14289
  offset += line.length + 1;
14024
14290
  }
14025
14291
  }
14026
- function findMarker(text, marker, from = 0) {
14027
- for (const { line, start, inFence } of scanLines(text)) {
14028
- if (!inFence && start >= from && line.startsWith(marker)) {
14029
- return start;
14030
- }
14031
- }
14032
- return -1;
14033
- }
14034
- function hasLegacyMemoryBlock(text) {
14035
- return findMarker(text, LEGACY_MD_START) !== -1;
14036
- }
14037
- async function ensureClaudeMdPointer(cwd = repoRoot()) {
14038
- const claudeMdPath = (0, import_node_path10.join)(cwd, "CLAUDE.md");
14039
- let existing = "";
14040
- if ((0, import_node_fs12.existsSync)(claudeMdPath)) {
14041
- existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
14042
- }
14043
- let startTag = CLAUDE_MD_START;
14044
- let endTag = CLAUDE_MD_END;
14045
- let startIdx = findMarker(existing, CLAUDE_MD_START);
14046
- let endIdx = startIdx === -1 ? -1 : findMarker(existing, CLAUDE_MD_END, startIdx + CLAUDE_MD_START.length);
14047
- if (startIdx === -1 || endIdx === -1) {
14048
- const legacyStart = findMarker(existing, LEGACY_MD_START);
14049
- const legacyEnd = legacyStart === -1 ? -1 : findMarker(existing, LEGACY_MD_END, legacyStart + LEGACY_MD_START.length);
14050
- if (legacyStart !== -1 && legacyEnd !== -1) {
14051
- startTag = LEGACY_MD_START;
14052
- endTag = LEGACY_MD_END;
14053
- startIdx = legacyStart;
14054
- endIdx = legacyEnd;
14055
- } else if (startIdx !== -1 && endIdx === -1 || legacyStart !== -1 && legacyEnd === -1) {
14056
- printWarn(
14057
- "CLAUDE.md: found an unterminated memory marker (a start marker with no matching end marker). Leaving the file untouched to avoid erasing your text \u2014 please close or remove the stray marker so Verity can manage the block."
14058
- );
14059
- return;
14060
- }
14061
- }
14062
- const hasBlock = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx;
14063
- let preserved = PRESERVE_PLACEHOLDER;
14064
- if (hasBlock) {
14065
- const interior = existing.slice(startIdx + startTag.length, endIdx);
14066
- const captured = extractPreserveContent(interior);
14067
- if (captured !== null) {
14068
- preserved = captured;
14069
- } else {
14070
- const rescued = stripKnownProse(interior).trim();
14071
- if (rescued) {
14072
- preserved = rescued;
14073
- printWarn(
14074
- `CLAUDE.md: moved ${rescued.split("\n").length} line(s) of hand-edited text from inside the verity-memory markers into a preserve region (it now survives regeneration). Put durable guidance OUTSIDE the markers to keep it fully under your control.`
14075
- );
14076
- }
14077
- }
14078
- }
14079
- const block = [
14080
- CLAUDE_MD_START,
14081
- CLAUDE_MD_PROSE,
14082
- "",
14083
- PRESERVE_START,
14084
- preserved,
14085
- PRESERVE_END,
14086
- CLAUDE_MD_END
14087
- ].join("\n");
14088
- let next;
14089
- if (hasBlock) {
14090
- next = existing.slice(0, startIdx) + block + existing.slice(endIdx + endTag.length);
14091
- } else if (existing.trim() === "") {
14092
- next = block + "\n";
14093
- } else {
14094
- next = existing.replace(/\n*$/, "") + "\n\n" + block + "\n";
14095
- }
14096
- if (next === existing) return;
14097
- await (0, import_promises8.writeFile)(claudeMdPath, next);
14098
- }
14099
- function extractPreserveContent(interior) {
14100
- for (const [start, end] of [
14101
- [PRESERVE_START, PRESERVE_END],
14102
- [LEGACY_PRESERVE_START, LEGACY_PRESERVE_END]
14103
- ]) {
14104
- const s = interior.indexOf(start);
14105
- const e = interior.indexOf(end);
14106
- if (s !== -1 && e !== -1 && e >= s) {
14107
- return interior.slice(s + start.length, e).replace(/^\n+|\n+$/g, "");
14108
- }
14109
- }
14110
- return null;
14111
- }
14112
- function stripKnownProse(interior) {
14113
- const trimmed = interior.replace(/^\n+/, "");
14114
- for (const prose of [
14115
- CLAUDE_MD_PROSE,
14116
- CLAUDE_MD_PROSE_PRE_REFLECT,
14117
- CLAUDE_MD_PROSE_PRE_WAIVE,
14118
- CLAUDE_MD_PROSE_PRE_IGNORE,
14119
- CLAUDE_MD_PROSE_LEGACY
14120
- ]) {
14121
- if (trimmed.startsWith(prose)) return trimmed.slice(prose.length);
14122
- }
14123
- return trimmed;
14124
- }
14125
- var SCHEMA_TEMPLATE = `# Memory Graph Schema (v1)
14126
-
14127
- ## Node format
14128
-
14129
- Each node is a markdown file with YAML frontmatter:
14130
-
14131
- \`\`\`yaml
14132
- ---
14133
- schema: 1
14134
- id: n001-slug
14135
- kind: decision | quality | security | intent | gotcha | pattern | domain | integration
14136
- title: "Short title (\u2264200 chars)"
14137
- domains: [tag1, tag2]
14138
- file_globs: ["src/auth/**"]
14139
- confidence: 0.5-1.0
14140
- status: active | archived | superseded | orphan_flagged
14141
- source: extractor | user | imported
14142
- # ... (see full schema in MEMORY-GRAPH-PRD \xA76.3)
14143
- ---
14144
-
14145
- # Title
14146
-
14147
- Body content (\u22648KB). Use [[node-id]] wikilinks for cross-references.
14148
- \`\`\`
14149
-
14150
- ## Edge types
14151
-
14152
- | Edge | Meaning |
14153
- |------|---------|
14154
- | related | Loose association |
14155
- | supersedes | A replaces B |
14156
- | contradicts | A and B disagree |
14157
- | caused_by | Something in B led to A |
14158
- | example_of | A is an instance of B |
14159
-
14160
- ## Domains
14161
-
14162
- | Directory | Purpose |
14163
- |-----------|---------|
14164
- | decisions/ | Architectural choices (ADR-style) |
14165
- | quality/ | Quality patterns |
14166
- | security/ | Security constraints |
14167
- | intent/ | Intent templates |
14168
- | gotchas/ | Footguns and surprises |
14169
- | patterns/ | Code conventions |
14170
- | domain/ | Business logic concepts |
14171
- | integrations/ | External system knowledge |
14172
- | _archive/ | Superseded nodes |
14173
- `;
14174
-
14175
- // src/lib/dossier-session.ts
14176
- var import_node_fs17 = require("node:fs");
14177
- var import_node_crypto7 = require("node:crypto");
14178
- var import_node_path13 = require("node:path");
14179
-
14180
- // src/lib/analysis-mode.ts
14181
- var DEBUG_PHRASES = [
14182
- "not working",
14183
- "doesn't work",
14184
- "doesn't work",
14185
- "does not work",
14186
- "isn't working",
14187
- "is not working",
14188
- "can't figure out",
14189
- "stack trace"
14190
- ];
14191
- var DEBUG_WORDS = [
14192
- "fix",
14193
- "bug",
14194
- "broken",
14195
- "crash",
14196
- "crashing",
14197
- "failing",
14198
- "debug",
14199
- "debugging",
14200
- "investigate",
14201
- "troubleshoot",
14202
- "regression",
14203
- "wrong"
14204
- ];
14205
- var DEBUG_PATTERN = new RegExp(
14206
- [
14207
- ...DEBUG_PHRASES.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")),
14208
- ...DEBUG_WORDS.map((w) => `\\b${w}\\b`)
14209
- ].join("|"),
14210
- "i"
14211
- );
14212
- var FALSE_POSITIVE_PATTERNS = [
14213
- /\b(?:add|create|implement|write|build|design|set\s*up)\b.{0,20}\berror\b/i,
14214
- /\berror\s+handling\b/i,
14215
- /\berror\s+boundar(?:y|ies)\b/i,
14216
- /\berror\s+(?:type|class|page|component|message|code|enum)\b/i,
14217
- /\b(?:add|create|implement|write|build)\b.{0,20}\b(?:fix|debug|issue)\b/i
14218
- ];
14219
- function hasDebugIntent(prompt) {
14220
- if (!DEBUG_PATTERN.test(prompt)) return false;
14221
- for (const fp of FALSE_POSITIVE_PATTERNS) {
14222
- if (fp.test(prompt)) return false;
14223
- }
14224
- return true;
14225
- }
14226
- var GIT_ONLY_PATTERN = /\b(commit|push|deploy|merge|rebase|tag|release|publish|ship)\b/i;
14227
- 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;
14228
- function isGitOnlyPrompt(prompt) {
14229
- if (!GIT_ONLY_PATTERN.test(prompt)) return false;
14230
- if (CODE_AUTHORING_PATTERN.test(prompt)) return false;
14231
- return true;
14232
- }
14233
- function reconcileAnalysisMode(predictedMode, signals) {
14234
- const mode2 = resolveAnalysisMode(predictedMode, signals);
14235
- if (mode2 !== "skip") return mode2;
14236
- const windowIsOrphaned = signals.actionSummary?.transcript_windowed === "orphaned";
14237
- if (windowIsOrphaned && !signals.sessionAuthoredCode) return "standard";
14238
- return mode2;
14239
- }
14240
- function resolveAnalysisMode(predictedMode, signals) {
14241
- if (!predictedMode || !isValidMode(predictedMode)) {
14242
- return detectAnalysisMode(
14243
- signals.noFilesChanged,
14244
- signals.assistantResponse,
14245
- signals.conversationPrompts,
14246
- signals.actionSummary,
14247
- signals.sessionAuthoredCode
14248
- );
14249
- }
14250
- const agentAuthoredCode = !!(signals.actionSummary && (signals.actionSummary.files_edited.length > 0 || signals.actionSummary.files_created.length > 0)) || !!signals.sessionAuthoredCode;
14251
- const agentInvestigated = didAgentInvestigate(signals.actionSummary);
14252
- switch (predictedMode) {
14253
- case "skip":
14254
- if (agentAuthoredCode) return "standard";
14255
- return "skip";
14256
- case "plan":
14257
- if (agentAuthoredCode) return "standard";
14258
- return "plan";
14259
- case "debug":
14260
- return "debug";
14261
- case "standard":
14262
- if (!!signals.actionSummary && !agentAuthoredCode && !!signals.assistantResponse) {
14263
- return agentInvestigated ? "plan" : "skip";
14264
- }
14265
- return "standard";
14266
- }
14267
- }
14268
- function didAgentInvestigate(summary) {
14269
- if (!summary) return false;
14270
- return summary.files_read.length > 0 || summary.searches > 0 || summary.commands.length > 0 || summary.subagents > 0 || summary.web_fetches > 0;
14271
- }
14272
- function isValidMode(mode2) {
14273
- return mode2 === "standard" || mode2 === "plan" || mode2 === "debug" || mode2 === "skip";
14274
- }
14275
- function detectAnalysisMode(noFilesChanged, assistantResponse, conversationPrompts, actionSummary, sessionAuthoredCode) {
14276
- const agentAuthoredCode = !!(actionSummary && (actionSummary.files_edited.length > 0 || actionSummary.files_created.length > 0)) || !!sessionAuthoredCode;
14277
- if (conversationPrompts.length > 0 && conversationPrompts.every(isGitOnlyPrompt)) {
14278
- if (!agentAuthoredCode) return "skip";
14279
- }
14280
- if (noFilesChanged && !!assistantResponse && !agentAuthoredCode) {
14281
- return "plan";
14282
- }
14283
- if (!!actionSummary && !agentAuthoredCode && !!assistantResponse) {
14284
- return didAgentInvestigate(actionSummary) ? "plan" : "skip";
14285
- }
14286
- for (const prompt of conversationPrompts) {
14287
- if (hasDebugIntent(prompt)) {
14288
- return "debug";
14289
- }
14290
- }
14291
- return "standard";
14292
- }
14293
- 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;
14294
- var GIT_PLUMBING_RE = /^\s*git\s+(?:merge|rebase|stash|cherry-pick|revert|pull|fetch|checkout|switch|reset|restore|clean)\b/i;
14295
- 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;
14296
- var CHAIN_RE = /&&|\||;|\$\(|\x60/;
14297
- function isNonAuthoringCommand(cmd) {
14298
- if (typeof cmd !== "string" || cmd.trim().length === 0) return false;
14299
- if (FILE_MUTATE_RE.test(cmd)) return false;
14300
- if (CHAIN_RE.test(cmd)) return false;
14301
- return GIT_PLUMBING_RE.test(cmd) || READ_ONLY_RE.test(cmd);
14302
- }
14303
- function hasNonEditAuthorship(actionSummary, sessionAuthoredCode) {
14304
- if (!actionSummary) return sessionAuthoredCode;
14305
- if ((actionSummary.subagents ?? 0) > 0) return true;
14306
- if (Object.keys(actionSummary.tool_counts ?? {}).some((t) => t.startsWith("mcp__"))) return true;
14307
- const commands = actionSummary.commands ?? [];
14308
- if (commands.some((c) => FILE_MUTATE_RE.test(c))) return true;
14309
- if (sessionAuthoredCode) {
14310
- const allSafe = commands.length > 0 && commands.every(isNonAuthoringCommand);
14311
- if (!allSafe) return true;
14312
- }
14313
- return false;
14314
- }
14315
- function scopeToAuthored(files, actionSummary) {
14316
- if (!actionSummary) return { files, signal: "no-transcript" };
14317
- const touched = [...actionSummary.files_edited ?? [], ...actionSummary.files_created ?? []];
14318
- if (touched.length === 0) return { files: [], signal: "none-authored" };
14319
- return { files: narrowToAgentAuthored(files, actionSummary), signal: "authored" };
14320
- }
14321
- function narrowToAgentAuthored(files, actionSummary) {
14322
- if (!actionSummary) return files;
14323
- const touched = [
14324
- ...actionSummary.files_edited,
14325
- ...actionSummary.files_created
14326
- ];
14327
- if (touched.length === 0) return files;
14328
- return files.filter((f) => {
14329
- const suffix = "/" + f;
14330
- return touched.some((t) => t === f || t.endsWith(suffix));
14331
- });
14332
- }
14333
-
14334
- // src/lib/skip-detection.ts
14335
- function isBareAckPrompt(prompt) {
14336
- if (typeof prompt !== "string") return false;
14337
- const trimmed = prompt.trim();
14338
- if (trimmed.length === 0) return false;
14339
- if (trimmed.length > 20) return false;
14340
- 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;
14341
- return bareAckPattern.test(trimmed);
14342
- }
14343
- function isContinuationPrompt(prompt) {
14344
- if (typeof prompt !== "string") return false;
14345
- const trimmed = prompt.trim();
14346
- if (trimmed.length === 0) return false;
14347
- if (trimmed.length > 24) return false;
14348
- 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;
14349
- return continuation.test(trimmed) || isBareAckPrompt(trimmed);
14350
- }
14351
- function resolveGoalPrompt(prompts) {
14352
- if (prompts.length === 0) return null;
14353
- const latest = prompts[prompts.length - 1];
14354
- if (!isContinuationPrompt(latest.prompt)) return { entry: latest, turnsBack: 0 };
14355
- for (let i = prompts.length - 2; i >= 0; i--) {
14356
- if (!isContinuationPrompt(prompts[i].prompt)) {
14357
- return { entry: prompts[i], turnsBack: prompts.length - 1 - i };
14292
+ function findMarker(text, marker, from = 0) {
14293
+ for (const { line, start, inFence } of scanLines(text)) {
14294
+ if (!inFence && start >= from && line.startsWith(marker)) {
14295
+ return start;
14358
14296
  }
14359
14297
  }
14360
- return { entry: latest, turnsBack: 0 };
14298
+ return -1;
14361
14299
  }
14362
- function isReflectionQuestion(response) {
14363
- if (!response || typeof response !== "string") return false;
14364
- const markers = [
14365
- /reflection\s+for\s+future\s+agents/i,
14366
- /what(?:'s|\s+is)\s+one\s+thing\s+you\s+learned/i,
14367
- /say\s+['"]?skip['"]?\s+to\s+skip/i,
14368
- /quick\s+reflection\s+question/i,
14369
- // Post-flip (VRT-21): the agent drafts the reflection itself and, when
14370
- // interactive, asks the user to confirm/correct before recording. That
14371
- // turn authors no code either, so it's still a reflection turn.
14372
- /reflection\s+draft/i,
14373
- /confirm,?\s+correct,?\s+or\s+add/i
14374
- ];
14375
- return markers.some((m) => m.test(response));
14300
+ function hasLegacyMemoryBlock(text) {
14301
+ return findMarker(text, LEGACY_MD_START) !== -1;
14376
14302
  }
14377
- function isMetaTaskLabel(label2) {
14378
- if (label2 === null || label2 === void 0) return false;
14379
- if (typeof label2 !== "string") return false;
14380
- const trimmed = label2.trim();
14381
- if (trimmed.length === 0) return true;
14382
- const metaPatterns = [
14383
- /^verity\s+[\w-]+\s+response$/i,
14384
- // "Verity reflect response"
14385
- /^simple user response$/i,
14386
- /^verity\s+command$/i,
14387
- // "Verity command"
14388
- /^user\s+(question|reply|response|ack)$/i
14389
- ];
14390
- return metaPatterns.some((p) => p.test(trimmed));
14303
+ async function ensureClaudeMdPointer(cwd = repoRoot()) {
14304
+ const claudeMdPath = (0, import_node_path10.join)(cwd, "CLAUDE.md");
14305
+ let existing = "";
14306
+ if ((0, import_node_fs12.existsSync)(claudeMdPath)) {
14307
+ existing = await (0, import_promises8.readFile)(claudeMdPath, "utf-8");
14308
+ }
14309
+ let startTag = CLAUDE_MD_START;
14310
+ let endTag = CLAUDE_MD_END;
14311
+ let startIdx = findMarker(existing, CLAUDE_MD_START);
14312
+ let endIdx = startIdx === -1 ? -1 : findMarker(existing, CLAUDE_MD_END, startIdx + CLAUDE_MD_START.length);
14313
+ if (startIdx === -1 || endIdx === -1) {
14314
+ const legacyStart = findMarker(existing, LEGACY_MD_START);
14315
+ const legacyEnd = legacyStart === -1 ? -1 : findMarker(existing, LEGACY_MD_END, legacyStart + LEGACY_MD_START.length);
14316
+ if (legacyStart !== -1 && legacyEnd !== -1) {
14317
+ startTag = LEGACY_MD_START;
14318
+ endTag = LEGACY_MD_END;
14319
+ startIdx = legacyStart;
14320
+ endIdx = legacyEnd;
14321
+ } else if (startIdx !== -1 && endIdx === -1 || legacyStart !== -1 && legacyEnd === -1) {
14322
+ printWarn(
14323
+ "CLAUDE.md: found an unterminated memory marker (a start marker with no matching end marker). Leaving the file untouched to avoid erasing your text \u2014 please close or remove the stray marker so Verity can manage the block."
14324
+ );
14325
+ return;
14326
+ }
14327
+ }
14328
+ const hasBlock = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx;
14329
+ let preserved = PRESERVE_PLACEHOLDER;
14330
+ if (hasBlock) {
14331
+ const interior = existing.slice(startIdx + startTag.length, endIdx);
14332
+ const captured = extractPreserveContent(interior);
14333
+ if (captured !== null) {
14334
+ preserved = captured;
14335
+ } else {
14336
+ const rescued = stripKnownProse(interior).trim();
14337
+ if (rescued) {
14338
+ preserved = rescued;
14339
+ printWarn(
14340
+ `CLAUDE.md: moved ${rescued.split("\n").length} line(s) of hand-edited text from inside the verity-memory markers into a preserve region (it now survives regeneration). Put durable guidance OUTSIDE the markers to keep it fully under your control.`
14341
+ );
14342
+ }
14343
+ }
14344
+ }
14345
+ const block = [
14346
+ CLAUDE_MD_START,
14347
+ CLAUDE_MD_PROSE,
14348
+ "",
14349
+ PRESERVE_START,
14350
+ preserved,
14351
+ PRESERVE_END,
14352
+ CLAUDE_MD_END
14353
+ ].join("\n");
14354
+ let next;
14355
+ if (hasBlock) {
14356
+ next = existing.slice(0, startIdx) + block + existing.slice(endIdx + endTag.length);
14357
+ } else if (existing.trim() === "") {
14358
+ next = block + "\n";
14359
+ } else {
14360
+ next = existing.replace(/\n*$/, "") + "\n\n" + block + "\n";
14361
+ }
14362
+ if (next === existing) return;
14363
+ await (0, import_promises8.writeFile)(claudeMdPath, next);
14391
14364
  }
14392
- function shouldSkipForBareAck(input) {
14393
- if (!isBareAckPrompt(input.prompt)) return false;
14394
- if (input.turnAuthoredCode) return false;
14395
- return input.canSeeTurnAuthorship;
14365
+ function extractPreserveContent(interior) {
14366
+ for (const [start, end] of [
14367
+ [PRESERVE_START, PRESERVE_END],
14368
+ [LEGACY_PRESERVE_START, LEGACY_PRESERVE_END]
14369
+ ]) {
14370
+ const s = interior.indexOf(start);
14371
+ const e = interior.indexOf(end);
14372
+ if (s !== -1 && e !== -1 && e >= s) {
14373
+ return interior.slice(s + start.length, e).replace(/^\n+|\n+$/g, "");
14374
+ }
14375
+ }
14376
+ return null;
14396
14377
  }
14397
- function isCommandOnlyTurn(input) {
14398
- if (!input.authorshipIsObservable) return false;
14399
- if (input.userCommandsTruncated) return false;
14400
- const commands = input.userCommands ?? [];
14401
- if (commands.length === 0) return false;
14402
- if (input.agentAuthoredFiles > 0) return false;
14403
- if (input.agentToolCalls > 0) return false;
14404
- return commands.every(isNonAuthoringCommand);
14378
+ function stripKnownProse(interior) {
14379
+ const trimmed = interior.replace(/^\n+/, "");
14380
+ for (const prose of [
14381
+ CLAUDE_MD_PROSE,
14382
+ CLAUDE_MD_PROSE_PRE_REFLECT,
14383
+ CLAUDE_MD_PROSE_PRE_WAIVE,
14384
+ CLAUDE_MD_PROSE_PRE_IGNORE,
14385
+ CLAUDE_MD_PROSE_LEGACY
14386
+ ]) {
14387
+ if (trimmed.startsWith(prose)) return trimmed.slice(prose.length);
14388
+ }
14389
+ return trimmed;
14405
14390
  }
14391
+ var SCHEMA_TEMPLATE = `# Memory Graph Schema (v1)
14392
+
14393
+ ## Node format
14394
+
14395
+ Each node is a markdown file with YAML frontmatter:
14396
+
14397
+ \`\`\`yaml
14398
+ ---
14399
+ schema: 1
14400
+ id: n001-slug
14401
+ kind: decision | quality | security | intent | gotcha | pattern | domain | integration
14402
+ title: "Short title (\u2264200 chars)"
14403
+ domains: [tag1, tag2]
14404
+ file_globs: ["src/auth/**"]
14405
+ confidence: 0.5-1.0
14406
+ status: active | archived | superseded | orphan_flagged
14407
+ source: extractor | user | imported
14408
+ # ... (see full schema in MEMORY-GRAPH-PRD \xA76.3)
14409
+ ---
14410
+
14411
+ # Title
14412
+
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");
14406
14445
 
14407
14446
  // src/lib/pending-repeat.ts
14408
14447
  var STOP = /* @__PURE__ */ new Set([
@@ -15863,15 +15902,18 @@ function registerIntentCommands(program2) {
15863
15902
  if (deferredToPlugin("intent capture", event.session_id ?? process.env.CLAUDE_SESSION_ID ?? null)) {
15864
15903
  process.exit(0);
15865
15904
  }
15905
+ const isPrimitive = isSlashCommand(prompt);
15866
15906
  const authForScope = await resolveToken(program2.opts().token);
15867
15907
  const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
15868
15908
  const scopeSession = event.session_id || process.env.CLAUDE_SESSION_ID || "";
15869
- await appendToConversationBuffer(prompt, sessionScopeKey(scopeToken, scopeSession));
15870
- try {
15871
- const tok = await resolveToken(globals.token);
15872
- const session = sessionDossier(tok.ok ? tok.data.token : null, event.session_id ?? null);
15873
- if (session) recordGoal(session.d, prompt);
15874
- } catch {
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
+ }
15875
15917
  }
15876
15918
  try {
15877
15919
  await ensureMemoryDir();
@@ -17807,6 +17849,9 @@ function createRun(opts, globals) {
17807
17849
  };
17808
17850
  }
17809
17851
 
17852
+ // src/commands/analyze/index.ts
17853
+ var import_node_fs38 = require("node:fs");
17854
+
17810
17855
  // src/lib/repo-context.ts
17811
17856
  var import_node_child_process7 = require("node:child_process");
17812
17857
  var import_node_os3 = require("node:os");
@@ -18831,21 +18876,19 @@ function refResolves(frame, ref) {
18831
18876
  return frameGit(frame, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]) !== "";
18832
18877
  }
18833
18878
  var SHA_RE2 = /^[0-9a-f]{40}$/;
18834
- function baselineShaAt(frame) {
18835
- if (!frame.worktreeRoot) return null;
18836
- try {
18837
- const sha = (0, import_node_fs26.readFileSync)((0, import_node_path19.join)(frame.worktreeRoot, BASELINE_SHA_FILE), "utf-8").trim();
18838
- if (!SHA_RE2.test(sha)) return null;
18839
- return refResolves(frame, sha) ? sha : null;
18840
- } catch {
18841
- return null;
18879
+ function stagedRange(frame) {
18880
+ if (!frame.worktreeRoot) return { kind: "nothing", base: null, head: "INDEX", via: "refused" };
18881
+ const mergeHead = frame.gitDir ? (0, import_node_path19.join)(frame.gitDir, "MERGE_HEAD") : null;
18882
+ if (mergeHead && (0, import_node_fs26.existsSync)(mergeHead)) {
18883
+ const vsHead = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "HEAD"]).split("\n").filter(Boolean));
18884
+ const vsMerge = new Set(frameGit(frame, ["diff", "--cached", "--name-only", "MERGE_HEAD"]).split("\n").filter(Boolean));
18885
+ const resolutions = [...vsHead].filter((f) => vsMerge.has(f) && !isVerityOwnedPath(f));
18886
+ return { kind: "merge", base: "HEAD", head: "INDEX", via: "merge-resolutions", files: resolutions };
18842
18887
  }
18843
- }
18844
- function stagedRange() {
18845
18888
  return { kind: "staged", base: "HEAD", head: "INDEX", via: "index" };
18846
18889
  }
18847
18890
  function resolvePushRange(frame, command, on) {
18848
- const nothing = (via) => ({ kind: "nothing", base: null, head: "HEAD", via });
18891
+ const nothing = (via2) => ({ kind: "nothing", base: null, head: "HEAD", via: via2 });
18849
18892
  if (!frame.worktreeRoot) return nothing("refused");
18850
18893
  const found = findMomentSegment(command, on);
18851
18894
  const segment = found ? splitSegments(command)[found.segmentIndex] : "";
@@ -18853,54 +18896,32 @@ function resolvePushRange(frame, command, on) {
18853
18896
  if (target.isDelete) return nothing("deletion");
18854
18897
  const head = target.srcRef ?? "HEAD";
18855
18898
  if (!refResolves(frame, head)) return nothing(`src-unresolvable:${head}`);
18856
- const srcName = target.srcRef;
18857
- const branchForRemote = srcName ?? frame.branch;
18858
- const candidates = [];
18859
- if (target.remote && (target.dstRef ?? srcName)) {
18860
- const dstName = (target.dstRef ?? srcName).replace(/^refs\/heads\//, "");
18861
- candidates.push({ ref: `refs/remotes/${target.remote}/${dstName}`, via: `refspec:${target.remote}/${dstName}` });
18862
- }
18863
- if (target.remote && !srcName && !target.dstRef && frame.branch) {
18864
- candidates.push({ ref: `refs/remotes/${target.remote}/${frame.branch}`, via: `remote:${target.remote}/${frame.branch}` });
18865
- }
18866
- candidates.push({ ref: srcName ? `${srcName}@{push}` : "@{push}", via: "@{push}" });
18867
- candidates.push({ ref: srcName ? `${srcName}@{upstream}` : "@{upstream}", via: "@{upstream}" });
18868
- if (branchForRemote) {
18869
- candidates.push({
18870
- ref: `refs/remotes/origin/${branchForRemote.replace(/^refs\/heads\//, "")}`,
18871
- via: `origin/${branchForRemote.replace(/^refs\/heads\//, "")}`
18872
- });
18873
- }
18874
- for (const c of candidates) {
18875
- if (!refResolves(frame, c.ref)) continue;
18876
- const mergeBase = frameGit(frame, ["merge-base", c.ref, head]);
18877
- if (SHA_RE2.test(mergeBase)) return { kind: "push", base: mergeBase, head, via: c.via };
18899
+ const remotePattern = target.remote ? `--remotes=${target.remote}` : "--remotes";
18900
+ const via = target.remote ? `publication:${target.remote}` : "publication";
18901
+ const commits = frameGit(frame, ["rev-list", head, "--not", remotePattern]).split("\n").filter(Boolean);
18902
+ if (commits.length === 0) return { kind: "nothing", base: null, head, via: "already-published" };
18903
+ const boundary = frameGit(frame, ["rev-list", head, "--not", remotePattern, "--boundary"]).split("\n").filter((l) => l.startsWith("-")).map((l) => l.slice(1));
18904
+ const base = boundary.find((s) => SHA_RE2.test(s)) ?? null;
18905
+ const files = /* @__PURE__ */ new Set();
18906
+ for (const sha of commits) {
18907
+ for (const f of frameGit(frame, ["diff-tree", "--no-commit-id", "--name-only", "-r", sha]).split("\n")) {
18908
+ if (f && !isVerityOwnedPath(f)) files.add(f);
18909
+ }
18878
18910
  }
18879
- const baseline = baselineShaAt(frame);
18880
- if (baseline) return { kind: "baseline", base: baseline, head, via: "review-baseline" };
18881
- if (refResolves(frame, `${head}~1`)) return { kind: "last-commit", base: `${head}~1`, head, via: `${head}~1` };
18882
- return nothing("no-parent");
18911
+ return { kind: "push", base, head, via, files: [...files], commits };
18883
18912
  }
18884
18913
  function rangeFiles(frame, range) {
18885
- let out;
18886
- switch (range.kind) {
18887
- case "staged":
18888
- out = frameGit(frame, ["diff", "--cached", "--name-only"]);
18889
- break;
18890
- case "push":
18891
- case "baseline":
18892
- case "last-commit":
18893
- out = frameGit(frame, ["diff", "--name-only", range.base, range.head === "INDEX" ? "HEAD" : range.head]);
18894
- break;
18895
- case "nothing":
18896
- return [];
18914
+ if (range.files) return range.files.filter((f) => !isVerityOwnedPath(f));
18915
+ if (range.kind === "staged") {
18916
+ return frameGit(frame, ["diff", "--cached", "--name-only"]).split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
18897
18917
  }
18898
- return out.split("\n").filter((l) => l.length > 0).filter((f) => !isVerityOwnedPath(f));
18918
+ return [];
18899
18919
  }
18900
18920
  function rangeChangeSignals(frame, range, paths) {
18901
18921
  const out = /* @__PURE__ */ new Map();
18902
18922
  if (range.kind === "nothing" || paths.length === 0) return out;
18903
- const args = range.kind === "staged" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
18923
+ if (range.kind === "push" && !range.base) return out;
18924
+ const args = range.kind === "staged" || range.kind === "merge" ? ["diff", "--cached", "--unified=0"] : ["diff", "--unified=0", range.base, range.head === "INDEX" ? "HEAD" : range.head];
18904
18925
  const diff = frameGit(frame, [...args, "--", ...paths]);
18905
18926
  let current = null;
18906
18927
  let oldSide = null;
@@ -18932,8 +18953,9 @@ function rangeChangeSignals(frame, range, paths) {
18932
18953
  return out;
18933
18954
  }
18934
18955
  function rangeMessages(frame, range) {
18935
- if (range.kind === "staged" || range.kind === "nothing" || !range.base) return "";
18936
- return frameGit(frame, ["log", `${range.base}..${range.head === "INDEX" ? "HEAD" : range.head}`, "--format=%B%x00"]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
18956
+ if (range.kind !== "push" || !range.commits || range.commits.length === 0) return "";
18957
+ const commits = range.commits.slice(0, 100);
18958
+ return frameGit(frame, ["show", "-s", "--format=%B%x00", ...commits]).split("\0").map((s) => s.trim()).filter(Boolean).join("\n\n");
18937
18959
  }
18938
18960
  function frameTelemetry(frame, range, divergence) {
18939
18961
  const t = {
@@ -19515,7 +19537,7 @@ function channelSilence(input) {
19515
19537
  // src/lib/cli-version.ts
19516
19538
  function cliVersion() {
19517
19539
  try {
19518
- return true ? "0.31.2" : "dev";
19540
+ return true ? "0.31.4" : "dev";
19519
19541
  } catch {
19520
19542
  return "dev";
19521
19543
  }
@@ -20734,7 +20756,20 @@ async function evidence(run2) {
20734
20756
  widened_to: allForReview.length
20735
20757
  });
20736
20758
  }
20737
- const baseForReview = narrowingIsTrustworthy ? scoped.files : recoveredScope.length > 0 ? recoveredScope : allForReview;
20759
+ const authorshipWasObservable = scoped.signal === "authored" && actionSummary?.transcript_windowed !== "orphaned";
20760
+ if (!narrowingIsTrustworthy && recoveredScope.length === 0 && authorshipWasObservable) {
20761
+ logEvent("authored_nothing_reviewable", {
20762
+ touched: (actionSummary?.files_edited?.length ?? 0) + (actionSummary?.files_created?.length ?? 0),
20763
+ candidates: allForReview.length
20764
+ });
20765
+ }
20766
+ const baseForReview = chooseReviewScope({
20767
+ narrowingIsTrustworthy,
20768
+ scopedFiles: scoped.files,
20769
+ recoveredScope,
20770
+ allForReview,
20771
+ authorshipWasObservable
20772
+ });
20738
20773
  const recentForReview = narrowToRecent(baseForReview, baselineSessionId);
20739
20774
  if (!opts.skipStatic && isCodacyAvailable()) {
20740
20775
  let allScannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles]));
@@ -22700,6 +22735,10 @@ function registerAnalyzeCommand(program2) {
22700
22735
  }
22701
22736
  var tracing = () => process.env.VERITY_TRACE_PHASES === "1";
22702
22737
  async function runAnalyze(opts, globals) {
22738
+ if (!verityConfigured()) {
22739
+ (0, import_node_fs38.writeSync)(2, '[verity] not set up in this project \u2014 run "verity init" first.\n');
22740
+ process.exit(0);
22741
+ }
22703
22742
  const run2 = createRun(opts, globals);
22704
22743
  installRunEvidence(run2);
22705
22744
  for (const [name, phase] of PIPELINE) {
@@ -22718,7 +22757,6 @@ async function runAnalyze(opts, globals) {
22718
22757
  }
22719
22758
 
22720
22759
  // src/commands/baseline.ts
22721
- var import_node_fs38 = require("node:fs");
22722
22760
  function registerBaselineCommands(program2) {
22723
22761
  const baseline = program2.command("baseline").description("Manage the task-start working-tree baseline");
22724
22762
  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) => {
@@ -22727,7 +22765,16 @@ function registerBaselineCommands(program2) {
22727
22765
  process.chdir(repoRoot());
22728
22766
  } catch {
22729
22767
  }
22730
- if (!(0, import_node_fs38.existsSync)(VERITY_DIR)) {
22768
+ if (!verityConfigured()) {
22769
+ const how = isPluginInvocation() ? "Offer to run /verity:setup for the user." : "Offer to run `verity init` (or /verity-setup) for the user.";
22770
+ process.stdout.write(
22771
+ JSON.stringify({
22772
+ hookSpecificOutput: {
22773
+ hookEventName: "SessionStart",
22774
+ additionalContext: `Verity is installed but this project is not set up yet, so the quality gate will not review anything here. ${how}`
22775
+ }
22776
+ }) + "\n"
22777
+ );
22731
22778
  process.exit(0);
22732
22779
  }
22733
22780
  let sessionId = opts.sessionId;
@@ -22817,8 +22864,8 @@ async function runReview(opts, globals) {
22817
22864
  for (const p of specPaths) {
22818
22865
  if (!(0, import_node_fs39.existsSync)(p)) continue;
22819
22866
  try {
22820
- const { readFileSync: readFileSync27 } = await import("node:fs");
22821
- const content = readFileSync27(p, "utf-8");
22867
+ const { readFileSync: readFileSync26 } = await import("node:fs");
22868
+ const content = readFileSync26(p, "utf-8");
22822
22869
  specs.push({ path: p, content: content.slice(0, 10240) });
22823
22870
  } catch {
22824
22871
  }
@@ -22975,13 +23022,14 @@ function registerGuardCommand(program2) {
22975
23022
  });
22976
23023
  }
22977
23024
  function resolveMomentRange(moment, frame, command, on) {
22978
- return moment === "pre-commit" ? stagedRange() : resolvePushRange(frame, command, on);
23025
+ return moment === "pre-commit" ? stagedRange(frame) : resolvePushRange(frame, command, on);
22979
23026
  }
22980
23027
  function describeRange(range) {
22981
23028
  if (range.kind === "staged") return "staged";
22982
- if (range.kind === "nothing" || !range.base) return null;
22983
- const base = /^[0-9a-f]{40}$/.test(range.base) ? range.base.slice(0, 7) : range.base;
22984
- return `${base}..${range.head} via ${range.via}`;
23029
+ if (range.kind === "merge") return `merge (${range.via})`;
23030
+ if (range.kind === "nothing") return range.via === "already-published" ? "already published" : null;
23031
+ const base = range.base && /^[0-9a-f]{40}$/.test(range.base) ? range.base.slice(0, 7) : range.base;
23032
+ return base ? `${base}..${range.head} via ${range.via}` : `via ${range.via}`;
22985
23033
  }
22986
23034
  function matchFlagValue(command, flags) {
22987
23035
  const re = new RegExp(`(?<![\\w-])(?:${flags})(?:=|\\s+)('((?:[^'\\\\]|\\\\.)*)'|"((?:[^"\\\\]|\\\\.)*)"|([^\\s'"-][^\\s]*))`);
@@ -24645,6 +24693,61 @@ async function reconcileOwnWiring(moments) {
24645
24693
  printWarn(" Enable one: verity hooks install --moments stop");
24646
24694
  }
24647
24695
  }
24696
+ async function checkPrerequisites(step) {
24697
+ step("Checking prerequisites");
24698
+ const prereqs = await checkPrereqs({ install: true });
24699
+ for (const c of prereqs.checks) {
24700
+ if (c.status === "ok") {
24701
+ if (c.justInstalled) continue;
24702
+ printInfo(` ${c.label} ${c.detail} \u2713`);
24703
+ } else {
24704
+ printWarn(` ${c.label}: ${c.detail}`);
24705
+ if (c.remedy) printWarn(` ${c.remedy}`);
24706
+ }
24707
+ }
24708
+ if (prereqs.blocked) {
24709
+ printError("A required prerequisite is missing \u2014 cannot continue.");
24710
+ process.exit(1);
24711
+ }
24712
+ return prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
24713
+ }
24714
+ async function scaffoldProject(step, defaultsOnly) {
24715
+ step("Knowledge base, .gitignore and CLAUDE.md");
24716
+ await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
24717
+ await ensureMemoryDir();
24718
+ const ignoreResult = ensureVerityGitignore();
24719
+ if (ignoreResult === "failed") {
24720
+ printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
24721
+ printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
24722
+ } else if (ignoreResult === "conflict") {
24723
+ printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
24724
+ printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
24725
+ printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
24726
+ printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
24727
+ } else if (ignoreResult === "repaired") {
24728
+ printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
24729
+ } else {
24730
+ printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
24731
+ }
24732
+ const tracked = committedVerityState();
24733
+ if (tracked.length > 0) {
24734
+ printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
24735
+ const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
24736
+ if (untrack) {
24737
+ const result = untrackVerityState();
24738
+ if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
24739
+ else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
24740
+ } else {
24741
+ printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
24742
+ }
24743
+ }
24744
+ try {
24745
+ await ensureClaudeMdPointer();
24746
+ printInfo(" CLAUDE.md instructions \u2713");
24747
+ } catch (err) {
24748
+ printWarn(` Could not update CLAUDE.md: ${err.message}`);
24749
+ }
24750
+ }
24648
24751
  function registerInitCommand(program2) {
24649
24752
  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(
24650
24753
  "--plugin-mode",
@@ -24686,22 +24789,7 @@ function registerInitCommand(program2) {
24686
24789
  }
24687
24790
  console.log("");
24688
24791
  }
24689
- step("Checking prerequisites");
24690
- const prereqs = await checkPrereqs({ install: true });
24691
- for (const c of prereqs.checks) {
24692
- if (c.status === "ok") {
24693
- if (c.justInstalled) continue;
24694
- printInfo(` ${c.label} ${c.detail} \u2713`);
24695
- } else {
24696
- printWarn(` ${c.label}: ${c.detail}`);
24697
- if (c.remedy) printWarn(` ${c.remedy}`);
24698
- }
24699
- }
24700
- if (prereqs.blocked) {
24701
- printError("A required prerequisite is missing \u2014 cannot continue.");
24702
- process.exit(1);
24703
- }
24704
- const claudeInstalled = prereqs.checks.some((c) => c.id === "claude" && c.status === "ok");
24792
+ const claudeInstalled = await checkPrerequisites(step);
24705
24793
  console.log("");
24706
24794
  if (pluginMode) {
24707
24795
  printInfo("Skipping skills \u2014 the Verity plugin provides them, namespaced as /verity:<name>.");
@@ -24715,41 +24803,7 @@ function registerInitCommand(program2) {
24715
24803
  if (defaultsOnly) {
24716
24804
  printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
24717
24805
  }
24718
- step("Knowledge base, .gitignore and CLAUDE.md");
24719
- await (0, import_promises14.mkdir)(VERITY_DIR, { recursive: true });
24720
- await ensureMemoryDir();
24721
- const ignoreResult = ensureVerityGitignore();
24722
- if (ignoreResult === "failed") {
24723
- printWarn(" .gitignore: could not write the Verity block \u2014 add it manually");
24724
- printWarn(" (.verity/ holds copies of analyzed files, including any secret the gate flagged)");
24725
- } else if (ignoreResult === "conflict") {
24726
- printWarn(" .gitignore: the Verity block is in place but git still ignores .verity/standard.yaml");
24727
- printWarn(" Something outside this file covers it \u2014 a global (~/.gitignore) or nested");
24728
- printWarn(" .gitignore, or a pattern we do not recognise. Check: git check-ignore -v .verity/standard.yaml");
24729
- printWarn(" Until it is fixed, the Standard and the knowledge graph cannot be committed.");
24730
- } else if (ignoreResult === "repaired") {
24731
- printInfo(" .gitignore: rewrote `.verity/` to `.verity/*` so the standard stays committable \u2713");
24732
- } else {
24733
- printInfo(` .gitignore: Verity block ${ignoreResult === "added" ? "added" : "already covered"} \u2713`);
24734
- }
24735
- const tracked = committedVerityState();
24736
- if (tracked.length > 0) {
24737
- printWarn(` ${tracked.length} Verity state file(s) are tracked in git (e.g. ${tracked[0]}).`);
24738
- const untrack = defaultsOnly ? false : await promptYes(" Untrack them now (files stay on disk)? [Y/n] ", { nonInteractive: false });
24739
- if (untrack) {
24740
- const result = untrackVerityState();
24741
- if (result === "untracked") printInfo(" Untracked (staged) \u2014 commit to finish \u2713");
24742
- else if (result === "failed") printWarn(' Could not untrack \u2014 run "git rm -r --cached .verity" manually');
24743
- } else {
24744
- printWarn(" Left tracked. Fix with: git rm -r --cached .verity && git add .verity/standard.yaml .verity/memory");
24745
- }
24746
- }
24747
- try {
24748
- await ensureClaudeMdPointer();
24749
- printInfo(" CLAUDE.md instructions \u2713");
24750
- } catch (err) {
24751
- printWarn(` Could not update CLAUDE.md: ${err.message}`);
24752
- }
24806
+ await scaffoldProject(step, defaultsOnly);
24753
24807
  const globalVerityDir = (0, import_node_path29.join)(process.env.HOME ?? "", ".verity");
24754
24808
  await (0, import_promises14.mkdir)(globalVerityDir, { recursive: true });
24755
24809
  console.log("");
@@ -24811,7 +24865,7 @@ function registerInitCommand(program2) {
24811
24865
  ...telemetryChoice ? { telemetry: telemetryChoice } : {},
24812
24866
  init: {
24813
24867
  completed_at: (/* @__PURE__ */ new Date()).toISOString(),
24814
- cli_version: true ? "0.31.2" : "dev"
24868
+ cli_version: true ? "0.31.4" : "dev"
24815
24869
  }
24816
24870
  });
24817
24871
  } catch (err) {
@@ -25480,8 +25534,8 @@ function registerTelemetryCommands(program2) {
25480
25534
  }
25481
25535
 
25482
25536
  // src/cli.ts
25483
- program.name("verity").description("CLI for Verity quality gate service").version("0.31.2").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) => {
25484
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.2");
25537
+ program.name("verity").description("CLI for Verity quality gate service").version("0.31.4").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) => {
25538
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.4");
25485
25539
  setUserNamedServiceUrl(program.opts().serviceUrl);
25486
25540
  try {
25487
25541
  await foldLegacyLocalCredential();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codacy/verity-cli",
3
- "version": "0.31.2",
3
+ "version": "0.31.4",
4
4
  "description": "CLI for Verity quality gate service",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://verity.md",