@vibe-commit/capture 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/agents/claude_code.d.ts +11 -0
  4. package/dist/agents/claude_code.js +162 -0
  5. package/dist/agents/claude_code.js.map +1 -0
  6. package/dist/agents/codex_cli.d.ts +15 -0
  7. package/dist/agents/codex_cli.js +67 -0
  8. package/dist/agents/codex_cli.js.map +1 -0
  9. package/dist/agents/cursor.d.ts +21 -0
  10. package/dist/agents/cursor.js +89 -0
  11. package/dist/agents/cursor.js.map +1 -0
  12. package/dist/agents/registry.d.ts +184 -0
  13. package/dist/agents/registry.js +212 -0
  14. package/dist/agents/registry.js.map +1 -0
  15. package/dist/agents/types.d.ts +279 -0
  16. package/dist/agents/types.js +67 -0
  17. package/dist/agents/types.js.map +1 -0
  18. package/dist/commands/connect.d.ts +36 -0
  19. package/dist/commands/connect.js +499 -0
  20. package/dist/commands/connect.js.map +1 -0
  21. package/dist/commands/context.d.ts +45 -0
  22. package/dist/commands/context.js +14 -0
  23. package/dist/commands/context.js.map +1 -0
  24. package/dist/commands/report.d.ts +105 -0
  25. package/dist/commands/report.js +430 -0
  26. package/dist/commands/report.js.map +1 -0
  27. package/dist/commands/status.d.ts +29 -0
  28. package/dist/commands/status.js +236 -0
  29. package/dist/commands/status.js.map +1 -0
  30. package/dist/commands/why.d.ts +349 -0
  31. package/dist/commands/why.js +937 -0
  32. package/dist/commands/why.js.map +1 -0
  33. package/dist/consent.d.ts +59 -0
  34. package/dist/consent.js +111 -0
  35. package/dist/consent.js.map +1 -0
  36. package/dist/copy/claims.d.ts +79 -0
  37. package/dist/copy/claims.js +343 -0
  38. package/dist/copy/claims.js.map +1 -0
  39. package/dist/copy/grades.d.ts +117 -0
  40. package/dist/copy/grades.js +96 -0
  41. package/dist/copy/grades.js.map +1 -0
  42. package/dist/copy/index.d.ts +12 -0
  43. package/dist/copy/index.js +13 -0
  44. package/dist/copy/index.js.map +1 -0
  45. package/dist/copy/strings.d.ts +1328 -0
  46. package/dist/copy/strings.js +1243 -0
  47. package/dist/copy/strings.js.map +1 -0
  48. package/dist/copy/time.d.ts +27 -0
  49. package/dist/copy/time.js +43 -0
  50. package/dist/copy/time.js.map +1 -0
  51. package/dist/credential.d.ts +211 -0
  52. package/dist/credential.js +298 -0
  53. package/dist/credential.js.map +1 -0
  54. package/dist/exit.d.ts +59 -0
  55. package/dist/exit.js +60 -0
  56. package/dist/exit.js.map +1 -0
  57. package/dist/git.d.ts +299 -0
  58. package/dist/git.js +653 -0
  59. package/dist/git.js.map +1 -0
  60. package/dist/hooks/entry.d.ts +277 -0
  61. package/dist/hooks/entry.js +1092 -0
  62. package/dist/hooks/entry.js.map +1 -0
  63. package/dist/hooks/post_commit.d.ts +59 -0
  64. package/dist/hooks/post_commit.js +160 -0
  65. package/dist/hooks/post_commit.js.map +1 -0
  66. package/dist/hooks/post_rewrite.d.ts +59 -0
  67. package/dist/hooks/post_rewrite.js +161 -0
  68. package/dist/hooks/post_rewrite.js.map +1 -0
  69. package/dist/hooks/session_end.d.ts +101 -0
  70. package/dist/hooks/session_end.js +120 -0
  71. package/dist/hooks/session_end.js.map +1 -0
  72. package/dist/index.d.ts +34 -0
  73. package/dist/index.js +360 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/install.d.ts +153 -0
  76. package/dist/install.js +284 -0
  77. package/dist/install.js.map +1 -0
  78. package/dist/json.d.ts +100 -0
  79. package/dist/json.js +94 -0
  80. package/dist/json.js.map +1 -0
  81. package/dist/oauth/discovery.d.ts +96 -0
  82. package/dist/oauth/discovery.js +163 -0
  83. package/dist/oauth/discovery.js.map +1 -0
  84. package/dist/oauth/lock.d.ts +100 -0
  85. package/dist/oauth/lock.js +287 -0
  86. package/dist/oauth/lock.js.map +1 -0
  87. package/dist/oauth/loopback.d.ts +85 -0
  88. package/dist/oauth/loopback.js +124 -0
  89. package/dist/oauth/loopback.js.map +1 -0
  90. package/dist/oauth/pkce.d.ts +58 -0
  91. package/dist/oauth/pkce.js +77 -0
  92. package/dist/oauth/pkce.js.map +1 -0
  93. package/dist/oauth/read.d.ts +120 -0
  94. package/dist/oauth/read.js +144 -0
  95. package/dist/oauth/read.js.map +1 -0
  96. package/dist/oauth/session.d.ts +80 -0
  97. package/dist/oauth/session.js +112 -0
  98. package/dist/oauth/session.js.map +1 -0
  99. package/dist/oauth/signin.d.ts +178 -0
  100. package/dist/oauth/signin.js +225 -0
  101. package/dist/oauth/signin.js.map +1 -0
  102. package/dist/oauth/token.d.ts +139 -0
  103. package/dist/oauth/token.js +183 -0
  104. package/dist/oauth/token.js.map +1 -0
  105. package/dist/paths.d.ts +366 -0
  106. package/dist/paths.js +460 -0
  107. package/dist/paths.js.map +1 -0
  108. package/dist/policy.d.ts +195 -0
  109. package/dist/policy.js +244 -0
  110. package/dist/policy.js.map +1 -0
  111. package/dist/post.d.ts +302 -0
  112. package/dist/post.js +384 -0
  113. package/dist/post.js.map +1 -0
  114. package/dist/project.d.ts +99 -0
  115. package/dist/project.js +192 -0
  116. package/dist/project.js.map +1 -0
  117. package/dist/redact.d.ts +175 -0
  118. package/dist/redact.js +700 -0
  119. package/dist/redact.js.map +1 -0
  120. package/dist/runtime.d.ts +30 -0
  121. package/dist/runtime.js +37 -0
  122. package/dist/runtime.js.map +1 -0
  123. package/dist/spawn_budget.d.ts +101 -0
  124. package/dist/spawn_budget.js +124 -0
  125. package/dist/spawn_budget.js.map +1 -0
  126. package/dist/spool.d.ts +390 -0
  127. package/dist/spool.js +556 -0
  128. package/dist/spool.js.map +1 -0
  129. package/dist/state.d.ts +109 -0
  130. package/dist/state.js +217 -0
  131. package/dist/state.js.map +1 -0
  132. package/dist/system_message.d.ts +101 -0
  133. package/dist/system_message.js +141 -0
  134. package/dist/system_message.js.map +1 -0
  135. package/dist/term.d.ts +112 -0
  136. package/dist/term.js +134 -0
  137. package/dist/term.js.map +1 -0
  138. package/dist/version.d.ts +16 -0
  139. package/dist/version.js +17 -0
  140. package/dist/version.js.map +1 -0
  141. package/package.json +50 -0
