@vibe-commit/capture 0.1.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.
Files changed (141) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/agents/claude_code.d.ts +11 -0
  4. package/dist/agents/claude_code.js +162 -0
  5. package/dist/agents/claude_code.js.map +1 -0
  6. package/dist/agents/codex_cli.d.ts +15 -0
  7. package/dist/agents/codex_cli.js +67 -0
  8. package/dist/agents/codex_cli.js.map +1 -0
  9. package/dist/agents/cursor.d.ts +21 -0
  10. package/dist/agents/cursor.js +89 -0
  11. package/dist/agents/cursor.js.map +1 -0
  12. package/dist/agents/registry.d.ts +184 -0
  13. package/dist/agents/registry.js +212 -0
  14. package/dist/agents/registry.js.map +1 -0
  15. package/dist/agents/types.d.ts +279 -0
  16. package/dist/agents/types.js +67 -0
  17. package/dist/agents/types.js.map +1 -0
  18. package/dist/commands/connect.d.ts +36 -0
  19. package/dist/commands/connect.js +499 -0
  20. package/dist/commands/connect.js.map +1 -0
  21. package/dist/commands/context.d.ts +45 -0
  22. package/dist/commands/context.js +14 -0
  23. package/dist/commands/context.js.map +1 -0
  24. package/dist/commands/report.d.ts +105 -0
  25. package/dist/commands/report.js +430 -0
  26. package/dist/commands/report.js.map +1 -0
  27. package/dist/commands/status.d.ts +29 -0
  28. package/dist/commands/status.js +236 -0
  29. package/dist/commands/status.js.map +1 -0
  30. package/dist/commands/why.d.ts +349 -0
  31. package/dist/commands/why.js +937 -0
  32. package/dist/commands/why.js.map +1 -0
  33. package/dist/consent.d.ts +59 -0
  34. package/dist/consent.js +111 -0
  35. package/dist/consent.js.map +1 -0
  36. package/dist/copy/claims.d.ts +79 -0
  37. package/dist/copy/claims.js +343 -0
  38. package/dist/copy/claims.js.map +1 -0
  39. package/dist/copy/grades.d.ts +117 -0
  40. package/dist/copy/grades.js +96 -0
  41. package/dist/copy/grades.js.map +1 -0
  42. package/dist/copy/index.d.ts +12 -0
  43. package/dist/copy/index.js +13 -0
  44. package/dist/copy/index.js.map +1 -0
  45. package/dist/copy/strings.d.ts +1328 -0
  46. package/dist/copy/strings.js +1243 -0
  47. package/dist/copy/strings.js.map +1 -0
  48. package/dist/copy/time.d.ts +27 -0
  49. package/dist/copy/time.js +43 -0
  50. package/dist/copy/time.js.map +1 -0
  51. package/dist/credential.d.ts +211 -0
  52. package/dist/credential.js +298 -0
  53. package/dist/credential.js.map +1 -0
  54. package/dist/exit.d.ts +59 -0
  55. package/dist/exit.js +60 -0
  56. package/dist/exit.js.map +1 -0
  57. package/dist/git.d.ts +299 -0
  58. package/dist/git.js +653 -0
  59. package/dist/git.js.map +1 -0
  60. package/dist/hooks/entry.d.ts +277 -0
  61. package/dist/hooks/entry.js +1092 -0
  62. package/dist/hooks/entry.js.map +1 -0
  63. package/dist/hooks/post_commit.d.ts +59 -0
  64. package/dist/hooks/post_commit.js +160 -0
  65. package/dist/hooks/post_commit.js.map +1 -0
  66. package/dist/hooks/post_rewrite.d.ts +59 -0
  67. package/dist/hooks/post_rewrite.js +161 -0
  68. package/dist/hooks/post_rewrite.js.map +1 -0
  69. package/dist/hooks/session_end.d.ts +101 -0
  70. package/dist/hooks/session_end.js +120 -0
  71. package/dist/hooks/session_end.js.map +1 -0
  72. package/dist/index.d.ts +34 -0
  73. package/dist/index.js +360 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/install.d.ts +153 -0
  76. package/dist/install.js +284 -0
  77. package/dist/install.js.map +1 -0
  78. package/dist/json.d.ts +100 -0
  79. package/dist/json.js +94 -0
  80. package/dist/json.js.map +1 -0
  81. package/dist/oauth/discovery.d.ts +96 -0
  82. package/dist/oauth/discovery.js +163 -0
  83. package/dist/oauth/discovery.js.map +1 -0
  84. package/dist/oauth/lock.d.ts +100 -0
  85. package/dist/oauth/lock.js +287 -0
  86. package/dist/oauth/lock.js.map +1 -0
  87. package/dist/oauth/loopback.d.ts +85 -0
  88. package/dist/oauth/loopback.js +124 -0
  89. package/dist/oauth/loopback.js.map +1 -0
  90. package/dist/oauth/pkce.d.ts +58 -0
  91. package/dist/oauth/pkce.js +77 -0
  92. package/dist/oauth/pkce.js.map +1 -0
  93. package/dist/oauth/read.d.ts +120 -0
  94. package/dist/oauth/read.js +144 -0
  95. package/dist/oauth/read.js.map +1 -0
  96. package/dist/oauth/session.d.ts +80 -0
  97. package/dist/oauth/session.js +112 -0
  98. package/dist/oauth/session.js.map +1 -0
  99. package/dist/oauth/signin.d.ts +178 -0
  100. package/dist/oauth/signin.js +225 -0
  101. package/dist/oauth/signin.js.map +1 -0
  102. package/dist/oauth/token.d.ts +139 -0
  103. package/dist/oauth/token.js +183 -0
  104. package/dist/oauth/token.js.map +1 -0
  105. package/dist/paths.d.ts +366 -0
  106. package/dist/paths.js +460 -0
  107. package/dist/paths.js.map +1 -0
  108. package/dist/policy.d.ts +195 -0
  109. package/dist/policy.js +244 -0
  110. package/dist/policy.js.map +1 -0
  111. package/dist/post.d.ts +302 -0
  112. package/dist/post.js +384 -0
  113. package/dist/post.js.map +1 -0
  114. package/dist/project.d.ts +99 -0
  115. package/dist/project.js +192 -0
  116. package/dist/project.js.map +1 -0
  117. package/dist/redact.d.ts +175 -0
  118. package/dist/redact.js +700 -0
  119. package/dist/redact.js.map +1 -0
  120. package/dist/runtime.d.ts +30 -0
  121. package/dist/runtime.js +37 -0
  122. package/dist/runtime.js.map +1 -0
  123. package/dist/spawn_budget.d.ts +101 -0
  124. package/dist/spawn_budget.js +124 -0
  125. package/dist/spawn_budget.js.map +1 -0
  126. package/dist/spool.d.ts +390 -0
  127. package/dist/spool.js +556 -0
  128. package/dist/spool.js.map +1 -0
  129. package/dist/state.d.ts +109 -0
  130. package/dist/state.js +217 -0
  131. package/dist/state.js.map +1 -0
  132. package/dist/system_message.d.ts +101 -0
  133. package/dist/system_message.js +141 -0
  134. package/dist/system_message.js.map +1 -0
  135. package/dist/term.d.ts +112 -0
  136. package/dist/term.js +134 -0
  137. package/dist/term.js.map +1 -0
  138. package/dist/version.d.ts +16 -0
  139. package/dist/version.js +17 -0
  140. package/dist/version.js.map +1 -0
  141. package/package.json +50 -0
