@vibe-commit/capture 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +80 -5
  2. package/dist/agent_hooks.d.ts +8 -0
  3. package/dist/agent_hooks.js +31 -1
  4. package/dist/agent_hooks.js.map +1 -1
  5. package/dist/agents/claude_code.d.ts +34 -0
  6. package/dist/agents/claude_code.js +141 -8
  7. package/dist/agents/claude_code.js.map +1 -1
  8. package/dist/agents/codex_cli.d.ts +13 -0
  9. package/dist/agents/codex_cli.js +241 -2
  10. package/dist/agents/codex_cli.js.map +1 -1
  11. package/dist/agents/cursor.d.ts +34 -0
  12. package/dist/agents/cursor.js +115 -1
  13. package/dist/agents/cursor.js.map +1 -1
  14. package/dist/agents/session_listing.d.ts +29 -0
  15. package/dist/agents/session_listing.js +49 -0
  16. package/dist/agents/session_listing.js.map +1 -0
  17. package/dist/agents/types.d.ts +118 -8
  18. package/dist/agents/types.js +17 -3
  19. package/dist/agents/types.js.map +1 -1
  20. package/dist/commands/approval_pending.d.ts +9 -0
  21. package/dist/commands/approval_pending.js +23 -0
  22. package/dist/commands/approval_pending.js.map +1 -0
  23. package/dist/commands/auth.js +31 -11
  24. package/dist/commands/auth.js.map +1 -1
  25. package/dist/commands/backfill.d.ts +160 -0
  26. package/dist/commands/backfill.js +748 -0
  27. package/dist/commands/backfill.js.map +1 -0
  28. package/dist/commands/browser_credential.d.ts +2 -0
  29. package/dist/commands/browser_credential.js +31 -14
  30. package/dist/commands/browser_credential.js.map +1 -1
  31. package/dist/commands/connect.d.ts +137 -0
  32. package/dist/commands/connect.js +203 -109
  33. package/dist/commands/connect.js.map +1 -1
  34. package/dist/commands/connect_picker.d.ts +27 -0
  35. package/dist/commands/connect_picker.js +415 -0
  36. package/dist/commands/connect_picker.js.map +1 -0
  37. package/dist/commands/context.d.ts +16 -0
  38. package/dist/commands/context.js +27 -0
  39. package/dist/commands/context.js.map +1 -1
  40. package/dist/commands/credential_problem.d.ts +15 -4
  41. package/dist/commands/credential_problem.js +29 -22
  42. package/dist/commands/credential_problem.js.map +1 -1
  43. package/dist/commands/device_credential.d.ts +2 -0
  44. package/dist/commands/device_credential.js +16 -14
  45. package/dist/commands/device_credential.js.map +1 -1
  46. package/dist/commands/finalize.d.ts +95 -0
  47. package/dist/commands/finalize.js +249 -74
  48. package/dist/commands/finalize.js.map +1 -1
  49. package/dist/commands/finish_mint.d.ts +2 -6
  50. package/dist/commands/finish_mint.js +10 -2
  51. package/dist/commands/finish_mint.js.map +1 -1
  52. package/dist/commands/report.js +38 -30
  53. package/dist/commands/report.js.map +1 -1
  54. package/dist/commands/scan.d.ts +162 -0
  55. package/dist/commands/scan.js +507 -0
  56. package/dist/commands/scan.js.map +1 -0
  57. package/dist/commands/status.js +119 -22
  58. package/dist/commands/status.js.map +1 -1
  59. package/dist/commands/status_hooks.d.ts +14 -0
  60. package/dist/commands/status_hooks.js +81 -0
  61. package/dist/commands/status_hooks.js.map +1 -0
  62. package/dist/commands/status_machine.d.ts +18 -0
  63. package/dist/commands/status_machine.js +149 -0
  64. package/dist/commands/status_machine.js.map +1 -0
  65. package/dist/commands/why.js +44 -31
  66. package/dist/commands/why.js.map +1 -1
  67. package/dist/consent.d.ts +111 -0
  68. package/dist/consent.js +203 -1
  69. package/dist/consent.js.map +1 -1
  70. package/dist/copy/error_codes.d.ts +222 -0
  71. package/dist/copy/error_codes.js +105 -0
  72. package/dist/copy/error_codes.js.map +1 -0
  73. package/dist/copy/index.d.ts +2 -2
  74. package/dist/copy/index.js +2 -2
  75. package/dist/copy/index.js.map +1 -1
  76. package/dist/copy/strings.d.ts +497 -29
  77. package/dist/copy/strings.js +601 -35
  78. package/dist/copy/strings.js.map +1 -1
  79. package/dist/copy/time.d.ts +11 -0
  80. package/dist/copy/time.js +18 -0
  81. package/dist/copy/time.js.map +1 -1
  82. package/dist/credential.d.ts +5 -1
  83. package/dist/credential.js +2 -2
  84. package/dist/credential.js.map +1 -1
  85. package/dist/git.d.ts +43 -0
  86. package/dist/git.js +95 -0
  87. package/dist/git.js.map +1 -1
  88. package/dist/hooks/entry.d.ts +18 -1
  89. package/dist/hooks/entry.js +110 -13
  90. package/dist/hooks/entry.js.map +1 -1
  91. package/dist/hooks/post_commit.js +3 -3
  92. package/dist/hooks/post_commit.js.map +1 -1
  93. package/dist/hooks/post_rewrite.js +6 -4
  94. package/dist/hooks/post_rewrite.js.map +1 -1
  95. package/dist/hooks/session_start.d.ts +93 -0
  96. package/dist/hooks/session_start.js +199 -0
  97. package/dist/hooks/session_start.js.map +1 -0
  98. package/dist/index.d.ts +1 -1
  99. package/dist/index.js +56 -8
  100. package/dist/index.js.map +1 -1
  101. package/dist/install.d.ts +57 -0
  102. package/dist/install.js +54 -20
  103. package/dist/install.js.map +1 -1
  104. package/dist/json.d.ts +32 -9
  105. package/dist/json.js +35 -8
  106. package/dist/json.js.map +1 -1
  107. package/dist/machine_state.d.ts +52 -0
  108. package/dist/machine_state.js +63 -0
  109. package/dist/machine_state.js.map +1 -0
  110. package/dist/oauth/session.d.ts +1 -0
  111. package/dist/org_pending.d.ts +52 -0
  112. package/dist/org_pending.js +109 -0
  113. package/dist/org_pending.js.map +1 -0
  114. package/dist/paths.d.ts +50 -0
  115. package/dist/paths.js +64 -0
  116. package/dist/paths.js.map +1 -1
  117. package/dist/picker.d.ts +180 -0
  118. package/dist/picker.js +477 -0
  119. package/dist/picker.js.map +1 -0
  120. package/dist/picker_checklist.d.ts +125 -0
  121. package/dist/picker_checklist.js +375 -0
  122. package/dist/picker_checklist.js.map +1 -0
  123. package/dist/policy.d.ts +54 -0
  124. package/dist/policy.js +47 -0
  125. package/dist/policy.js.map +1 -1
  126. package/dist/post.d.ts +50 -16
  127. package/dist/post.js +220 -16
  128. package/dist/post.js.map +1 -1
  129. package/dist/state.d.ts +6 -0
  130. package/dist/state.js +38 -1
  131. package/dist/state.js.map +1 -1
  132. package/dist/system_message.d.ts +8 -1
  133. package/dist/system_message.js +19 -1
  134. package/dist/system_message.js.map +1 -1
  135. package/dist/term.d.ts +28 -2
  136. package/dist/term.js +23 -2
  137. package/dist/term.js.map +1 -1
  138. package/dist/verb_help.d.ts +13 -0
  139. package/dist/verb_help.js +40 -0
  140. package/dist/verb_help.js.map +1 -0
  141. package/dist/version.d.ts +1 -1
  142. package/dist/version.js +1 -1
  143. package/package.json +5 -9
