@vibe-commit/capture 0.6.1 → 0.8.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/README.md +191 -9
- package/dist/account_not_here.d.ts +15 -0
- package/dist/account_not_here.js +25 -0
- package/dist/account_not_here.js.map +1 -0
- package/dist/agent_hooks.d.ts +8 -0
- package/dist/agent_hooks.js +31 -1
- package/dist/agent_hooks.js.map +1 -1
- package/dist/agents/claude_code.d.ts +34 -0
- package/dist/agents/claude_code.js +141 -8
- package/dist/agents/claude_code.js.map +1 -1
- package/dist/agents/codex_cli.d.ts +13 -0
- package/dist/agents/codex_cli.js +241 -2
- package/dist/agents/codex_cli.js.map +1 -1
- package/dist/agents/cursor.d.ts +34 -0
- package/dist/agents/cursor.js +115 -1
- package/dist/agents/cursor.js.map +1 -1
- package/dist/agents/session_listing.d.ts +29 -0
- package/dist/agents/session_listing.js +49 -0
- package/dist/agents/session_listing.js.map +1 -0
- package/dist/agents/types.d.ts +118 -8
- package/dist/agents/types.js +17 -3
- package/dist/agents/types.js.map +1 -1
- package/dist/commands/approval_pending.d.ts +9 -0
- package/dist/commands/approval_pending.js +23 -0
- package/dist/commands/approval_pending.js.map +1 -0
- package/dist/commands/auth.d.ts +7 -0
- package/dist/commands/auth.js +52 -11
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/backfill.d.ts +160 -0
- package/dist/commands/backfill.js +748 -0
- package/dist/commands/backfill.js.map +1 -0
- package/dist/commands/browser_credential.d.ts +6 -2
- package/dist/commands/browser_credential.js +34 -14
- package/dist/commands/browser_credential.js.map +1 -1
- package/dist/commands/connect.d.ts +146 -0
- package/dist/commands/connect.js +216 -117
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/connect_account.d.ts +135 -0
- package/dist/commands/connect_account.js +321 -0
- package/dist/commands/connect_account.js.map +1 -0
- package/dist/commands/connect_picker.d.ts +27 -0
- package/dist/commands/connect_picker.js +549 -0
- package/dist/commands/connect_picker.js.map +1 -0
- package/dist/commands/context.d.ts +16 -0
- package/dist/commands/context.js +27 -0
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/credential_problem.d.ts +15 -4
- package/dist/commands/credential_problem.js +29 -22
- package/dist/commands/credential_problem.js.map +1 -1
- package/dist/commands/device_credential.d.ts +5 -1
- package/dist/commands/device_credential.js +17 -14
- package/dist/commands/device_credential.js.map +1 -1
- package/dist/commands/finalize.d.ts +95 -0
- package/dist/commands/finalize.js +249 -74
- package/dist/commands/finalize.js.map +1 -1
- package/dist/commands/finish_mint.d.ts +30 -6
- package/dist/commands/finish_mint.js +26 -6
- package/dist/commands/finish_mint.js.map +1 -1
- package/dist/commands/report.js +38 -30
- package/dist/commands/report.js.map +1 -1
- package/dist/commands/scan.d.ts +162 -0
- package/dist/commands/scan.js +507 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.d.ts +19 -1
- package/dist/commands/status.js +232 -26
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/status_hooks.d.ts +14 -0
- package/dist/commands/status_hooks.js +81 -0
- package/dist/commands/status_hooks.js.map +1 -0
- package/dist/commands/status_machine.d.ts +27 -0
- package/dist/commands/status_machine.js +210 -0
- package/dist/commands/status_machine.js.map +1 -0
- package/dist/commands/sync_reason.d.ts +17 -0
- package/dist/commands/sync_reason.js +25 -0
- package/dist/commands/sync_reason.js.map +1 -0
- package/dist/commands/why.js +44 -31
- package/dist/commands/why.js.map +1 -1
- package/dist/consent.d.ts +217 -0
- package/dist/consent.js +400 -1
- package/dist/consent.js.map +1 -1
- package/dist/copy/error_codes.d.ts +222 -0
- package/dist/copy/error_codes.js +105 -0
- package/dist/copy/error_codes.js.map +1 -0
- package/dist/copy/index.d.ts +2 -2
- package/dist/copy/index.js +2 -2
- package/dist/copy/index.js.map +1 -1
- package/dist/copy/strings.d.ts +617 -30
- package/dist/copy/strings.js +741 -36
- package/dist/copy/strings.js.map +1 -1
- package/dist/copy/time.d.ts +11 -0
- package/dist/copy/time.js +18 -0
- package/dist/copy/time.js.map +1 -1
- package/dist/credential.d.ts +5 -1
- package/dist/credential.js +2 -2
- package/dist/credential.js.map +1 -1
- package/dist/git.d.ts +43 -0
- package/dist/git.js +95 -0
- package/dist/git.js.map +1 -1
- package/dist/grants_sync.d.ts +165 -0
- package/dist/grants_sync.js +289 -0
- package/dist/grants_sync.js.map +1 -0
- package/dist/grants_verb.d.ts +70 -0
- package/dist/grants_verb.js +121 -0
- package/dist/grants_verb.js.map +1 -0
- package/dist/hooks/entry.d.ts +18 -1
- package/dist/hooks/entry.js +117 -14
- package/dist/hooks/entry.js.map +1 -1
- package/dist/hooks/post_commit.js +3 -3
- package/dist/hooks/post_commit.js.map +1 -1
- package/dist/hooks/post_rewrite.js +6 -4
- package/dist/hooks/post_rewrite.js.map +1 -1
- package/dist/hooks/session_grants.d.ts +59 -0
- package/dist/hooks/session_grants.js +91 -0
- package/dist/hooks/session_grants.js.map +1 -0
- package/dist/hooks/session_start.d.ts +114 -0
- package/dist/hooks/session_start.js +243 -0
- package/dist/hooks/session_start.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +76 -11
- package/dist/index.js.map +1 -1
- package/dist/install.d.ts +57 -0
- package/dist/install.js +54 -20
- package/dist/install.js.map +1 -1
- package/dist/json.d.ts +32 -9
- package/dist/json.js +35 -8
- package/dist/json.js.map +1 -1
- package/dist/machine_state.d.ts +52 -0
- package/dist/machine_state.js +63 -0
- package/dist/machine_state.js.map +1 -0
- package/dist/oauth/retire.d.ts +10 -0
- package/dist/oauth/retire.js +13 -0
- package/dist/oauth/retire.js.map +1 -1
- package/dist/oauth/session.d.ts +1 -0
- package/dist/org_pending.d.ts +52 -0
- package/dist/org_pending.js +109 -0
- package/dist/org_pending.js.map +1 -0
- package/dist/paths.d.ts +70 -0
- package/dist/paths.js +90 -0
- package/dist/paths.js.map +1 -1
- package/dist/pending_revoke.d.ts +117 -0
- package/dist/pending_revoke.js +233 -0
- package/dist/pending_revoke.js.map +1 -0
- package/dist/pending_revoke_file.d.ts +55 -0
- package/dist/pending_revoke_file.js +74 -0
- package/dist/pending_revoke_file.js.map +1 -0
- package/dist/picker.d.ts +208 -0
- package/dist/picker.js +526 -0
- package/dist/picker.js.map +1 -0
- package/dist/picker_checklist.d.ts +141 -0
- package/dist/picker_checklist.js +411 -0
- package/dist/picker_checklist.js.map +1 -0
- package/dist/policy.d.ts +70 -1
- package/dist/policy.js +68 -2
- package/dist/policy.js.map +1 -1
- package/dist/post.d.ts +91 -18
- package/dist/post.js +259 -27
- package/dist/post.js.map +1 -1
- package/dist/state.d.ts +6 -0
- package/dist/state.js +38 -1
- package/dist/state.js.map +1 -1
- package/dist/system_message.d.ts +32 -2
- package/dist/system_message.js +47 -5
- package/dist/system_message.js.map +1 -1
- package/dist/term.d.ts +28 -2
- package/dist/term.js +23 -2
- package/dist/term.js.map +1 -1
- package/dist/verb_help.d.ts +13 -0
- package/dist/verb_help.js +40 -0
- package/dist/verb_help.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -9
package/dist/copy/strings.js
CHANGED
|
@@ -28,12 +28,20 @@ export const HELP = {
|
|
|
28
28
|
commands: {
|
|
29
29
|
// `CR-216/U2`. FIRST in the list by habit, not by sequence: that "nothing for `connect` to use until
|
|
30
30
|
// this has run" reason has been false since `CR-226` (`connect` signs in itself on a machine with no
|
|
31
|
-
// credential). `connect` is the golden path; this verb renews or replaces the credential, and
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
auth: "Renew or replace this machine's credential.
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
// credential). `connect` is the golden path; this verb renews or replaces the credential, and on a terminal it
|
|
32
|
+
// then continues into `connect` — inside a git work tree, or (0.7.0, D32) outside one on a machine that captures
|
|
33
|
+
// nothing yet. With `--stdin` or no terminal it saves the credential and stops there.
|
|
34
|
+
auth: "Renew or replace this machine's credential. On a terminal it then goes on to connect: in a git repo, or on a machine that captures nothing yet. Otherwise it only saves the credential.",
|
|
35
|
+
// 0.7.0 (D34): from any folder. A repo with a remote is chosen in the picker; a repo with no remote keeps the old flow.
|
|
36
|
+
connect: "Choose the repositories to capture, from any folder, and connect them. On a machine with no credential it signs you in after you say yes, unless you choose g, which signs in first to read your account's grants.",
|
|
37
|
+
// `D33 · 27A`. A public verb so an AI agent can list capturable repos without entering a consent
|
|
38
|
+
// flow. It opens no session file and changes no setting, which is what lets it be run before `connect`.
|
|
39
|
+
// ⚠ NOT "changes nothing" (PR 1 F1): opening Codex's index read-only makes SQLite create its own
|
|
40
|
+
// `-wal`/`-shm` journal files beside it, as any SQLite reader does, so the line claims only what is true.
|
|
41
|
+
scan: "List the repositories coding agents have been used in on this machine. Uploads nothing and changes no setting.",
|
|
42
|
+
// `D14`/`D15`. Offered by `connect` too. Asks first (default N) and says what it will not do.
|
|
43
|
+
backfill: "Upload earlier coding-agent sessions still on this machine, for the repositories you chose. Asks first; no commit links.",
|
|
44
|
+
status: "Show whether capture is on for this repo, or for this machine outside a repo, and when it last succeeded.",
|
|
37
45
|
off: "Stop capturing for this repo.",
|
|
38
46
|
// VERBATIM-APPROVED (D61 §PS6). Any reword needs a new claims-register row.
|
|
39
47
|
why: "Show the conversation turns recorded against the commit that last changed this line.",
|
|
@@ -42,6 +50,117 @@ export const HELP = {
|
|
|
42
50
|
finalize: "Close a finished session's last turn so commits made in it can be recorded.",
|
|
43
51
|
},
|
|
44
52
|
docsUrl: "https://vibecommit.ai/docs/what-we-upload",
|
|
53
|
+
/**
|
|
54
|
+
* PR 6 item 2 (devex review): what `<verb> --help` prints BEYOND the one sentence above, which stays the first
|
|
55
|
+
* paragraph. Agents read `--help` first, and until now the flags and exit codes existed only in the README.
|
|
56
|
+
*
|
|
57
|
+
* ⚠ Every flag here is one the verb's own argv reader acts on (`src/index.ts`, and each verb's parser), and every exit
|
|
58
|
+
* code is one the verb returns (`src/exit.ts`). A flag the verb ignores, or a code it never returns, would be copy no
|
|
59
|
+
* gate can check, so `test/verb-help.test.ts` ties each list to the source it describes.
|
|
60
|
+
*/
|
|
61
|
+
verbs: {
|
|
62
|
+
connect: {
|
|
63
|
+
usage: "vibecommit connect [--device] [--sign-in] [--typed]",
|
|
64
|
+
options: [
|
|
65
|
+
["--device", "Sign in with a code approved on another device, for a machine with no browser."],
|
|
66
|
+
["--sign-in", "Sign in through the browser before connecting."],
|
|
67
|
+
["--typed", "Use the typed prompt even on a terminal, for a screen reader."],
|
|
68
|
+
],
|
|
69
|
+
exits: [
|
|
70
|
+
["0", "Done."],
|
|
71
|
+
["1", "Failed, or a replacement credential was declined."],
|
|
72
|
+
["2", "Bad arguments, or no person at a terminal to answer the consent question."],
|
|
73
|
+
["3", "Not connected: this machine's credential is not accepted."],
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
auth: {
|
|
77
|
+
usage: "vibecommit auth [--stdin | --paste | --device]",
|
|
78
|
+
options: [
|
|
79
|
+
["--stdin", "Read the credential from standard input and stop after saving it."],
|
|
80
|
+
["--paste", "Skip the browser and paste a credential made in Settings."],
|
|
81
|
+
["--device", "Sign in with a code approved on another device."],
|
|
82
|
+
],
|
|
83
|
+
exits: [
|
|
84
|
+
["0", "Credential saved."],
|
|
85
|
+
["1", "Nothing was saved."],
|
|
86
|
+
["2", "Bad arguments, such as a credential passed as an argument."],
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
scan: {
|
|
90
|
+
usage: "vibecommit scan [--json]",
|
|
91
|
+
options: [["--json", "Print one JSON document instead of text."]],
|
|
92
|
+
exits: [
|
|
93
|
+
["0", "Repositories found."],
|
|
94
|
+
["4", "None found."],
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
backfill: {
|
|
98
|
+
usage: "vibecommit backfill",
|
|
99
|
+
options: [],
|
|
100
|
+
exits: [
|
|
101
|
+
["0", "Done, declined at the question, or interrupted with Ctrl+C. Sessions held for an organization's approval count as done."],
|
|
102
|
+
["1", "The server stopped the run, could not be reached or refused this machine's credential, or it could not run. Nothing already sent is lost; run it again to resume."],
|
|
103
|
+
["2", "No terminal, or an argument was given."],
|
|
104
|
+
["3", "Not connected."],
|
|
105
|
+
["4", "Nothing to upload."],
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
status: {
|
|
109
|
+
usage: "vibecommit status [--json]",
|
|
110
|
+
options: [["--json", "Print one JSON document instead of text."]],
|
|
111
|
+
exits: [
|
|
112
|
+
["0", "Reported."],
|
|
113
|
+
["1", "Failed."],
|
|
114
|
+
["2", "Bad arguments."],
|
|
115
|
+
["3", "Not connected."],
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
why: {
|
|
119
|
+
usage: "vibecommit why <file>:<line> [--json]",
|
|
120
|
+
options: [["--json", "Print one JSON document instead of text."]],
|
|
121
|
+
exits: [
|
|
122
|
+
["0", "Answered."],
|
|
123
|
+
["1", "Failed."],
|
|
124
|
+
["2", "Bad arguments."],
|
|
125
|
+
["3", "Not connected."],
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
report: {
|
|
129
|
+
usage: "vibecommit report --since <date> [--ref <ref>] [--json]",
|
|
130
|
+
options: [
|
|
131
|
+
["--since", "Start of the window, as YYYY-MM-DD. Required."],
|
|
132
|
+
["--ref", "The branch or commit to count. Default: the current branch."],
|
|
133
|
+
["--json", "Print one JSON document instead of text."],
|
|
134
|
+
],
|
|
135
|
+
exits: [
|
|
136
|
+
["0", "Reported."],
|
|
137
|
+
["1", "Failed."],
|
|
138
|
+
["2", "Bad arguments."],
|
|
139
|
+
["3", "Not connected."],
|
|
140
|
+
["4", "The window is empty: nothing to report."],
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
finalize: {
|
|
144
|
+
usage: "vibecommit finalize --session <id> | --latest [--force]",
|
|
145
|
+
options: [
|
|
146
|
+
["--session", "The session to close."],
|
|
147
|
+
["--latest", "The most recent session, when only one is recent."],
|
|
148
|
+
["--force", "Skip the check that the transcript has stopped changing. Only for a session that is over."],
|
|
149
|
+
],
|
|
150
|
+
exits: [
|
|
151
|
+
["0", "Closed: the server confirmed the last turn is recorded."],
|
|
152
|
+
["1", "Not confirmed closed: not a git repository, a transcript that cannot be read, a credential that is no longer accepted, or a delivery that failed."],
|
|
153
|
+
["2", "Nothing was sent: bad arguments, an unknown or still-running session, or several recent sessions for --latest."],
|
|
154
|
+
["3", "Not connected: this repo is not captured, or this machine has no credential."],
|
|
155
|
+
["5", "Delivered, but the server did not confirm it. Treat it as not closed."],
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
/** The two section labels of a verb's help. */
|
|
160
|
+
optionsLabel: "Options:",
|
|
161
|
+
/** A verb with no flags of its own (`backfill`) says so under the label, so "Options:" never sits over nothing. */
|
|
162
|
+
noOptions: "None. It takes no arguments.",
|
|
163
|
+
exitCodesLabel: "Exit codes:",
|
|
45
164
|
};
|
|
46
165
|
/**
|
|
47
166
|
* The ENTRY POINT's own chrome and its two usage failures — `CR-090d`.
|
|
@@ -74,8 +193,14 @@ export const USAGE = {
|
|
|
74
193
|
usageLabel: "Usage:",
|
|
75
194
|
commandsLabel: "Commands:",
|
|
76
195
|
docsLabel: "Docs:",
|
|
77
|
-
/**
|
|
78
|
-
|
|
196
|
+
/**
|
|
197
|
+
* The unknown-verb block (PR 7, Q6: `UNKNOWN_COMMAND`, routed through the shared renderer). The help text is no longer
|
|
198
|
+
* dumped after it, so the fix — the way to the list of commands — is the last thing on screen. The verb is whatever
|
|
199
|
+
* the user typed, so it is cut to a length the line can hold.
|
|
200
|
+
*/
|
|
201
|
+
unknownCommandWhat: (verb) => `\`${verb.length > 40 ? `${verb.slice(0, 39)}…` : verb}\` is not a vibecommit command.`,
|
|
202
|
+
unknownCommandFix: "See the commands:",
|
|
203
|
+
unknownCommandHelp: "vibecommit --help",
|
|
79
204
|
};
|
|
80
205
|
/** `vibecommit status` (CR-021). */
|
|
81
206
|
/**
|
|
@@ -108,6 +233,52 @@ export const STATUS = {
|
|
|
108
233
|
* changed them to match the screen rather than changing the screen.
|
|
109
234
|
*/
|
|
110
235
|
repoLabel: "repo",
|
|
236
|
+
/** 0.7.0, D6 · 2A: why this repo is on. One row under `repo`, only inside a repo that is on. */
|
|
237
|
+
basisLabel: "basis",
|
|
238
|
+
basisOwner: (owner) => `owner grant ${owner}/`,
|
|
239
|
+
basisRepo: "repo grant",
|
|
240
|
+
basisClone: "per clone",
|
|
241
|
+
/**
|
|
242
|
+
* Slice 2 (S4), brief §3.3: inside a repo that is NOT on, the account may still grant it. The account never admits a repo on
|
|
243
|
+
* its own (R3-22): naming it here is `connect`'s picker, and the action row already offers `connect`.
|
|
244
|
+
*/
|
|
245
|
+
basisAccountOnly: "granted on your account, not on this machine",
|
|
246
|
+
/**
|
|
247
|
+
* Slice 2 (S4), brief §3.3: the machine screen's account rows, after `off` and before `held`. `on account` lists what the
|
|
248
|
+
* user's account grants that THIS machine has not named (an owner as `owner/`); the second line says what to do about it.
|
|
249
|
+
*/
|
|
250
|
+
accountLabel: "on account",
|
|
251
|
+
accountNotHere: (connect) => `not on this machine · to capture them here, run: ${connect}`,
|
|
252
|
+
/** Changes queued for the account that no sync has delivered yet. They go out with the next sync; only `connect`, `status`, `off` and a terminal `auth` sync. */
|
|
253
|
+
pendingLabel: "pending",
|
|
254
|
+
pendingValue: (n) => `${n} ${n === 1 ? "change" : "changes"} not yet sent to your account · sent at the next sync`,
|
|
255
|
+
/**
|
|
256
|
+
* VQ's ruling (ii): a queued change the server refused with 400 is kept (a consent statement is never dropped) but a retry
|
|
257
|
+
* fails again, so this row does not say "the next command retries". The URL is the one the web app's error state uses.
|
|
258
|
+
*/
|
|
259
|
+
pendingRejectedValue: (n) => `${n} ${n === 1 ? "change" : "changes"} your account didn't accept · report this: github.com/Vibe-Commit/claude-plugin/issues`,
|
|
260
|
+
/**
|
|
261
|
+
* The first sync uploads what this machine already named (per-clone consents made under 0.6.x/0.7.0 reach the account, R3-21).
|
|
262
|
+
* Slugs only, never paths; more than three names shows two and "and N more", and the full list follows, one per line. The names arrive as ONE string, a name per line, because the copy corpus renders every template with string arguments.
|
|
263
|
+
*/
|
|
264
|
+
accountSaving: (namesOnePerLine) => {
|
|
265
|
+
const names = namesOnePerLine.split("\n");
|
|
266
|
+
return `Saving this machine's repository choices to your VibeCommit account: ${names.length > 3 ? `${names.slice(0, 2).join(", ")} and ${names.length - 2} more` : names.join(", ")}.`;
|
|
267
|
+
},
|
|
268
|
+
/** The machine-level screen (any folder that is not a work tree). */
|
|
269
|
+
offMachine: "Capture is not on for any repository on this machine.",
|
|
270
|
+
ownersLabel: "owners",
|
|
271
|
+
reposLabel: "repos",
|
|
272
|
+
offLabel: "off",
|
|
273
|
+
/**
|
|
274
|
+
* Machine-level `status`: the repos whose own last upload was refused because their org has not approved capture. §10.2's held
|
|
275
|
+
* vocabulary — the beat word `held`, and the backfill screen's "the org must approve capture" (Alexander, 2026-10-24 17:20).
|
|
276
|
+
*/
|
|
277
|
+
heldLabel: "held",
|
|
278
|
+
heldReason: "the org must approve capture",
|
|
279
|
+
ownerTail: "all its repos, including ones you clone later",
|
|
280
|
+
changeLabel: "Change what is captured",
|
|
281
|
+
turnOffInside: "Turn one repo off, run inside it",
|
|
111
282
|
lastSuccessLabel: "last sent",
|
|
112
283
|
dashboardLabel: "dashboard",
|
|
113
284
|
/** `<slug> (git toplevel <path>)` — §10.3's repo row. */
|
|
@@ -128,6 +299,21 @@ export const STATUS = {
|
|
|
128
299
|
* after the rows, with the glyph turned `warn`. The date is absolute and in UTC.
|
|
129
300
|
*/
|
|
130
301
|
credentialEnding: (date, authCommand) => `This machine's credential ends ${date}. Run \`${authCommand}\` to renew it.`,
|
|
302
|
+
/**
|
|
303
|
+
* 0.7.0. A `grants.json` that is present and unreadable denies capture for every clone that has a remote
|
|
304
|
+
* (`consent.ts`'s `isCaptureAllowed`), and a hook cannot say so, so `status` does. The file name is the user's
|
|
305
|
+
* to fix; `connect` is where the next consent lands once it is readable.
|
|
306
|
+
*/
|
|
307
|
+
grantsUnreadable: (connect) => `~/.vibecommit/grants.json can't be read, so capture is stopped on this machine for every repository that has a remote. Fix the file or move it aside, then run \`${connect}\`.`,
|
|
308
|
+
/**
|
|
309
|
+
* 0.7.0, `SessionStart` (T21). `npm i -g` alone never adds the event to an agent's config, so a 0.6.x machine is
|
|
310
|
+
* missing it until `connect` runs. Only said on the npm channel: the plugin ships its own `hooks.json`, and its
|
|
311
|
+
* contract (no `SessionStart`, no git hooks) is unchanged.
|
|
312
|
+
*/
|
|
313
|
+
sessionStartMissing: (agents, connect) => `${agents} will not add commit hooks to a new clone: VibeCommit's SessionStart hook is not registered there. Run \`${connect}\` to register it.`,
|
|
314
|
+
/** 0.7.0, D4 · A. A hook the user deleted stays deleted; `connect` is the watched way back. */
|
|
315
|
+
commitHooksOff: (connect) => `Commit hooks are off in this repository: sessions are captured, commits are not linked to them. Run \`${connect}\` here to add them.`,
|
|
316
|
+
commitHooksForeign: (connect) => `This clone has its own commit hook, so VibeCommit's is not installed here. Run \`${connect}\` here to chain it.`,
|
|
131
317
|
credentialShadowed: "VIBECOMMIT_TOKEN is set and takes precedence over the credential saved on this machine. Unset it to use the saved one.",
|
|
132
318
|
/**
|
|
133
319
|
* `CR-228`, `TODOS[142]` — bytes the client ADVANCED PAST without delivering.
|
|
@@ -154,8 +340,8 @@ export const STATUS = {
|
|
|
154
340
|
* turn only when a following prompt arrives, so nothing will ever seal this one and the commits made in
|
|
155
341
|
* it cannot be recorded. States the consequence and the command; promises nothing about the commits.
|
|
156
342
|
*/
|
|
157
|
-
openTail: (sessions) => `${sessions === 1 ? "1 session has" : `${sessions} sessions have`} ended with the last turn still ` +
|
|
158
|
-
|
|
343
|
+
openTail: (sessions, finalizeLatest) => `${sessions === 1 ? "1 session has" : `${sessions} sessions have`} ended with the last turn still ` +
|
|
344
|
+
`open, so commits made in that turn are not recorded. Run \`${finalizeLatest}\` to close it.`,
|
|
159
345
|
/**
|
|
160
346
|
* `TODOS[145]` — commits the client has seen and is holding because no turn has closed yet. Normal
|
|
161
347
|
* mid-session; shown as a fact, not as a fault.
|
|
@@ -259,12 +445,12 @@ export const STATUS = {
|
|
|
259
445
|
* cannot tell a crashed session from one idle at lunch, and closing a session still in progress seals
|
|
260
446
|
* a turn that was not actually over.
|
|
261
447
|
*/
|
|
262
|
-
heldIdle: (bytes, sessions, commits, ids = []) => {
|
|
448
|
+
heldIdle: (bytes, sessions, commits, finalizeSession, ids = []) => {
|
|
263
449
|
const who = sessions === 1 ? `session ${namedIds(ids)}` : `${sessions} sessions (${namedIds(ids)})`;
|
|
264
450
|
const commitsClause = commits > 0 ? `, including ${commits} ${commits === 1 ? "commit" : "commits"}` : "";
|
|
265
451
|
const action = sessions === 1
|
|
266
|
-
? `If that session is finished,
|
|
267
|
-
:
|
|
452
|
+
? `If that session is finished, \`${finalizeSession} ${ids[0]}\` tries to send them and, if that works, closes it`
|
|
453
|
+
: `If a session above is finished, \`${finalizeSession} <id>\` tries to send its bytes and, if that works, closes it`;
|
|
268
454
|
return (`${BYTE_COUNT.format(bytes)} ${bytes === 1 ? "byte" : "bytes"} held from ${who}${commitsClause}, with no hook in over 30 ` +
|
|
269
455
|
`minutes. ${action}; anything held past 24 hours is written off instead.`);
|
|
270
456
|
},
|
|
@@ -299,7 +485,18 @@ export const CONNECT = {
|
|
|
299
485
|
* the nouns are transcribed rather than reworded: *"the output of commands it
|
|
300
486
|
* ran"*, never *"Bash tool results"*.
|
|
301
487
|
*/
|
|
302
|
-
consentHeading: "VibeCommit uploads your
|
|
488
|
+
consentHeading: "VibeCommit uploads your coding-agent transcripts for this repo.",
|
|
489
|
+
/**
|
|
490
|
+
* D23 · 17A: the picker path's heading (every `connect` that shows the picker, in a repo or not). `consentHeading`
|
|
491
|
+
* above remains only on the no-remote (`local:`) in-repo path, where there is nothing to pick (D35 · 29A: "Claude
|
|
492
|
+
* Code transcripts" understated what uploads once `connect` also wired Codex and Cursor hooks, 0.2.0).
|
|
493
|
+
*/
|
|
494
|
+
consentHeadingPicker: "VibeCommit uploads your coding-agent transcripts for the repositories you choose.",
|
|
495
|
+
/**
|
|
496
|
+
* ONE sentence, not a paragraph, added on the picker path only (design doc step 2, "The disclosure"): what the grant
|
|
497
|
+
* covers beyond the repos named on screen. The four paragraphs under either heading stay byte-identical.
|
|
498
|
+
*/
|
|
499
|
+
consentScope: "The choice covers future clones of these repositories too, adds commit hooks to each clone where it can, and is saved to your VibeCommit account so your other machines running VibeCommit 0.8.0 or later can offer it.",
|
|
303
500
|
/**
|
|
304
501
|
* ⚠ "private-repo paths" IS THE APPROVED WORDING, and it is the reason
|
|
305
502
|
* `private` is deliberately NOT registered in `claims.ts`: a substring gate on
|
|
@@ -407,11 +604,33 @@ export const CONNECT = {
|
|
|
407
604
|
// --- Mechanism copy (CR-016). ---
|
|
408
605
|
/** The `[y/N]` suffix. The capital is the default and the default is NO. */
|
|
409
606
|
consentAnswers: "[y/N]",
|
|
410
|
-
/**
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
607
|
+
/**
|
|
608
|
+
* Non-TTY refusal — DESIGN.md §13.7 exit 2, D65 §DR7. PR 6 item 6: written for an AGENT that hit it, which is who reaches
|
|
609
|
+
* this branch. It says who must answer ("a person"), that an agent cannot, and what to relay, and it carries the code
|
|
610
|
+
* `CONSENT_NEEDS_TTY` for a wrapper to key on — PR 7 registered it and renders it on the block's own code line, so it is not
|
|
611
|
+
* repeated in the sentence. No `--yes`: that would bypass consent (D65 §DR7). The `what` line has no "you" (§13.6).
|
|
612
|
+
*/
|
|
613
|
+
nonInteractiveWhat: "Consent needs a person at a terminal.",
|
|
614
|
+
nonInteractiveWhy: "An agent cannot answer it, and standard input is not a terminal. Nothing was read and nothing was uploaded.",
|
|
615
|
+
/** A repo with no remote has a folder to name; the label is the fix and the command follows it. */
|
|
616
|
+
nonInteractiveFix: (folder) => `Ask the user to run this in ${folder}:`,
|
|
617
|
+
/**
|
|
618
|
+
* The picker path's refusal (VQ + VT, PR 6 item 6 fit): it names no folder (the picker is not about one repo), has no
|
|
619
|
+
* "you" in the `what` line, and is reached BEFORE the scan beat, so "Nothing was read and nothing was uploaded" stays
|
|
620
|
+
* literally true.
|
|
621
|
+
*/
|
|
622
|
+
pickerNonInteractiveWhat: "Choosing repositories needs a person at a terminal.",
|
|
623
|
+
pickerNonInteractiveWhy: "An agent cannot answer it, and standard input is not a terminal, so no repository can be chosen and consent cannot be asked. Nothing was read and nothing was uploaded.",
|
|
624
|
+
pickerNonInteractiveFix: "Ask the user to run this in a terminal:",
|
|
414
625
|
alreadyConnected: "Capture is already on for this repository.",
|
|
626
|
+
/**
|
|
627
|
+
* 0.7.0. `grants.json` is present and unreadable, which denies this repository whatever consent it has (see
|
|
628
|
+
* `consent.ts`'s `isCaptureAllowed`). Answering the consent prompt could not change that, so `connect` stops
|
|
629
|
+
* BEFORE asking rather than take a yes it cannot honour.
|
|
630
|
+
*/
|
|
631
|
+
grantsUnreadableWhat: "Capture can't be turned on: ~/.vibecommit/grants.json can't be read.",
|
|
632
|
+
grantsUnreadableWhy: "Capture is stopped on this machine for every repository that has a remote until the file is readable. Nothing was changed.",
|
|
633
|
+
grantsUnreadableFixLabel: "Fix the file or move it aside, then run:",
|
|
415
634
|
/**
|
|
416
635
|
* The browser sign-in beat (PKCE loopback → minted ingest credential) is the
|
|
417
636
|
* oauth lane's, not this task's. Until it lands, the supported path is the one
|
|
@@ -531,12 +750,12 @@ export const COMMIT_HOOK = {
|
|
|
531
750
|
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.`,
|
|
532
751
|
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.`,
|
|
533
752
|
already: "Commit capture was already on for this clone.",
|
|
534
|
-
perClone:
|
|
753
|
+
perClone: (connect) => `Run \`${connect}\` in each clone you want commits recorded for — git hooks are per-clone, not per-account.`,
|
|
535
754
|
hooksPathWhat: "Commit capture could NOT be installed for this clone.",
|
|
536
755
|
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.`,
|
|
537
756
|
hooksPathFixLabel: "Fix, either one:",
|
|
538
757
|
hooksPathFixUnset: "git config --unset core.hooksPath",
|
|
539
|
-
hooksPathFixManual:
|
|
758
|
+
hooksPathFixManual: (postCommit) => `or add \`${postCommit}\` to your existing post-commit hook`,
|
|
540
759
|
failedWhat: "Commit capture could NOT be installed for this clone.",
|
|
541
760
|
failedWhy: (why) => `Writing the \`post-commit\` hook failed: ${why}. Transcript capture is unaffected; only commit observation is.`,
|
|
542
761
|
};
|
|
@@ -567,7 +786,7 @@ export const AGENT_HOOKS = {
|
|
|
567
786
|
refusedWhat: (agent) => `Session capture could NOT be set up for ${agent}.`,
|
|
568
787
|
refusedWhy: (path, why) => `${path} could not be read as JSON (${why}), and it was left exactly as it is. Overwriting a file we cannot parse would discard settings we cannot see.`,
|
|
569
788
|
refusedFixLabel: "Fix:",
|
|
570
|
-
refusedFix: (path) => `check ${path} for a syntax error, then run
|
|
789
|
+
refusedFix: (path, connect) => `check ${path} for a syntax error, then run \`${connect}\` again`,
|
|
571
790
|
failedWhat: (agent) => `Session capture could NOT be set up for ${agent}.`,
|
|
572
791
|
failedWhy: (path, why) => `Writing ${path} failed: ${why}. Commit capture and any agent already configured are unaffected.`,
|
|
573
792
|
};
|
|
@@ -697,10 +916,391 @@ export const SIGNIN = {
|
|
|
697
916
|
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.",
|
|
698
917
|
busyFix: "Run it again in a moment:",
|
|
699
918
|
};
|
|
919
|
+
/**
|
|
920
|
+
* `vibecommit scan` — design review D17 · 11A, D26, D27, D33 · 27A, and the screen VQ drew for it
|
|
921
|
+
* (style guide §10.13) on 2026-10-24.
|
|
922
|
+
*
|
|
923
|
+
* ⛔ **The nouns are the agent-neutral ones** (D11 · A, D35): "coding agent", never "Claude Code
|
|
924
|
+
* session", because the scan covers three agents and the picker's heading says the same. The agent
|
|
925
|
+
* NAMES appear only on a repo's second line, where they are facts about which store a count came from.
|
|
926
|
+
*
|
|
927
|
+
* ⚠ **Every sentence here is a claims-register row** (the register is `code`'s, VR's PR). None says
|
|
928
|
+
* "every" or "all": the scan counts transcripts still on disk, and what `cleanupPeriodDays` already
|
|
929
|
+
* deleted is not among them.
|
|
930
|
+
*/
|
|
931
|
+
export const SCAN = {
|
|
932
|
+
/** The beat line, drawn in the existing beat shape and overwritten once with `done` (D17). */
|
|
933
|
+
beat: "Looking for repositories on this machine...",
|
|
934
|
+
beatDone: "done",
|
|
935
|
+
found: (count) => `Found ${count} ${count === 1 ? "repository" : "repositories"} you've used a coding agent in on this machine.`,
|
|
936
|
+
sessions: (count) => `${count} ${count === 1 ? "session" : "sessions"}`,
|
|
937
|
+
since: (date) => `since ${date}`,
|
|
938
|
+
/** What an agent is called on a repo's second line. The ids are wire values, not labels. */
|
|
939
|
+
agent: { "claude-code": "Claude Code", "codex-cli": "Codex", cursor: "Cursor" },
|
|
940
|
+
notListed: "Not listed here:",
|
|
941
|
+
noRemote: (count, connect) => count === 1 ? `1 repo with no remote: run \`${connect}\` inside it` : `${count} repos with no remote: run \`${connect}\` inside each`,
|
|
942
|
+
/**
|
|
943
|
+
* The picker's own line (PR 1 ruling 2): ONE count and ONE line for a folder with two or more
|
|
944
|
+
* candidate paths, a hashed name (cut, so it cannot be inverted) and a deleted sibling. Claude Code's
|
|
945
|
+
* and Cursor's only — a Codex row records its real `cwd` and is never ambiguous.
|
|
946
|
+
*/
|
|
947
|
+
ambiguous: (count) => count === 1
|
|
948
|
+
? "1 session in a folder that can't be matched to one repo"
|
|
949
|
+
: `${count} sessions in folders that can't be matched to one repo`,
|
|
950
|
+
/** Scan-only (ruling 5a): a folder, or a Codex `cwd`, that exists but is not inside a git work tree. */
|
|
951
|
+
nonRepo: (count) => count === 1
|
|
952
|
+
? "1 session in a folder that isn't a git repository"
|
|
953
|
+
: `${count} sessions in folders that aren't git repositories`,
|
|
954
|
+
missingFolder: (count) => count === 1
|
|
955
|
+
? "1 session in a folder that no longer exists"
|
|
956
|
+
: `${count} sessions in folders that no longer exist`,
|
|
957
|
+
/**
|
|
958
|
+
* ⛔ CLAIMS ONLY WHAT GIT SHOWS (ruling 7). The test is a session's mtime against the OLDEST reflog entry,
|
|
959
|
+
* and `gc.reflogExpire` prunes a long-lived clone's oldest entries, so "older than the reflog" is the fact
|
|
960
|
+
* and "the folder was reused" is a possibility — the same repo deleted and cloned again reads the same.
|
|
961
|
+
*/
|
|
962
|
+
pathReused: (count) => count === 1
|
|
963
|
+
? "1 session older than its clone's reflog (possibly a reused folder)"
|
|
964
|
+
: `${count} sessions older than their clone's reflog (possibly a reused folder)`,
|
|
965
|
+
/** One clone: the path rides the line. More: this heading, then one indented path per clone. */
|
|
966
|
+
noReflogOne: (path) => `1 clone with no reflog: ${path} (backfill skips it)`,
|
|
967
|
+
noReflogMany: (count) => `${count} clones with no reflog (backfill skips them):`,
|
|
968
|
+
/** Rollouts on disk that no index row names (ruling 5c) — counted, never opened. */
|
|
969
|
+
unindexed: (count) => count === 1
|
|
970
|
+
? "1 Codex session missing from its thread index"
|
|
971
|
+
: `${count} Codex sessions missing from its thread index`,
|
|
972
|
+
/**
|
|
973
|
+
* `Codex sessions couldn't be read (unrecognised index)`, one line per unreadable agent (R3-15). The
|
|
974
|
+
* bracket names a cause the user can act on; a plain read failure (ruling 6) has none to name, so it
|
|
975
|
+
* gets none. The `--json` `reason` is the same three words either way.
|
|
976
|
+
*/
|
|
977
|
+
unreadable: (agent, reason) => reason === null
|
|
978
|
+
? `${agent} sessions couldn't be read`
|
|
979
|
+
: `${agent} sessions couldn't be read (${reason})`,
|
|
980
|
+
next: "Choose what is captured:",
|
|
981
|
+
/** Exit 4. Both screens end on the same command. */
|
|
982
|
+
none: "No coding-agent sessions were found on this machine.",
|
|
983
|
+
/**
|
|
984
|
+
* Exit 4 with `Not listed here:` lines: sessions exist that could not be listed, so `none` would be
|
|
985
|
+
* untrue. `none` prints only when that block is empty, i.e. the scan saw no session at all (VQ, PR 1
|
|
986
|
+
* ruling 3).
|
|
987
|
+
*/
|
|
988
|
+
noneListable: "No repository could be listed from the sessions on this machine.",
|
|
989
|
+
noneNext: "Choose owners or repos anyway:",
|
|
990
|
+
};
|
|
991
|
+
/**
|
|
992
|
+
* `vibecommit backfill` — style guide §10.14, design review D14 · 8A, D15 · 9A; capture 0.7.0, PR 5.
|
|
993
|
+
*
|
|
994
|
+
* Every sentence here is a claims-register row. ⛔ Three promises it must never make: that a backfilled session is
|
|
995
|
+
* LINKED to a commit (it arrives without links, and the copy says so), that "every" or "all" earlier sessions are
|
|
996
|
+
* uploaded (only the ones still on this machine and not yet sent), and that a held session will upload by itself (no
|
|
997
|
+
* hook fires for an old session, so the held line points at a re-run).
|
|
998
|
+
*
|
|
999
|
+
* The two folder reasons are different facts (VQ ruling): `folder differs` is a RECORDED folder that is not this clone,
|
|
1000
|
+
* `folder not recorded` is a transcript that records none.
|
|
1001
|
+
*/
|
|
1002
|
+
export const BACKFILL = {
|
|
1003
|
+
/**
|
|
1004
|
+
* Inside `connect` the repos are the ones it just showed, so "these"; run on its own there is no "these", so it names whose
|
|
1005
|
+
* they are (VQ, PR 5 review).
|
|
1006
|
+
*/
|
|
1007
|
+
offerHeading: (repos, standalone) => standalone
|
|
1008
|
+
? `Earlier sessions are still on this machine for ${repos} ${repos === 1 ? "repository" : "repositories"} you chose.`
|
|
1009
|
+
: `Earlier sessions are still on this machine for ${repos} of these repos.`,
|
|
1010
|
+
/** The count on a repo row. "Not yet uploaded", so it is never mistaken for the picker's total on disk. */
|
|
1011
|
+
notYetUploaded: (count) => `${count} not yet uploaded`,
|
|
1012
|
+
/**
|
|
1013
|
+
* Cursor sessions are not offered (Alexander, 2026-10-24 13:54 EST: Cursor's transcripts record no folder, so the
|
|
1014
|
+
* folder check cannot run). One muted line, only when a repo you chose has some.
|
|
1015
|
+
*/
|
|
1016
|
+
cursorNotUploaded: (count) => count === 1
|
|
1017
|
+
? "1 Cursor session isn't uploaded: Cursor doesn't record the folder a session started in."
|
|
1018
|
+
: `${count} Cursor sessions aren't uploaded: Cursor doesn't record the folder a session started in.`,
|
|
1019
|
+
/** Two lines, as §10.14 draws them. */
|
|
1020
|
+
/**
|
|
1021
|
+
* A session over the 50 MiB cap is left out of the count and named here, so the offer never promises an upload the run would
|
|
1022
|
+
* skip on every re-run. (Sub-agent streams are not capped.)
|
|
1023
|
+
*/
|
|
1024
|
+
overSizeNote: (count) => count === 1 ? "1 session over 50 MiB isn't uploaded." : `${count} sessions over 50 MiB aren't uploaded.`,
|
|
1025
|
+
/**
|
|
1026
|
+
* A clone with no reflog cannot be tested for a reused path (a transcript older than the clone's first reflog entry), and
|
|
1027
|
+
* that test is the only guard against one, so its sessions are left out of the count and named here with the clone.
|
|
1028
|
+
*/
|
|
1029
|
+
noReflogNote: (count, path) => count === 1
|
|
1030
|
+
? `1 session in a clone with no reflog isn't uploaded: ${path}`
|
|
1031
|
+
: `${count} sessions in a clone with no reflog aren't uploaded: ${path}`,
|
|
1032
|
+
uploadNote: ["They upload one at a time, the same way new sessions do, without", "commit links."],
|
|
1033
|
+
question: (sessions) => sessions === 1 ? "Upload this session now? [y/N] " : `Upload these ${sessions} sessions now? [y/N] `,
|
|
1034
|
+
/** A no. A completed consent flow (§13.7), not an error. */
|
|
1035
|
+
declined: (backfill) => `Nothing was uploaded. Run \`${backfill}\` any time.`,
|
|
1036
|
+
nothingToUpload: "Nothing to upload: no earlier session on this machine is waiting for a repository you've chosen.",
|
|
1037
|
+
runHeading: (sessions) => sessions === 1 ? "Uploading 1 session..." : `Uploading ${sessions} sessions, one at a time...`,
|
|
1038
|
+
/** Outcome fragments on a repo line, joined by " · ". */
|
|
1039
|
+
uploaded: (count) => `${count} uploaded`,
|
|
1040
|
+
skipped: (count, reason) => `${count} skipped, ${reason}`,
|
|
1041
|
+
held: (count) => `${count} held, the org must approve capture`,
|
|
1042
|
+
failed: (count, reason) => `${count} failed, ${reason}`,
|
|
1043
|
+
stopped: "stopped",
|
|
1044
|
+
/** Skip reasons. */
|
|
1045
|
+
overSize: "over 50 MiB",
|
|
1046
|
+
folderDiffers: "folder differs",
|
|
1047
|
+
folderNotRecorded: "folder not recorded",
|
|
1048
|
+
/** The transcript could not be opened at all (permissions), which is a different fact from one that records no folder. */
|
|
1049
|
+
couldntBeRead: "couldn't be read",
|
|
1050
|
+
/** The file vanished between the offer and its upload: a skip, not a refusal by the server. */
|
|
1051
|
+
fileGone: "the file is gone",
|
|
1052
|
+
/** `vibecommit off` (or a grant removed) while the run was going: the rest of that repo is not sent. */
|
|
1053
|
+
turnedOff: "turned off while this ran",
|
|
1054
|
+
outsideAgentFolder: "not in the agent's session folder",
|
|
1055
|
+
/** Failure reasons: a `never`. The server's own code is never shown (§13.6: no bare status, no raw error). */
|
|
1056
|
+
refusedByServer: "the server refused it",
|
|
1057
|
+
repositoryForbidden: "another organization owns this repository",
|
|
1058
|
+
noRepoIdentity: "no repository identity",
|
|
1059
|
+
unrecognisedFileName: "the file name isn't a session id",
|
|
1060
|
+
/** The summary. The glyph is `ok` only when nothing was held, stopped or failed. */
|
|
1061
|
+
summary: (uploaded, total, skipped, held, failed) => {
|
|
1062
|
+
const noun = total === 1 ? "session" : "sessions";
|
|
1063
|
+
const tail = [
|
|
1064
|
+
skipped > 0 ? `${skipped} skipped` : null,
|
|
1065
|
+
held > 0 ? `${held} held` : null,
|
|
1066
|
+
failed > 0 ? `${failed} failed` : null,
|
|
1067
|
+
].filter((part) => part !== null);
|
|
1068
|
+
return `Uploaded ${uploaded} of ${total} ${noun}.${tail.length > 0 ? ` ${tail.join(", ")}.` : ""}`;
|
|
1069
|
+
},
|
|
1070
|
+
/** `owners` is the held repos' owners, already phrased ("acme-corp", "acme-corp or other-org"). */
|
|
1071
|
+
heldBlock: (owners) => `Held sessions wait for ${owners} to approve capture:`,
|
|
1072
|
+
heldThenRun: "Then run:",
|
|
1073
|
+
/**
|
|
1074
|
+
* The stop block: a server `later` (5xx, 429) stops the run, with the resume fix. ⛔ It states only what landed: `uploaded` is
|
|
1075
|
+
* sessions the server took, not sessions looked at, and "not yet uploaded" is what a re-run will OFFER again (skipped and held
|
|
1076
|
+
* sessions among them), not a promise of what it will send.
|
|
1077
|
+
*/
|
|
1078
|
+
stopWhat: (uploaded, total) => `Upload stopped: ${uploaded} of ${total} ${total === 1 ? "session" : "sessions"} uploaded.`,
|
|
1079
|
+
stopWhy: (notUploaded) => `The server asked the client to try again later. Nothing already sent is lost, and a re-run offers the ${notUploaded} ${notUploaded === 1 ? "session" : "sessions"} not yet uploaded.`,
|
|
1080
|
+
/** No response at all (a refused connection, a timeout): not the server asking anything. */
|
|
1081
|
+
stopWhyUnreachable: (notUploaded) => `The server could not be reached. Nothing already sent is lost, and a re-run offers the ${notUploaded} ${notUploaded === 1 ? "session" : "sessions"} not yet uploaded.`,
|
|
1082
|
+
stopFixLabel: "Resume:",
|
|
1083
|
+
/** Ctrl+C at the y/N or during the upload. Exit 0: capture is on. */
|
|
1084
|
+
interrupted: (backfill) => `Stopped. A re-run of ${backfill} sends what is left.`,
|
|
1085
|
+
/** Exit 3. */
|
|
1086
|
+
notConnectedWhat: "This machine isn't connected.",
|
|
1087
|
+
notConnectedWhy: "`backfill` uploads with this machine's credential, and there isn't one. Nothing was read.",
|
|
1088
|
+
notConnectedFixLabel: "Connect first:",
|
|
1089
|
+
/** `grants.json` is present and unreadable: every repo with a remote is denied, so there is nothing honest to offer. */
|
|
1090
|
+
grantsUnreadableWhat: "Backfill can't run: ~/.vibecommit/grants.json can't be read.",
|
|
1091
|
+
grantsUnreadableWhy: "Capture is stopped on this machine for every repository that has a remote until the file is readable. Nothing was read and nothing was uploaded.",
|
|
1092
|
+
grantsUnreadableFixLabel: "Fix the file or move it aside, then run:",
|
|
1093
|
+
/** Exit 2. A prompt that proceeds when nobody can answer it is not a prompt. */
|
|
1094
|
+
nonInteractiveWhat: "`backfill` needs an interactive terminal.",
|
|
1095
|
+
nonInteractiveWhy: "Standard input is not a terminal, so the question before uploading cannot be answered. Nothing was read and nothing was uploaded.",
|
|
1096
|
+
nonInteractiveFix: "Run this in a terminal:",
|
|
1097
|
+
/** The credential was rejected mid-run (a 401): the same block `finalize` shows, in this verb's words. */
|
|
1098
|
+
credentialStoppedWhat: "Upload stopped: this machine's credential is no longer accepted.",
|
|
1099
|
+
credentialStoppedWhy: "Nothing already sent is lost, and a re-run offers the sessions not yet uploaded.",
|
|
1100
|
+
credentialStoppedFixLabel: "Renew it, then run backfill again:",
|
|
1101
|
+
usageWhat: "That is not something `vibecommit backfill` understands.",
|
|
1102
|
+
usageWhy: "Nothing was read and nothing was uploaded. `backfill` takes no arguments.",
|
|
1103
|
+
};
|
|
1104
|
+
/**
|
|
1105
|
+
* `vibecommit connect` from any folder — the picker, the grant block, the hooks step and the endings (0.7.0, PR 3).
|
|
1106
|
+
* Style guide §10.12, copied from the merged guide and the design doc; the design doc wins where they differ.
|
|
1107
|
+
*/
|
|
1108
|
+
export const PICKER = {
|
|
1109
|
+
onNow: "on now",
|
|
1110
|
+
turnedOff: "turned off",
|
|
1111
|
+
/** Why an `on now` row is on (D5 · 1A), so a 0.6.x upgrader sees what is already captured. */
|
|
1112
|
+
reasonOwner: "owner",
|
|
1113
|
+
reasonRepo: "repo",
|
|
1114
|
+
reasonPerClone: "per clone",
|
|
1115
|
+
/** The owner row ALWAYS states how many repos `aN` covers (D9), so a hidden repo is never covered unseen. */
|
|
1116
|
+
ownerAll: (count) => `all ${count} ${count === 1 ? "repo" : "repos"} here, including repos you clone later`,
|
|
1117
|
+
ownerOnReposHere: (count) => `${count} ${count === 1 ? "repo" : "repos"} here`,
|
|
1118
|
+
turnOnHint: "choose it to turn it back on",
|
|
1119
|
+
/** Where the working folder is a repo, the row is marked so a pre-tick (or a `$HOME` dotfiles repo) is legible. */
|
|
1120
|
+
thisRepo: "this repo",
|
|
1121
|
+
thisFolder: "this folder",
|
|
1122
|
+
moreTyped: (count, owner) => `${count} more in ${owner}/ · type more to list them`,
|
|
1123
|
+
moreChecklist: (count, owner) => `${count} more in ${owner}/`,
|
|
1124
|
+
/** The typed prompt (D8). */
|
|
1125
|
+
choose: (hasOwners) => hasOwners
|
|
1126
|
+
? "Choose a1, numbers (1 2), owner/ or owner/repo."
|
|
1127
|
+
: "Choose numbers (1 2), owner/ or owner/repo.",
|
|
1128
|
+
removeHint: "Remove one with -N, -owner/ or -owner/repo.",
|
|
1129
|
+
enterNothing: "Press Enter to change nothing.",
|
|
1130
|
+
/** D34 in the typed prompt: the working folder's repo is pre-selected, so Enter leads to the (default No) y/N. */
|
|
1131
|
+
enterThisRepo: (slug) => `Press Enter to choose ${slug}, this repo.`,
|
|
1132
|
+
promptMark: "> ",
|
|
1133
|
+
/** `tokens` is the already-joined list (`9 (no row 9), acme, corp`): the corpus renders every template with a string. */
|
|
1134
|
+
notUnderstood: (tokens) => `Not understood: ${tokens}. Nothing was chosen.`,
|
|
1135
|
+
noRow: (n) => `${n} (no row ${n})`,
|
|
1136
|
+
alreadyOn: (token, remove) => `${token} (already on; ${remove} stops it)`,
|
|
1137
|
+
notOn: (token) => `${token} (not on)`,
|
|
1138
|
+
/** The empty screen (D12 · 6A): a new laptop, no agent session on disk at all. */
|
|
1139
|
+
emptyNone: "No coding-agent sessions were found on this machine.",
|
|
1140
|
+
emptyExpected: "That's expected on a new machine. Type the owners or repos to capture; every clone of them is captured once Claude Code, Codex or Cursor runs in it.",
|
|
1141
|
+
emptyType: "Type owner/ or owner/repo, for example alexvibecommit/.",
|
|
1142
|
+
/** Sessions exist but none could be listed (the `Not listed here:` block is not empty): scan's own wording. */
|
|
1143
|
+
emptyListable: "Type owner/ or owner/repo, for example alexvibecommit/, to capture a repository that is not listed.",
|
|
1144
|
+
/** The checklist (D28). */
|
|
1145
|
+
hint: "↑↓ move · space choose · enter done · t typed prompt · ctrl+c cancel",
|
|
1146
|
+
hintTyped: (connectTyped) => `For a screen reader, run: ${connectTyped}`,
|
|
1147
|
+
/** The enter-with-nothing lines (D5). */
|
|
1148
|
+
nothingGranted: (connect) => `Nothing granted. Run \`${connect}\` again any time.`,
|
|
1149
|
+
noChanges: (summary) => `No changes. Capture stays on for ${summary}.`,
|
|
1150
|
+
cancelled: "Cancelled. Nothing was changed.",
|
|
1151
|
+
/** The grant block (D7 · 3A). */
|
|
1152
|
+
chose: "You chose:",
|
|
1153
|
+
labelOwners: "owners",
|
|
1154
|
+
labelRepos: "repos",
|
|
1155
|
+
labelTurnOn: "turn on",
|
|
1156
|
+
labelStop: "stop",
|
|
1157
|
+
ownerHere: (count) => `(${count} here, and later clones)`,
|
|
1158
|
+
noCloneHere: "(no clone here yet)",
|
|
1159
|
+
questionGrant: "Upload transcripts from these repositories?",
|
|
1160
|
+
questionStop: "Stop capturing these repositories?",
|
|
1161
|
+
stopped: "Stopped capturing the repositories above.",
|
|
1162
|
+
/** "1 owner and 2 repos" — the on-now summary of Enter-with-grants, `auth`'s one line and the status heading. */
|
|
1163
|
+
summary: (owners, repos) => {
|
|
1164
|
+
const o = `${owners} ${owners === 1 ? "owner" : "owners"}`;
|
|
1165
|
+
const r = `${repos} ${repos === 1 ? "repo" : "repos"}`;
|
|
1166
|
+
if (owners > 0 && repos > 0)
|
|
1167
|
+
return `${o} and ${r}`;
|
|
1168
|
+
return owners > 0 ? o : r;
|
|
1169
|
+
},
|
|
1170
|
+
authStays: (summary) => `Capture stays on for ${summary} on this machine.`,
|
|
1171
|
+
/** The hooks step (D13 · 7A). */
|
|
1172
|
+
hooksBeat: (clones) => `Adding commit hooks to ${clones} ${clones === 1 ? "clone" : "clones"}...`,
|
|
1173
|
+
hooksDone: "done",
|
|
1174
|
+
hooksCount: (added, chained, notAdded) => {
|
|
1175
|
+
const head = `${added} added${chained > 0 ? `, ${chained} of them after your own hook` : ""}`;
|
|
1176
|
+
return notAdded > 0 ? `${head} · ${notAdded} not added` : head;
|
|
1177
|
+
},
|
|
1178
|
+
hooksNotAdded: (count) => `Commit hooks were not added to ${count} ${count === 1 ? "clone" : "clones"}:`,
|
|
1179
|
+
hooksPathReason: "core.hooksPath is set",
|
|
1180
|
+
/** A hook write that FAILED is a real exception with its own reason (VQ, item 9): neither dropped nor given `hooksPath`'s fix. */
|
|
1181
|
+
hooksFailedReason: "the hook could not be written",
|
|
1182
|
+
hooksStillCaptured: "Sessions there are still captured. To record commits too, either:",
|
|
1183
|
+
hooksStillCapturedOther: "Sessions there are still captured.",
|
|
1184
|
+
hooksFailedFixLabel: "Where the hook could not be written, try again:",
|
|
1185
|
+
/** "Capture is on" (D18 · 12A, D19 · 13A). */
|
|
1186
|
+
onFor: (summary) => `Capture is on for ${summary} on this machine.`,
|
|
1187
|
+
onProof: "The next Claude Code, Codex or Cursor session in any of them is captured. In Claude Code, its first reply then shows \"VibeCommit captured this session.\"",
|
|
1188
|
+
stopOne: "To stop one repo, run inside it:",
|
|
1189
|
+
/**
|
|
1190
|
+
* Alexander's "caveat for other owners" (2026-10-24; amended 17:20), on the "Capture is on" block. It prints for the owners the
|
|
1191
|
+
* test capture did NOT check: from `~` that is every granted owner (there is no session to probe), and in a repo it is every
|
|
1192
|
+
* granted owner except the one whose delta the server answered with a 2xx. For an owner that is not the signed-in person's own
|
|
1193
|
+
* account the client cannot know whether an org is yet to approve capture, so it says one may be. Never printed when every
|
|
1194
|
+
* such owner is the user's own account.
|
|
1195
|
+
*/
|
|
1196
|
+
ownerApproval: (status) => `An organization can require an owner's approval before its repos are captured; once a session in one of its repos has tried to upload, ${status} shows any that are waiting.`,
|
|
1197
|
+
onNewLaptop: (names) => `Capture is on for ${names} on this machine.`,
|
|
1198
|
+
onNewLaptopProof: "No clone of it is here yet. Clone one and open a coding agent in it; that session is captured.",
|
|
1199
|
+
// --- Slice 2 (S4 part 3, capture 0.8.0): the account's grants in `connect`. Brief §2-§3, VQ's rulings 2026-10-24. ---
|
|
1200
|
+
/** The group of account grants this machine has not named (§2): numbered, unticked until chosen, like any other row. */
|
|
1201
|
+
onAccount: "on your account",
|
|
1202
|
+
onAccountHint: "choose it to capture it here",
|
|
1203
|
+
/** A `turn on` row whose repo was turned off on the ACCOUNT (not only here): the yes re-includes it everywhere. */
|
|
1204
|
+
turnedOffOnAccount: "(turned off on your account)",
|
|
1205
|
+
/** Screen 1a: the second line of the prompt block, on a machine with no credential, a terminal and a browser to sign in with. */
|
|
1206
|
+
gLine: "Or type g for the repositories your account already grants. It signs in first.",
|
|
1207
|
+
/** The one token a mixed answer is refused for; `notUnderstood` wraps it in D8's line. */
|
|
1208
|
+
gMixedToken: "g can't be combined with other choices",
|
|
1209
|
+
/** Screen 1b: the grant block's heading when the list is the account's, not the person's picks. */
|
|
1210
|
+
accountGrants: "Your account grants:",
|
|
1211
|
+
/** Screen 1b, N: the credential `g` signed in with is revoked. The removed-here variant is a revoke that could not reach the server. */
|
|
1212
|
+
gDeclinedRevoked: "Nothing is captured on this machine. The credential made for this was revoked.",
|
|
1213
|
+
gDeclinedRemoved: "Nothing is captured on this machine. The credential made for this was removed here; the server will be told on the next vibecommit command that contacts the server.",
|
|
1214
|
+
/** Ctrl+C after a mint (VQ's correction): never "Nothing was changed", because a credential was made and then removed. */
|
|
1215
|
+
gCancelledRevoked: "Cancelled. Nothing is captured on this machine. The credential made for this was revoked.",
|
|
1216
|
+
gCancelledRemoved: "Cancelled. Nothing is captured on this machine. The credential made for this was removed here; the server will be told on the next vibecommit command that contacts the server.",
|
|
1217
|
+
/**
|
|
1218
|
+
* `g` stopped because this machine could not make the write-ahead record of the credential it had just been given (an unwritable,
|
|
1219
|
+
* unreadable or full queue). The credential is revoked at once (`REPLACE.declinedRevoked`), or `REPLACE.declinedOrphan` says where
|
|
1220
|
+
* to remove it. An insecure queue file gets the insecure-file block instead of this line.
|
|
1221
|
+
*/
|
|
1222
|
+
gNotRecorded: "g stopped: this machine couldn't keep a record of the credential it was just given.",
|
|
1223
|
+
/**
|
|
1224
|
+
* The yes found `g`'s own record of the credential UNUSABLE (the queue file became insecure or unreadable while the question was
|
|
1225
|
+
* open): nothing was revoked by anyone, this machine just cannot keep the record, so no credential is kept.
|
|
1226
|
+
*/
|
|
1227
|
+
gRecordUnusable: (connect) => `This machine's record of the credential this run had made couldn't be used, so none was kept. The repositories you chose stay named here; run \`${connect}\` again to sign in.`,
|
|
1228
|
+
/**
|
|
1229
|
+
* The yes found a `credentials.json` already there (another run saved one while the question was open): that one is kept, this run's
|
|
1230
|
+
* is not, and the grants the yes wrote are saved — capture IS on, so this never says nothing is captured. The fate of the
|
|
1231
|
+
* credential this run made follows on its own line (`REPLACE.declinedRevoked`, `gRemovedWillTell`, or `REPLACE.declinedOrphan`).
|
|
1232
|
+
*/
|
|
1233
|
+
gOccupied: (connect) => `A credential was saved on this machine while this question was open, so that one is kept and the one this run made was not. The repositories you chose are saved; run \`${connect}\` again to finish.`,
|
|
1234
|
+
/** The fate line for `gOccupied` when a readable row still holds the credential this run made. */
|
|
1235
|
+
gRemovedWillTell: "The credential just created was removed here; the server will be told on the next vibecommit command that contacts the server.",
|
|
1236
|
+
/**
|
|
1237
|
+
* The yes found `g`'s own record of the credential gone: another vibecommit command (a retry that ran while the question was open)
|
|
1238
|
+
* had already revoked it. No credential is kept, and the grants the yes wrote stay. `connect` is THIS install's command.
|
|
1239
|
+
*/
|
|
1240
|
+
gRevokedElsewhere: (connect) => `Another vibecommit command revoked the credential this run had made, so none was kept. The repositories you chose stay named here; run \`${connect}\` again to sign in.`,
|
|
1241
|
+
/** Screen 1c: `g` found no grants on the account. The credential is kept and the person chooses here. */
|
|
1242
|
+
gEmpty: "Your account doesn't grant any repositories yet. Choose them here instead:",
|
|
1243
|
+
/**
|
|
1244
|
+
* `g` signed in but the account's grants could not be read (the server did not answer, or answered off-contract). It must not
|
|
1245
|
+
* say the account is empty, and it says what to do: run `connect` again. `connect` is THIS install's command (a function of it).
|
|
1246
|
+
*/
|
|
1247
|
+
gUnavailable: (connect) => `The repositories your account grants couldn't be read right now. Run \`${connect}\` again to choose from them.`,
|
|
1248
|
+
/** Screen 2: a machine joining an account by picking local repos is asked about the rest of the account's grants. */
|
|
1249
|
+
joinAlso: (namesOnePerLine) => {
|
|
1250
|
+
const names = namesOnePerLine.split("\n");
|
|
1251
|
+
const head = names.length > 3
|
|
1252
|
+
? `${names.slice(0, 2).join(", ")} and ${names.length - 2} more`
|
|
1253
|
+
: names.length > 1
|
|
1254
|
+
? `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`
|
|
1255
|
+
: names[0];
|
|
1256
|
+
return `Your account also grants ${head}.`;
|
|
1257
|
+
},
|
|
1258
|
+
joinQuestion: "Capture those here too?",
|
|
1259
|
+
/**
|
|
1260
|
+
* Under "Capture is on" when the sync after `connect`'s writes did not reach the server (VQ, on VT's question). Capture IS on —
|
|
1261
|
+
* local grants admit the repo — so this is not a warning; it only tells the truth about the account. Only `connect`, `status` and
|
|
1262
|
+
* `off` sync, so it names `status` (the install channel's command); a 400 and a 401 have their own lines (below and `ERRORS`).
|
|
1263
|
+
*/
|
|
1264
|
+
accountNotTold: (status) => `Your account hasn't been told yet; running \`${status}\` retries.`,
|
|
1265
|
+
/**
|
|
1266
|
+
* "0.8.0 truth fixes" F2: the same line on a machine that HAS SYNCED before. There a grant the account has not been told about
|
|
1267
|
+
* holds the repository's sessions on this machine (`isCaptureAllowed`, step 3), so "Capture is on" needs the truth about it.
|
|
1268
|
+
* A machine that has never synced holds nothing and keeps `accountNotTold`.
|
|
1269
|
+
*/
|
|
1270
|
+
accountNotToldHeld: (status) => `Your account hasn't been told yet. Until it is, sessions in the repositories you just chose that it doesn't grant wait on this machine; running \`${status}\` retries.`,
|
|
1271
|
+
joinDeclined: (status) => `They stay granted on your account and aren't captured on this machine. ${status} lists them.`,
|
|
1272
|
+
};
|
|
700
1273
|
/** `vibecommit off`. */
|
|
701
1274
|
export const OFF = {
|
|
702
1275
|
done: "Capture is off for this repository.",
|
|
1276
|
+
/**
|
|
1277
|
+
* 0.7.0, a claims-register row. A repo with a remote is turned off BY SLUG, which reaches every clone of it on this
|
|
1278
|
+
* machine. Slice 2 extends the sentence to the other machines once they sync; until then it says only what is true.
|
|
1279
|
+
*/
|
|
1280
|
+
doneSlug: (slug) => `Turned off ${slug} for every clone on this machine.`,
|
|
1281
|
+
/** D31: bare `off` outside a repository. The picker is where an owner or another repo is unticked. */
|
|
1282
|
+
outsideRepoWhat: "`off` turns off the repository you're in.",
|
|
1283
|
+
outsideRepoFixLabel: "To stop capturing an owner or another repo, untick it in the picker:",
|
|
1284
|
+
/** `grants.json` is unreadable, so the exclusion could not be recorded. The clone's own consent was still withdrawn. */
|
|
1285
|
+
grantsUnreadableWhat: "The turn-off could not be recorded: ~/.vibecommit/grants.json can't be read.",
|
|
1286
|
+
grantsUnreadableWhy: "Capture is already stopped for this repository while the file can't be read, and this clone's own consent was withdrawn.",
|
|
1287
|
+
grantsUnreadableFixLabel: "Fix the file or move it aside, then run:",
|
|
703
1288
|
alreadyOff: "Capture was already off for this repository.",
|
|
1289
|
+
/**
|
|
1290
|
+
* Slice 2 (S4), brief §2 and §5. On a machine with a credential the exclusion is also queued for the account and synced
|
|
1291
|
+
* (3 s), so the success line says where else it reaches; `unsyncedOthers` says who keeps capturing until they sync; and
|
|
1292
|
+
* `notSentYet` is the design's sentence for a write that could not reach the server (it stays queued).
|
|
1293
|
+
*/
|
|
1294
|
+
doneSlugAccount: (slug) => `Turned off ${slug} for every clone on this machine, and on your other machines running VibeCommit 0.8.0 or later once they sync.`,
|
|
1295
|
+
// ⛔ A 0.6.x/0.7.0 machine never syncs, so every sentence about "your other machines" names the version that does (VQ, on #87).
|
|
1296
|
+
unsyncedOthers: (n) => n === 1
|
|
1297
|
+
? "1 of your machines hasn't synced and keeps capturing it until it syncs on VibeCommit 0.8.0 or later."
|
|
1298
|
+
: `${n} of your machines haven't synced and keep capturing it until they sync on VibeCommit 0.8.0 or later.`,
|
|
1299
|
+
notSentYet: "Stopped here. Your other machines running VibeCommit 0.8.0 or later don't know yet; it will retry.",
|
|
1300
|
+
/** The write was stopped, not just unsent (a 400, a 401, too large, unreadable): "it will retry" would be false, so it does not say so. */
|
|
1301
|
+
notSentStopped: "Stopped here. Your other machines running VibeCommit 0.8.0 or later don't know yet.",
|
|
1302
|
+
/** The account already had it turned off (another machine, or the dashboard); this machine now agrees. */
|
|
1303
|
+
alreadyOffAccount: "Already off on your account; now off on this machine too.",
|
|
704
1304
|
note: "Sessions already recorded are unaffected.",
|
|
705
1305
|
// `TODOS[184]`. `off` used to ignore `argv` entirely and always run — a stray
|
|
706
1306
|
// argument, including `--help`'s own flag landing here by a bug, must never
|
|
@@ -989,7 +1589,10 @@ export const WHY = {
|
|
|
989
1589
|
*/
|
|
990
1590
|
skewWhat: "The record service sent an answer this version is too old to show.",
|
|
991
1591
|
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.",
|
|
1592
|
+
/** The PLUGIN channel's remedy (`COMMANDS.pluginInstall`). */
|
|
992
1593
|
skewFix: "Reinstall the VibeCommit plugin to pick up the current client, then run this again:",
|
|
1594
|
+
/** The npm channel's remedy (`COMMANDS.npmUpdate`): an npm install has no plugin to reinstall. */
|
|
1595
|
+
skewFixNpm: "Update VibeCommit to pick up the current client, then run this again:",
|
|
993
1596
|
/** The bearer is dead after one refresh and one retry. Exit 3, not 1. */
|
|
994
1597
|
signedOutWhat: "This machine is not signed in to VibeCommit.",
|
|
995
1598
|
signedOutWhy: "Reading a record needs a signed-in user, and no usable sign-in was found on this machine.",
|
|
@@ -1276,20 +1879,44 @@ export const ABSENCE = {
|
|
|
1276
1879
|
* know were bound: `closed` says the last turn is recorded, not that any commit matched it.
|
|
1277
1880
|
*/
|
|
1278
1881
|
export const FINALIZE = {
|
|
1279
|
-
|
|
1882
|
+
// PR 7 (Q7): where the remedy is a command it sits in the §13.6 fix block, not inside the `what` line. The `what` lines
|
|
1883
|
+
// below lost exactly the clause that moved; the fix commands are the ones the old sentence named.
|
|
1884
|
+
needTarget: "Say which session to close.",
|
|
1885
|
+
needTargetFix: "Name one, or take the latest:",
|
|
1280
1886
|
badSessionId: "That is not a session id (letters, digits, dot, dash and underscore only).",
|
|
1281
|
-
noRecentSession: "No session in this repository has delivered anything in the last 24 hours.
|
|
1282
|
-
|
|
1887
|
+
noRecentSession: "No session in this repository has delivered anything in the last 24 hours.",
|
|
1888
|
+
noRecentSessionFix: "Name a session:",
|
|
1889
|
+
severalRecent: "More than one session in this repository is recent.",
|
|
1890
|
+
severalRecentFix: "Name one:",
|
|
1891
|
+
/** `finalize --session <id>` for a session that is known, e.g. the one whose delivery just failed. */
|
|
1892
|
+
commandSession: (id) => `vibecommit finalize --session ${id}`,
|
|
1893
|
+
/** The same with the placeholder, for a fix line written before a session is known. */
|
|
1894
|
+
commandSessionAny: "vibecommit finalize --session <id>",
|
|
1895
|
+
/** The command without its id, for a sentence that names the id itself (`status`'s held-idle line). */
|
|
1896
|
+
commandSessionBase: "vibecommit finalize --session",
|
|
1897
|
+
commandSessionForce: (id) => `vibecommit finalize --session ${id} --force`,
|
|
1283
1898
|
recentRow: (id, minutesAgo) => `${id} (${minutesAgo} min ago)`,
|
|
1284
1899
|
unknownSession: (id) => `This repository has no captured session ${id}.`,
|
|
1285
1900
|
transcriptMissing: (id) => `Could not find the transcript for session ${id}.`,
|
|
1286
|
-
stillWriting: "That session's transcript changed a moment ago.
|
|
1901
|
+
stillWriting: "That session's transcript changed a moment ago.",
|
|
1902
|
+
stillWritingFix: "Wait for the session to finish, or if it is over, run:",
|
|
1287
1903
|
closed: (id) => `Closed session ${id}: its last turn is recorded, so commits made in it can now be recorded.`,
|
|
1288
1904
|
alreadyClosed: (id) => `Session ${id} had nothing left open. Nothing changed.`,
|
|
1289
1905
|
notAcknowledged: "The server accepted the data but did not confirm that it closed the session; it may predate this command. The last turn may still be open.",
|
|
1290
|
-
refused: "The server declined to close the session on this attempt.
|
|
1291
|
-
|
|
1292
|
-
|
|
1906
|
+
refused: "The server declined to close the session on this attempt.",
|
|
1907
|
+
retryFix: "Run it again:",
|
|
1908
|
+
notDelivered: "Could not deliver the session. Nothing was closed.",
|
|
1909
|
+
credentialStopped: "VibeCommit stopped sending for this credential.",
|
|
1910
|
+
credentialStoppedFix: "Replace it:",
|
|
1911
|
+
/**
|
|
1912
|
+
* PR 7 (Q2, VQ's wording): the ingest address in the environment (`VIBECOMMIT_INGEST_URL`) is one this client refuses to
|
|
1913
|
+
* send to, so retrying cannot help. Shared by `finalize` and `backfill`, so the `why` says only "Nothing was sent": it must
|
|
1914
|
+
* not claim a session was or was not closed. The VALUE is never echoed (a URL can carry a secret).
|
|
1915
|
+
*/
|
|
1916
|
+
ingestUrlWhat: "The address VibeCommit sends sessions to can't be used.",
|
|
1917
|
+
ingestUrlWhy: "VIBECOMMIT_INGEST_URL must be an https address, or an http address on this machine, and it isn't. Nothing was sent.",
|
|
1918
|
+
ingestUrlFix: "Correct it, or remove it to use the default address:",
|
|
1919
|
+
ingestUrlCommand: "unset VIBECOMMIT_INGEST_URL",
|
|
1293
1920
|
/**
|
|
1294
1921
|
* `TODOS[177]` follow-up (VL, 2026-10-08, VG's silent-loss finding) — ONE stderr line for the whole run when
|
|
1295
1922
|
* `finalize` sets any inferred-squash pairs aside (the main delivery, the drain loop, or both, summed). Printed
|
|
@@ -1317,8 +1944,8 @@ export const REJECTED = {
|
|
|
1317
1944
|
* command across lines; `unconfirmed` is their join, the sentence the plan wrote.
|
|
1318
1945
|
*/
|
|
1319
1946
|
unconfirmedWhat: "Couldn't confirm this machine's credential with the server.",
|
|
1320
|
-
unconfirmedFix:
|
|
1321
|
-
unconfirmed:
|
|
1947
|
+
unconfirmedFix: (status) => `Run \`${status}\` later.`,
|
|
1948
|
+
unconfirmed: (status) => `Couldn't confirm this machine's credential with the server. Run \`${status}\` later.`,
|
|
1322
1949
|
};
|
|
1323
1950
|
/** `TODOS[184]`-style one-sentence facts the retire step prints; every one is true whatever the server did. */
|
|
1324
1951
|
export const RETIRE = {
|
|
@@ -1350,6 +1977,12 @@ export const REPLACE = {
|
|
|
1350
1977
|
questionUnconfirmed: "Upload this machine's transcripts to the workspace you signed in to?",
|
|
1351
1978
|
replacesValue: (org) => `credential for ${org}`,
|
|
1352
1979
|
question: (org) => `Upload this machine's transcripts to ${org}?`,
|
|
1980
|
+
/**
|
|
1981
|
+
* 0.7.0, D7 · 3A: on the PICKER path the question names the repos, not the machine, and is the second of at most three
|
|
1982
|
+
* questions in a run. The workspace is cut to 28 columns (not `question`'s 30) so the line stays within 80 with `[y/N]`.
|
|
1983
|
+
*/
|
|
1984
|
+
questionRepos: (org) => `Upload transcripts from the repos above to ${org}?`,
|
|
1985
|
+
questionReposUnconfirmed: "Upload transcripts from the repos above to your signed-in workspace?",
|
|
1353
1986
|
declined: "Nothing was saved. The approval is discarded.",
|
|
1354
1987
|
/** Ctrl+C at the question (the prompt was aborted): the same outcome as N, said as what it was. */
|
|
1355
1988
|
cancelled: "Cancelled. Nothing was saved.",
|
|
@@ -1444,13 +2077,19 @@ export const UPDATE = {
|
|
|
1444
2077
|
plugin: (latest) => `capture ${latest} is available: update the VibeCommit plugin in Claude Code.`,
|
|
1445
2078
|
};
|
|
1446
2079
|
export const ERRORS = {
|
|
1447
|
-
notConnected: "This repository is not connected.
|
|
2080
|
+
notConnected: "This repository is not connected.",
|
|
2081
|
+
/** PR 7 (Q7): the remedy was a sentence inside the `what` line; it is a command now, so it moves to the fix block. */
|
|
2082
|
+
notConnectedFix: "Connect it:",
|
|
1448
2083
|
networkLater: "Could not reach VibeCommit. This session will be recorded on the next turn.",
|
|
1449
2084
|
// `11A`. The `what` of the §13.6 rejected-credential block (`REJECTED`), not a second sentence: the
|
|
1450
2085
|
// old line sent users to `connect`, which printed "Connected" over the same dead credential (G1).
|
|
1451
2086
|
authFatal: "This machine's credential is no longer accepted.",
|
|
1452
2087
|
payloadNever: "This session was too large to record and has been skipped.",
|
|
1453
2088
|
notAGitRepo: "Not a git repository.",
|
|
2089
|
+
/** PR 7 (VQ): names the folder, tilde-shortened and cut from the LEFT by the caller (`truncatePath`, §13.4). */
|
|
2090
|
+
notAGitRepoWhy: (folder) => `${folder} is not inside a git work tree.`,
|
|
2091
|
+
/** A fix with no command to give: the remedy is where to run it, so the block has a label and no commands. */
|
|
2092
|
+
notAGitRepoFix: "Run it again inside a git repository.",
|
|
1454
2093
|
};
|
|
1455
2094
|
/**
|
|
1456
2095
|
* The interactive top-level catch (CR-022).
|
|
@@ -1470,7 +2109,8 @@ export const INTERNAL = {
|
|
|
1470
2109
|
unexpectedWhat: "VibeCommit stopped on an unexpected error.",
|
|
1471
2110
|
unexpectedWhy: "The command did not finish.",
|
|
1472
2111
|
unexpectedFix: "Run it again with the error detail shown:",
|
|
1473
|
-
|
|
2112
|
+
// The command is this install's own (PR 7): `node <path> <command>` on a plugin-only machine, which has no `vibecommit`.
|
|
2113
|
+
unexpectedDebugCommand: (command) => `VIBECOMMIT_DEBUG=1 ${command} <command>`,
|
|
1474
2114
|
};
|
|
1475
2115
|
/**
|
|
1476
2116
|
* Credential-loading failures (CR-016).
|
|
@@ -1498,6 +2138,10 @@ export const CREDENTIAL = {
|
|
|
1498
2138
|
insecureFileModeLabel: "mode",
|
|
1499
2139
|
insecureFileWhy: "It was not read. A credential file must be readable only by its owner.",
|
|
1500
2140
|
insecureFileFix: "Restore the mode, then reconnect:",
|
|
2141
|
+
/** The FOLDER (`~/.vibecommit`) is open to other accounts, not the file: same code, its own what, why and `chmod`. */
|
|
2142
|
+
insecureDirWhat: "The folder that holds the credential file is open to other accounts on this machine.",
|
|
2143
|
+
insecureDirWhyLabel: "folder",
|
|
2144
|
+
insecureDirWhy: "The credential was not read. This folder must be accessible only to its owner.",
|
|
1501
2145
|
unreadableWhat: "The credential file could not be read.",
|
|
1502
2146
|
unreadableWhy: "It exists but does not contain a credential in the expected form.",
|
|
1503
2147
|
/** `CR-216/U4`. Was "Replace it by reconnecting:" — see `wrongClassFix` above. */
|
|
@@ -1531,15 +2175,26 @@ export const AUTH = {
|
|
|
1531
2175
|
nextCommand: "vibecommit connect",
|
|
1532
2176
|
emptyWhat: "No credential was given.",
|
|
1533
2177
|
emptyWhy: "Nothing was read, so nothing was written.",
|
|
1534
|
-
|
|
2178
|
+
/**
|
|
2179
|
+
* PR 6 item 1. Was "Mint one, then run:" over the URL alone, so the label promised a command and printed a link, and the
|
|
2180
|
+
* non-interactive path was named nowhere. The URL comes FIRST (inside the label, where it cannot be wrapped away from
|
|
2181
|
+
* its sentence) and the commands LAST, so the fix stays last (§13.6). A function because `URLS` is declared below.
|
|
2182
|
+
*/
|
|
2183
|
+
mintFix: (url) => `Mint one at ${url}, then run either:`,
|
|
2184
|
+
/**
|
|
2185
|
+
* The two ways to hand it over: piped (a script or an agent), or typed at a terminal. `--stdin` never continues into `connect`
|
|
2186
|
+
* (D32 guard 1). `auth` is `authCommandFor(env, selfPath)`: a plugin-only machine has no `vibecommit` on PATH, so the line names
|
|
2187
|
+
* `node <path> auth` there (README, "Keeping a machine connected"), and handing it `vibecommit auth` would be a new wrong output.
|
|
2188
|
+
*/
|
|
2189
|
+
mintCommands: (auth) => [`printf %s "$TOKEN" | ${auth} --stdin`, auth],
|
|
1535
2190
|
wrongClassWhat: "That is not a VibeCommit ingest credential.",
|
|
1536
2191
|
wrongClassWhy: `An ingest credential begins ${INGEST_TOKEN_PREFIX}. Nothing was written and nothing was sent.`,
|
|
1537
|
-
wrongClassFix: "Mint one, then run:",
|
|
1538
2192
|
argvWhat: "A credential must not be passed as an argument.",
|
|
1539
2193
|
argvWhy: "Arguments are written to your shell history and are readable by other processes on this machine while the command runs. Treat the one you just typed as exposed and revoke it.",
|
|
1540
2194
|
argvFix: "Read it from the terminal, or from a pipe:",
|
|
1541
2195
|
argvFixPrompt: "vibecommit auth",
|
|
1542
|
-
|
|
2196
|
+
/** A pipe, so `channelCommand` cannot rewrite it: it takes the channel's `auth` (`authCommandFor`) instead. */
|
|
2197
|
+
argvFixStdin: (auth) => `printf %s "$TOKEN" | ${auth} --stdin`,
|
|
1543
2198
|
// --- `CR-226`: the browser mint, and the paste it falls back to. ---
|
|
1544
2199
|
/**
|
|
1545
2200
|
* Printed before the request, naming the label the credential will carry in
|
|
@@ -1590,7 +2245,14 @@ export const RUNTIME = {
|
|
|
1590
2245
|
* 4. No URL longer than the line.
|
|
1591
2246
|
*/
|
|
1592
2247
|
export const SYSTEM_MESSAGE = {
|
|
1593
|
-
|
|
2248
|
+
/**
|
|
2249
|
+
* State 1. The command is the channel's (`installChannel`): `vibecommit connect` on npm, `node <path> connect` for the plugin
|
|
2250
|
+
* bundle, which has no `vibecommit` on PATH (slice 2, design Next Steps 6, the mechanism state 2 already uses). On npm the line is
|
|
2251
|
+
* the same bytes it always was.
|
|
2252
|
+
*/
|
|
2253
|
+
notConnected: (connectCommand) => `VibeCommit is installed but not connected. Run \`${connectCommand}\` to start capturing.`,
|
|
2254
|
+
/** State 1 when the channel's command would blow `credentialRejectedBudget` (a long plugin realpath). */
|
|
2255
|
+
notConnectedFallback: "VibeCommit is installed but not connected. Connect it: vibecommit.ai/install",
|
|
1594
2256
|
/**
|
|
1595
2257
|
* State 2 — `11A`. House prefix kept, ONE line, and the cause is no longer named: the server's 401
|
|
1596
2258
|
* body is opaque on purpose (it cannot say "revoked" vs "expired"), so "revoked" was a guess that
|
|
@@ -1630,6 +2292,35 @@ export const SYSTEM_MESSAGE = {
|
|
|
1630
2292
|
* users and its hash chain detects post-write tampering").
|
|
1631
2293
|
*/
|
|
1632
2294
|
captureConfirmed: (dashboardUrl) => `VibeCommit captured this session. ${dashboardUrl}`,
|
|
2295
|
+
/**
|
|
2296
|
+
* States 6-9 — style guide §10.1 (0.7.0, design review D16 · 10A), all emitted by the `SessionStart` hook. One
|
|
2297
|
+
* plain-text line each, ≤120 characters, no SGR. The wording is the design doc's and the merged style guide's,
|
|
2298
|
+
* byte for byte; `test/session-start.test.ts` pins the length of every one.
|
|
2299
|
+
*
|
|
2300
|
+
* 6 — hooks added, once per clone. 7 — `core.hooksPath` is set, once per clone. 8 — the clone has its own
|
|
2301
|
+
* commit hook, once per clone. 9 — `grants.json` is unreadable, once per (machine, day).
|
|
2302
|
+
*/
|
|
2303
|
+
hooksAdded: "VibeCommit added its post-commit and post-rewrite hooks to this clone, so commits can link to sessions.",
|
|
2304
|
+
hooksPathSet: "VibeCommit captures sessions here, not commits: core.hooksPath is set. Run `vibecommit status` here for the fix.",
|
|
2305
|
+
ownCommitHook: "VibeCommit didn't add its commit hook here: this clone has its own. Run `vibecommit connect` here to chain it.",
|
|
2306
|
+
grantsUnreadable: "VibeCommit capture stopped on this machine: ~/.vibecommit/grants.json can't be read. Run `vibecommit status`.",
|
|
2307
|
+
/**
|
|
2308
|
+
* State 10 — slice 2 (`CR-259`, brief §3 item 4). Emitted by the `SessionStart` hook, once per (machine, account epoch), when its one
|
|
2309
|
+
* bounded `GET` found account grants this machine has not named. SessionStart-only like states 6-9 (the plugin registers no
|
|
2310
|
+
* SessionStart), so it names the CLI by its own name, in backticks as 7-9 do; ≤120 characters.
|
|
2311
|
+
*
|
|
2312
|
+
* The counts are of what `status` lists under "on account", split: `owners` are the account's owner grants this machine has not named
|
|
2313
|
+
* (`octocat/`), `repos` its repository grants not named here, not under a named owner, and not turned off. The forms: "2 repositories",
|
|
2314
|
+
* "1 owner and 3 repositories", "2 owners"; the singulars are "1 repository" and "1 owner".
|
|
2315
|
+
*/
|
|
2316
|
+
accountGrantsNotHere: (owners, repos) => {
|
|
2317
|
+
const o = `${owners} ${owners === 1 ? "owner" : "owners"}`;
|
|
2318
|
+
const r = `${repos} ${repos === 1 ? "repository" : "repositories"}`;
|
|
2319
|
+
const what = owners > 0 && repos > 0 ? `${o} and ${r}` : owners > 0 ? o : r;
|
|
2320
|
+
return `Your account grants ${what} not captured on this machine yet. Run \`vibecommit status\`.`;
|
|
2321
|
+
},
|
|
2322
|
+
/** State 10 when the counts would blow the ≤120 budget (an absurd number of grants): no counts, the same command. */
|
|
2323
|
+
accountGrantsNotHereFallback: "Your account grants repositories not captured on this machine yet. Run `vibecommit status`.",
|
|
1633
2324
|
};
|
|
1634
2325
|
/**
|
|
1635
2326
|
* The out-of-tree redaction marker — `CR-024d`, D57 §OV8d.
|
|
@@ -1660,6 +2351,10 @@ export const REDACTION = {
|
|
|
1660
2351
|
/** Commands the copy points at. One definition, so a rename cannot half-land. */
|
|
1661
2352
|
export const COMMANDS = {
|
|
1662
2353
|
connect: "vibecommit connect",
|
|
2354
|
+
/** `D33 · 27A`. */
|
|
2355
|
+
scan: "vibecommit scan",
|
|
2356
|
+
/** `D14`/`D15`. */
|
|
2357
|
+
backfill: "vibecommit backfill",
|
|
1663
2358
|
/**
|
|
1664
2359
|
* `CR-216/U2`. The only verb a credential FAULT's fix line may name — it can
|
|
1665
2360
|
* replace the credential from any state. `CR-226` lets `connect` mint one
|
|
@@ -1680,6 +2375,10 @@ export const COMMANDS = {
|
|
|
1680
2375
|
/** The ref form, for the detached-HEAD refusal. */
|
|
1681
2376
|
reportRef: "vibecommit report --since 2026-07-08 --ref main",
|
|
1682
2377
|
off: "vibecommit off",
|
|
2378
|
+
/** The picker's screen-reader flag: a FLAG on `connect`, like `signIn`. */
|
|
2379
|
+
connectTyped: "vibecommit connect --typed",
|
|
2380
|
+
/** The git hook's own verb. `hooksPathFixManual` tells a user to add it to a hook they keep. */
|
|
2381
|
+
postCommit: "vibecommit post-commit",
|
|
1683
2382
|
/**
|
|
1684
2383
|
* `CR-086`. The ARGUMENT SHAPE, shown once so the usage error and the retry
|
|
1685
2384
|
* fix line cannot drift apart. There is no argument parser in this package —
|
|
@@ -1690,6 +2389,8 @@ export const COMMANDS = {
|
|
|
1690
2389
|
*/
|
|
1691
2390
|
why: "vibecommit why src/auth/session.ts:47",
|
|
1692
2391
|
chmodCredentials: "chmod 600 ~/.vibecommit/credentials.json",
|
|
2392
|
+
/** The folder's remedy, for `CREDENTIAL_FILE_INSECURE` when the folder is the one with the wrong mode. */
|
|
2393
|
+
chmodDirectory: "chmod 700 ~/.vibecommit",
|
|
1693
2394
|
/**
|
|
1694
2395
|
* ⛔ THE INSTALL COMMAND, TRANSCRIBED FROM `claude-plugin/README.md:12` — and
|
|
1695
2396
|
* it is deliberately NOT an invented update verb (`U1`, 2026-08-30).
|
|
@@ -1707,6 +2408,8 @@ export const COMMANDS = {
|
|
|
1707
2408
|
* together — the wording and the command are one unit.
|
|
1708
2409
|
*/
|
|
1709
2410
|
pluginInstall: "/plugin install vibecommit@vibecommit-capture",
|
|
2411
|
+
/** The npm channel's way to a newer client: the package's own documented install, at `latest`. */
|
|
2412
|
+
npmUpdate: "npm install -g @vibe-commit/capture@latest",
|
|
1710
2413
|
};
|
|
1711
2414
|
/**
|
|
1712
2415
|
* Every URL the CLI prints. Here rather than inline for the same reason as the
|
|
@@ -1730,6 +2433,8 @@ export const URLS = {
|
|
|
1730
2433
|
* deepening this constant is that task's, not a later guess.
|
|
1731
2434
|
*/
|
|
1732
2435
|
commits: "https://vibecommit.ai/app/commits",
|
|
2436
|
+
/** `/app/conversations`: where an uploaded backfill lands (D19). `/app` (`dashboard`) is the ending after a no. */
|
|
2437
|
+
conversations: "https://vibecommit.ai/app/conversations",
|
|
1733
2438
|
/**
|
|
1734
2439
|
* One recorded session — the ending's link when the ingest 2xx carried a `conversation_id` (`DX D8`).
|
|
1735
2440
|
* Verified by file: `vibecommit-web` `app/app/conversations/[id]/page.tsx` exists on `main`, and an
|