@@ -0,0 +1,1328 @@
1
+ /**
2
+ * The copy catalogue — CR-090 precondition (1).
3
+ *
4
+ * EVERY user-facing string in this package lives here. Command bodies contain
5
+ * NO inline string literals. This is not a style preference: the claims gate can
6
+ * only check copy it can find, and a literal inside a command body is invisible
7
+ * to it. The capture client is greenfield exactly once, so this constraint is
8
+ * free now and a refactor later.
9
+ *
10
+ * Commit headers are NOT here — they come from `grades.ts`, which is the single
11
+ * renderer that takes the grade. Adding a header string to this file would
12
+ * create the second path that the structural adjacency rule exists to forbid.
13
+ *
14
+ * @provenance vibecommit-mcp src/scrubber/index.ts — [REDACTED:<kind>] marker shape, borrowed
15
+ * @provenance vibecommit-web app/app/commits/page.tsx — route existence, verified
16
+ * @provenance vibecommit-schema capture_turns column list — MEASURED ABSENCE, cited (CR-086)
17
+ */
18
+ import { EXIT } from "../exit.js";
19
+ /** `--help` copy. The `why` line is verbatim-approved (D61 §PS6) — do not reword. */
20
+ export declare const HELP: {
21
+ readonly usage: "vibecommit <command> [options]";
22
+ readonly tagline: "Record Claude Code sessions against the commits they produced.";
23
+ readonly commands: {
24
+ readonly connect: "Connect this repo and capture the current session.";
25
+ readonly status: "Show whether capture is on for this repo and when it last succeeded.";
26
+ readonly off: "Stop capturing for this repo.";
27
+ readonly why: "Show the conversation turns recorded against the commit that last changed this line.";
28
+ readonly report: "Summarise capture coverage over a time window.";
29
+ };
30
+ readonly docsUrl: "https://vibecommit.ai/docs/what-we-upload";
31
+ };
32
+ /**
33
+ * The ENTRY POINT's own chrome and its two usage failures — `CR-090d`.
34
+ *
35
+ * These five strings were inline literals in `src/index.ts` (`:74`, `:76`, `:87`,
36
+ * `:146`, `:163`) — user-facing, and invisible to the claims gate for six waves
37
+ * because the gate can only check copy it can find.
38
+ *
39
+ * ## Why a separate object rather than three more fields on `HELP`
40
+ *
41
+ * `HELP` is the help SCREEN's content — the tagline, the usage line, the per-verb
42
+ * descriptions, the docs URL — and the claims corpus tags the whole object with
43
+ * one surface, `help`. Only three of these five render there. `unknownCommand` is
44
+ * the usage-error path, so folding it into `HELP` would tag a §13.6 error
45
+ * message as help copy and check it against the wrong surface.
46
+ *
47
+ * ⚠ `notImplemented` LIVED HERE AND IS GONE, REMOVED BY `CR-108`. It was what
48
+ * `why` and `report` printed while unbuilt, and `report` was its last caller —
49
+ * **every verb in `VERBS` is now implemented**, so the string had no route to a
50
+ * screen. Copy that cannot be reached is copy no gate can check, and leaving it
51
+ * would have invited the next reader to wire a sixth verb to it silently. The surface map is only as
52
+ * honest as the objects it keys on, so the entry point gets its own.
53
+ *
54
+ * ⚠ The three labels are LABELS, not sentences: `renderHelp()` composes each with
55
+ * a single space and the result must stay byte-identical at 80 columns
56
+ * (`DESIGN.md` §13.4, `test/help.golden.txt`). A trailing space moved in here
57
+ * would be invisible in review and would move the golden file.
58
+ */
59
+ export declare const USAGE: {
60
+ readonly usageLabel: "Usage:";
61
+ readonly commandsLabel: "Commands:";
62
+ readonly docsLabel: "Docs:";
63
+ /** The unknown-verb line. Help follows it, so this says only what went wrong. */
64
+ readonly unknownCommand: (verb: string) => string;
65
+ };
66
+ /** `vibecommit status` (CR-021). */
67
+ export declare const STATUS: {
68
+ readonly onForRepo: "Capture is on for this repository.";
69
+ readonly offForRepo: "Capture is off for this repository.";
70
+ readonly neverSent: "No session has been recorded yet.";
71
+ /**
72
+ * The §13.5 gutter keys. LOWERCASE because style guide §10.3 draws them that
73
+ * way, and §10.3 is the ruling (D65) — `lastSuccessLabel` and
74
+ * `dashboardLabel` were title-case and referenced by nothing, so `CR-021`
75
+ * changed them to match the screen rather than changing the screen.
76
+ */
77
+ readonly repoLabel: "repo";
78
+ readonly lastSuccessLabel: "last sent";
79
+ readonly dashboardLabel: "dashboard";
80
+ /** `<slug> (git toplevel <path>)` — §10.3's repo row. */
81
+ readonly repoToplevel: (slug: string, path: string) => string;
82
+ /** `4 minutes ago · seq 41`. No turn count — see the module note in status.ts. */
83
+ readonly lastSuccessValue: (age: string, seq: number) => string;
84
+ readonly fixCommandLabel: "To reconnect, run";
85
+ /** §10.3's two trailing actions, rendered as an aligned pair. */
86
+ readonly turnOffLabel: "Turn capture off for this repo";
87
+ readonly revokeLabel: "Revoke this machine's credential";
88
+ };
89
+ /**
90
+ * `vibecommit connect` (CR-025 owns the live-capture ending).
91
+ *
92
+ * ⚠ THE DISCLOSURE PARAGRAPHS ARE NOW HERE — `CR-111d` landed them, transcribed
93
+ * from style guide §10.2 rather than paraphrased. They replaced three
94
+ * placeholder one-liners. D64 falsified the trust argument the earlier draft
95
+ * leaned on: this repo is PRIVATE with MIT intact, so "read the source" is false
96
+ * and BLOCKED by the register. What survives is `Vibe-Commit/claude-plugin`,
97
+ * which vendors the exact binary — and "inspect the build" is WEAKER than "read
98
+ * the source" and may not be dressed up as it (D65 §DR7).
99
+ *
100
+ * The MECHANISM copy sits beside them and belongs to other rows: the prompt, the
101
+ * two answers, the refusals, `CR-025`'s ending and `CR-110`'s fifth beat.
102
+ *
103
+ * Also locked and enforced by the mechanism, not by these strings: default `N`,
104
+ * non-TTY refuses with exit 2, and no reassurance adjectives (*secure, safe,
105
+ * protected, private, encrypted*) anywhere on the screen (D65 §DR7).
106
+ */
107
+ export declare const CONNECT: {
108
+ /**
109
+ * ⚠ NOT PARAPHRASED. §10.2's rule 4 — *every noun names something actually
110
+ * uploaded, in the USER's vocabulary* — is the one rule no gate can check, so
111
+ * the nouns are transcribed rather than reworded: *"the output of commands it
112
+ * ran"*, never *"Bash tool results"*.
113
+ */
114
+ readonly consentHeading: "VibeCommit uploads your Claude Code transcripts for this repo.";
115
+ /**
116
+ * ⚠ "private-repo paths" IS THE APPROVED WORDING, and it is the reason
117
+ * `private` is deliberately NOT registered in `claims.ts`: a substring gate on
118
+ * it would fire on this very sentence on day one, and the register's own
119
+ * §Lintable-unit section says a gate dies at its third false positive. So
120
+ * `private`-as-reassurance is a HUMAN review item here. This use is
121
+ * descriptive — it names what the paths ARE — and is the opposite of a
122
+ * reassurance.
123
+ */
124
+ readonly consentDetail: "That means the full text of the session: your prompts, the agent's replies, the arguments to every tool call, the file contents inside Edit records, and the output of commands it ran. File paths include private-repo paths.";
125
+ /**
126
+ * ⚠ THE NUMBER AND ITS QUALIFIER ARE ONE UNIT.
127
+ *
128
+ * **12 is `CR-042`'s counted figure** (merged, `6d8ec68`), taken rather than
129
+ * recounted: the patterns live in `vibecommit-mcp/src/scrubber/patterns.ts`,
130
+ * which is closed source and another chain's repo. `CR-042` exists precisely
131
+ * because the draft said 10 — a number is a claim, and this one is counted
132
+ * from code.
133
+ *
134
+ * The qualifier is as load-bearing as the number. Without *"it is a pattern
135
+ * list, not a general secret detector"* and *"a secret it has no pattern for
136
+ * is uploaded"*, the count reads as PROTECTION — which is the reassurance D65
137
+ * §DR7 bans, arriving as arithmetic instead of as an adjective.
138
+ */
139
+ readonly consentScrubber: "A scrubber removes 12 known secret formats (AWS keys, GitHub tokens, and similar). It is a pattern list, not a general secret detector: a secret it has no pattern for is uploaded. Command output is not path-filtered.";
140
+ /**
141
+ * ⛔ **THE SECOND DATA CLASS, AND THE DISCLOSURE DID NOT MENTION IT AT ALL —
142
+ * `T15`, `D190`.**
143
+ *
144
+ * The three paragraphs above enumerate TRANSCRIPT CONTENT. They were the whole
145
+ * disclosure, and commit SHAs have gone on the wire since `CR-170` without a
146
+ * word here. `D190` adds two more headers, one of which is a genuinely new
147
+ * class, so the copy is extended rather than the send being scoped — scoping
148
+ * the send would gut the feature.
149
+ *
150
+ * ## ⛔ WHAT IS HERE IS MEASURED FROM `buildIngestHeaders`, NOT FROM THE PLAN
151
+ *
152
+ * | said here | header | since |
153
+ * |---|---|---|
154
+ * | the commit ID | `x-commits` | `CR-170` |
155
+ * | the branch name | `x-head-branch` | `CR-170` |
156
+ * | the session it was linked to | `x-session-id` | pre-`CR-170` |
157
+ * | how that link was made | `x-commit-attributions` | `D190`, NEW |
158
+ * | the replaced commit's ID | `x-rewrites` | `D190`, NEW |
159
+ *
160
+ * ⛔ **AND WHAT IS DELIBERATELY NOT HERE, BECAUSE IT DOES NOT LEAVE THE
161
+ * MACHINE.** `post_commit.ts` gathers the changed-file list and the committer
162
+ * date because they are free at that moment, and `capSpool` returns only
163
+ * `{shas, attributions, count}`. **Describing them as uploaded would be a FALSE
164
+ * DISCLOSURE, which is as much a claims defect as a missing one** — and it is
165
+ * the easier mistake, because over-disclosing feels safe.
166
+ *
167
+ * ⚠ **`SpoolEntry.branch` and `delta.head.branch` are NOT the same noun.** The
168
+ * per-commit one stays local; the repo's CURRENT branch goes out on every
169
+ * delta as `x-head-branch` (`post.ts:228`, `entry.ts:612`). The branch name is
170
+ * named here on the strength of the second, never the first.
171
+ *
172
+ * ## ⛔ THE REWRITE SENTENCE IS THE ONE THIS STRING EXISTS FOR
173
+ *
174
+ * `x-rewrites` carries LOCAL, UNPUSHED REWRITE HISTORY — `ancestor:successor`
175
+ * pairs whose ancestor is unreachable from every branch and gone after
176
+ * `gc --prune=now`. Telling a user "we upload your transcripts" does not cover
177
+ * "we upload a record of the commits you rewrote before pushing". **Someone who
178
+ * squashes to bury a false start is entitled to know that the false start's ID
179
+ * left the machine.**
180
+ *
181
+ * ⚠ *"may never have left this machine"*, not *"was never pushed"*: you can
182
+ * amend a commit that WAS pushed and force-push it, so the stronger sentence is
183
+ * false in a real case.
184
+ *
185
+ * ## ⛔ NO NEGATIVE LIST, AND THAT IS A DECISION
186
+ *
187
+ * An earlier draft closed with *"commit messages, authors, dates and the
188
+ * changed-file list are not uploaded."* True today (`post.ts:166-174`), and
189
+ * CUT: `D190 §6` says this wire has **no compiler, no schema and no shared
190
+ * type**, so a negative claim in shipping copy goes false the first time anyone
191
+ * adds a header — silently, with no gate able to see it. Positive claims only.
192
+ *
193
+ * ⚠ Opens *"Not only the transcript"* because `consentHeading` still says
194
+ * *"uploads your Claude Code transcripts"*, which is now narrower than the
195
+ * truth. Repairing the scope where a skimmer meets it; the heading itself is a
196
+ * bigger copy change than this task was scoped for.
197
+ */
198
+ readonly consentCommits: "Not only the transcript. Commits you make while a session is recording are uploaded too: the commit ID, the branch name, the session it was linked to, and how that link was made. If you amend or rebase, the ID of the commit you replaced goes with them — that commit may never have left this machine, and git will eventually delete it from here.";
199
+ /** §10.2's first labelled link. Points at `HELP.docsUrl` — one definition. */
200
+ readonly consentDocsLabel: "Read the full list:";
201
+ /**
202
+ * ⚠ THE REPLACEMENT FOR THE OPEN-SOURCE CLAIM, and the ceiling on what it may
203
+ * say. D64 created this repo PRIVATE with MIT intact, so *"read the source"*
204
+ * is FALSE — the register's own signed-receipts failure mode, copy describing
205
+ * a mechanism that does not exist. What survives is `claude-plugin`, which D20
206
+ * keeps public non-negotiably and which vendors the exact binary that runs.
207
+ *
208
+ * **"Inspect the build" is WEAKER than "read the source" and may not be
209
+ * dressed up as it** (D65 §DR7). This line offers the artefact and claims
210
+ * nothing about readable source. All four phrasings are registered BLOCKED on
211
+ * the `connect` surface, so the gate refuses the smuggled version too.
212
+ */
213
+ readonly consentBundleLabel: "The binary that runs is here:";
214
+ readonly consentPrompt: "Continue?";
215
+ readonly declined: "Not connected. Nothing was uploaded.";
216
+ readonly capturing: "Recording this session…";
217
+ readonly doneHeading: "Connected.";
218
+ readonly doneUrlLabel: "View this session";
219
+ /** The `[y/N]` suffix. The capital is the default and the default is NO. */
220
+ readonly consentAnswers: "[y/N]";
221
+ /** Non-TTY refusal — DESIGN.md §13.7 exit 2, D65 §DR7. */
222
+ readonly nonInteractiveWhat: "Consent needs an interactive terminal.";
223
+ readonly nonInteractiveWhy: "Standard input is not a terminal, so the consent prompt cannot be answered. Nothing was read and nothing was uploaded.";
224
+ readonly nonInteractiveFix: "Run this in a terminal:";
225
+ readonly alreadyConnected: "Capture is already on for this repository.";
226
+ /**
227
+ * The browser sign-in beat (PKCE loopback → minted ingest credential) is the
228
+ * oauth lane's, not this task's. Until it lands, the supported path is the one
229
+ * `VIBECOMMIT_TOKEN` exists for. This copy states a mechanism, not a promise.
230
+ */
231
+ readonly credentialNeededWhat: "This machine has no ingest credential.";
232
+ readonly credentialNeededWhy: "Consent is recorded for this repository, but nothing can be uploaded without a credential for this machine.";
233
+ readonly credentialNeededFix: "Set one, then run connect again:";
234
+ readonly credentialNeededCommand: "export VIBECOMMIT_TOKEN=…";
235
+ /** `Capture is on for <repo>. Nothing else to do.` */
236
+ readonly doneForRepo: (repo: string) => string;
237
+ readonly stopLabel: "To stop";
238
+ /**
239
+ * No transcript to record. ONE state — `CR-112` (W8) owns the five-absence
240
+ * grammar as a set, and a sixth phrasing invented here is what that task
241
+ * exists to prevent.
242
+ */
243
+ readonly noTranscriptWhat: "No Claude Code session was found for this repository.";
244
+ readonly noTranscriptWhy: "Consent is recorded, but connect had nothing to record: no transcript exists yet for this project. Capture starts on its own the next time Claude Code runs here.";
245
+ readonly noTranscriptFix: "Nothing to fix. Start a session, then check:";
246
+ /**
247
+ * ⚠ `warn`, NEVER `bad`, and **EXIT 0, never 1**. That 403 means *later*, not
248
+ * *failed*: the offset holds and the backlog uploads in order on approval. A
249
+ * red exit tells a correctly-installed user they broke something, which moves
250
+ * the confusion report's T+9:00 give-up to T+0:45. `held` and `armed` are the
251
+ * two words doing the work — both mean *ready, not running*.
252
+ *
253
+ * `capture_policy` defaults to `approval_required`, so a member of a
254
+ * multi-member org sees this every time; solo orgs auto-approve inside the
255
+ * insert and never meet it.
256
+ */
257
+ readonly pendingWhat: "Your org needs to approve capture first.";
258
+ /**
259
+ * ⚠ NAMES THE REPO, NOT THE ORG — and that is a deviation from the style
260
+ * guide's drawn example, made deliberately. §10.2 renders *"acohan/repo
261
+ * belongs to the `vibecommit` org"*, and the client cannot produce that name:
262
+ * the 403 body discloses nothing (no org id, no owner list, by design), and
263
+ * `resolveRepoSlug` yields the GIT owner, which is a different thing from the
264
+ * VibeCommit org that holds the capture policy. Inventing one would be a
265
+ * confident lie; the link resolves it, which is what the link is for.
266
+ */
267
+ readonly pendingWhy: (repo: string) => string;
268
+ /**
269
+ * ⚠ THE SHARED CLAUSE, and it renders on EVERY branch. D81: `owners_notified`
270
+ * `null` = NOT DETERMINABLE, `0` = determinable and nobody was told, `> 0` =
271
+ * N notified — and **`> 0` ADDS to this sentence rather than replacing it**.
272
+ * One shared clause is why the two branches cannot drift apart later; a pair
273
+ * of independent paragraphs is how the false version gets reintroduced.
274
+ *
275
+ * It is also TRUE, and checked rather than inherited: the server upserts a
276
+ * `capture_requests` row BEFORE it emits the 403.
277
+ */
278
+ readonly pendingRecorded: "Your request is recorded.";
279
+ /**
280
+ * ⚠ THE ONLY BRANCH THAT MAY CLAIM A NOTIFICATION, and it is reachable only
281
+ * when the server says `> 0`. Today it never does — the field is hardcoded
282
+ * `null` until `CR-036`, which is provisioning-blocked — so this sentence is
283
+ * built and unreachable ON PURPOSE. **Never render it on the null branch.**
284
+ * D81: a hardcoded *"we notified the owners"* installs the most damaging
285
+ * possible mental model at the moment of highest trust — *the owner knows, so
286
+ * I just wait* — so the pending state would suppress its own workaround. A
287
+ * user told nothing at least asks in Slack.
288
+ *
289
+ * The branch is PERMANENT, not a stopgap: addresses bounce, domains land on
290
+ * suppression lists, and a sole owner's address can be dead. `CR-036` landing
291
+ * does not delete the other branch; it only makes this one reachable.
292
+ */
293
+ readonly pendingNotified: (owners: number) => string;
294
+ /** True by construction: 403 is `later`, so the offset holds and bytes stay on disk. */
295
+ readonly pendingBuffered: "Nothing is lost while you wait — turns are buffered on this machine and upload in order once an owner approves.";
296
+ readonly pendingTrackLabel: "Track it:";
297
+ /** `armed` = ready, not running. The install is correct; capture is waiting. */
298
+ readonly pendingArmed: (repo: string) => string;
299
+ };
300
+ /**
301
+ * The PATH clash — `CR-025`, and `DESIGN.md` §13.6's worked example is the spec.
302
+ *
303
+ * D55 accepted a residual bin collision as unlikely; the register corrects that
304
+ * to *closer to certain*, because the squatting package (`vibecommit@1.0.6`) is
305
+ * itself a commit CLI, so the overlap population is exactly developers who
306
+ * install commit tooling. **This detects the collision D55 accepted. It does not
307
+ * reopen the naming decision** — the bin name stays.
308
+ *
309
+ * ⚠ The plugin path in the fix line depends on `CR-027e` (renames the plugin
310
+ * directory to `vibecommit`) and `CR-028` (creates `bin/` at all), both W6, both
311
+ * in `claude-plugin`, both another builder's. Written as §13.6 draws it rather
312
+ * than softened to dodge the dependency.
313
+ */
314
+ /**
315
+ * The `post-commit` install report — `CR-170`, D154.
316
+ *
317
+ * ⛔ **EVERY OUTCOME SAYS SOMETHING, INCLUDING THE GOOD ONE.** Three of this
318
+ * path's failure modes are silent no-ops (D98's class): `core.hooksPath` makes
319
+ * `.git/hooks` dead, an existing hook must not be clobbered, and neither is
320
+ * visible without a line of output. A user who is told nothing cannot tell a
321
+ * working install from one that observes no commits for the life of the clone.
322
+ *
323
+ * ⚠ **AND IT SAYS "THIS CLONE", EVERY TIME.** Per-clone installation is the real
324
+ * cost of this design and the one thing a user will be surprised by later, so it
325
+ * is stated at the moment they can act on it rather than buried in documentation.
326
+ */
327
+ export declare const COMMIT_HOOK: {
328
+ readonly installed: (repo: string) => string;
329
+ readonly chained: (repo: string) => string;
330
+ readonly already: "Commit capture was already on for this clone.";
331
+ readonly perClone: "Run `vibecommit connect` in each clone you want commits recorded for — git hooks are per-clone, not per-account.";
332
+ readonly hooksPathWhat: "Commit capture could NOT be installed for this clone.";
333
+ readonly hooksPathWhy: (configured: string) => string;
334
+ readonly hooksPathFixLabel: "Fix, either one:";
335
+ readonly hooksPathFixUnset: "git config --unset core.hooksPath";
336
+ readonly hooksPathFixManual: "or add `vibecommit post-commit` to your existing post-commit hook";
337
+ readonly failedWhat: "Commit capture could NOT be installed for this clone.";
338
+ readonly failedWhy: (why: string) => string;
339
+ };
340
+ export declare const PATH_CLASH: {
341
+ readonly foreignWhat: "Another program owns the `vibecommit` command.";
342
+ readonly foreignWhy: (resolved: string) => string;
343
+ readonly absentWhat: "The `vibecommit` command is not on your PATH.";
344
+ readonly absentWhy: "Hook capture is unaffected — the plugin runs the binary by path — but typing `vibecommit status`, `why` or `report` will not find it.";
345
+ readonly fixLabel: "Fix, either one:";
346
+ /**
347
+ * FOREIGN only. The uninstall half is wrong for the absent case — telling
348
+ * someone to uninstall a package they demonstrably do not have is the kind of
349
+ * fix line that teaches users to stop reading fix lines. Found by running it.
350
+ */
351
+ readonly fixReplace: "npm uninstall -g vibecommit && npm i -g @vibe-commit/capture";
352
+ /** ABSENT: there is nothing to remove, only something to add. */
353
+ readonly fixInstall: "npm i -g @vibe-commit/capture";
354
+ readonly fixDirectLabel: "or invoke ours directly:";
355
+ /**
356
+ * `node …/bin/index.js`, not `…/bin/vibecommit`, and the difference is not
357
+ * cosmetic: `claude-plugin`'s `bin/` contains `index.js` and no file named
358
+ * `vibecommit`, and `index.js` carries a shebang but is NOT executable — so
359
+ * the shorter form fails twice over. `hooks.json` invokes it exactly this
360
+ * way, which is the shape that demonstrably works.
361
+ *
362
+ * This line asked for something impossible until DESIGN.md §13.6 was
363
+ * corrected — the same defect as the absent case above telling users to
364
+ * uninstall a package they do not have. **A fix line must be runnable, and
365
+ * the way you know is that you ran it.**
366
+ */
367
+ readonly fixDirect: "node ~/.claude/plugins/vibecommit/bin/index.js connect";
368
+ };
369
+ /**
370
+ * Browser sign-in — `CR-084d`, the beat `connect.ts:175-176` deferred to "the
371
+ * oauth lane" and this task is that lane.
372
+ *
373
+ * ⚠ Two rules these strings are written to, both from the claims register:
374
+ *
375
+ * 1. **No reassurance adjective anywhere** (D65 §DR7 registers *secure, safe,
376
+ * protected, encrypted* on the `connect` surface, which is where every
377
+ * string below renders). A sign-in screen is exactly where the temptation
378
+ * is strongest, and "we'll securely sign you in" says nothing a user can
379
+ * check.
380
+ * 2. **The `what` line never has "you" as its subject** and never carries a
381
+ * bare HTTP status (DESIGN.md §13.6). "Sign-in did not finish", not "you
382
+ * failed to sign in"; the CAUSE goes in the `why` line, in a noun the user
383
+ * recognises.
384
+ *
385
+ * `browserDone` and `browserRefused` are the only strings in this package a
386
+ * BROWSER renders rather than a terminal, which is why they carry no §13.5
387
+ * gutter and no §13.2 glyph: there is no terminal to degrade to. They are still
388
+ * copy, so they still live here and are still scanned by the claims gate.
389
+ */
390
+ export declare const SIGNIN: {
391
+ /** Printed before the browser opens, so the user knows what just happened. */
392
+ readonly opening: "Opening your browser to sign in…";
393
+ /**
394
+ * The headless fallback. NOT an error: a machine with no browser is an
395
+ * ordinary place to run this, and the URL is a complete substitute.
396
+ */
397
+ readonly manualLabel: "Open this URL to finish signing in:";
398
+ readonly waiting: "Waiting for the browser…";
399
+ readonly done: "Signed in.";
400
+ /** What the browser tab shows. One line each — nothing is styled. */
401
+ readonly browserDone: "Signed in. You can close this tab and return to your terminal.";
402
+ readonly browserRefused: "This request did not match the sign-in waiting in your terminal.";
403
+ readonly noServerWhat: "VibeCommit could not find the sign-in service.";
404
+ /** `unreachable` — nothing answered. Says what was tried, not what is wrong. */
405
+ readonly noServerUnreachableWhy: "The server did not answer the request that says where to sign in. It may be down, or this machine may have no route to it.";
406
+ /** Any `malformed` fault. One sentence, because the user's move is the same. */
407
+ readonly noServerMalformedWhy: "The server answered, but the document that says where to sign in is not one this version can use.";
408
+ /**
409
+ * The override was refused before anything was sent. Names the variable and
410
+ * the rule, because this one is entirely in the user's hands to fix.
411
+ */
412
+ readonly noServerRefusedWhy: "VIBECOMMIT_MCP_URL is set to an address this client will not send a sign-in to. Only https, or http on the loopback, is allowed.";
413
+ readonly noServerFix: "Check the service, then run:";
414
+ readonly deniedWhat: "Sign-in was declined.";
415
+ readonly deniedWhy: "The sign-in screen returned without granting access. Nothing was changed on this machine.";
416
+ readonly deniedFix: "To try again, run:";
417
+ readonly timeoutWhat: "Sign-in did not finish in time.";
418
+ readonly timeoutWhy: "The browser did not come back before the deadline. Nothing was changed on this machine.";
419
+ readonly timeoutFix: "To try again, run:";
420
+ readonly rejectedWhat: "The sign-in service refused this client.";
421
+ /**
422
+ * ⚠ `unauthorized_client` is the expected answer TODAY: this CLI's client id
423
+ * is not in the server's registered set (see `src/oauth/token.ts`). The copy
424
+ * names the state without promising when it changes — a date here would be a
425
+ * claim the register would have to carry.
426
+ */
427
+ readonly rejectedWhy: "The service did not accept this client's sign-in request.";
428
+ readonly rejectedFix: "Report this, with the command you ran:";
429
+ readonly unreachableWhat: "VibeCommit could not reach the sign-in service.";
430
+ readonly unreachableWhy: "The request did not complete. Nothing was changed on this machine.";
431
+ readonly unreachableFix: "Check the connection, then run:";
432
+ readonly malformedWhat: "The sign-in service returned an unexpected answer.";
433
+ readonly malformedWhy: "The response was not in the form this version can use. No session was stored.";
434
+ readonly malformedFix: "Report this, with the command you ran:";
435
+ /** A session already exists, so sign-in is a no-op. */
436
+ readonly alreadySignedIn: "Already signed in on this machine.";
437
+ /**
438
+ * ⚠ THE COPY THIS OBJECT DEFERRED BY TASK NUMBER, now written by the first
439
+ * renderer. `why` is that renderer; `report` (`CR-108`, W10) is the second and
440
+ * inherits these rather than inventing a second phrasing.
441
+ *
442
+ * ⚠ AND THIS EXPORT NOW RENDERS ON TWO SURFACES. `SURFACES` in
443
+ * `test/copy-claims.test.ts` gained `why` beside `connect` for exactly these
444
+ * four strings — a surface map that still said `connect` alone would be
445
+ * checking them against the wrong blocked set.
446
+ *
447
+ * `expired` — the refresh grant was REFUSED. The session is over; only a new
448
+ * sign-in fixes it. Distinct from `busy`, which is a wait.
449
+ */
450
+ readonly expiredWhat: "This machine's sign-in has ended.";
451
+ readonly expiredWhy: "The stored sign-in could not be renewed, so nothing could be read. It may have been revoked, or it may simply have run out.";
452
+ readonly expiredFix: "Sign in again, then run the command again:";
453
+ /**
454
+ * `busy` — another process holds the refresh lock and did not release it in
455
+ * time. NOT a failure of the session: the fix is to run it again, and saying
456
+ * "sign in again" here would send a user to re-authenticate over a lock.
457
+ *
458
+ * ⚠ The `what` line's subject is the state, never "you" (DESIGN.md §13.6).
459
+ */
460
+ readonly busyWhat: "Another VibeCommit command is renewing this machine's sign-in.";
461
+ readonly busyWhy: "Only one process may renew a sign-in at a time, and the one holding it did not finish. Nothing was read and the stored sign-in is unchanged.";
462
+ readonly busyFix: "Run it again in a moment:";
463
+ };
464
+ /** `vibecommit off`. */
465
+ export declare const OFF: {
466
+ readonly done: "Capture is off for this repository.";
467
+ readonly alreadyOff: "Capture was already off for this repository.";
468
+ readonly note: "Sessions already recorded are unaffected.";
469
+ };
470
+ /**
471
+ * `vibecommit why` — `CR-086`, W9.
472
+ *
473
+ * The screen is drawn in style guide §10.4 (layout), §10.5 (sub-agent
474
+ * attribution) and §10.6-A/B (the two absences this verb owns), and the copy is
475
+ * NORMATIVELY §R3's decision table in `docs/designs/claims-audit-why.md`. §10.4
476
+ * is explicit that §R3 governs and that it must not be paraphrased here, so the
477
+ * per-grade sentences live in `grades.ts` behind the one header renderer and
478
+ * this object holds only what §R3 tables SEPARATELY from the grade.
479
+ *
480
+ * ## ⚠ THE HOLE THIS SURFACE SHIPS WITH, HELD OPEN AND NAMED — `CR-129`
481
+ *
482
+ * §R3's sub-agent attribution table has three rows and **two of them read data
483
+ * nothing produces**. Measured at `vibecommit-schema` `2c77400`,
484
+ * `vibecommit-mcp` `eeda878` and here at `4dcf47d`, every tracked blob read as
485
+ * utf8 (never `grep` — `src/redact.ts` carries literal NUL bytes and `grep -I`
486
+ * skips it silently):
487
+ *
488
+ * - `capture_turns.subagent_count` — **0 hits in all three repos** at those
489
+ * SHAs. `TODOS[74]` assumed the column; nothing had added it.
490
+ * ⚠ **UPDATED BY `CR-129d` (W10): THE COLUMN NOW EXISTS AND STILL HAS NO
491
+ * PRODUCER.** `CR-129a` added it at `vibecommit-schema` `f3022c5` —
492
+ * `integer`, nullable, no default. Nothing writes it (mcp's
493
+ * `insertCaptureTurns` is a seven-key literal) and nothing emits it
494
+ * (`blame_commit` selects `position, turn_hash`), so the marker still
495
+ * cannot render from real data. The measurement above is kept because it
496
+ * is dated and was true; **this note is the correction, not a rewrite.**
497
+ * ⚠ A six-column tally stood here and is gone — it was an exact
498
+ * transcription of the original `create table` that three later
499
+ * migrations emptied, including one renaming `workspace_id` to `org_id`.
500
+ * D101: write the predicate, never the count.
501
+ * - `commit_file_attribution` EXISTS (`CR-070`,
502
+ * `20260814000000_cr070_commit_graph.sql:278`) and **has no writer**:
503
+ * `CR-072` shipped the edge only and said so
504
+ * (`vibecommit-mcp/src/conversation/edge_derivation.ts:56-64`). That table
505
+ * is where `agent_id` lives.
506
+ *
507
+ * So `agent_id` is a column on a table nothing writes, and `subagent_count` is
508
+ * not a column at all. `TODOS[74]` says of that minimum: *"E1 cannot ship
509
+ * without it… silence about a delegated edit is an overclaim about who wrote
510
+ * the code."* Both halves are true, and the resolution is neither to omit the
511
+ * requirement nor to fake it: **the absence renders, in the absence grammar,
512
+ * naming what it is and what would change it.**
513
+ *
514
+ * ⚠ `agent_id` NULL is SEMANTIC — the schema comment says *"NULL = the main
515
+ * thread. The empty string is refused because '' would render as an unnamed
516
+ * sub-agent — a plausible wrong answer rather than an error."* **`null` and
517
+ * ABSENT are therefore different facts**: `null` means we know it was the main
518
+ * thread; absent means we know nothing. An unmarked turn rendered from an
519
+ * absent field is the overclaim, so `why.ts` requires the field to be PRESENT
520
+ * before it renders a turn unmarked, and marks every turn otherwise.
521
+ *
522
+ * ## What is deliberately NOT here
523
+ *
524
+ * No grade word. §13.3: *"Grade words appear in `--json` only. Prose never
525
+ * prints a bare grade"* — the two-tier structural summary plus the per-edge
526
+ * evidence sentence render instead, and the evidence sentence is `grades.ts`'s.
527
+ *
528
+ * No `⛓` / `◇` glyphs. §10.5 draws them for the WEB surface; `DESIGN.md` §13.2's
529
+ * `GLYPH` table has exactly three entries (`ok` / `warn` / `bad`) and neither is
530
+ * in it. The CLI's binding requirement is §R3's prose evidence line, which
531
+ * renders. Inventing a fourth glyph is a report to the orchestrator, not an edit
532
+ * to `term.ts`.
533
+ *
534
+ * No colour on the attribution marker or on the grade (D65 §DR3, §DR4).
535
+ * `agent_id` is an ACTOR IDENTITY, the same class as a branch name, and a
536
+ * green/yellow/red grade ramp asserts a ranking §R3 forbids.
537
+ */
538
+ export declare const WHY: {
539
+ /**
540
+ * ⚠ THIS is where D61 §PS6's file scope lives, and the reason it moved here.
541
+ * `commit_file_attribution` has no line ranges, so every sentence that
542
+ * INTRODUCES TURNS is file-scoped — and §PS6 requires the file scope to render
543
+ * on the same screen as the line number, which it does: the identity line
544
+ * above carries the line, this sentence carries the file.
545
+ *
546
+ * ⚠ §R3 bans a causal vocabulary on this sentence at all grades — *produced,
547
+ * caused, wrote this line, because, the reason, responsible for, explains, led
548
+ * to*. "Touched" is the approved verb and it is the weakest true one.
549
+ */
550
+ readonly turnsIntro: (file: string) => string;
551
+ /**
552
+ * §R3's `match_kind` table, the `probable` row — `CR-138d` / `CR-109d`.
553
+ *
554
+ * ⚠ **THE ONLY DIFFERENCE BETWEEN THE TWO VARIANTS, AND IT MUST STAY VISIBLE.**
555
+ * §R3: `exact` renders the squash copy *unqualified* — the pair came from a
556
+ * recorded successor row — while `probable` renders it **plus a stated
557
+ * basis**. Rendering them identically is the folding D67 forbids: *"the
558
+ * disclosure would become the thing that hides the distinction."*
559
+ *
560
+ * ⚠ D105 makes this permanent rather than cosmetic: `match_kind` is in
561
+ * `commit_sha_successors`' primary key and `reject_history_mutation` raises
562
+ * on every UPDATE including `service_role`, **so a wrong row cannot be
563
+ * corrected.** A guess labelled as a guess is the only honest rendering.
564
+ *
565
+ * ⚠ `witnessed` is §R3's own word and the choice is deliberate. The obvious
566
+ * phrasing would have reached for the `observed` family, which is BLOCKED on
567
+ * this surface because it names a mechanism that exists in no repo (D113 §1)
568
+ * — a blocked claim arriving through a synonym nobody was checking. This
569
+ * comment names the trap without spelling the phrase, so a source-text scan
570
+ * does not fire on the note explaining the avoidance.
571
+ */
572
+ readonly squashProbableBasis: "These two commits were matched by patch id rather than by a recorded successor row, so the pairing is read off the commits themselves and is not something capture witnessed.";
573
+ /**
574
+ * ⚠ WHY THE TWO SHAS ON THE SQUASH SCREEN ARE DIFFERENT LENGTHS.
575
+ *
576
+ * Two builders — this one and `CR-101`'s on the web surface — read the
577
+ * asymmetry as a bug, and **the ruling was to explain it rather than remove
578
+ * it** (D120 §1 as revised). Web keeps parity by carrying the full value in
579
+ * the DOM; a terminal line has nowhere to put it, so the line says why.
580
+ *
581
+ * ⚠ **THE SENTENCE DOES NOT TELL THE READER TO RUN `git show` ON IT**, and
582
+ * that restraint is measured rather than cautious: cloned over the git
583
+ * transport after a squash-merge with branch delete, the original object is
584
+ * ABSENT and `git show` fails on the full sha as surely as on a short one.
585
+ * What the full value keeps is that it stays a complete identifier somebody
586
+ * else can look up. Promising a local command that fails in exactly the case
587
+ * this state exists for would be the plausible-wrong-answer class in copy.
588
+ */
589
+ readonly squashShaLengths: "The recorded commit is written out in full because a squashed commit is often missing from a working copy, and a shortened id cannot be looked up once the commit is gone.";
590
+ /** §10.4's turn row: `turn 12 [Edit] session.ts`. */
591
+ readonly turnLabel: "turn";
592
+ /** §10.4: excerpts are `muted` and `>`-prefixed — verbatim, never a summary. */
593
+ readonly excerptPrefix: ">";
594
+ /** Row 1 — `agent_id IS NOT NULL`. An identity, rendered plain (D65 §DR3). */
595
+ readonly subAgentMarker: (agentId: string) => string;
596
+ /**
597
+ * Row 2 — `capture_turns.subagent_count > 0`.
598
+ *
599
+ * ⚠ **NO PRODUCER EXISTS FOR THIS AND THE COLUMN IS NOT IN ANY SCHEMA.** It is
600
+ * implemented against a validated OPTIONAL payload field so that the day the
601
+ * column exists the row renders, and it is exercised in the suite against a
602
+ * synthetic payload — but nothing on the wire can populate it today, and this
603
+ * comment is the only honest place to say so. It is NOT rendered from a zero
604
+ * and NOT rendered from an absent field.
605
+ */
606
+ readonly subAgentCountMarker: (count: number) => string;
607
+ /**
608
+ * The marker for a turn whose actor we cannot name — `CR-129`'s hole, on the
609
+ * turn itself rather than only in a footnote. §10.5: *"the turn header and the
610
+ * file-attribution table ALWAYS name the actor… hiding an actor"* is the thing
611
+ * that makes a claim. When we cannot name one, we say that, per turn.
612
+ */
613
+ readonly actorUnrecordedMarker: "· actor not recorded";
614
+ /**
615
+ * The `CR-129` note, in the absence grammar — absence, cause, remedy.
616
+ *
617
+ * ⚠ NOT an `ABSENCE` state and it may not become one. Those five are
618
+ * whole-screen empty states that all exit 4; this is a caveat on a screen that
619
+ * HAS content, and `test/copy-absence.test.ts` asserts the set is exactly
620
+ * five. Same ruling the `ABSENCE` docblock already makes for the
621
+ * shallow-clone refusal: not every absence sentence is an absence STATE.
622
+ */
623
+ readonly attributionWhat: "Sub-agent attribution is not recorded for these turns.";
624
+ /**
625
+ * ⚠ "on this screen", not "below" — caught by rendering it. The note sits
626
+ * AFTER the turn list, so a positional word was pointing the wrong way. A
627
+ * sentence that describes its own layout breaks silently when the layout
628
+ * moves; one that names the screen does not.
629
+ */
630
+ readonly attributionWhy: "The per-turn attribution this marker reads has no writer yet, so no turn on this screen can be shown as main-thread work or as delegated work.";
631
+ /**
632
+ * ⚠ THE SECOND SENTENCE IS THE LOAD-BEARING ONE. Without it, an unmarked turn
633
+ * reads as a finding that the main thread wrote it — which is the inversion
634
+ * the register permanently BLOCKS, arriving through silence instead of through
635
+ * a sentence.
636
+ */
637
+ readonly attributionNotAFinding: "An unmarked turn here is a missing record, not a finding about who wrote the code.";
638
+ readonly attributionFix: "Nothing to fix on this machine. The marker renders once attribution is recorded.";
639
+ /**
640
+ * §R3, verbatim. ⚠ **The selection rule is ORDINAL AND STATED** — *"in turn
641
+ * order"*, never *"most relevant"*, which is a ranking, which is server-side
642
+ * analysis and reverses D48–D51.
643
+ *
644
+ * ⚠ **Every number here comes from the PAYLOAD.** D60 §D1a: *"the client may
645
+ * render; it may not analyze."* Windowing an already-ordered list for display
646
+ * is rendering; DERIVING the numbers is not, so `why.ts` never counts the
647
+ * array it was handed.
648
+ */
649
+ readonly truncation: (from: string, to: string, total: string) => string;
650
+ /** §R3's L2: files in the commit whose `turn_hash` is NULL are COUNTED, never dropped. */
651
+ readonly filesWithoutTurn: (count: string) => string;
652
+ /** The one-file form. `1 more files` is the tell that a count was never read. */
653
+ readonly fileWithoutTurn: "1 more file in this commit has no linked turn.";
654
+ readonly usageWhat: "This command needs a file and a line number.";
655
+ readonly usageWhy: "`why` answers for one line, so it cannot run without one. Nothing was read and nothing was sent.";
656
+ readonly usageFix: "Name the file and the line:";
657
+ /**
658
+ * `--json` — the REFUSAL COPY THAT STOOD HERE IS GONE, and its removal is the
659
+ * deliverable rather than a tidy-up (`CR-149`, D122 §2).
660
+ *
661
+ * It read *"`why` cannot produce JSON yet"*, and its docblock argued the
662
+ * refusal was *"the honest minimum rather than a gap"*. That was honest as a
663
+ * STOPGAP and false as a claim about design intent: **§13.3 REQUIRES `--json`
664
+ * on the read verbs**, so the flag was an UNBUILT FEATURE wearing the shape of
665
+ * a designed position — which is the harder defect to see, because a refusal
666
+ * reads as a decision. `src/json.ts` is the document; `status`, `why` and
667
+ * `report` all emit one.
668
+ *
669
+ * Nothing replaces these three constants. A verb that CAN answer the flag has
670
+ * no refusal to word.
671
+ */
672
+ /**
673
+ * The shallow-clone REFUSAL (D67's posture, D98's hazard class).
674
+ *
675
+ * ⚠ A REFUSAL IS NOT AN ABSENCE. `git blame` on a shallow clone attributes to
676
+ * the graft boundary and returns a plausible WRONG commit — a call that fails
677
+ * announces itself; a call that returns a plausible answer is absorbed by the
678
+ * caller. Shallow clones live in CI containers, which is exactly where nobody
679
+ * is watching. The `ABSENCE` docblock already rules on this for `report`: it
680
+ * is not a sixth state and one may not be written there.
681
+ */
682
+ readonly shallowWhat: "This is a shallow clone, so `git blame` cannot be trusted here.";
683
+ readonly shallowWhy: "A shallow clone stops at its graft boundary and attributes every older line to the oldest commit it holds. That answer looks ordinary and is wrong, so it is refused rather than shown.";
684
+ readonly shallowFix: "Fetch the full history, then run this again:";
685
+ readonly shallowFixCommand: "git fetch --unshallow";
686
+ /** `git blame` itself failed — no such file, no such line, or git said no. */
687
+ readonly blameWhat: "`git blame` could not attribute that line.";
688
+ readonly blameWhy: "git did not return a commit for it. The path may not be tracked in this repository, or the file may have fewer lines than the one named.";
689
+ readonly blameFix: "Check the path and the line, then run this again:";
690
+ /**
691
+ * ⚠ A READ-LANE FAILURE MUST PRINT AND EXIT NON-ZERO. The whole reason the
692
+ * exit contract was split is that the hook rule — exit 0, stdout empty — would
693
+ * have made this verb fail SILENTLY on a 5xx. `src/exit.ts` says so at the top.
694
+ *
695
+ * ⚠ And never a bare HTTP status (§13.6). The status is what we know; it is
696
+ * not what the user can act on.
697
+ */
698
+ readonly unreachableWhat: "VibeCommit could not reach the record service.";
699
+ readonly unreachableWhy: "The request did not complete, so no answer was read. The service may be down, or this machine may have no route to it.";
700
+ readonly serverErrorWhat: "The record service could not answer that.";
701
+ readonly serverErrorWhy: "The request reached the service and it returned a failure instead of a record. Nothing on this machine was changed.";
702
+ readonly malformedWhat: "The record service returned an answer this version cannot read.";
703
+ readonly malformedWhy: "The response was not in the form this version expects, so none of it was rendered rather than part of it being guessed at.";
704
+ readonly readFix: "Try again, and report it if it continues:";
705
+ /**
706
+ * ⛔ VERSION SKEW — DISTINCT FROM `malformed`, AND THE DISTINCTION IS THE
707
+ * WHOLE POINT (`U1`, 2026-08-30).
708
+ *
709
+ * `parseBlameCommitPayload` is a STRICT ALLOWLIST on `state`: a value it does
710
+ * not know returns null, and null rendered as "malformed". So the day the
711
+ * server added a state, every client older than that day told the user its
712
+ * ANSWER WAS BROKEN — when the answer was fine and the CLIENT was old.
713
+ * `edge_unreadable` is the first such addition and it would have been the
714
+ * first casualty.
715
+ *
716
+ * ⚠ THE ROOT CAUSE, NOT THE SYMPTOM. Without this, every future state the
717
+ * server adds needs a lockstep client release FOREVER, and the coupling is
718
+ * invisible until a user hits it — worse here than in most protocols, because
719
+ * the vendored bundle in `claude-plugin/bin/` lags this source (10 commits at
720
+ * the time of writing) so "old client" is the NORMAL case, not the edge one.
721
+ *
722
+ * ## Why this is not a sixth ABSENCE state
723
+ *
724
+ * It is not an absence. Nothing is missing from the record — the server
725
+ * answered, and this build cannot render what it said. Same reason the
726
+ * shallow-clone refusal is not one: a REFUSAL and a SKEW are both about this
727
+ * client's limits, and `ABSENCE` is about the record's contents.
728
+ *
729
+ * ⚠ It still follows CR-112's grammar (absence · cause · remedy), because
730
+ * that grammar is what makes a dead end actionable, and here — unlike most of
731
+ * `ABSENCE` — the remedy is real: upgrading genuinely fixes it.
732
+ *
733
+ * ⚠ THE COPY MAY NOT NAME THE STATE IT COULD NOT READ. The whole premise is
734
+ * that this build does not know what that value means; printing it would
735
+ * invite the reader to interpret a token this client cannot interpret either.
736
+ */
737
+ readonly skewWhat: "The record service sent an answer this version is too old to show.";
738
+ readonly skewWhy: "The service described this commit in a way this build does not recognise. The record itself is fine — this copy of the client cannot render it, so it showed nothing rather than guessing at part of it.";
739
+ readonly skewFix: "Reinstall the VibeCommit plugin to pick up the current client, then run this again:";
740
+ /** The bearer is dead after one refresh and one retry. Exit 3, not 1. */
741
+ readonly signedOutWhat: "This machine is not signed in to VibeCommit.";
742
+ readonly signedOutWhy: "Reading a record needs a signed-in user, and no usable sign-in was found on this machine.";
743
+ readonly signedOutFix: "Sign in, then run the command again:";
744
+ };
745
+ /**
746
+ * `vibecommit report` (CR-108).
747
+ *
748
+ * NOTE: this surface ships NO durability copy until the E2 metric is defined
749
+ * (D61 §PS6, and the claims-register `Durable / survives / durability` row).
750
+ *
751
+ * ⚠ THE TWO EMPTY-WINDOW STATES MOVED. They were `emptyNoCommits` and
752
+ * `emptyNoCapture` here, and they were the absence clause and NOTHING ELSE — no
753
+ * cause, no remedy. `CR-112` re-homed both into `ABSENCE` below, where they
754
+ * carry the full grammar and sit beside the three `why` states they have to
755
+ * agree with. Do not reintroduce a short form here: two half-states in one
756
+ * module and five whole ones in another is the drift `CR-112` exists to stop.
757
+ */
758
+ export declare const REPORT: {
759
+ readonly coverageLabel: "Commits with a recorded session";
760
+ readonly gradeMixLabel: "Evidence";
761
+ /**
762
+ * ⚠ **THE DEFINITION IS PART OF THE NUMBER, NOT A PREFACE.** D67 cleared
763
+ * §PS6's durability block on stated terms, and the first is that the
764
+ * definition, the percentage and the grade mix render together — *"without
765
+ * them, `observed` correlation-in-time is laundered into a KPI."* A build
766
+ * that ships the number alone does not lift the block.
767
+ *
768
+ * ⚠ **THE REF IS IN THE SENTENCE, ALWAYS** — *"a bare percentage is
769
+ * meaningless because the number is ref-relative."* `commit_coverage` makes
770
+ * `ref` a REQUIRED payload field for exactly this reason.
771
+ */
772
+ readonly definition: (ref: string) => string;
773
+ readonly rate: (reachable: string, held: string, ref: string, percent: string) => string;
774
+ /** §10.3 draws `Of those:` and then the per-grade split. */
775
+ readonly gradeMixIntro: "Of those:";
776
+ /**
777
+ * ⚠ **THE GRADE WORDS THEMSELVES MAY NOT REACH THE SCREEN**, so the mix is
778
+ * rendered through these labels — DESIGN.md §13.3 (grade words in `--json`
779
+ * only), D65 §DR4 (no grade word on screen), and `test/why.test.ts` already
780
+ * enforces it for the other read verb.
781
+ *
782
+ * ⚠ §10.3's DRAWING VIOLATES THAT RULE and this deliberately departs from it:
783
+ * it draws *"41 observed during a session"*, which prints the bare grade
784
+ * word. D113 §1 makes it worse than a style slip — `observed` is BLOCKED
785
+ * because it names **a mechanism that exists in no repo**, and its listed
786
+ * phrasings include *seen during the session* and *watched*, which are the
787
+ * two shortenings anyone would reach for next. The `observed` label below
788
+ * mirrors `grades.ts`'s own approved sentence, *"This commit appeared while
789
+ * the session was running"*, which says the same fact and claims no witness.
790
+ * The §10.3 fix is owed in `code/` and is not this package's to write.
791
+ */
792
+ readonly gradeDerivedLabel: "recorded in the transcript";
793
+ readonly gradeObservedLabel: "appeared while the session ran";
794
+ readonly gradeDeclaredLabel: "reported by the agent";
795
+ /** `<n> <label>`, joined by the renderer. Singular and plural read alike. */
796
+ readonly gradeCount: (count: string, label: string) => string;
797
+ /**
798
+ * ⚠ **THE SEPARATE LABELLED LINE, IN OUR WORDS** — D67: *"only exact
799
+ * `commit_sha_successors` rows count toward the numerator; patch-id matches
800
+ * render as a SEPARATE LABELLED 'probable' LINE and are never folded into the
801
+ * rate."*
802
+ *
803
+ * ⚠ **AND IT IS NOT THE SERVER'S SENTENCE.** `vibecommit-mcp`
804
+ * `src/read/coverage.ts` emits `probable_line.label` — a user-facing string
805
+ * minted in another repository, on this payload. D112 §3 ruled on exactly
806
+ * that shape: **the wire carries the fact, the client owns the words.** That
807
+ * ruling was measured in `src/read/blame.ts` and the class is NOT closed one
808
+ * file over, so this renders here and the wire's label is never printed.
809
+ */
810
+ readonly probable: (count: string) => string;
811
+ readonly probableOne: "1 more matched by patch id, reported separately and not counted above.";
812
+ /**
813
+ * ⚠ **A SEPARATE PHRASING FROM `WHY.shallowWhat`, AND THE REASON IS THAT IT
814
+ * IS A DIFFERENT FACT.** `why`'s says *"`git blame` cannot be trusted here"*,
815
+ * which is true of blame and says nothing about reachability. On a shallow
816
+ * clone the commits that would answer *"is this sha still reachable"* may
817
+ * simply not be present, so the rate would be wrong rather than missing. The
818
+ * REMEDY is genuinely the same fact, so `shallowFix` and `shallowFixCommand`
819
+ * are SHARED rather than re-worded — that is the duplication to avoid.
820
+ */
821
+ readonly shallowWhat: "This is a shallow clone, so coverage cannot be computed here.";
822
+ readonly shallowWhy: "Coverage asks which recorded commits are still reachable, and a shallow clone does not hold the history that answers it. The number would be wrong rather than missing.";
823
+ /**
824
+ * ⚠ FAILS CLOSED. If git cannot answer reachability for a commit we hold, the
825
+ * rate would be computed over a set we do not actually know — so it refuses.
826
+ * Counting an unanswerable probe as unreachable would DEFLATE the number
827
+ * silently, which is D98's class: no obviously-broken shape.
828
+ */
829
+ readonly unknownReachabilityWhat: "Coverage could not be computed for every recorded commit.";
830
+ readonly unknownReachabilityWhy: "Git could not say whether some recorded commits are still reachable from this ref. A share computed over the rest would be a smaller number presented as a complete one.";
831
+ readonly unknownReachabilityFix: "Fetch the full history, then run this again:";
832
+ readonly refWhat: "This command needs a ref it can name.";
833
+ readonly refWhy: "Coverage is relative to a ref, so the answer is meaningless without one, and HEAD is detached here. Nothing was read and nothing was sent.";
834
+ readonly refFix: "Name the ref:";
835
+ /**
836
+ * The window, as the two empty states name it.
837
+ *
838
+ * ⚠ **THE WINDOW BOUNDS THE LOCAL HALF ONLY.** `commit_coverage` accepts no
839
+ * `since`, so the server's totals are repository-wide and the RATE claims no
840
+ * window — see `report.ts`. This string appears only on the two empty states,
841
+ * which are answered from the clone.
842
+ */
843
+ readonly window: (since: string) => string;
844
+ readonly usageWhat: "This command needs a start date.";
845
+ readonly usageWhy: "The window bounds which commits are counted, so the answer is not defined without one. Nothing was read and nothing was sent.";
846
+ readonly usageFix: "Name the date the window starts:";
847
+ readonly sinceWhat: "That start date could not be read.";
848
+ readonly sinceWhy: "The window must be a calendar date, written as YYYY-MM-DD. A date git cannot read would silently widen the window.";
849
+ readonly sinceFix: "Write the date as YYYY-MM-DD:";
850
+ };
851
+ /**
852
+ * THE FIVE ABSENCE STATES, AS ONE SET — `CR-112`, D65.
853
+ *
854
+ * Five screens, owned by four different tasks, written by different builders at
855
+ * different times: cold start and no-edge (`CR-086`, W9), the two `report` empty
856
+ * windows (`CR-108`, W10), and squash-resolved (`CR-109`). D65 put them in one
857
+ * row so they are ONE set with ONE grammar rather than four people's four ways
858
+ * of saying nothing was found.
859
+ *
860
+ * ## The grammar, and it is the three fields
861
+ *
862
+ * **State the absence (`what`), state the cause (`why`), state what would change
863
+ * it (`fix`). NEVER "nothing found".** The fields ARE the grammar — they are
864
+ * `renderErrorBlock`'s shape (`src/term.ts`) and `DESIGN.md` §13.6's *what, why,
865
+ * fix — fix last, always, because the last thing on screen is what gets copied*.
866
+ * **A state with a blank `why` is a state that says "nothing found" with extra
867
+ * steps**, which is why `test/copy-absence.test.ts` asserts every field of every
868
+ * state explicitly and scans all of them for the empty-result vocabulary.
869
+ *
870
+ * That test is the mechanism. This docblock is not: `CR-090d` is one wave old
871
+ * and its whole lesson is that `strings.ts` DECLARED "no inline literals" while
872
+ * five sat in `src/index.ts` for six waves, because a docblock is not a gate.
873
+ *
874
+ * ## `exit` rides WITH the copy, deliberately
875
+ *
876
+ * `EXIT.empty` (4) already exists and its docblock already carries the reason:
877
+ * collapsing an empty window into `1` teaches a wrapper that absence of a record
878
+ * means the tool broke — one `||` away from the permanently BLOCKED claim that
879
+ * absence of an edge means no agent was involved. This task adds no exit code;
880
+ * it makes five screens use the one that exists. Binding it to the state is the
881
+ * only way that survives four builders across three waves — a builder who
882
+ * renders the state gets the code with it, instead of reading a sentence that
883
+ * asks them to remember.
884
+ *
885
+ * ## ⚠ AN EMPTY WINDOW RENDERS NO NUMBER — D67 RESOLVED FOR THIS CASE
886
+ *
887
+ * D67 cleared the §PS6 durability block *on its stated terms*: the metric, the
888
+ * coverage percentage and the grade mix render **TOGETHER**, per grade, never
889
+ * blended, ref named inline. **On an empty window there is no metric, so
890
+ * "together or not at all" resolves to NOT AT ALL.** Neither `report` state
891
+ * carries a percentage, a grade mix, or any durability vocabulary:
892
+ *
893
+ * - **State 3's denominator is zero.** A percentage over an empty denominator
894
+ * is not a small number, it is NOT A NUMBER — and `0%` there would be a
895
+ * coverage claim manufactured from no data. That is D98's plausible-wrong-
896
+ * answer class exactly: a call that fails announces itself, a call that
897
+ * returns a plausible answer is absorbed by the caller, and `0%` has no
898
+ * obviously-broken shape.
899
+ * - **State 4's numerator is zero over a real denominator**, so `0%` is
900
+ * arithmetically defensible — and there is still no grade mix to render
901
+ * beside it, because a grade mix over zero edges is empty. D67's rule is
902
+ * that the three render together or the block does not lift. They cannot.
903
+ * So state 4 renders no number either.
904
+ *
905
+ * Consequently `claims.ts`'s `durable` / `durability` / `survives` rows stay
906
+ * BLOCKED on the `report` surface and that is correct, not an oversight.
907
+ *
908
+ * ## ⚠ A CONTRACT ON THE CALLER (`CR-108`, W10): ESTABLISH THE CLONE IS COMPLETE
909
+ *
910
+ * **The caller MUST establish that the clone is complete before selecting either
911
+ * `report` empty state.** D67: on a shallow clone the metric REFUSES to render,
912
+ * and that refusal is load-bearing rather than defensive — it is what keeps the
913
+ * number from being quietly wrong in CI containers, which is where shallow
914
+ * clones live. The refusal fires BEFORE either empty state is reachable, and it
915
+ * has to: on a shallow clone *"no commits in this window"* is a claim the clone
916
+ * cannot support, because the window may simply predate the graft boundary.
917
+ *
918
+ * State 3's sentence is therefore written UNQUALIFIED, as if the clone were
919
+ * complete — because the refusal guarantees it is. `test/copy-absence.test.ts`
920
+ * pins both halves: that this contract is stated here, and that no state hedges
921
+ * about clones.
922
+ *
923
+ * ⚠ **A shallow-clone refusal is a REFUSAL, not an absence. It is NOT a state
924
+ * here and one may not be written for it.** Inventing a phrasing for something
925
+ * that is not an empty RESULT is the thing this set exists to prevent.
926
+ *
927
+ * ⚠⚠ THIS PARAGRAPH SAID "NOT A SIXTH STATE" AND THE SET IS NOW SIX. The
928
+ * sentence was right about its subject and wrong as a general rule, so it is
929
+ * narrowed rather than deleted — the bar is not "five forever", it is that a
930
+ * state here must be an EMPTY RESULT the server actually reports, not a local
931
+ * refusal and not a rephrasing of one that exists. `edgeUnreadable` (`U1`,
932
+ * 2026-08-30) clears that bar: mcp emits `edge_unreadable` as a distinct wire
933
+ * state, and no other entry can render it without asserting something false.
934
+ * A shallow-clone refusal still fails the bar, for the reason above.
935
+ */
936
+ /**
937
+ * The six states. A name per state, so the set can be asserted exactly.
938
+ *
939
+ * ⚠ Adding one means updating `EXPECTED_STATES` in `test/copy-absence.test.ts`
940
+ * in the same commit, which is deliberate: that cell exists so a state cannot
941
+ * appear without a reviewer reading the argument for it.
942
+ */
943
+ export type AbsenceStateName = "coldStart" | "noEdge" | "edgeUnreadable" | "reportNoCommits" | "reportNoSessions" | "squashResolved";
944
+ /**
945
+ * A field is a plain string, or a template when the grammar needs a value
946
+ * interpolated — the cause clause is window- and ref-relative, so two states
947
+ * cannot be plain strings without dropping the very thing that makes them a
948
+ * cause. **That is the GRAMMAR's requirement (state the cause), not D67's metric
949
+ * rule**: a named ref here is not a smuggled durability claim, and a later
950
+ * reader should not mistake it for one.
951
+ */
952
+ export type AbsenceField = string | ((...args: string[]) => string);
953
+ export interface AbsenceState {
954
+ /** The absence. What is not there. */
955
+ readonly what: AbsenceField;
956
+ /** The cause. Why it is not there. Never blank. */
957
+ readonly why: AbsenceField;
958
+ /** What would change it. "Nothing to fix" is a legitimate answer; blank is not. */
959
+ readonly fix: AbsenceField;
960
+ /** Always `EXIT.empty`. Absence is not failure — see the note above. */
961
+ readonly exit: typeof EXIT.empty;
962
+ }
963
+ export declare const ABSENCE: {
964
+ /**
965
+ * 1 — COLD START. Owner: `CR-086` (W9), and `CR-086` CORRECTED ITS SCOPE.
966
+ *
967
+ * ## ⚠ It said MACHINE; the state it names is a REPOSITORY
968
+ *
969
+ * The shipped text was *"No sessions have been recorded on this MACHINE
970
+ * yet"*, and neither half of that survived measurement. `why`'s answer is
971
+ * repository-scoped — the payload that selects this state answers for one
972
+ * repo — and a developer with capture running in three repos and none here
973
+ * would have been told capture had never run at all. Nothing in the client
974
+ * holds a machine-wide "never captured" fact either: `state.ts` keys sends by
975
+ * project.
976
+ *
977
+ * ## Why it does not render §10.6-A's two extra facts, and that is deliberate
978
+ *
979
+ * §10.6-A and §R3 draw *"That commit predates capture on this repo (first
980
+ * capture 2026-08-08). … 14 commits captured since."* **Both numbers are
981
+ * facts this client does not hold.** `state.ts` records `lastSendForRepo` —
982
+ * the LAST send, never a FIRST-capture date — and the commit count is
983
+ * `commit_coverage`'s, a server figure behind `CR-085`. D81's order applies:
984
+ * make the claim honest before building the mechanism.
985
+ *
986
+ * So the state says the thing that IMPLIES §10.6-A's headline without
987
+ * asserting either number: **if no session was ever recorded for this
988
+ * repository, then every commit in it predates capture** — including this
989
+ * one. That is §10.6-A's meaning, carried by a fact the payload can state.
990
+ * The date and the count are registered as owed (see the PR) rather than
991
+ * approximated, because a first-capture date guessed from a last-send record
992
+ * is D98's plausible-wrong-answer class exactly.
993
+ *
994
+ * ⚠ NO SIXTH STATE WAS ADDED, and none may be: `test/copy-absence.test.ts`
995
+ * asserts the EXACT set of five.
996
+ */
997
+ readonly coldStart: {
998
+ readonly what: "No session has been recorded for this repository yet.";
999
+ readonly why: "Capture has not sent anything for this repository, so no commit in it has a conversation record — including this one.";
1000
+ readonly fix: "Connect this repository, then work as usual:";
1001
+ readonly exit: 4;
1002
+ };
1003
+ /**
1004
+ * 2 — NO-EDGE EMPTY. Capture is working; the commit `git blame` returned has
1005
+ * no edge. Owner: `CR-086` (W9).
1006
+ *
1007
+ * ⚠ The `why` states a DEFINITION, not a diagnosis. "This commit was made
1008
+ * without an agent" is the permanently BLOCKED claim (see `exit.ts`), and a
1009
+ * sentence guessing at the cause of one absent record is one edit away from
1010
+ * it. What is safe is what makes a record exist at all.
1011
+ */
1012
+ readonly noEdge: {
1013
+ readonly what: "No recorded session is linked to this commit.";
1014
+ readonly why: "A commit is linked to a session only when capture was running for the session that produced it. This is a missing record, not a finding about the commit.";
1015
+ readonly fix: "Nothing to fix. A record cannot be added to a commit after the fact.";
1016
+ readonly exit: 4;
1017
+ };
1018
+ /**
1019
+ * 3 — `report` EMPTY WINDOW A: NO COMMITS. Owner: `CR-108` (W10).
1020
+ *
1021
+ * Names the WINDOW and the REF, because the answer is relative to both: there
1022
+ * may be commits on another branch, or outside these dates, and a sentence
1023
+ * that named neither would read as a claim about the repository.
1024
+ *
1025
+ * Written unqualified — see the clone contract above.
1026
+ */
1027
+ readonly reportNoCommits: {
1028
+ readonly what: (ref: string, window: string) => string;
1029
+ readonly why: (ref: string) => string;
1030
+ readonly fix: "Widen the window, or name a different ref, and run this again.";
1031
+ readonly exit: 4;
1032
+ };
1033
+ /**
1034
+ * 4 — `report` EMPTY WINDOW B: COMMITS, NO SESSIONS. Owner: `CR-108` (W10).
1035
+ *
1036
+ * ⚠ 3 and 4 are DIFFERENT FACTS and collapsing them reads as a coverage claim
1037
+ * we cannot make. The counted commits are the whole difference: this window
1038
+ * has them and has no session for them, which is not the same as having
1039
+ * nothing in it.
1040
+ */
1041
+ readonly reportNoSessions: {
1042
+ readonly what: (count: string, window: string) => string;
1043
+ readonly why: "A commit has a recorded session only when capture was running for the session that produced it. The window bounds this answer.";
1044
+ readonly fix: "Work with capture on, then run this again over a window that includes those commits.";
1045
+ readonly exit: 4;
1046
+ };
1047
+ /**
1048
+ * 5 — SQUASH-RESOLVED. `git blame` returns the squashed SHA on `main` while
1049
+ * every edge we hold binds a pre-squash original. Owner: `CR-109`.
1050
+ *
1051
+ * ⚠ D65 §DR5: if the blamed SHA is not a SHA we hold an edge for, the header
1052
+ * MAY NOT claim the transcript recorded it. **Both SHAs render** — a
1053
+ * single-SHA sentence here is exactly the overclaim this state exists to
1054
+ * prevent. This is GitHub's DEFAULT merge button, so it is the common path.
1055
+ */
1056
+ readonly squashResolved: {
1057
+ readonly what: (blamed: string, recorded: string) => string;
1058
+ readonly why: (blamed: string) => string;
1059
+ readonly fix: "Nothing to fix. Read the session as the record of the original commit.";
1060
+ readonly exit: 4;
1061
+ };
1062
+ /**
1063
+ * 6 — EDGE UNREADABLE. ⛔ THE SET IS NOW SIX, AND THIS IS THE FIRST ADDITION.
1064
+ * Owner: `U1`. ⛔ **FOUNDER-RATIFIED, 2026-08-31 — `D203`.** Not an
1065
+ * implementer's judgement call: three independent tripwires forbade a sixth
1066
+ * state, they all fired, and the decision to clear them was put to the
1067
+ * founder and ruled rather than argued past. `D203` carries the reasoning and
1068
+ * the cost that was spent (reversibility — a field promotes to a state later,
1069
+ * a shipped state does not un-ship).
1070
+ *
1071
+ * ⚠ The tripwires are CLOSURE TRIPWIRES, not a cap, and they did exactly what
1072
+ * they exist for: this state could not enter the set without a reviewer being
1073
+ * made to read the argument for it.
1074
+ *
1075
+ * ## ⚠ THIS IS THE ONE ABSENCE STATE THAT IS NOT AN ABSENCE OF RECORD
1076
+ *
1077
+ * The other five say some version of "we hold nothing here". This one says
1078
+ * the OPPOSITE: **we hold a link for this commit and could not render the
1079
+ * conversation behind it.** Reading it as "no session" inverts it.
1080
+ *
1081
+ * It exists because mcp used to return `no_edge` for this case, which
1082
+ * reported "this repository is captured and this commit has no linked
1083
+ * session" about a commit we demonstrably hold a link for — a false negative
1084
+ * on the only question `why` asks. `blame.ts` now emits `edge_unreadable`
1085
+ * and this is its rendering.
1086
+ *
1087
+ * ⚠ IT IS THE DIRECT-EDGE TWIN OF `squashResolved`, which already carries
1088
+ * exactly this shape one hop later (a mapping we resolved whose evidence will
1089
+ * not render). Same fact, different route to it. That symmetry is the reason
1090
+ * this is a sixth state rather than a caveat note: `attributionNote`'s shape
1091
+ * is a caveat on a screen WITH content, and this screen has none.
1092
+ *
1093
+ * ## ⚠ THE `fix` MAY NOT PROMISE A REPAIR
1094
+ *
1095
+ * CR-112 requires the remedy to say what would CHANGE the state, and here the
1096
+ * honest answer is often "nothing the reader can do": if the turn content was
1097
+ * erased, or was never uploaded, the evidence is gone and the link stays.
1098
+ * Saying "re-run capture" would be a plausible-wrong-answer (D98) — it
1099
+ * repairs neither case. So the line states the two conditions and stops.
1100
+ *
1101
+ * ⚠ NOT "corrupt", "lost", or "missing data" — all three are diagnoses of a
1102
+ * cause this client did not measure. What we know is that the server held a
1103
+ * link and returned no readable turns; the reason is the server's and is not
1104
+ * on the wire.
1105
+ */
1106
+ readonly edgeUnreadable: {
1107
+ readonly what: "A recorded session is linked to this commit, but its conversation could not be read.";
1108
+ readonly why: "The link between this commit and a session is on record. The turns behind that link came back without readable content, so there is a record here and no conversation to show for it. This is a gap in what can be displayed, not a missing link.";
1109
+ readonly fix: "Nothing to fix from here. Turn content can be unreadable because it was erased on request or was never uploaded, and neither is repaired by re-running capture.";
1110
+ readonly exit: 4;
1111
+ };
1112
+ };
1113
+ /** Failure-class copy (CR-018). Three classes: later / never / fatal. */
1114
+ export declare const ERRORS: {
1115
+ readonly notConnected: "This repository is not connected. Run `vibecommit connect`.";
1116
+ readonly networkLater: "Could not reach VibeCommit. This session will be recorded on the next turn.";
1117
+ readonly authFatal: "VibeCommit could not authenticate. Run `vibecommit connect` to reconnect.";
1118
+ readonly payloadNever: "This session was too large to record and has been skipped.";
1119
+ readonly notAGitRepo: "Not a git repository.";
1120
+ };
1121
+ /**
1122
+ * The interactive top-level catch (CR-022).
1123
+ *
1124
+ * **The hook half of the contract does not apply here** (D61 round 3 §D8). A hook
1125
+ * swallows everything and exits 0 because a non-zero hook derails the developer's
1126
+ * live turn; an interactive verb that did the same would report success for work
1127
+ * it did not do. So an unexpected exception gets §13.6's shape — what, why, fix —
1128
+ * and exit 1.
1129
+ *
1130
+ * ⚠ The `why` line says only what we actually know. "Nothing was uploaded" would
1131
+ * be a claim, not a fact: an exception can land after a send, and copy that
1132
+ * guesses in the reassuring direction is the failure the claims register exists
1133
+ * to stop.
1134
+ */
1135
+ export declare const INTERNAL: {
1136
+ readonly unexpectedWhat: "VibeCommit stopped on an unexpected error.";
1137
+ readonly unexpectedWhy: "The command did not finish.";
1138
+ readonly unexpectedFix: "Run it again with the error detail shown:";
1139
+ readonly unexpectedDebugCommand: "VIBECOMMIT_DEBUG=1 vibecommit <command>";
1140
+ };
1141
+ /**
1142
+ * Credential-loading failures (CR-016).
1143
+ *
1144
+ * §13.6's shape — what, why, fix, fix last. **The `what` line never has "you" as
1145
+ * its subject** and never carries a bare HTTP status. None of these strings
1146
+ * contains any part of a credential, and none can: the plaintext only leaves
1147
+ * `IngestCredential` through `expose()`, which no copy path calls.
1148
+ */
1149
+ export declare const CREDENTIAL: {
1150
+ readonly wrongClassWhat: "The credential set for this machine is not an ingest credential.";
1151
+ readonly wrongClassEnvWhy: "VIBECOMMIT_TOKEN is set, but its value is not a VibeCommit ingest credential. It was not sent anywhere.";
1152
+ readonly wrongClassFileWhy: "The credential on disk is not a VibeCommit ingest credential. It was not sent anywhere.";
1153
+ readonly wrongClassFix: "Mint a fresh one:";
1154
+ readonly insecureFileWhat: "The credential file is readable by other accounts on this machine.";
1155
+ readonly insecureFileWhyLabel: "file";
1156
+ readonly insecureFileModeLabel: "mode";
1157
+ readonly insecureFileWhy: "It was not read. A credential file must be readable only by its owner.";
1158
+ readonly insecureFileFix: "Restore the mode, then reconnect:";
1159
+ readonly unreadableWhat: "The credential file could not be read.";
1160
+ readonly unreadableWhy: "It exists but does not contain a credential in the expected form.";
1161
+ readonly unreadableFix: "Replace it by reconnecting:";
1162
+ };
1163
+ /**
1164
+ * The Node floor (D57 plan §DX11). `NODE_FLOOR_TEXT` and the running version are
1165
+ * interpolated by the caller so there is still exactly one copy of the sentence.
1166
+ */
1167
+ export declare const RUNTIME: {
1168
+ readonly floorWhat: "VibeCommit needs a newer Node.";
1169
+ readonly floorWhyPrefix: "This package requires Node";
1170
+ readonly floorWhyMiddle: "or newer. It is running on Node";
1171
+ readonly floorFix: "Upgrade Node, then run:";
1172
+ };
1173
+ /**
1174
+ * The `systemMessage` channel — style guide §10.1, three states, once per
1175
+ * session (D57 plan §DX3).
1176
+ *
1177
+ * ⚠ Four rules that are easy to break and expensive to notice:
1178
+ * 1. ONE LINE each. Two lines in an agent transcript reads as tool noise and
1179
+ * gets tuned out, which returns us to the failure DX3 exists to fix.
1180
+ * 2. NO SGR ESCAPES, ever. This is a JSON string field rendered by Claude
1181
+ * Code's own UI; embedded escapes are undefined behaviour (DESIGN.md §13.9).
1182
+ * 3. All three are FAILURE states. The positive fourth state (`capture
1183
+ * confirmed`) is `CR-023`, and it is a two-clause amendment — it inverts
1184
+ * "stdout empty on the happy path". Do not add it here as if it were a
1185
+ * shape-preserving addition.
1186
+ * 4. No URL longer than the line.
1187
+ */
1188
+ export declare const SYSTEM_MESSAGE: {
1189
+ readonly notConnected: "VibeCommit is installed but not connected. Run `vibecommit connect` to start capturing.";
1190
+ readonly credentialRevoked: "VibeCommit capture stopped: this machine's credential was revoked. Run `vibecommit connect`.";
1191
+ /** Templated on both versions — style guide §10.1 state 3. */
1192
+ readonly unsupportedRuntime: (floor: string, running: string) => string;
1193
+ /**
1194
+ * State 4 — style guide §10.1, and the ONLY positive one. `CR-023d`.
1195
+ *
1196
+ * Templated on the URL rather than inlining it, for the same reason
1197
+ * `unsupportedRuntime` is templated: `URLS` is declared below this object, so
1198
+ * a direct reference would read it in its temporal dead zone.
1199
+ *
1200
+ * ⚠ The style guide's example ends `.../app/commits/a8f2c91`, which is
1201
+ * ILLUSTRATIVE and not constructible here. A hook cannot know which commit a
1202
+ * session will resolve to — that mapping happens server-side and
1203
+ * asynchronously, long after this process has exited — so the generic
1204
+ * dashboard URL is substituted. See the PR.
1205
+ *
1206
+ * Claims-register: "captured" is not a blocked phrase, and it is a weaker
1207
+ * claim than the register's own boundary line ("the record is append-only for
1208
+ * users and its hash chain detects post-write tampering").
1209
+ */
1210
+ readonly captureConfirmed: (dashboardUrl: string) => string;
1211
+ };
1212
+ /**
1213
+ * The out-of-tree redaction marker — `CR-024d`, D57 §OV8d.
1214
+ *
1215
+ * User-visible: it lands in the record a customer reads back, so it clears
1216
+ * `docs/claims-register.md`. It says WHAT HAPPENED and nothing about how safe
1217
+ * anything is — no *secure / safe / protected / private / encrypted* — and it
1218
+ * claims no completeness, because OV8d's redaction is explicitly PARTIAL
1219
+ * (`capture-refactor-three-buyers.md`:387) and "complete / all / every" is a
1220
+ * blocked claim unless the count and the selection rule render inline.
1221
+ *
1222
+ * The byte count IS the count rendering inline, and "outside this repository"
1223
+ * is the selection rule. Both are literally true of the span replaced.
1224
+ *
1225
+ * Shape borrowed from the server scrubber's `[REDACTED:<kind>]` — read for its
1226
+ * shape only, never imported: this package is MIT and `vibecommit-mcp` is
1227
+ * closed, so lifting code across would be a licensing violation (D60 §D1a).
1228
+ */
1229
+ export declare const REDACTION: {
1230
+ /** Padded to the exact byte length of what it replaces — see `redact.ts`. */
1231
+ readonly head: (bytes: number) => string;
1232
+ /** Fill between head and tail. ASCII, one byte per column. */
1233
+ readonly fill: ".";
1234
+ readonly tail: "]";
1235
+ /** For spans too short for the counted form. */
1236
+ readonly short: "[OUT-OF-TREE]";
1237
+ };
1238
+ /** Commands the copy points at. One definition, so a rename cannot half-land. */
1239
+ export declare const COMMANDS: {
1240
+ readonly connect: "vibecommit connect";
1241
+ /** `CR-084d`. A FLAG, not a verb — `--help`'s verb list and its golden file do not move. */
1242
+ readonly signIn: "vibecommit connect --sign-in";
1243
+ readonly status: "vibecommit status";
1244
+ /**
1245
+ * `CR-108`. The ARGUMENT SHAPE, shown once so the usage error and the retry
1246
+ * fix line cannot drift apart — the same convention `why` uses.
1247
+ */
1248
+ readonly report: "vibecommit report --since 2026-07-08";
1249
+ /** The ref form, for the detached-HEAD refusal. */
1250
+ readonly reportRef: "vibecommit report --since 2026-07-08 --ref main";
1251
+ readonly off: "vibecommit off";
1252
+ /**
1253
+ * `CR-086`. The ARGUMENT SHAPE, shown once so the usage error and the retry
1254
+ * fix line cannot drift apart. There is no argument parser in this package —
1255
+ * `connect` reads `argv.includes("--sign-in")` and that is the whole
1256
+ * convention — so `file:line` is the smallest shape that satisfies the
1257
+ * verbatim-approved `--help` sentence, and it is the form editors and `grep`
1258
+ * already print.
1259
+ */
1260
+ readonly why: "vibecommit why src/auth/session.ts:47";
1261
+ readonly chmodCredentials: "chmod 600 ~/.vibecommit/credentials.json";
1262
+ /**
1263
+ * ⛔ THE INSTALL COMMAND, TRANSCRIBED FROM `claude-plugin/README.md:12` — and
1264
+ * it is deliberately NOT an invented update verb (`U1`, 2026-08-30).
1265
+ *
1266
+ * The skew remedy wants "get a newer client". There is **no documented update
1267
+ * command anywhere in this product**: grepped `claude-plugin/README.md`, its
1268
+ * manifests, and this package — the README documents `marketplace add` and
1269
+ * `install`, and nothing else. Printing `/plugin update vibecommit` because it
1270
+ * reads plausibly is D98's class exactly: a command-shaped string that may not
1271
+ * exist, handed to a user who is already stuck.
1272
+ *
1273
+ * So the fix line names the install command that IS documented, and
1274
+ * `WHY.skewFix` is worded as REINSTALL rather than update, which is true of
1275
+ * this string. ⚠ If a real update verb is ever documented, change both
1276
+ * together — the wording and the command are one unit.
1277
+ */
1278
+ readonly pluginInstall: "/plugin install vibecommit@vibecommit-capture";
1279
+ };
1280
+ /**
1281
+ * Every URL the CLI prints. Here rather than inline for the same reason as the
1282
+ * rest: a link is user-visible copy, the claims gate can only check what it can
1283
+ * find, and `CR-114` is moving `/app`'s information architecture underneath us.
1284
+ */
1285
+ export declare const URLS: {
1286
+ readonly dashboard: "https://vibecommit.ai/app";
1287
+ readonly settings: "https://vibecommit.ai/app/settings";
1288
+ /**
1289
+ * The captures INDEX — `CR-025`. Style guide §10.2 draws
1290
+ * `/app/commits/<a-sha>`, and that route DOES NOT EXIST: `CR-114` (W3) created
1291
+ * `app/app/commits/page.tsx`, a flat index, with no `[sha]` segment and no
1292
+ * rewrite, so a per-commit URL 404s. Verified against `vibecommit-web`
1293
+ * `origin/main` rather than assumed.
1294
+ *
1295
+ * Printing a URL that 404s at the moment of highest trust is the specific
1296
+ * failure this ending exists to avoid. `CR-101` (W10) adds the detail page;
1297
+ * deepening this constant is that task's, not a later guess.
1298
+ */
1299
+ readonly commits: "https://vibecommit.ai/app/commits";
1300
+ /**
1301
+ * Where an org member goes to see their pending capture request — `CR-110`.
1302
+ *
1303
+ * ⚠ VERIFIED BY FILE, NOT BY PROBE. Every `/app/*` path on that site returns
1304
+ * an identical 307 whether or not the route exists, so a 307 is NEVER evidence
1305
+ * a route is real. This one was confirmed by reading
1306
+ * `vibecommit-web/app/app/settings/capture-access/page.tsx` on `origin/main`,
1307
+ * with `app/api/capture-access/{policy,decide}/route.ts` beside it (`CR-035c`).
1308
+ *
1309
+ * Deeper than the style guide's drawn `/app`, deliberately: the beat's whole
1310
+ * job is to be actionable, and the generic dashboard does not show the request.
1311
+ */
1312
+ readonly captureAccess: "https://vibecommit.ai/app/settings/capture-access";
1313
+ /**
1314
+ * The public bundle that vendors the exact binary — `CR-111d`, D20, D64.
1315
+ *
1316
+ * ⚠ THE ONLY HONEST TRUST OFFER LEFT. D64 made `vibecommit-capture` PRIVATE
1317
+ * (MIT intact — the licence is the invariant, the visibility is not), so
1318
+ * linking this repo would be linking a 404 and claiming readable source would
1319
+ * be false. D20 keeps `claude-plugin` public non-negotiably.
1320
+ *
1321
+ * ⚠ AND THE SENTENCE BESIDE IT IS ONLY TRUE ONCE `CR-029e` LANDS. Measured at
1322
+ * `claude-plugin` `1349896`, `bin/` was vendored from `vibecommit-capture`
1323
+ * `8e772f1` — nine commits and eight tasks behind, with no `redact.js` at all.
1324
+ * `CR-029e` re-vendors it. Written as §10.2 draws it, with the dependency
1325
+ * disclosed in the PR rather than silently shipped.
1326
+ */
1327
+ readonly pluginBundle: "https://github.com/Vibe-Commit/claude-plugin";
1328
+ };