@@ -137,13 +137,27 @@ export type RedactionProfile = "claude-code" | "codex-cli" | "cursor";
137
137
  */
138
138
  export type DelegatedTranscripts = "discover" | "announce";
139
139
  /**
140
- * The three hook events DESIGN.md §13.7 names.
140
+ * The four hook events this client registers.
141
141
  *
142
- * Both members of this lane fire the same three; that is exactly why
142
+ * Every member of this lane fires the same four; that is exactly why
143
143
  * `hook_event_name` was REJECTED as an adapter selector (`CR-183`) — the events
144
144
  * are shared, so they discriminate nothing.
145
+ *
146
+ * ⛔ **`SessionStart` is the fourth, and it is the one that never uploads** (0.7.0,
147
+ * eng review D3 · A). `Stop`, `PreCompact` and `SessionEnd` flow into the code
148
+ * that reads a transcript and sends it; `SessionStart` must only install git
149
+ * hooks and say so, so `hooks/entry.ts` resolves the canonical event FIRST and
150
+ * branches to its own handler before the consent gate and before anything reads
151
+ * `transcript_path`. It sits in this list, and not in a table of its own, so the
152
+ * compiler makes each dialect declare its budget and its spelling.
153
+ *
154
+ * event Claude Code Codex Cursor reads a transcript
155
+ * Stop Stop Stop stop yes
156
+ * PreCompact PreCompact PreCompact preCompact yes
157
+ * SessionEnd SessionEnd SessionEnd sessionEnd yes
158
+ * SessionStart SessionStart SessionStart sessionStart NEVER
145
159
  */
146
- export declare const HOOK_EVENTS: readonly ["Stop", "PreCompact", "SessionEnd"];
160
+ export declare const HOOK_EVENTS: readonly ["Stop", "PreCompact", "SessionEnd", "SessionStart"];
147
161
  export type HookEventName = (typeof HOOK_EVENTS)[number];
