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