@@ -0,0 +1,499 @@
1
+ /**
2
+ * `vibecommit connect` — the install moment.
3
+ *
4
+ * This task owns the CONSENT GATE and the runtime floor. Two later tasks own the
5
+ * rest of the screen and must not be front-run:
6
+ * - `CR-111d` landed the disclosure copy, transcribed from style guide §10.2.
7
+ * D64 falsified the trust argument the earlier draft used (this repo is
8
+ * private, so "read the source" is false and BLOCKED). The approved
9
+ * replacement links `Vibe-Commit/claude-plugin`, which vendors the exact
10
+ * binary — and "inspect the build" is WEAKER than "read the source" and may
11
+ * not be dressed up as it.
12
+ * - `CR-025` owns the ending: capture the current session and print the live
13
+ * `/app/commits/<sha>` URL. The seam is marked below.
14
+ *
15
+ * `CR-084d` lands the browser sign-in beat (PKCE loopback → a user-principal
16
+ * token for the READ lane) behind `--sign-in`. It is opt-in and not part of the
17
+ * default install for one reason: **sign-in does not produce an ingest
18
+ * credential**, so it cannot replace the beat below. No route in
19
+ * `vibecommit-mcp` mints a `vcik_` — `mintIngestCredential` exists and has no
20
+ * call site outside that repo's own tests — so `VIBECOMMIT_TOKEN` remains the
21
+ * supported path for capture, exactly as before.
22
+ *
23
+ * @provenance vibecommit-mcp src/oauth/ingest_credential.ts — no route mints one, verified
24
+ */
25
+ import { CONNECT, COMMANDS, COMMIT_HOOK, ERRORS, HELP, PATH_CLASH, RUNTIME, SIGNIN, URLS, } from "../copy/index.js";
26
+ import { dialectFor } from "../agents/registry.js";
27
+ import { grantProject, isAffirmative, isProjectAllowed } from "../consent.js";
28
+ import { loadCredential } from "../credential.js";
29
+ import { EXIT } from "../exit.js";
30
+ import { resolveRepoSlug } from "../git.js";
31
+ import { confinementRoots, readSpan, DEFAULT_HOOK_BUDGET_MS } from "../hooks/entry.js";
32
+ import { classifyInstall, installPostCommitHook } from "../install.js";
33
+ import { mcpUrl } from "../oauth/discovery.js";
34
+ import { loadSession } from "../oauth/session.js";
35
+ import { openBrowser, signIn } from "../oauth/signin.js";
36
+ import { CAPTURE_NOT_APPROVED, deliver, ingestUrl, } from "../post.js";
37
+ import { resolveProjectKeys } from "../project.js";
38
+ import { meetsNodeFloor, NODE_FLOOR_TEXT } from "../runtime.js";
39
+ import { paint, renderErrorBlock, truncatePath, wrap } from "../term.js";
40
+ import { writeLines } from "./context.js";
41
+ export async function connect(ctx, options = {}) {
42
+ // (1) The runtime floor refuses LOUDLY here, unlike in the hook: the user is
43
+ // watching and can act, and D57 §DX11 asks for exactly that asymmetry.
44
+ if (!meetsNodeFloor(ctx.nodeVersion)) {
45
+ writeLines(ctx.stderr, renderErrorBlock({
46
+ kind: "bad",
47
+ what: RUNTIME.floorWhat,
48
+ why: [
49
+ `${RUNTIME.floorWhyPrefix} ${NODE_FLOOR_TEXT} ${RUNTIME.floorWhyMiddle} ${ctx.nodeVersion}.`,
50
+ ],
51
+ fixLabel: RUNTIME.floorFix,
52
+ fixes: [COMMANDS.connect],
53
+ }, ctx.colour));
54
+ return EXIT.failure;
55
+ }
56
+ // (2) BOTH KEYS (`D184 §1`). Outside a work tree there is nothing to consent
57
+ // ABOUT, and inventing a key from cwd would grant consent to something the
58
+ // user cannot see in `status`.
59
+ //
60
+ // ⛔ THE GRANT BELOW IS THE ONLY WRITER OF THE ALLOW LIST, so this verb decides
61
+ // what every gate can ever match. It writes `keys.consent` — the git common
62
+ // dir — which is what admits the worktrees of this clone as well as the clone
63
+ // itself. Granting the toplevel here while `entry.ts` and `post_commit.ts`
64
+ // check the common dir is a TOTAL, SILENT capture outage in every repository
65
+ // (`D184 §9`), and no cell that grants and checks through one helper can see
66
+ // it.
67
+ const keys = resolveProjectKeys(ctx.cwd);
68
+ if (keys === null) {
69
+ writeLines(ctx.stderr, renderErrorBlock({ kind: "bad", what: ERRORS.notAGitRepo, why: [] }, ctx.colour));
70
+ return EXIT.failure;
71
+ }
72
+ if (isProjectAllowed(ctx.home, keys.consent)) {
73
+ writeLines(ctx.stdout, wrap(CONNECT.alreadyConnected, 2));
74
+ return await afterConsent(ctx, keys, options);
75
+ }
76
+ // (3) The disclosure, then the gate.
77
+ writeLines(ctx.stdout, disclosure(ctx));
78
+ // (4) Non-TTY REFUSES. A consent prompt that proceeds when nobody can answer
79
+ // it is not a consent prompt (D65 §DR7). Exit 2 — DESIGN.md §13.7 names this
80
+ // case explicitly, so it is a usage error and not a failure.
81
+ if (!ctx.stdinIsTty) {
82
+ writeLines(ctx.stderr, renderErrorBlock({
83
+ kind: "bad",
84
+ what: CONNECT.nonInteractiveWhat,
85
+ why: [CONNECT.nonInteractiveWhy],
86
+ fixLabel: CONNECT.nonInteractiveFix,
87
+ fixes: [COMMANDS.connect],
88
+ }, ctx.colour));
89
+ return EXIT.usage;
90
+ }
91
+ // (5) The default is N. Anything that is not an explicit yes is a no.
92
+ const answer = await ctx.ask(` ${CONNECT.consentPrompt} ${paint(ctx.colour, "muted", CONNECT.consentAnswers)} `);
93
+ if (!isAffirmative(answer)) {
94
+ writeLines(ctx.stdout, ["", ...wrap(CONNECT.declined, 2)]);
95
+ // Declining is a successful consent flow, not a failure. Exit 0 (§13.7).
96
+ return EXIT.ok;
97
+ }
98
+ grantProject(ctx.home, keys.consent, ctx.now());
99
+ return await afterConsent(ctx, keys, options);
100
+ }
101
+ /**
102
+ * Consent is settled. Sign in if asked, then do the credential beat.
103
+ *
104
+ * The two are sequential and NOT conditional on each other, because they are
105
+ * different lanes: sign-in establishes the user principal the READ lane needs,
106
+ * and the credential beat is about the machine credential the HOOK lane needs.
107
+ * A failure in one says nothing about the other — which is why a failed sign-in
108
+ * returns here rather than falling through to report the install as fine.
109
+ */
110
+ async function afterConsent(ctx, keys, options) {
111
+ if (options.signIn === true) {
112
+ const code = await signInBeat(ctx);
113
+ if (code !== EXIT.ok)
114
+ return code;
115
+ }
116
+ return await credentialBeat(ctx, keys);
117
+ }
118
+ /**
119
+ * The browser sign-in beat — `CR-084d`.
120
+ *
121
+ * Reachable only behind `--sign-in`. That is deliberate and not timidity: an
122
+ * unconditional beat here would open a browser during every `connect`, including
123
+ * the ones run by a wrapper, and sign-in cannot mint the credential the rest of
124
+ * `connect` is about. When a read verb lands (`why` — `CR-086`; `report` —
125
+ * `CR-108`) it calls `readWithSession`, which drives the same session and needs
126
+ * no beat here at all.
127
+ */
128
+ async function signInBeat(ctx) {
129
+ if (loadSession(ctx.home).kind === "ok") {
130
+ writeLines(ctx.stdout, wrap(SIGNIN.alreadySignedIn, 2));
131
+ return EXIT.ok;
132
+ }
133
+ const endpoint = mcpUrl(ctx.env);
134
+ if (endpoint === null) {
135
+ writeLines(ctx.stderr, signInError(ctx, SIGNIN.noServerWhat, SIGNIN.noServerRefusedWhy));
136
+ return EXIT.failure;
137
+ }
138
+ const outcome = await signIn({
139
+ home: ctx.home,
140
+ mcpEndpoint: endpoint,
141
+ fetch,
142
+ nowMs: () => ctx.now().getTime(),
143
+ openBrowser,
144
+ onAuthorizeUrl: (url, opened) => {
145
+ writeLines(ctx.stdout, [
146
+ "",
147
+ ...wrap(opened ? SIGNIN.opening : SIGNIN.manualLabel, 2),
148
+ // `accent` on a URL is §13.1's one exception, and the line carries no
149
+ // second colour. Printed on BOTH paths: a user who wants to open it in a
150
+ // different browser than the default should not have to guess it.
151
+ ` ${paint(ctx.colour, "accent", url)}`,
152
+ ...(opened ? wrap(SIGNIN.waiting, 2) : []),
153
+ ]);
154
+ },
155
+ page: { done: SIGNIN.browserDone, refused: SIGNIN.browserRefused },
156
+ });
157
+ if (outcome.kind === "ok") {
158
+ writeLines(ctx.stdout, ["", ...wrap(SIGNIN.done, 2)]);
159
+ return EXIT.ok;
160
+ }
161
+ writeLines(ctx.stderr, ["", ...signInFailure(ctx, outcome)]);
162
+ return EXIT.failure;
163
+ }
164
+ /** §13.6's shape for a sign-in failure. One renderer, six outcomes. */
165
+ function signInFailure(ctx, outcome) {
166
+ switch (outcome.kind) {
167
+ case "no-server":
168
+ return signInError(ctx, SIGNIN.noServerWhat, outcome.detail === "unreachable"
169
+ ? SIGNIN.noServerUnreachableWhy
170
+ : SIGNIN.noServerMalformedWhy, SIGNIN.noServerFix);
171
+ case "denied":
172
+ return signInError(ctx, SIGNIN.deniedWhat, SIGNIN.deniedWhy, SIGNIN.deniedFix);
173
+ case "timeout":
174
+ return signInError(ctx, SIGNIN.timeoutWhat, SIGNIN.timeoutWhy, SIGNIN.timeoutFix);
175
+ case "rejected":
176
+ return signInError(ctx, SIGNIN.rejectedWhat, SIGNIN.rejectedWhy, SIGNIN.rejectedFix);
177
+ case "unreachable":
178
+ return signInError(ctx, SIGNIN.unreachableWhat, SIGNIN.unreachableWhy, SIGNIN.unreachableFix);
179
+ case "malformed":
180
+ return signInError(ctx, SIGNIN.malformedWhat, SIGNIN.malformedWhy, SIGNIN.malformedFix);
181
+ }
182
+ }
183
+ function signInError(ctx, what, why, fixLabel = SIGNIN.noServerFix) {
184
+ return renderErrorBlock({ kind: "bad", what, why: [why], fixLabel, fixes: [COMMANDS.signIn] }, ctx.colour);
185
+ }
186
+ /**
187
+ * The disclosure block — `CR-111d`, style guide §10.2 transcribed.
188
+ *
189
+ * FOUR paragraphs and two labelled links: what is uploaded, what that means
190
+ * concretely, the scrubber's STATED LIMIT, and — `T15` — the COMMITS, which are
191
+ * a second data class the first three never mentioned. Then the docs list and
192
+ * the public bundle.
193
+ *
194
+ * ⚠ The rules it satisfies that no gate can check (§10.2 rules 3-6): every noun
195
+ * names something actually uploaded IN THE USER'S VOCABULARY (*"the output of
196
+ * commands it ran"*, never *"Bash tool results"*); the scrubber is a pattern
197
+ * list with a stated limit, never protection; no reassurance adjective appears;
198
+ * and "open source" does not. The claims gate registers four of the five
199
+ * adjectives on this surface — **`private` is deliberately NOT registered**,
200
+ * because the approved copy itself says "private-repo paths", so a substring
201
+ * gate would fire on approved copy on day one. That one is a HUMAN review item.
202
+ */
203
+ function disclosure(ctx) {
204
+ // §10.2 aligns the two link URLs in one column. The width is DERIVED from the
205
+ // labels rather than hardcoded, so editing a label cannot silently break the
206
+ // alignment — and the four trailing spaces are the gutter the guide draws.
207
+ const gutter = Math.max(CONNECT.consentDocsLabel.length, CONNECT.consentBundleLabel.length) + 4;
208
+ return [
209
+ ...wrap(CONNECT.consentHeading, 2),
210
+ "",
211
+ ...wrap(CONNECT.consentDetail, 2),
212
+ "",
213
+ ...wrap(CONNECT.consentScrubber, 2),
214
+ "",
215
+ // ⛔ THE SECOND DATA CLASS (`T15`, `D190`). The three paragraphs above are
216
+ // TRANSCRIPT CONTENT and were the whole disclosure; commit SHAs have been on
217
+ // the wire since `CR-170` with no word here, and `D190` adds the attribution
218
+ // rung and LOCAL, UNPUSHED REWRITE HISTORY. Placed AFTER the scrubber so the
219
+ // transcript thread — what is uploaded, then that its secret scrubbing has a
220
+ // stated limit — is not split by a different subject.
221
+ ...wrap(CONNECT.consentCommits, 2),
222
+ "",
223
+ // Both URLs have ONE definition each, in the copy module. `accent` is
224
+ // allowed on a URL (§13.1) and neither line carries a second colour.
225
+ ` ${CONNECT.consentDocsLabel.padEnd(gutter)}${paint(ctx.colour, "accent", HELP.docsUrl)}`,
226
+ // ⚠ THE HONEST REPLACEMENT FOR THE OPEN-SOURCE CLAIM. D64 made this repo
227
+ // private with MIT intact, so "read the source" is false and is registered
228
+ // BLOCKED on this surface. `claude-plugin` is public non-negotiably (D20)
229
+ // and vendors the binary — an artefact to inspect, which is WEAKER than
230
+ // readable source and is not dressed up as it (D65 §DR7).
231
+ ` ${CONNECT.consentBundleLabel.padEnd(gutter)}${paint(ctx.colour, "accent", URLS.pluginBundle)}`,
232
+ "",
233
+ ];
234
+ }
235
+ /**
236
+ * The credential beat.
237
+ *
238
+ * `CR-025` replaces this with the real ending: capture the current session and
239
+ * print the live `/app/commits/<sha>` URL. It also owns the PATH-clash check —
240
+ * the npm package `vibecommit` (a different project) installs a bin of the same
241
+ * name, and on npm >= 7 the clash surfaces as EEXIST during the first install of
242
+ * exactly the population that installs commit tooling.
243
+ */
244
+ async function credentialBeat(ctx, keys) {
245
+ const load = loadCredential({ env: ctx.env, home: ctx.home });
246
+ if (load.kind === "ok") {
247
+ return await captureBeat(ctx, keys, load.credential);
248
+ }
249
+ writeLines(ctx.stderr, renderErrorBlock({
250
+ kind: "warn",
251
+ what: CONNECT.credentialNeededWhat,
252
+ why: [CONNECT.credentialNeededWhy],
253
+ fixLabel: CONNECT.credentialNeededFix,
254
+ fixes: [CONNECT.credentialNeededCommand, COMMANDS.connect],
255
+ }, ctx.colour));
256
+ return EXIT.notConnected;
257
+ }
258
+ /**
259
+ * The ending — D57 §DX5's Sentry-wizard test-event pattern.
260
+ *
261
+ * The install does not end on "configured", it ends on EVIDENCE: record this
262
+ * session, then hand over a URL that resolves. §10.2 draws it.
263
+ *
264
+ * Two things §10.2 draws that are deliberately NOT here:
265
+ * - **`4 turns · 1 commit`.** Counting turns means parsing the transcript, and
266
+ * the client may render but may not analyze (D60 §D6). It is not obtainable
267
+ * from the server either: `send()` returns a status and discards the body,
268
+ * so no id, sha or count exists on this side of the wire.
269
+ * - **`Opening your browser to sign in...`.** That beat is the oauth lane's.
270
+ * Until PKCE lands, `VIBECOMMIT_TOKEN` is the supported path.
271
+ */
272
+ async function captureBeat(ctx, keys, credential) {
273
+ // ⚠ EVERY USE BELOW BUT ONE IS THE TOPLEVEL ROLE — locating the transcript,
274
+ // the `repoKey` binding, the slug, the hook install and the copy the user
275
+ // reads. The exception is the redaction root set at `readSpan`, which takes
276
+ // BOTH keys through `confinementRoots`. Aliased rather than renamed
277
+ // throughout, so the diff shows the line whose ROLE changed and not fourteen
278
+ // that did not.
279
+ const projectKey = keys.worktree;
280
+ // ⛔ SKIPPED ENTIRELY ON A DIALECT WITH NO LOCATOR — `CR-183`. `connect` is
281
+ // typed by a human and is handed no `transcript_path`, so finding the running
282
+ // session is a per-agent capability rather than something this verb can do.
283
+ // The absence is a real answer and not a gap to fill: the available fallback
284
+ // is reading a `cwd` out of the transcript's own records, which is ANALYSIS
285
+ // and D60 §D6 forbids it. The refusal below would also be a false statement —
286
+ // it names Claude Code, and this is not that agent.
287
+ const locate = dialectFor(ctx.agentId).locateCurrentTranscript;
288
+ const found = locate === undefined ? null : locate(ctx.home, ctx.env, projectKey);
289
+ if (locate !== undefined && found === null) {
290
+ writeLines(ctx.stderr, renderErrorBlock({
291
+ kind: "warn",
292
+ what: CONNECT.noTranscriptWhat,
293
+ why: [CONNECT.noTranscriptWhy],
294
+ fixLabel: CONNECT.noTranscriptFix,
295
+ fixes: [COMMANDS.status],
296
+ }, ctx.colour));
297
+ return EXIT.failure;
298
+ }
299
+ const url = ingestUrl(ctx.env);
300
+ let delivery = null;
301
+ if (found !== null && url !== null) {
302
+ // Through `deliver()`, so the offset ledger, the failure policy and
303
+ // `CR-024d`'s redaction all apply exactly as they do for a hook. A second
304
+ // upload path here would bypass all three.
305
+ delivery = await deliver({
306
+ home: ctx.home,
307
+ env: ctx.env,
308
+ url,
309
+ credential,
310
+ repoKey: projectKey,
311
+ repoSlug: resolveRepoSlug(projectKey),
312
+ sessionId: found.sessionId,
313
+ fileKey: "main",
314
+ // ⛔ The LOCATED path (`CR-204`), so this test capture names its producer
315
+ // on the wire exactly as a hook's would — and names it the same way,
316
+ // from the root that contains the file rather than from `ctx.agentId`.
317
+ // ⚠ The LOCATOR above is still the flag's: `connect` is looking FOR a
318
+ // transcript, so there is no path yet to ask a root about.
319
+ transcriptPath: found.path,
320
+ timeoutMs: DEFAULT_HOOK_BUDGET_MS,
321
+ nowMs: ctx.now().getTime(),
322
+ }, found.size,
323
+ // ⛔ THE ROOT SET, and it is `confinementRoots` — THE SAME FUNCTION THE
324
+ // HOOK PATH CALLS (`entry.ts`), not a set built here. `D184 §3`: the
325
+ // confinement root is `[worktree, mainToplevel]` and the consent key is
326
+ // NEVER a member, so this does not follow the flip above.
327
+ //
328
+ // ⚠ WHY IT CANNOT STAY THE SINGLETON IT WAS: `connect` runs in whatever
329
+ // cwd a human typed it in, which is reachable from inside a worktree. With
330
+ // one root, this preview would redact main-clone content that the hook
331
+ // path transmits — the two would disagree SILENTLY, and the preview is the
332
+ // artefact the user reads to decide whether redaction works. `isInside`
333
+ // fail-closes an unresolvable path to REDACT, so the disagreement shows up
334
+ // as a plausible, well-formed, emptier payload rather than an error.
335
+ (from, to) => readSpan(found.path, from, to, confinementRoots(keys), ctx.home, ctx.env));
336
+ }
337
+ // THE FIFTH BEAT — `CR-110`, D65 §DR6, D81.
338
+ //
339
+ // ⚠ ONE outcome is branched on, identified by its ERROR CODE. The general rule
340
+ // below still stands: `later` and `never` are transient or per-payload (D58)
341
+ // and the offset holds, so a 503 must still print the ordinary ending.
342
+ const pending = approvalPending(delivery);
343
+ if (pending !== null)
344
+ return approvalPendingBeat(ctx, projectKey, pending.ownersNotified);
345
+ // The beat, then the URL on its own line, then what-now. Every OTHER outcome
346
+ // of the send is deliberately not branched on: telling a user their install
347
+ // failed because one POST 503'd would be false.
348
+ writeLines(ctx.stdout, [
349
+ "",
350
+ ...wrap(`${CONNECT.capturing} ${CONNECT.doneHeading}`, 2),
351
+ "",
352
+ ` ${paint(ctx.colour, "accent", URLS.commits)}`,
353
+ "",
354
+ ...wrap(CONNECT.doneForRepo(truncatePath(projectKey, 52)), 2),
355
+ ...wrap(`${CONNECT.stopLabel}: ${COMMANDS.off}`, 2),
356
+ ]);
357
+ // ⛔ COMMIT CAPTURE IS INSTALLED AND REPORTED HERE (`CR-170`, D154). Three of
358
+ // its failure modes are silent no-ops, so every outcome prints — including
359
+ // success, which is the only place the PER-CLONE cost can be stated at a
360
+ // moment the user can act on it.
361
+ commitHookBeat(ctx, projectKey);
362
+ return pathClash(ctx);
363
+ }
364
+ /**
365
+ * Install the `post-commit` hook for this clone and SAY WHAT HAPPENED.
366
+ *
367
+ * ⚠ **Never changes the exit code.** Commit observation is an addition to
368
+ * transcript capture, not a precondition for it: a repository whose
369
+ * `core.hooksPath` points elsewhere still captures transcripts perfectly, and
370
+ * exiting non-zero would tell the user their connection failed when it did not.
371
+ * The report is the whole response.
372
+ */
373
+ function commitHookBeat(ctx, projectKey) {
374
+ const outcome = installPostCommitHook(projectKey, ctx.selfPath);
375
+ const repo = truncatePath(projectKey, 52);
376
+ if (outcome.kind === "hooks-path" || outcome.kind === "failed") {
377
+ writeLines(ctx.stdout, [
378
+ "",
379
+ ...renderErrorBlock({
380
+ kind: "warn",
381
+ what: outcome.kind === "hooks-path" ? COMMIT_HOOK.hooksPathWhat : COMMIT_HOOK.failedWhat,
382
+ why: [
383
+ outcome.kind === "hooks-path"
384
+ ? COMMIT_HOOK.hooksPathWhy(outcome.configured)
385
+ : COMMIT_HOOK.failedWhy(outcome.why),
386
+ ],
387
+ fixLabel: COMMIT_HOOK.hooksPathFixLabel,
388
+ fixes: [COMMIT_HOOK.hooksPathFixUnset, COMMIT_HOOK.hooksPathFixManual],
389
+ }, ctx.colour),
390
+ ]);
391
+ return;
392
+ }
393
+ const line = outcome.kind === "installed"
394
+ ? COMMIT_HOOK.installed(repo)
395
+ : outcome.kind === "chained"
396
+ ? COMMIT_HOOK.chained(repo)
397
+ : COMMIT_HOOK.already;
398
+ writeLines(ctx.stdout, ["", ...wrap(line, 2), ...wrap(COMMIT_HOOK.perClone, 2)]);
399
+ }
400
+ /**
401
+ * Is this delivery the org-approval-pending outcome, and nothing else?
402
+ *
403
+ * ⚠ **BRANCHES ON THE ERROR CODE, NEVER ON THE 403 STATUS**, and that is the
404
+ * whole correctness content of this function. `repository_forbidden` is ALSO a
405
+ * 403 and deliberately carries no `owners_notified` — so a client that keyed on
406
+ * the status would tell a user whose repository simply is not theirs that *their
407
+ * org needs to approve capture*: a false statement about someone else's org, at
408
+ * the moment of highest trust. A test asserting "a 403 prints the pending beat"
409
+ * passes against exactly that defect, which is why the suite drives both codes.
410
+ *
411
+ * Returns the `owners_notified` value on a match. `undefined` — the key absent —
412
+ * is normalised to `null`, because both mean *not determinable* to the copy and
413
+ * neither may assert delivery.
414
+ */
415
+ function approvalPending(delivery) {
416
+ if (delivery === null || delivery.kind !== "attempted")
417
+ return null;
418
+ if (delivery.detail?.code !== CAPTURE_NOT_APPROVED)
419
+ return null;
420
+ return { ownersNotified: delivery.detail.ownersNotified ?? null };
421
+ }
422
+ /**
423
+ * The fifth beat — style guide §10.2, D65 §DR6, D81.
424
+ *
425
+ * ⚠ `warn`, never `bad`. ⚠ **`EXIT.ok`, never `EXIT.failure` and never
426
+ * `EXIT.notConnected`** — the user is correctly installed and capture is armed.
427
+ * That 403 means *later*, not *failed*: `policy.ts` classes it `later`, so the
428
+ * offset holds and the backlog uploads in order once an owner approves. A red
429
+ * exit here tells a correctly-installed user they broke something.
430
+ */
431
+ function approvalPendingBeat(ctx, projectKey, ownersNotified) {
432
+ // ⚠ THE SHARED CLAUSE FIRST, THE NOTIFICATION SECOND — it ADDS, it does not
433
+ // replace (D81). Composed here rather than as two independent paragraphs, so
434
+ // the branches cannot drift apart: there is exactly one place the
435
+ // recorded-request sentence exists.
436
+ //
437
+ // ⚠ AND `null` NEVER REACHES THE NOTIFICATION CLAUSE. `null` = not
438
+ // determinable, `0` = determinable and nobody was told; both render the
439
+ // recorded-go-ask branch alone. The owner genuinely is not emailed until
440
+ // `CR-036`, which is provisioning-blocked — disclosed, not concealed.
441
+ const why = ownersNotified !== null && ownersNotified > 0
442
+ ? `${CONNECT.pendingRecorded} ${CONNECT.pendingNotified(ownersNotified)}`
443
+ : CONNECT.pendingRecorded;
444
+ // ⚠ STDOUT, not stderr, and the rule is consistent in this file: stderr
445
+ // accompanies a NON-ZERO exit here (`credentialBeat` → 3, `noTranscript` → 1,
446
+ // `pathClash` → 1, the sign-in failures → 1). This beat exits 0, so it is the
447
+ // install's OUTCOME rather than a diagnostic — and §10.2 draws it as one
448
+ // screen, which `2>/dev/null` would otherwise cut in half, leaving "Capture is
449
+ // armed" with the reason stripped off it.
450
+ writeLines(ctx.stdout, [
451
+ "",
452
+ ...renderErrorBlock({
453
+ kind: "warn",
454
+ what: CONNECT.pendingWhat,
455
+ why: [CONNECT.pendingWhy(resolveRepoSlug(projectKey)), why, CONNECT.pendingBuffered],
456
+ fixLabel: CONNECT.pendingTrackLabel,
457
+ fixes: [URLS.captureAccess],
458
+ }, ctx.colour),
459
+ "",
460
+ ...wrap(CONNECT.pendingArmed(truncatePath(projectKey, 52)), 2),
461
+ ]);
462
+ // The PATH clash still runs — it is about the verbs a human types and is
463
+ // orthogonal to whether capture is approved. It can still exit 1.
464
+ return pathClash(ctx);
465
+ }
466
+ /**
467
+ * Does the `vibecommit` a user types belong to us? — `DESIGN.md` §13.6's worked
468
+ * example, which was drawn for this check.
469
+ *
470
+ * **Runs LAST, deliberately.** The clash is about the verbs a human types, not
471
+ * about whether capture works; running it first would refuse to connect a user
472
+ * whose install is otherwise fine, and hook capture is unaffected either way
473
+ * because the plugin invokes the binary by path.
474
+ *
475
+ * Exit 1, not 2 and not 3: the user typed nothing wrong (2 is usage) and may be
476
+ * perfectly well connected (3 is not-connected). §13.7.
477
+ */
478
+ function pathClash(ctx) {
479
+ const verdict = classifyInstall(ctx.env, ctx.selfPath);
480
+ if (verdict.kind === "ours")
481
+ return EXIT.ok;
482
+ writeLines(ctx.stderr, renderErrorBlock({
483
+ kind: "bad",
484
+ what: verdict.kind === "foreign" ? PATH_CLASH.foreignWhat : PATH_CLASH.absentWhat,
485
+ why: [
486
+ verdict.kind === "foreign"
487
+ ? PATH_CLASH.foreignWhy(truncatePath(verdict.resolved, 52))
488
+ : PATH_CLASH.absentWhy,
489
+ ],
490
+ fixLabel: PATH_CLASH.fixLabel,
491
+ fixes: [
492
+ verdict.kind === "foreign" ? PATH_CLASH.fixReplace : PATH_CLASH.fixInstall,
493
+ PATH_CLASH.fixDirectLabel,
494
+ PATH_CLASH.fixDirect,
495
+ ],
496
+ }, ctx.colour));
497
+ return EXIT.failure;
498
+ }
499
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * What an interactive verb is handed.
3
+ *
4
+ * Everything ambient — env, home, cwd, the TTY answer, the clock — arrives as
5
+ * data. No verb calls `os.homedir()`, `process.cwd()` or `Date.now()` itself,
6
+ * which is what lets the tests drive a real binary against a temp HOME without
7
+ * the risk of a test run touching the developer's own credentials.
8
+ */
9
+ import type { Writable } from "node:stream";
10
+ export interface CommandContext {
11
+ readonly env: NodeJS.ProcessEnv;
12
+ readonly home: string;
13
+ readonly cwd: string;
14
+ readonly nodeVersion: string;
15
+ /** §13.3 — resolved once at process start, never re-derived per line. */
16
+ readonly colour: boolean;
17
+ /** stdin is a TTY. The consent gate refuses when it is not (§13.7 exit 2). */
18
+ readonly stdinIsTty: boolean;
19
+ readonly stdout: Writable;
20
+ readonly stderr: Writable;
21
+ /** Ask the consent question. Only reached when `stdinIsTty` is true. */
22
+ readonly ask: (question: string) => Promise<string>;
23
+ readonly now: () => Date;
24
+ /**
25
+ * This process's entry point — `process.argv[1]`. Ambient, so it arrives as
26
+ * data like everything else here: `CR-025`'s PATH check compares it against
27
+ * the `vibecommit` on PATH, and a test cannot drive that without supplying
28
+ * both sides.
29
+ */
30
+ readonly selfPath: string;
31
+ /**
32
+ * Which agent this invocation was told it is running under — `CR-183`,
33
+ * `--agent=<id>`, resolved once in `index.ts`.
34
+ *
35
+ * OPTIONAL, and absent means the default dialect (`claude-code`). Every
36
+ * install that predates the flag omits it, so the absent case is the common
37
+ * one rather than an edge; `dialectFor(undefined)` answers it directly and
38
+ * there is no second place that has to remember the default.
39
+ *
40
+ * A LITERAL, not a narrowed id: an unrecognised value must never stop capture
41
+ * (plan §D7), so what travels is what the operator wrote.
42
+ */
43
+ readonly agentId?: string;
44
+ }
45
+ export declare function writeLines(stream: Writable, lines: readonly string[]): void;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * What an interactive verb is handed.
3
+ *
4
+ * Everything ambient — env, home, cwd, the TTY answer, the clock — arrives as
5
+ * data. No verb calls `os.homedir()`, `process.cwd()` or `Date.now()` itself,
6
+ * which is what lets the tests drive a real binary against a temp HOME without
7
+ * the risk of a test run touching the developer's own credentials.
8
+ */
9
+ export function writeLines(stream, lines) {
10
+ if (lines.length === 0)
11
+ return;
12
+ stream.write(`${lines.join("\n")}\n`);
13
+ }
14
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwCH,MAAM,UAAU,UAAU,CAAC,MAAgB,EAAE,KAAwB;IACnE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC"}