148
162
  /**
149
163
  * One event's two clocks, and the relation between them is the point.
@@ -173,6 +187,77 @@ export interface AgentHookConfig {
173
187
  readonly path: (home: string, env: NodeJS.ProcessEnv) => string;
174
188
  readonly shape: HookDocShape;
175
189
  }
190
+ /**
191
+ * One transcript file a source adapter saw — `D11 · A`, `scan`.
192
+ *
193
+ * ⛔ **`stat`ed for its times and NEVER OPENED.** Everything the picker, the
194
+ * offer and the backfill count comes from names and times, because opening a
195
+ * transcript of a repo nobody granted is the read the consent design forbids. A
196
+ * field that could only be filled by reading the file does not belong here.
197
+ */
198
+ export interface SourceSession {
199
+ readonly path: string;
200
+ readonly mtimeMs: number;
201
+ /**
202
+ * When the agent itself says the session began, where it keeps that. Codex's
203
+ * index does (`created_at`); the folder-keyed agents do not, and for them the
204
+ * file's mtime stands in everywhere a date is shown.
205
+ */
206
+ readonly startedMs?: number;
207
+ }
208
+ /** One agent project folder: its encoded name, and the sessions found in it. */
209
+ export interface SourceFolder {
210
+ readonly name: string;
211
+ readonly sessions: readonly SourceSession[];
212
+ }
213
+ /** One index row: a session whose agent already recorded the folder it ran in. */
214
+ export interface SourceRow {
215
+ readonly cwd: string;
216
+ readonly origin: string | null;
217
+ readonly session: SourceSession;
218
+ }
219
+ /** Why a store could not be listed. Each is a different line in `Not listed here`. */
220
+ export type UnreadableReason = "unrecognised index" | "index locked" | "unreadable";
221
+ /**
222
+ * What one agent's store yields, without a transcript being opened.
223
+ *
224
+ * The two shapes are the two ways the three agents keep sessions, and they are
225
+ * not interchangeable: a FOLDER-keyed agent (Claude Code, Cursor) names the
226
+ * folder by lossy encoding, so the engine has to resolve it against the
227
+ * filesystem and can fail to (`encode` and `isHashed` are what that walk needs);
228
+ * an INDEXED agent (Codex) records the real `cwd`, so it is never ambiguous.
229
+ *
230
+ * ⛔ **`unreadable` is an answer, not an exception.** One agent's store failing
231
+ * hides that agent's rows and nothing else's, and `scan` reports it; a thrown
232
+ * error here would take the other two agents' rows down with it.
233
+ */
234
+ export type SessionSource = {
235
+ readonly kind: "folders";
236
+ readonly folders: readonly SourceFolder[];
237
+ /** This dialect's `encodeProjectPath`, handed over so the walk uses the same one. */
238
+ readonly encode: (path: string) => string;
239
+ /** Is this folder name a truncated-and-hashed one, which cannot be inverted? */
240
+ readonly isHashed: (name: string) => boolean;
241
+ /**
242
+ * Paths the agent ITSELF recorded having run in — a second skip signal. A
243
+ * folder whose name encodes to a recorded path that no longer exists is one
244
+ * whose sessions may belong to that deleted path, whatever else now matches.
245
+ * Empty for an agent that keeps no such record.
246
+ */
247
+ readonly recorded: readonly string[];
248
+ } | {
249
+ readonly kind: "rows";
250
+ readonly rows: readonly SourceRow[];
251
+ /**
252
+ * Rollout files on disk that no index row names — counted, never opened. Only
253
+ * the index's own agent has the notion; a folder-keyed agent has no index to
254
+ * be missing from.
255
+ */
256
+ readonly unindexed: number;
257
+ } | {
258
+ readonly kind: "unreadable";
259
+ readonly reason: UnreadableReason;
260
+ };
176
261
  /** Where `connect` found the session running right now. */
177
262
  export interface LocatedTranscript {
178
263
  readonly path: string;
@@ -260,11 +345,14 @@ export interface TranscriptDialect {
260
345
  * (lost permanently — that event has no next invocation) and no gap-stamp
261
346
  * reserve. Nothing raises and the hook still exits 0.
262
347
  *
263
- * ⚠ **Widening `HookEventName` was the obvious fix and it is the wrong one.**
264
- * The three map 1:1, so a rename belongs in a per-dialect table; widening the
265
- * union would make `Readonly<Record<HookEventName, EventBudget>>` non-total
266
- * over the real event set and force every dialect to declare budgets for
267
- * events it can never receive.
348
+ * ⚠ **Widening `HookEventName` was the wrong fix for a RENAME, and it is the right
349
+ * one for a new event** (0.7.0, eng review D3 · A). The first three map 1:1, so a
350
+ * rename belongs in a per-dialect table. `SessionStart` is a new event that ALL
351
+ * THREE agents deliver (Claude Code and Codex measured live, Cursor read out of
352
+ * the shipped bundle — see each dialect), so the argument that stopped the
353
+ * widening — "force every dialect to declare budgets for events it can never
354
+ * receive" — no longer holds, and the compiler is now the thing that makes a
355
+ * dialect added later declare its `SessionStart` spelling and budget.
268
356
  *
269
357
  * ## ⚠ MEASURED per dialect, and the two measurements differ in kind
270
358
  *
@@ -304,6 +392,28 @@ export interface TranscriptDialect {
304
392
  * is exactly the cap Codex silently clamps to — measured, not inferred.
305
393
  */
306
394
  readonly hookConfig: AgentHookConfig;
395
+ /**
396
+ * ⛔ **WHAT SESSIONS DOES THIS AGENT HAVE ON DISK — without opening one.**
397
+ * `D11 · A`, `scan`, the picker and the backfill offer.
398
+ *
399
+ * Listed by NAME and TIME only (`SourceSession`). The consent design is that no
400
+ * path opens a transcript of an ungranted repo, so the thing that decides what
401
+ * to offer cannot be the thing that reads the transcripts. ⚠ Each adapter fails
402
+ * closed on its own: an unreadable store is `{ kind: "unreadable" }` and hides
403
+ * that agent's rows only.
404
+ *
405
+ * ⛔ **REQUIRED, not optional, and that is `admitsFile`'s device.** A dialect
406
+ * added later must ANSWER how its sessions are found, and the compiler is what
407
+ * asks; an agent that cannot answer without opening a transcript returns
408
+ * `unreadable` and says so, which is a decision a reviewer sees.
409
+ *
410
+ * ⚠ A MEASURED requirement and not a speculative one: Open Question 3 found the
411
+ * two folder-keyed agents encode a path DIFFERENTLY (Claude Code per UTF-16
412
+ * unit with no collapsing; Cursor collapsing runs and trimming the ends), and
413
+ * the shipped `connect` looked in the wrong folder for any repo path holding an
414
+ * `_` or a space because it assumed one rule.
415
+ */
416
+ readonly sessionSource: (home: string, env: NodeJS.ProcessEnv) => SessionSource;
307
417
  /** Per-event clocks. See `EventBudget` for the relation that binds them. */
308
418
  readonly events: Readonly<Record<HookEventName, EventBudget>>;
309
419
  readonly delegatedTranscripts: DelegatedTranscripts;
@@ -57,11 +57,25 @@
57
57
  */
58
58
  export const UNKNOWN_AGENT_ID = "unknown";
59
59
  /**
60
- * The three hook events DESIGN.md §13.7 names.
60
+ * The four hook events this client registers.
61
61
  *
62
- * Both members of this lane fire the same three; that is exactly why
62
+ * Every member of this lane fires the same four; that is exactly why
63
63
  * `hook_event_name` was REJECTED as an adapter selector (`CR-183`) — the events
64
64
  * are shared, so they discriminate nothing.
65
+ *
66
+ * ⛔ **`SessionStart` is the fourth, and it is the one that never uploads** (0.7.0,
67
+ * eng review D3 · A). `Stop`, `PreCompact` and `SessionEnd` flow into the code
68
+ * that reads a transcript and sends it; `SessionStart` must only install git
69
+ * hooks and say so, so `hooks/entry.ts` resolves the canonical event FIRST and
70
+ * branches to its own handler before the consent gate and before anything reads
71
+ * `transcript_path`. It sits in this list, and not in a table of its own, so the
72
+ * compiler makes each dialect declare its budget and its spelling.
73
+ *
74
+ * event Claude Code Codex Cursor reads a transcript
75
+ * Stop Stop Stop stop yes
76
+ * PreCompact PreCompact PreCompact preCompact yes
77
+ * SessionEnd SessionEnd SessionEnd sessionEnd yes
78
+ * SessionStart SessionStart SessionStart sessionStart NEVER
65
79
  */
66
- export const HOOK_EVENTS = ["Stop", "PreCompact", "SessionEnd"];
80
+ export const HOOK_EVENTS = ["Stop", "PreCompact", "SessionEnd", "SessionStart"];
67
81
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AA4BH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AA6D1C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAU,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AA4BH;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AA6D1C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,CAAU,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The org-approval-pending block — style guide §10.2's "held" beat, D65 §DR6, D81. One definition for the two places that
3
+ * print it: `connect`, on the 403 its own test capture got, and `status`, for a repo whose last upload got that same 403.
4
+ *
5
+ * ⚠ EXISTING COPY ONLY. `status` adds no sentence of its own; it prints what `connect` prints, so the two cannot drift
6
+ * (`org_pending.ts` records that the 403 happened). The shared clause renders on EVERY branch and `owners_notified` `> 0`
7
+ * ADDS to it (D81); `null` and `0` never claim a notification.
8
+ */
9
+ export declare function approvalPendingBlock(colour: boolean, repoSlug: string, ownersNotified: number | null): string[];
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The org-approval-pending block — style guide §10.2's "held" beat, D65 §DR6, D81. One definition for the two places that
3
+ * print it: `connect`, on the 403 its own test capture got, and `status`, for a repo whose last upload got that same 403.
4
+ *
5
+ * ⚠ EXISTING COPY ONLY. `status` adds no sentence of its own; it prints what `connect` prints, so the two cannot drift
6
+ * (`org_pending.ts` records that the 403 happened). The shared clause renders on EVERY branch and `owners_notified` `> 0`
7
+ * ADDS to it (D81); `null` and `0` never claim a notification.
8
+ */
9
+ import { CONNECT, URLS } from "../copy/index.js";
10
+ import { renderErrorBlock } from "../term.js";
11
+ export function approvalPendingBlock(colour, repoSlug, ownersNotified) {
12
+ const why = ownersNotified !== null && ownersNotified > 0
13
+ ? `${CONNECT.pendingRecorded} ${CONNECT.pendingNotified(ownersNotified)}`
14
+ : CONNECT.pendingRecorded;
15
+ return renderErrorBlock({
16
+ kind: "warn",
17
+ what: CONNECT.pendingWhat,
18
+ why: [CONNECT.pendingWhy(repoSlug), why, CONNECT.pendingBuffered],
19
+ fixLabel: CONNECT.pendingTrackLabel,
20
+ fixes: [URLS.captureAccess],
21
+ }, colour);
22
+ }
23
+ //# sourceMappingURL=approval_pending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval_pending.js","sourceRoot":"","sources":["../../src/commands/approval_pending.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,UAAU,oBAAoB,CAAC,MAAe,EAAE,QAAgB,EAAE,cAA6B;IACnG,MAAM,GAAG,GACP,cAAc,KAAK,IAAI,IAAI,cAAc,GAAG,CAAC;QAC3C,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE;QACzE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;IAC9B,OAAO,gBAAgB,CACrB;QACE,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,OAAO,CAAC,WAAW;QACzB,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,eAAe,CAAC;QACjE,QAAQ,EAAE,OAAO,CAAC,iBAAiB;QACnC,KAAK,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC;KAC5B,EACD,MAAM,CACP,CAAC;AACJ,CAAC"}
@@ -73,11 +73,13 @@
73
73
  * @provenance vibecommit-mcp src/transport/server.ts — POST /oauth/ingest-credential is the mint route, verified
74
74
  * @provenance vibecommit-web app/api/ingest-credentials/route.ts — the dashboard producer the paste path still serves, verified
75
75
  */
76
- import { AUTH, COMMANDS, DEVICE, HELP, URLS } from "../copy/index.js";
77
- import { authCommandFor } from "../install.js";
76
+ import { AUTH, DEVICE, PICKER, URLS } from "../copy/index.js";
77
+ import { renderVerbHelp } from "../verb_help.js";
78
+ import { authCommandFor, channelCommand } from "../install.js";
78
79
  import { writeLines } from "./context.js";
79
80
  import { EXIT } from "../exit.js";
80
81
  import { INGEST_TOKEN_PREFIX, IngestCredential, saveCredential } from "../credential.js";
82
+ import { machineCapture, machineSummary } from "../machine_state.js";
81
83
  import { credentialsPath } from "../paths.js";
82
84
  import { resolveProjectKeys } from "../project.js";
83
85
  import { paint, renderErrorBlock, tildePath, wrap } from "../term.js";
@@ -98,10 +100,11 @@ export async function auth(ctx, argv, deps = {}) {
98
100
  if (positional.length > 0) {
99
101
  writeLines(ctx.stderr, renderErrorBlock({
100
102
  kind: "bad",
103
+ code: "CREDENTIAL_IN_ARGV",
101
104
  what: AUTH.argvWhat,
102
105
  why: [AUTH.argvWhy],
103
106
  fixLabel: AUTH.argvFix,
104
- fixes: [AUTH.argvFixPrompt, AUTH.argvFixStdin],
107
+ fixes: [channelCommand(ctx.env, ctx.selfPath, AUTH.argvFixPrompt), AUTH.argvFixStdin(authCommandFor(ctx.env, ctx.selfPath))],
105
108
  }, ctx.colour));
106
109
  return EXIT.usage;
107
110
  }
@@ -110,7 +113,8 @@ export async function auth(ctx, argv, deps = {}) {
110
113
  // exactly that a usage error) runs first. By the time this is reached, `positional.length === 0`, so
111
114
  // `--help` still has no side effects — nothing below has read or written anything yet either way.
112
115
  if (argv.includes("--help") || argv.includes("-h")) {
113
- writeLines(ctx.stdout, [HELP.commands.auth]);
116
+ const helpVerb = "auth"; // an identifier, not a literal, at the writer: `copy-inline-literals` follows argument positions
117
+ writeLines(ctx.stdout, renderVerbHelp(helpVerb));
114
118
  return EXIT.ok;
115
119
  }
116
120
  const piped = argv.includes(STDIN_FLAG) || !ctx.stdinIsTty;
@@ -119,6 +123,7 @@ export async function auth(ctx, argv, deps = {}) {
119
123
  writeLines(ctx.stderr, renderErrorBlock(piped
120
124
  ? {
121
125
  kind: "bad",
126
+ code: "SIGNIN_CODE_NEEDS_TTY",
122
127
  what: DEVICE.needsTerminalWhat,
123
128
  why: [DEVICE.needsTerminalWhy],
124
129
  fixLabel: DEVICE.needsTerminalFix,
@@ -126,10 +131,11 @@ export async function auth(ctx, argv, deps = {}) {
126
131
  }
127
132
  : {
128
133
  kind: "bad",
134
+ code: "AUTH_FLAGS_CONFLICT",
129
135
  what: DEVICE.conflictWhat,
130
136
  why: [DEVICE.conflictWhy],
131
137
  fixLabel: DEVICE.conflictFix,
132
- fixes: [COMMANDS.auth, `${COMMANDS.auth} ${PASTE_FLAG}`],
138
+ fixes: [authCommandFor(ctx.env, ctx.selfPath), `${authCommandFor(ctx.env, ctx.selfPath)} ${PASTE_FLAG}`],
133
139
  }, ctx.colour));
134
140
  return EXIT.usage;
135
141
  }
@@ -161,10 +167,11 @@ export async function auth(ctx, argv, deps = {}) {
161
167
  if (secret === "") {
162
168
  writeLines(ctx.stderr, renderErrorBlock({
163
169
  kind: "bad",
170
+ code: "CREDENTIAL_EMPTY",
164
171
  what: AUTH.emptyWhat,
165
172
  why: [AUTH.emptyWhy],
166
- fixLabel: AUTH.emptyFix,
167
- fixes: [URLS.settings],
173
+ fixLabel: AUTH.mintFix(URLS.settings),
174
+ fixes: AUTH.mintCommands(authCommandFor(ctx.env, ctx.selfPath)),
168
175
  }, ctx.colour));
169
176
  return EXIT.failure;
170
177
  }
@@ -175,10 +182,11 @@ export async function auth(ctx, argv, deps = {}) {
175
182
  if (!secret.startsWith(INGEST_TOKEN_PREFIX)) {
176
183
  writeLines(ctx.stderr, renderErrorBlock({
177
184
  kind: "bad",
185
+ code: "CREDENTIAL_NOT_INGEST",
178
186
  what: AUTH.wrongClassWhat,
179
187
  why: [AUTH.wrongClassWhy],
180
- fixLabel: AUTH.wrongClassFix,
181
- fixes: [URLS.settings],
188
+ fixLabel: AUTH.mintFix(URLS.settings),
189
+ fixes: AUTH.mintCommands(authCommandFor(ctx.env, ctx.selfPath)),
182
190
  }, ctx.colour));
183
191
  return EXIT.failure;
184
192
  }
@@ -194,14 +202,26 @@ export async function auth(ctx, argv, deps = {}) {
194
202
  * block and the exit are exactly what they were.
195
203
  */
196
204
  async function saved(ctx, deps, piped, notes = []) {
197
- const shouldConnect = !piped && deps.connect !== undefined && resolveProjectKeys(ctx.cwd) !== null;
205
+ // 0.7.0, D32 · 26A (supersedes only L6's "outside a repository" clause). Inside a work tree the chain is unchanged (L2).
206
+ // Outside one, the continuation is the picker flow, and only on a machine that has nothing captured yet; a machine that
207
+ // already captures prints one line and no picker. ⛔ BOTH of 0.6.0's other guards stay: `piped` (`--stdin`, or no
208
+ // terminal) NEVER continues, so a CI runner with no grant still saves the credential and exits 0 instead of reaching the
209
+ // non-terminal refusal and exiting 2; and the continuation gets no `browser` (`index.ts` hands `connect` none), so a
210
+ // second sign-in cannot start.
211
+ const inRepo = resolveProjectKeys(ctx.cwd) !== null;
212
+ const capture = inRepo || piped || deps.connect === undefined ? null : machineCapture(ctx.home);
213
+ const shouldConnect = !piped && deps.connect !== undefined && (inRepo || capture?.kind === "none");
214
+ const staysOn = capture?.kind === "on";
198
215
  writeLines(ctx.stdout, [
199
216
  ...renderErrorBlock({
200
217
  kind: "ok",
201
218
  what: AUTH.savedWhat,
202
219
  why: [AUTH.savedWhy(tildePath(credentialsPath(ctx.home), ctx.home)), ...notes],
203
- ...(shouldConnect ? {} : { fixLabel: AUTH.nextLabel, fixes: [AUTH.nextCommand] }),
220
+ ...(shouldConnect || staysOn
221
+ ? {}
222
+ : { fixLabel: AUTH.nextLabel, fixes: [channelCommand(ctx.env, ctx.selfPath, AUTH.nextCommand)] }),
204
223
  }, ctx.colour),
224
+ ...(capture?.kind === "on" ? ["", ...wrap(PICKER.authStays(machineSummary(capture)), 2)] : []),
205
225
  ]);
206
226
  if (shouldConnect && deps.connect !== undefined)
207
227
  return await deps.connect(ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * `vibecommit backfill` — style guide §10.14, design review D14 · 8A, D15 · 9A; capture 0.7.0, PR 5.
3
+ *
4
+ * Claude Code keeps about 30 days of transcripts and then deletes them. This uploads the ones still on this machine,
5
+ * for the repositories the user chose, once they say yes — and says plainly that a backfilled session arrives WITHOUT
6
+ * commit links. It is offered by `connect` (`backfillForConnect`, for its seam) and is a verb in its own right.
7
+ *
8
+ * ## What it is made of
9
+ *
10
+ * - **Which sessions** come from the scan engine (`scanMachine`): folder names and Codex's index, no transcript opened.
11
+ * Only repos the consent gate admits (`isCaptureAllowed`) are offered, and only sessions with unsent bytes. A session in
12
+ * a clone with no reflog, over the 50 MiB cap, or from Cursor is left OUT of the count and named in a muted line, so the
13
+ * offer never promises an upload the run would skip.
14
+ * - **How one is sent** is `closeSession` (the code `finalize` runs): sub-agent streams first, then the main stream, then
15
+ * — only for a session quiet for 30 minutes — the seal. Through `deliver()` with `byHook: false`, each clone's own
16
+ * confinement roots, and the hooks' own offset ledger, so a re-run sends only what is new.
17
+ * - **Each session goes through its OWN agent's dialect** (transport, redaction profile, confinement root); the file is
18
+ * admitted only if it lies under that agent's transcript root (`admitsTranscript`), which for Codex is
19
+ * `~/.codex/sessions`.
20
+ * - **The consent gate runs again before EACH session.** The offer is a plan; `vibecommit off` in another terminal during a
21
+ * long run must stop the rest of that repo, so `uploadOne` asks `isCaptureAllowed` first.
22
+ *
23
+ * ## ⛔ The one transcript read before an upload (exception 1)
24
+ *
25
+ * Alexander ruled (2026-10-24 10:53 EST) that the deleted-sibling case — a folder whose original path is gone and whose
26
+ * encoded name now resolves to a different surviving repo — is CLOSED, not accepted. So, after both the grant y/N and the
27
+ * backfill y/N, one session at a time just before its upload, a Claude Code session has ONE value read from its
28
+ * transcript: the `cwd` of the first record that carries one (`firstRecordedCwd`). It uploads only if that path is
29
+ * absolute, exists, is a git work tree, and is the same clone (common dir) the folder was matched to. This is an exception
30
+ * to D60 §D6 (the client may display transcripts but not analyze them), and it is the ONLY one: the scan, the picker and
31
+ * the offer open no transcript, and nothing else is read from this one. Codex needs no check (its index already holds the
32
+ * real `cwd`). Cursor records no folder at all (measured, STATIC), so its sessions are never uploaded and are counted instead.
33
+ */
34
+ import type { AgentId } from "../agents/types.js";
35
+ import { type CommandContext } from "./context.js";
36
+ import { closeSession } from "./finalize.js";
37
+ import { scanMachine, type ScanResult } from "./scan.js";
38
+ /** One session that may be uploaded: a file the scan attributed to a granted repo, with unsent bytes. */
39
+ interface Candidate {
40
+ readonly agent: AgentId;
41
+ /** The repo's slug as the plan names it — what the consent gate is asked about again before the upload. */
42
+ readonly slug: string;
43
+ readonly sessionId: string | null;
44
+ readonly path: string;
45
+ readonly dir: string;
46
+ readonly clone: string;
47
+ readonly commonDir: string;
48
+ readonly mtimeMs: number;
49
+ readonly startedMs: number;
50
+ readonly size: number;
51
+ }
52
+ interface RepoPlan {
53
+ readonly slug: string;
54
+ readonly candidates: readonly Candidate[];
55
+ }
56
+ export interface BackfillPlan {
57
+ readonly repos: readonly RepoPlan[];
58
+ /** Sessions that will be offered. */
59
+ readonly total: number;
60
+ /** Cursor sessions in the repos chosen that have unsent bytes. Never uploaded (OQ 7), so never in `total`. */
61
+ readonly cursorSessions: number;
62
+ /** Sessions over the 50 MiB cap, left out of `total` and named in one line. */
63
+ readonly overSize: number;
64
+ /** Sessions in a clone with no reflog, left out of `total`: the reflog test is the only guard against a reused path. */
65
+ readonly noReflog: readonly {
66
+ readonly path: string;
67
+ readonly sessions: number;
68
+ }[];
69
+ }
70
+ /** How `runBackfill` ended — what a caller (`connect`) needs to choose its closing URL. */
71
+ export type BackfillEnd = "uploaded" | "declined" | "nothing" | "interrupted" | "stopped" | "refused";
72
+ export interface BackfillResult {
73
+ readonly exit: number;
74
+ readonly end: BackfillEnd;
75
+ readonly uploaded: number;
76
+ }
77
+ /** What the folder check found. A transcript that cannot be opened is a different fact from one that records no folder. */
78
+ export type RecordedCwd = {
79
+ readonly kind: "found";
80
+ readonly cwd: string;
81
+ } | {
82
+ readonly kind: "none";
83
+ } | {
84
+ readonly kind: "unreadable";
85
+ };
86
+ export interface BackfillDeps {
87
+ readonly scanMachine?: typeof scanMachine;
88
+ readonly closeSession?: typeof closeSession;
89
+ /** Ctrl+C during the upload. Absent: a real SIGINT listener for the duration of the run. */
90
+ readonly signal?: AbortSignal;
91
+ /** How long a repo runs before its `…` line prints. A test seam. */
92
+ readonly slowAfterMs?: number;
93
+ /** The folder check's reader — a test seam. Defaults to `firstRecordedCwd`. */
94
+ readonly readCwd?: (path: string) => RecordedCwd;
95
+ /** Run inside `connect` (the repos are the ones it just showed). Absent: the verb on its own. */
96
+ readonly fromConnect?: boolean;
97
+ }
98
+ /**
99
+ * Plan the offer from a scan. ⛔ Opens no transcript: a session's size is its `lstat`, and "unsent" is a comparison with the
100
+ * offset ledger the hooks already keep — a state file, not a transcript.
101
+ */
102
+ export declare function planBackfill(ctx: Pick<CommandContext, "home" | "env">, scanFn?: typeof scanMachine): BackfillPlan;
103
+ /**
104
+ * The offer, as lines (§10.14, as VQ redrew it after the PR 5 review). SGR roles: `text` the repo rows and the question,
105
+ * `muted` the folder rows and the not-offered lines. `nowMs` is an argument so a golden file pins the clock.
106
+ *
107
+ * ## Widths
108
+ *
109
+ * ⛔ A folder path is cut from the LEFT to fit 80 columns whatever the slug width (§13.4, D27): the folder column is never
110
+ * narrowed to the slug column. The right-hand text (counts, agents) starts at one shared column: the slug column, or further
111
+ * right if a path needs it, and never so far that a line passes 80.
112
+ */
113
+ export declare function renderOffer(plan: BackfillPlan, options: {
114
+ colour: boolean;
115
+ home: string;
116
+ nowMs: number;
117
+ standalone?: boolean;
118
+ }): string[];
119
+ /**
120
+ * The `cwd` of the transcript's first record that has one. ⛔ ONE VALUE. The record is parsed to read its top-level `cwd`
121
+ * and nothing else is looked at, kept, returned or sent; a line that does not mention `"cwd"` is not parsed at all.
122
+ * `none` (no record carries one in the first `FOLDER_CHECK_MAX_BYTES`) and `unreadable` (the file cannot be opened) both
123
+ * fail closed — the session is skipped — but they are different facts and are told apart.
124
+ */
125
+ export declare function firstRecordedCwd(path: string): RecordedCwd;
126
+ /**
127
+ * Ask, then upload. Everything `backfill` and `connect` share. `connect` calls it AFTER its own grant y/N and its
128
+ * "Capture is on" block (through `backfillForConnect`), and chooses its closing URL from `end`.
129
+ */
130
+ export declare function runBackfill(ctx: CommandContext, deps?: BackfillDeps): Promise<BackfillResult>;
131
+ /**
132
+ * What `connect` needs back to choose its closing URL (D19). `stopped` means the run printed its stop block with the
133
+ * `vibecommit backfill` resume fix: `connect` still exits 0, because capture is already on (D18).
134
+ */
135
+ export type BackfillForConnect = {
136
+ readonly kind: "declined";
137
+ } | {
138
+ readonly kind: "ran";
139
+ readonly uploaded: number;
140
+ } | {
141
+ readonly kind: "stopped";
142
+ readonly uploaded: number;
143
+ };
144
+ /**
145
+ * `connect`'s backfill step. It is handed the repos `connect`'s own scan found (the picker's), asks the backfill y/N
146
+ * and runs, so the picker's scan is not repeated. It owns its Ctrl+C: stopping a backfill is `ran`, and the run has
147
+ * already printed the stop line. A no, nothing waiting, and a refusal are all `declined`: `connect` ends on `/app`.
148
+ *
149
+ * ⚠ The consent gate runs HERE, over what `connect` just granted, not over what the scan saw before the grant: a repo the
150
+ * user did not choose is dropped by `planBackfill` however it got into `repos`; so is a clone with no reflog, which the plan asks
151
+ * git about itself.
152
+ */
153
+ export declare function backfillForConnect(ctx: CommandContext, repos: ScanResult["repos"], deps?: BackfillDeps): Promise<BackfillForConnect>;
154
+ /**
155
+ * `vibecommit backfill`. Exit `0` done (held sessions included: org-pending is 0, as in §10.2), `1` stopped with the resume
156
+ * fix, `2` no terminal, `3` not connected, `4` nothing to upload. It ends on the dashboard URL after a completed run or a
157
+ * no, and on its resume fix after a stop.
158
+ */
159
+ export declare function backfill(ctx: CommandContext, argv?: readonly string[], deps?: BackfillDeps): Promise<number>;
160
+ export {};