@vibe-commit/capture 0.2.3 → 0.4.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 (58) hide show
  1. package/README.md +33 -2
  2. package/dist/agent_hooks.d.ts +53 -8
  3. package/dist/agent_hooks.js +180 -19
  4. package/dist/agent_hooks.js.map +1 -1
  5. package/dist/agent_install.d.ts +0 -17
  6. package/dist/agent_install.js +1 -24
  7. package/dist/agent_install.js.map +1 -1
  8. package/dist/agents/cursor.js +54 -18
  9. package/dist/agents/cursor.js.map +1 -1
  10. package/dist/commands/auth.d.ts +24 -9
  11. package/dist/commands/auth.js +38 -10
  12. package/dist/commands/auth.js.map +1 -1
  13. package/dist/commands/browser_credential.d.ts +89 -0
  14. package/dist/commands/browser_credential.js +181 -0
  15. package/dist/commands/browser_credential.js.map +1 -0
  16. package/dist/commands/connect.d.ts +26 -9
  17. package/dist/commands/connect.js +52 -90
  18. package/dist/commands/connect.js.map +1 -1
  19. package/dist/commands/finalize.d.ts +35 -0
  20. package/dist/commands/finalize.js +283 -0
  21. package/dist/commands/finalize.js.map +1 -0
  22. package/dist/commands/status.js +56 -2
  23. package/dist/commands/status.js.map +1 -1
  24. package/dist/copy/index.d.ts +1 -1
  25. package/dist/copy/index.js +1 -1
  26. package/dist/copy/index.js.map +1 -1
  27. package/dist/copy/strings.d.ts +81 -2
  28. package/dist/copy/strings.js +94 -1
  29. package/dist/copy/strings.js.map +1 -1
  30. package/dist/exit.d.ts +7 -0
  31. package/dist/exit.js +7 -0
  32. package/dist/exit.js.map +1 -1
  33. package/dist/hooks/entry.d.ts +83 -0
  34. package/dist/hooks/entry.js +117 -28
  35. package/dist/hooks/entry.js.map +1 -1
  36. package/dist/hooks/post_rewrite.js +20 -3
  37. package/dist/hooks/post_rewrite.js.map +1 -1
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.js +14 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/oauth/mint.d.ts +97 -0
  42. package/dist/oauth/mint.js +120 -0
  43. package/dist/oauth/mint.js.map +1 -0
  44. package/dist/oauth/signin.d.ts +25 -0
  45. package/dist/oauth/signin.js +30 -0
  46. package/dist/oauth/signin.js.map +1 -1
  47. package/dist/post.d.ts +53 -0
  48. package/dist/post.js +73 -5
  49. package/dist/post.js.map +1 -1
  50. package/dist/spool.d.ts +37 -0
  51. package/dist/spool.js +244 -36
  52. package/dist/spool.js.map +1 -1
  53. package/dist/state.d.ts +152 -0
  54. package/dist/state.js +255 -2
  55. package/dist/state.js.map +1 -1
  56. package/dist/version.d.ts +1 -1
  57. package/dist/version.js +1 -1
  58. package/package.json +1 -1
package/README.md CHANGED
@@ -30,12 +30,43 @@ the approved direction points at `Vibe-Commit/claude-plugin`, which vendors the
30
30
  | `vibecommit off` | Stop capturing for this repo. |
31
31
  | `vibecommit why <file>:<line>` | Show the conversation turns recorded against the commit that last changed this line. |
32
32
  | `vibecommit report --since <when>` | Summarise capture coverage over a time window. |
33
+ | `vibecommit finalize --session <id>` \| `--latest` | Close a finished session's last turn so commits made in it can be recorded. |
34
+
35
+ ### One-shot runs: `vibecommit finalize`
36
+
37
+ A turn is recorded only once a following prompt opens the next one, so the **last turn of a
38
+ session is never closed by the session itself**. For a long session that costs nothing you can
39
+ see; for a script that runs a single prompt (`claude -p` in CI, a batch job) it means nothing
40
+ is recorded and no commit is linked. Nothing inside the process can know the session is over;
41
+ the caller can. After the run, say so:
42
+
43
+ ```sh
44
+ claude -p "…" --output-format stream-json … # session_id is in the stream
45
+ vibecommit finalize --session <session_id> # or --latest when only one session is recent
46
+ ```
47
+
48
+ Exit `0` — the server confirmed the last turn is recorded. Exit `5` — the data was delivered
49
+ but the server did not confirm it (a server older than this command): treat as **not** closed.
50
+ Exit `2` — nothing was sent (bad arguments, an unknown or still-running session, or several
51
+ recent sessions for `--latest`). Exit `3` — this repo is not connected. It is safe to run twice.
52
+ Resuming the session afterwards opens a new turn; nothing already recorded is changed.
53
+
54
+ `finalize` refuses a session whose transcript changed in the last few seconds. `--force` skips that check,
55
+ and **only belongs on a session that is over**: records the agent writes after a mid-turn `--force`
56
+ (assistant output with no following prompt) belong to no recorded turn.
57
+
58
+ ### What needs `vibecommit connect`
59
+
60
+ Two of the 0.4.0 fixes live in the git hooks that `vibecommit connect` installs: a second session's
61
+ first-turn commit binding to its own session, and a squash's intermediate commits answering with the
62
+ commit that replaced them. **The Claude Code plugin installs no git hooks**, so a plugin-only install does
63
+ not get those two; it gets `vibecommit finalize` and the delivery receipt `vibecommit status` shows.
33
64
 
34
65
  ## Status
35
66
 
36
- **Implemented**, not a scaffold. All five commands above have real bodies — OAuth sign-in
67
+ **Implemented**, not a scaffold. All six commands above have real bodies — OAuth sign-in
37
68
  and credential storage, git inspection, redaction, and the Claude Code hook contract — backed
38
- by an extensive test suite. Beyond the five interactive commands, this binary also answers a
69
+ by an extensive test suite. Beyond the six interactive commands, this binary also answers a
39
70
  hidden `hook` invocation (dispatched outside `--help`, since no one types it) for Claude Code's
40
71
  `Stop`, `PreCompact`, and `SessionEnd` events; that hook mode is what
41
72
  [`Vibe-Commit/claude-plugin`](https://github.com/Vibe-Commit/claude-plugin) vendors and wires up
@@ -22,13 +22,26 @@
22
22
  *
23
23
  * 1. **APPEND, never replace.** A foreign entry is never removed, rewritten or
24
24
  * reordered. Ours goes last.
25
- * 2. **IDEMPOTENT via a marker.** JSON has nowhere to put a comment, so ours
26
- * carries `_vibecommit`. Without it, `connect` run twice appends twice and
25
+ * 2. **IDEMPOTENT — by the marker when it survives, and by OUR EXACT COMMAND
26
+ * when it does not.** JSON has nowhere to put a comment, so ours carries
27
+ * `_vibecommit`. Without an identity, `connect` run twice appends twice and
27
28
  * the hook fires twice per event — two processes racing on one session
28
29
  * state file. ⚠ The marker field is TOLERATED by all three readers, and
29
30
  * that is measured rather than assumed: Codex parses and clamps with the
30
31
  * field present on the inner and outer object alike, and Cursor's validator
31
32
  * checks only known members (`HWu`/`jWu`/`$Wu` in its bundle).
33
+ *
34
+ * ⛔ **BUT TOLERATED ON READ IS NOT KEPT ON WRITE — `CR-224`, `TODOS[138]`.**
35
+ * Claude Code's own settings writer STRIPS unknown keys from hook entries
36
+ * the next time it saves `settings.json` for any reason. MEASURED against
37
+ * Claude Code 2.1.285 with a throwaway `CLAUDE_CONFIG_DIR`: an entry
38
+ * carrying `_vibecommit`, a second foreign key on the entry and a third on
39
+ * the inner hook went through `claude plugin marketplace add` (which writes
40
+ * `extraKnownMarketplaces`) and came back with ALL THREE gone — while an
41
+ * unknown TOP-LEVEL key survived, so it is the hooks schema that strips,
42
+ * not the file writer wholesale. So on the one machine that matters most the
43
+ * marker is gone by the second `connect`, and a marker-only identity
44
+ * appended a duplicate every run. See `isOurs` for what replaced it.
32
45
  * 3. ⛔ **REFUSE on unparseable, never overwrite.** A file we could not read is
33
46
  * a file whose contents we cannot preserve. Overwriting it would be the
34
47
  * destructive outcome wearing a success message — the exact defect class
@@ -57,17 +70,42 @@
57
70
  * registers nothing, and reports success — the same matches-nothing failure as
58
71
  * an unrecognised event name, relocated to the document level.
59
72
  */
73
+ import type { AgentId } from "./agents/types.js";
60
74
  /**
61
- * What marks an entry as ours.
75
+ * What marks an entry as ours — the FAST PATH, not the whole identity.
62
76
  *
63
- * ⚠ **A FIELD, not a substring of the command.** Matching on the command text
64
- * would claim a user's own hook that happens to mention this product, and would
65
- * lose track of ours the moment the binary path changed — which is exactly when
66
- * idempotence matters, because that is a re-`connect`.
77
+ * ⚠ **A FIELD, never a substring of the command.** A substring match would claim
78
+ * a user's own hook that happens to mention this product. ⛔ But a field is only
79
+ * as durable as the host lets it be, and Claude Code's writer drops it (rule 2
80
+ * above, measured). So an entry WITHOUT the marker is still ours when its
81
+ * command is one we could have written — see `isOurs`, which anchors on the
82
+ * WHOLE command string and never on part of it.
67
83
  */
68
84
  export declare const AGENT_HOOK_MARKER = "vibecommit-capture-hook v1";
69
85
  /** Which document shape an agent's hook config uses. See the module docblock. */
70
86
  export type HookDocShape = "claude" | "cursor";
87
+ /**
88
+ * The command an agent's config invokes.
89
+ *
90
+ * ⛔ **`hook` LEADS, AND THE FLAG NEVER DOES.** `invocationMode` returns `"hook"`
91
+ * only when `argv[0] === "hook"`, so a registered command whose first argument
92
+ * is `--agent=` runs the binary INTERACTIVELY — inside a hook, printing to a
93
+ * stream the agent reads back into the user's turn. `registry.ts` names this as
94
+ * the trap the wave was most likely to ship, and `test/agent-registry.test.ts`
95
+ * holds both arms.
96
+ *
97
+ * ⚠ **The flag is passed explicitly for every agent, Claude Code included**,
98
+ * even though `DEFAULT_AGENT_ID` makes it redundant there. A config that names
99
+ * its own agent is self-describing, and — since Cursor reads Claude Code's
100
+ * settings and maps them onto its own events — it is the only thing that tells
101
+ * the two invocations apart when both fire.
102
+ *
103
+ * ⚠ **It lives HERE, beside its own inverse (`HOOK_COMMAND`), since `CR-224`.**
104
+ * The merge below recognises our entry by this exact shape once the host has
105
+ * stripped the marker, so the builder and the recogniser are one grammar and
106
+ * must not be able to drift apart across two files.
107
+ */
108
+ export declare function hookCommand(binPath: string, agentId: AgentId): string;
71
109
  /** One event we want registered, already in the agent's own vocabulary. */
72
110
  export interface HookRegistration {
73
111
  /** The agent's OWN spelling — `SessionEnd` or `sessionEnd`, never canonical. */
@@ -100,7 +138,14 @@ export type MergeOutcome = {
100
138
  * That split is deliberate: every rule in the module docblock is a property of
101
139
  * this function, and none of them needs a real `~/.claude` to check.
102
140
  */
103
- export declare function mergeHookDocument(raw: string | null, shape: HookDocShape, registrations: readonly HookRegistration[]): MergeOutcome;
141
+ export declare function mergeHookDocument(raw: string | null, shape: HookDocShape, registrations: readonly HookRegistration[],
142
+ /**
143
+ * Does the binary an existing entry points at still exist? Consulted ONLY by
144
+ * the moved-directory arm of `commandIsOurs` (`TODOS[144]`). ⚠ The default
145
+ * answers "yes" — the direction in which a doubt leaves a WORKING hook alone
146
+ * and costs at most a duplicate. `writeAgentHooks` passes `existsSync`.
147
+ */
148
+ binaryExists?: (path: string) => boolean): MergeOutcome;
104
149
  export type AgentHookInstall = {
105
150
  readonly kind: "installed";
106
151
  readonly path: string;
@@ -22,13 +22,26 @@
22
22
  *
23
23
  * 1. **APPEND, never replace.** A foreign entry is never removed, rewritten or
24
24
  * reordered. Ours goes last.
25
- * 2. **IDEMPOTENT via a marker.** JSON has nowhere to put a comment, so ours
26
- * carries `_vibecommit`. Without it, `connect` run twice appends twice and
25
+ * 2. **IDEMPOTENT — by the marker when it survives, and by OUR EXACT COMMAND
26
+ * when it does not.** JSON has nowhere to put a comment, so ours carries
27
+ * `_vibecommit`. Without an identity, `connect` run twice appends twice and
27
28
  * the hook fires twice per event — two processes racing on one session
28
29
  * state file. ⚠ The marker field is TOLERATED by all three readers, and
29
30
  * that is measured rather than assumed: Codex parses and clamps with the
30
31
  * field present on the inner and outer object alike, and Cursor's validator
31
32
  * checks only known members (`HWu`/`jWu`/`$Wu` in its bundle).
33
+ *
34
+ * ⛔ **BUT TOLERATED ON READ IS NOT KEPT ON WRITE — `CR-224`, `TODOS[138]`.**
35
+ * Claude Code's own settings writer STRIPS unknown keys from hook entries
36
+ * the next time it saves `settings.json` for any reason. MEASURED against
37
+ * Claude Code 2.1.285 with a throwaway `CLAUDE_CONFIG_DIR`: an entry
38
+ * carrying `_vibecommit`, a second foreign key on the entry and a third on
39
+ * the inner hook went through `claude plugin marketplace add` (which writes
40
+ * `extraKnownMarketplaces`) and came back with ALL THREE gone — while an
41
+ * unknown TOP-LEVEL key survived, so it is the hooks schema that strips,
42
+ * not the file writer wholesale. So on the one machine that matters most the
43
+ * marker is gone by the second `connect`, and a marker-only identity
44
+ * appended a duplicate every run. See `isOurs` for what replaced it.
32
45
  * 3. ⛔ **REFUSE on unparseable, never overwrite.** A file we could not read is
33
46
  * a file whose contents we cannot preserve. Overwriting it would be the
34
47
  * destructive outcome wearing a success message — the exact defect class
@@ -57,21 +70,68 @@
57
70
  * registers nothing, and reports success — the same matches-nothing failure as
58
71
  * an unrecognised event name, relocated to the document level.
59
72
  */
60
- import { chmodSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
61
- import { dirname, join } from "node:path";
73
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
74
+ import { basename, dirname, join } from "node:path";
62
75
  /**
63
- * What marks an entry as ours.
76
+ * What marks an entry as ours — the FAST PATH, not the whole identity.
64
77
  *
65
- * ⚠ **A FIELD, not a substring of the command.** Matching on the command text
66
- * would claim a user's own hook that happens to mention this product, and would
67
- * lose track of ours the moment the binary path changed — which is exactly when
68
- * idempotence matters, because that is a re-`connect`.
78
+ * ⚠ **A FIELD, never a substring of the command.** A substring match would claim
79
+ * a user's own hook that happens to mention this product. ⛔ But a field is only
80
+ * as durable as the host lets it be, and Claude Code's writer drops it (rule 2
81
+ * above, measured). So an entry WITHOUT the marker is still ours when its
82
+ * command is one we could have written — see `isOurs`, which anchors on the
83
+ * WHOLE command string and never on part of it.
69
84
  */
70
85
  export const AGENT_HOOK_MARKER = "vibecommit-capture-hook v1";
71
86
  /** The field carrying it. Leading underscore: a hint that it is not the agent's. */
72
87
  const MARKER_KEY = "_vibecommit";
73
88
  /** The `version` we mint when CREATING a `cursor` document. */
74
89
  const CURSOR_CONFIG_VERSION = 1;
90
+ /**
91
+ * The command an agent's config invokes.
92
+ *
93
+ * ⛔ **`hook` LEADS, AND THE FLAG NEVER DOES.** `invocationMode` returns `"hook"`
94
+ * only when `argv[0] === "hook"`, so a registered command whose first argument
95
+ * is `--agent=` runs the binary INTERACTIVELY — inside a hook, printing to a
96
+ * stream the agent reads back into the user's turn. `registry.ts` names this as
97
+ * the trap the wave was most likely to ship, and `test/agent-registry.test.ts`
98
+ * holds both arms.
99
+ *
100
+ * ⚠ **The flag is passed explicitly for every agent, Claude Code included**,
101
+ * even though `DEFAULT_AGENT_ID` makes it redundant there. A config that names
102
+ * its own agent is self-describing, and — since Cursor reads Claude Code's
103
+ * settings and maps them onto its own events — it is the only thing that tells
104
+ * the two invocations apart when both fire.
105
+ *
106
+ * ⚠ **It lives HERE, beside its own inverse (`HOOK_COMMAND`), since `CR-224`.**
107
+ * The merge below recognises our entry by this exact shape once the host has
108
+ * stripped the marker, so the builder and the recogniser are one grammar and
109
+ * must not be able to drift apart across two files.
110
+ */
111
+ export function hookCommand(binPath, agentId) {
112
+ return `${quote(binPath)} hook --agent=${agentId}`;
113
+ }
114
+ /** Double-quote for the shells these configs are read by. */
115
+ function quote(value) {
116
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
117
+ }
118
+ /**
119
+ * ⛔ `hookCommand`'s output, recognised — ANCHORED AT BOTH ENDS.
120
+ *
121
+ * The quoted path is `quote`'s grammar exactly: any character except the four it
122
+ * escapes, or one of those four behind a backslash. `^…$` is the whole point:
123
+ * `time "<ours>" hook --agent=x`, `"<ours>" hook --agent=x && echo done` and
124
+ * `echo "<ours>" hook --agent=x` are all the USER's commands, and a pattern
125
+ * without both anchors would claim them.
126
+ */
127
+ const HOOK_COMMAND = /^"((?:[^"\\$`]|\\["\\$`])*)" hook --agent=([a-z][a-z0-9-]*)$/;
128
+ /** `hookCommand`, inverted. `null` for anything it could not have produced. */
129
+ function parseHookCommand(command) {
130
+ const match = HOOK_COMMAND.exec(command);
131
+ if (match === null)
132
+ return null;
133
+ return { binPath: match[1].replace(/\\(["\\$`])/g, "$1"), agentId: match[2] };
134
+ }
75
135
  /**
76
136
  * ⛔ **THE WHOLE MERGE, AS A PURE FUNCTION ON TEXT.**
77
137
  *
@@ -81,7 +141,14 @@ const CURSOR_CONFIG_VERSION = 1;
81
141
  * That split is deliberate: every rule in the module docblock is a property of
82
142
  * this function, and none of them needs a real `~/.claude` to check.
83
143
  */
84
- export function mergeHookDocument(raw, shape, registrations) {
144
+ export function mergeHookDocument(raw, shape, registrations,
145
+ /**
146
+ * Does the binary an existing entry points at still exist? Consulted ONLY by
147
+ * the moved-directory arm of `commandIsOurs` (`TODOS[144]`). ⚠ The default
148
+ * answers "yes" — the direction in which a doubt leaves a WORKING hook alone
149
+ * and costs at most a duplicate. `writeAgentHooks` passes `existsSync`.
150
+ */
151
+ binaryExists = () => true) {
85
152
  let doc;
86
153
  if (raw === null || raw.trim() === "") {
87
154
  doc = {};
@@ -133,30 +200,124 @@ export function mergeHookDocument(raw, shape, registrations) {
133
200
  }
134
201
  const entries = [...(current ?? [])];
135
202
  const ours = entryFor(shape, registration);
136
- const at = entries.findIndex(isOurs);
203
+ const mine = (entry) => isOurs(entry, shape, registration, binaryExists);
204
+ const at = entries.findIndex(mine);
137
205
  if (at >= 0) {
138
206
  // ⛔ REFRESH IN PLACE, and the position is kept. The binary path moves on
139
207
  // every reinstall, so this arm is the ordinary one on a re-`connect` — and
140
208
  // appending instead would grow the array without bound.
141
209
  entries[at] = ours;
210
+ // ⛔ AND EVERY OTHER COPY OF OURS GOES (`CR-224`). A machine that ran a
211
+ // marker-only `connect` N times after its host stripped the marker holds N
212
+ // copies, each spawning a process on every event. Refreshing only the first
213
+ // would leave N-1 of them firing forever; a re-run has to CONVERGE to one.
214
+ // Only entries `isOurs` claims are removed — rule 1 is about foreign ones,
215
+ // and the claim is anchored on the whole command, so this cannot reach one.
216
+ for (let i = entries.length - 1; i > at; i -= 1) {
217
+ if (mine(entries[i]))
218
+ entries.splice(i, 1);
219
+ }
142
220
  updated.push(registration.event);
143
221
  }
144
222
  else {
145
223
  entries.push(ours);
146
224
  added.push(registration.event);
147
225
  }
148
- foreign += entries.filter((entry) => !isOurs(entry)).length;
226
+ foreign += entries.filter((entry) => !mine(entry)).length;
149
227
  hooks[registration.event] = entries;
150
228
  }
151
229
  doc.hooks = hooks;
152
230
  return { kind: "merged", text: `${JSON.stringify(doc, null, 2)}\n`, added, updated, foreign };
153
231
  }
154
- /** Ours? ⛔ By the marker alone — never by the command, which changes. */
155
- function isOurs(entry) {
156
- return (entry !== null &&
157
- typeof entry === "object" &&
158
- !Array.isArray(entry) &&
159
- entry[MARKER_KEY] === AGENT_HOOK_MARKER);
232
+ /**
233
+ * Ours? ⛔ **By the marker, or by being EXACTLY an entry we could have written.**
234
+ *
235
+ * The marker is the fast path and is enough on its own — it is the only field
236
+ * nothing but this module writes. Without it (`CR-224`: Claude Code's writer
237
+ * strips it), an entry is ours only when ALL of these hold:
238
+ *
239
+ * 1. **Its keys are ours and nobody else's.** `{ hooks: [one hook] }` for the
240
+ * `claude` shape, `{ command, timeout }` for `cursor`, and the one inner
241
+ * hook carries only `type`/`command`/`timeout`. A `matcher`, a second
242
+ * inner hook or any other key means someone EDITED it, and refreshing it
243
+ * would silently delete their edit.
244
+ * 2. **Its command is ours** — see `commandIsOurs`.
245
+ *
246
+ * ⚠ **THE FAILURE DIRECTION IS DELIBERATE.** A false "not ours" costs one
247
+ * duplicate entry, which `claimFiring` (`CR-195`/U4a) already turns into a
248
+ * single capture. A false "ours" overwrites a user's hook — the one outcome this
249
+ * module exists to make impossible. Every doubt resolves to "not ours".
250
+ */
251
+ function isOurs(entry, shape, registration, binaryExists) {
252
+ if (!isRecord(entry))
253
+ return false;
254
+ if (entry[MARKER_KEY] === AGENT_HOOK_MARKER)
255
+ return true;
256
+ if (shape === "cursor") {
257
+ return (hasOnlyKeys(entry, ["command", "timeout"]) &&
258
+ typeof entry.command === "string" &&
259
+ commandIsOurs(entry.command, registration.command, binaryExists));
260
+ }
261
+ if (!hasOnlyKeys(entry, ["hooks"]) || !Array.isArray(entry.hooks) || entry.hooks.length !== 1) {
262
+ return false;
263
+ }
264
+ const inner = entry.hooks[0];
265
+ return (isRecord(inner) &&
266
+ hasOnlyKeys(inner, ["type", "command", "timeout"]) &&
267
+ inner.type === "command" &&
268
+ typeof inner.command === "string" &&
269
+ commandIsOurs(inner.command, registration.command, binaryExists));
270
+ }
271
+ /**
272
+ * ⛔ **THE WHOLE STRING, NEVER A SUBSTRING — the trap `TODOS[138]` names.**
273
+ *
274
+ * Two arms, and only two:
275
+ *
276
+ * - **Identical** to the command we are about to write. The ordinary re-run.
277
+ * - **`hookCommand`'s exact shape with only the binary's DIRECTORY changed.**
278
+ * This is the reinstall — an nvm switch or a new npm prefix moves
279
+ * `<prefix>/bin/vibecommit`, the command text moves with it, and an
280
+ * identical-only rule would append a second entry beside one that now
281
+ * points at a binary that may not exist (so fails on every event). Both
282
+ * sides must parse as the whole `"<path>" hook --agent=<id>` grammar, name
283
+ * the SAME agent (Cursor reads Claude Code's config, so the `--agent=` tail
284
+ * is what tells the two apart), and end in the SAME binary name. What a
285
+ * foreign tool would need to be claimed is our exact argv AND a binary with
286
+ * our name — which is our binary.
287
+ *
288
+ * ⚠ Invoking the same install by a different NAME (`…/bin/vibecommit` vs
289
+ * `…/dist/index.js`) is not matched and leaves one duplicate. That is the safe
290
+ * direction (see `isOurs`), and `index.js` is too generic a name to claim on.
291
+ *
292
+ * ⛔ **AND THE MOVED-DIRECTORY ARM CLAIMS ONLY A DEAD PATH (`TODOS[144]`).** A
293
+ * claimed entry is REPLACED, so this arm decides which binary the user's hooks
294
+ * run. Claiming a LIVE one hands the hook to whatever binary ran `connect` —
295
+ * and a throwaway install (`npx`, a temp prefix, a test harness's artifacts
296
+ * dir) is exactly that: Gate A repointed a machine's working capture at a
297
+ * directory about to be deleted, after which every hook fails quietly and
298
+ * capture stops. Without this arm the same run appends a second entry and the
299
+ * working one keeps firing (`claimFiring` makes it one capture). So: a moved
300
+ * path is claimed only when the binary it names is GONE — the reinstall this
301
+ * arm exists for (an uninstalled prefix, a removed nvm version) — and a live
302
+ * one is left alone. ⚠ Cost, accepted: two live installs leave two entries
303
+ * until one is removed; a duplicate is the safe failure, a hijack is not.
304
+ */
305
+ function commandIsOurs(candidate, ours, binaryExists) {
306
+ if (candidate === ours)
307
+ return true;
308
+ const theirs = parseHookCommand(candidate);
309
+ const mine = parseHookCommand(ours);
310
+ if (theirs === null || mine === null)
311
+ return false;
312
+ return (theirs.agentId === mine.agentId &&
313
+ basename(theirs.binPath) === basename(mine.binPath) &&
314
+ !binaryExists(theirs.binPath));
315
+ }
316
+ function isRecord(value) {
317
+ return value !== null && typeof value === "object" && !Array.isArray(value);
318
+ }
319
+ function hasOnlyKeys(value, allowed) {
320
+ return Object.keys(value).every((key) => allowed.includes(key));
160
321
  }
161
322
  /** Our entry, in the shape that agent's reader actually validates. */
162
323
  function entryFor(shape, registration) {
@@ -204,7 +365,7 @@ export function writeAgentHooks(path, shape, registrations) {
204
365
  // the write below rather than being guessed at here.
205
366
  raw = null;
206
367
  }
207
- const merged = mergeHookDocument(raw, shape, registrations);
368
+ const merged = mergeHookDocument(raw, shape, registrations, existsSync);
208
369
  if (merged.kind === "refused")
209
370
  return { kind: "refused", path, why: merged.why };
210
371
  const temp = join(dirname(path), `.${AGENT_HOOK_MARKER.replace(/\W+/g, "-")}.tmp`);
@@ -1 +1 @@
1
- {"version":3,"file":"agent_hooks.js","sourceRoot":"","sources":["../src/agent_hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent_hooks.js","sourceRoot":"","sources":["../src/agent_hooks.ts"],"names":[],"mappings":"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"}
@@ -46,23 +46,6 @@ export interface AgentHookOutcome {
46
46
  readonly path: string;
47
47
  readonly install: AgentHookInstall | null;
48
48
  }
49
- /**
50
- * The command an agent's config invokes.
51
- *
52
- * ⛔ **`hook` LEADS, AND THE FLAG NEVER DOES.** `invocationMode` returns `"hook"`
53
- * only when `argv[0] === "hook"`, so a registered command whose first argument
54
- * is `--agent=` runs the binary INTERACTIVELY — inside a hook, printing to a
55
- * stream the agent reads back into the user's turn. `registry.ts` names this as
56
- * the trap the wave was most likely to ship, and `test/agent-registry.test.ts`
57
- * holds both arms.
58
- *
59
- * ⚠ **The flag is passed explicitly for every agent, Claude Code included**,
60
- * even though `DEFAULT_AGENT_ID` makes it redundant there. A config that names
61
- * its own agent is self-describing, and — since Cursor reads Claude Code's
62
- * settings and maps them onto its own events — it is the only thing that tells
63
- * the two invocations apart when both fire.
64
- */
65
- export declare function hookCommand(binPath: string, agentId: AgentId): string;
66
49
  /** Every event this client registers, in one agent's own vocabulary. */
67
50
  export declare function registrationsFor(dialect: TranscriptDialect, binPath: string): readonly HookRegistration[];
68
51
  /** Is this agent on the machine at all? The DIRECTORY, never the config file. */
@@ -40,32 +40,9 @@
40
40
  */
41
41
  import { existsSync } from "node:fs";
42
42
  import { dirname } from "node:path";
43
- import { writeAgentHooks } from "./agent_hooks.js";
43
+ import { hookCommand, writeAgentHooks, } from "./agent_hooks.js";
44
44
  import { DIALECTS } from "./agents/registry.js";
45
45
  import { HOOK_EVENTS } from "./agents/types.js";
46
- /**
47
- * The command an agent's config invokes.
48
- *
49
- * ⛔ **`hook` LEADS, AND THE FLAG NEVER DOES.** `invocationMode` returns `"hook"`
50
- * only when `argv[0] === "hook"`, so a registered command whose first argument
51
- * is `--agent=` runs the binary INTERACTIVELY — inside a hook, printing to a
52
- * stream the agent reads back into the user's turn. `registry.ts` names this as
53
- * the trap the wave was most likely to ship, and `test/agent-registry.test.ts`
54
- * holds both arms.
55
- *
56
- * ⚠ **The flag is passed explicitly for every agent, Claude Code included**,
57
- * even though `DEFAULT_AGENT_ID` makes it redundant there. A config that names
58
- * its own agent is self-describing, and — since Cursor reads Claude Code's
59
- * settings and maps them onto its own events — it is the only thing that tells
60
- * the two invocations apart when both fire.
61
- */
62
- export function hookCommand(binPath, agentId) {
63
- return `${quote(binPath)} hook --agent=${agentId}`;
64
- }
65
- /** Double-quote for the shells these configs are read by. */
66
- function quote(value) {
67
- return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
68
- }
69
46
  /** Every event this client registers, in one agent's own vocabulary. */
70
47
  export function registrationsFor(dialect, binPath) {
71
48
  return HOOK_EVENTS.map((event) => ({
@@ -1 +1 @@
1
- {"version":3,"file":"agent_install.js","sourceRoot":"","sources":["../src/agent_install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAgD,MAAM,kBAAkB,CAAC;AACjG,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAwC,MAAM,mBAAmB,CAAC;AAStF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,OAAgB;IAC3D,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,iBAAiB,OAAO,EAAE,CAAC;AACrD,CAAC;AAED,6DAA6D;AAC7D,SAAS,KAAK,CAAC,KAAa;IAC1B,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,CAAC;AACpD,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAC9B,OAA0B,EAC1B,OAAe;IAEf,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC;QAChC,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;QACzC,sEAAsE;QACtE,sEAAsE;QACtE,4EAA4E;QAC5E,4EAA4E;QAC5E,mDAAmD;QACnD,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;KACtF,CAAC,CAAC,CAAC;AACN,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,OAA0B,EAAE,IAAY,EAAE,GAAsB;IAC3F,OAAO,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,GAAsB,EACtB,OAAe;IAEf,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACtD,CAAC;QACD,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,EAAE;YACnB,IAAI;YACJ,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;SAC7F,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"agent_install.js","sourceRoot":"","sources":["../src/agent_install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EACL,WAAW,EACX,eAAe,GAGhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAwC,MAAM,mBAAmB,CAAC;AAStF,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAC9B,OAA0B,EAC1B,OAAe;IAEf,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC;QAChC,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;QACzC,sEAAsE;QACtE,sEAAsE;QACtE,4EAA4E;QAC5E,4EAA4E;QAC5E,mDAAmD;QACnD,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;KACtF,CAAC,CAAC,CAAC;AACN,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAAC,OAA0B,EAAE,IAAY,EAAE,GAAsB;IAC3F,OAAO,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;AACjE,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAY,EACZ,GAAsB,EACtB,OAAe;IAEf,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAChD,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACtD,CAAC;QACD,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,EAAE;YACnB,IAAI;YACJ,OAAO,EAAE,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;SAC7F,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -19,27 +19,63 @@
19
19
  */
20
20
  import { cursorHooksPath, cursorTranscriptRoot, isCursorTranscript } from "../paths.js";
21
21
  /**
22
- * ⛔ **UNMEASURED, AND ADOPTED AS A FLOOR RATHER THAN GUESSED.**
22
+ * ⭐ **MEASURED. `D5`'s SECOND HALF IS ANSWERED (`TODOS[130]`), AND THE ANSWER IS
23
+ * THAT CURSOR IMPOSES NO NUMBER OF ITS OWN.**
23
24
  *
24
- * ⚠ **`D5` HAD TWO HALVES AND ONLY ONE OF THEM IS ANSWERED (D205).** It asked
25
- * for the exact `hook_event_name` strings Cursor sends *and* the timeouts its
26
- * `~/.cursor/hooks.json` registers. The STRINGS are now measured — see
27
- * `eventNames` below, read out of the shipped app bundle, which is what
28
- * discharged `CR-195`'s block. ⛔ **The TIMEOUTS below are still not measured**,
29
- * and are deliberately left as the floor they always were rather than quietly
30
- * promoted on the strength of the other half. Nothing installs a Cursor hook
31
- * today, so no Cursor hook has ever fired and these numbers remain unreachable
32
- * in production.
25
+ * `D5` asked for the exact `hook_event_name` strings Cursor sends *and* the
26
+ * timeouts its `~/.cursor/hooks.json` registers. D205 discharged the strings
27
+ * from the shipped app bundle. The timeouts are now read the same way, out of
28
+ * `workbench.desktop.main.js` — the validator `Fed` and the executor
29
+ * `_executeCommandHookScript`:
33
30
  *
34
- * ⚠ **The direction of safety is what picks them.** Too WIDE and the agent
35
- * kills the process before this client's watchdog fires — which loses the
36
- * exit-0/silent-stdout contract, the one outcome DESIGN.md §13.7 forbids. Too
37
- * TIGHT and an invocation sends less and the next one picks it up. So the
38
- * registry's tightest existing pair is taken as a FLOOR: it holds whatever
39
- * Cursor turns out to register, down to Codex's 3 s cap.
31
+ * | question | measured |
32
+ * |---|---|
33
+ * | is there a per-hook `timeout`? | **yes**, and it is in **SECONDS** |
34
+ * | is it clamped or capped? | **NO** — above `3600` Cursor only `console.warn`s |
35
+ * | rejected when? | non-number, or `<= 0`. Nothing else. |
36
+ * | default when omitted | **60 s** — `(t.timeout ?? ROi) * 1e3`, `ROi = 60` |
37
+ * | enforcement | real: killed, **exit code 124**, logged `timed out after Ns` |
38
+ * | on timeout | `failClosed: true` BLOCKS the tool; otherwise the step proceeds |
40
39
  *
41
- * ⛔ **This is a refusal to guess, not a measurement of Cursor**, and `CR-195`
42
- * owns the real numbers once `D5` comes back.
40
+ * ⛔ **SO THERE WAS NEVER A CURSOR NUMBER TO DISCOVER. Cursor honours whatever we
41
+ * register.** The old header said `CR-195` "owns the real numbers once `D5` comes
42
+ * back"; `D5` is back, and the real numbers are ours to choose.
43
+ *
44
+ * ## ⚠ THE NUMBERS BELOW DO NOT CHANGE — BUT THEIR JUSTIFICATION DOES
45
+ *
46
+ * They were a defensive floor against a cap that might exist. They are now a
47
+ * DELIBERATE PAIR, and the reasoning is no longer conditional:
48
+ *
49
+ * - We register **3 s**, so Cursor kills the hook at 3 s and not before.
50
+ * - Our own watchdog is **2.2 s**, so it always fires **800 ms FIRST**.
51
+ *
52
+ * That ordering is the whole point. `DESIGN.md §13.7` forbids one outcome — the
53
+ * agent killing us before we can exit 0 with silent stdout — and the old header
54
+ * could only *hope* the registered value held. It holds: the client watchdog
55
+ * leads the registered timeout by construction, on a value Cursor does not
56
+ * override.
57
+ *
58
+ * ⚠ And note the direction against Cursor's own default: **60 s**. Registering
59
+ * 3 s TIGHTENS by twenty-fold. An omitted `timeout` would leave a wedged hook
60
+ * stalling an agent step for a full minute, so writing this field is not
61
+ * ceremony — it is the difference between a 2.2 s worst case and a 60 s one.
62
+ *
63
+ * ## ⛔ WHAT THIS MEASUREMENT IS, AND WHAT IT IS NOT
64
+ *
65
+ * It is a reading of **shipped code**, not an observed firing — the same
66
+ * standing as D205's strings, and the same limit. It proves what Cursor's
67
+ * validator accepts and what its executor does with the value. It does **not**
68
+ * prove a hook of ours has been fired by a real Cursor session and killed at
69
+ * 3 s. That confirmation is cheap now that the signal is known — **exit code
70
+ * 124** and `Hook … timed out after 3s` in Cursor's log — and it is owed.
71
+ *
72
+ * ⚠ **Both clauses of the old header were false when it was written, which is
73
+ * why this row existed.** It said *"Nothing installs a Cursor hook today, so no
74
+ * Cursor hook has ever fired and these numbers remain unreachable in
75
+ * production."* `CURSOR` carries a `hookConfig`, so `installAgentHooks` writes
76
+ * `~/.cursor/hooks.json` on any machine with Cursor present — and that shipped
77
+ * in `0.2.0` and is live in `0.2.3`. The numbers were reachable, in users'
78
+ * hands, and documented as unreachable.
43
79
  */
44
80
  const CURSOR_REGISTERED_MS = 3_000;
45
81
  const CURSOR_CLIENT_MS = 2_200;
@@ -1 +1 @@
1
- {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGxF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,oBAAoB,GAAG,KAAK,CAAC;AACnC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,CAAC,MAAM,MAAM,GAAsB;IACvC,EAAE,EAAE,QAAQ;IACZ,SAAS,EAAE,QAAQ;IACnB,8EAA8E;IAC9E,2EAA2E;IAC3E,6EAA6E;IAC7E,uDAAuD;IACvD,cAAc,EAAE,oBAAoB;IACpC,wEAAwE;IACxE,uEAAuE;IACvE,UAAU,EAAE,kBAAkB;IAC9B,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,iEAAiE;IACjE,+EAA+E;IAC/E,mEAAmE;IACnE,EAAE;IACF,8EAA8E;IAC9E,4EAA4E;IAC5E,EAAE;IACF,6EAA6E;IAC7E,8EAA8E;IAC9E,qDAAqD;IACrD,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE;IAChF,yEAAyE;IACzE,4EAA4E;IAC5E,uEAAuE;IACvE,6EAA6E;IAC7E,2EAA2E;IAC3E,uDAAuD;IACvD,yEAAyE;IACzE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE;IACtE,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;KACF;IACD,wEAAwE;IACxE,uDAAuD;IACvD,+EAA+E;IAC/E,+EAA+E;IAC/E,8EAA8E;IAC9E,8EAA8E;IAC9E,+DAA+D;IAC/D,oBAAoB,EAAE,UAAU;IAChC,SAAS,EAAE,QAAQ;IACnB,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,+EAA+E;IAC/E,yBAAyB;IACzB,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI;IAC5B,8EAA8E;IAC9E,+EAA+E;IAC/E,4EAA4E;IAC5E,6EAA6E;IAC7E,0CAA0C;CAC3C,CAAC"}
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/agents/cursor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGxF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAM,oBAAoB,GAAG,KAAK,CAAC;AACnC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,CAAC,MAAM,MAAM,GAAsB;IACvC,EAAE,EAAE,QAAQ;IACZ,SAAS,EAAE,QAAQ;IACnB,8EAA8E;IAC9E,2EAA2E;IAC3E,6EAA6E;IAC7E,uDAAuD;IACvD,cAAc,EAAE,oBAAoB;IACpC,wEAAwE;IACxE,uEAAuE;IACvE,UAAU,EAAE,kBAAkB;IAC9B,8EAA8E;IAC9E,6EAA6E;IAC7E,4EAA4E;IAC5E,iEAAiE;IACjE,+EAA+E;IAC/E,mEAAmE;IACnE,EAAE;IACF,8EAA8E;IAC9E,4EAA4E;IAC5E,EAAE;IACF,6EAA6E;IAC7E,8EAA8E;IAC9E,qDAAqD;IACrD,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE;IAChF,yEAAyE;IACzE,4EAA4E;IAC5E,uEAAuE;IACvE,6EAA6E;IAC7E,2EAA2E;IAC3E,uDAAuD;IACvD,yEAAyE;IACzE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE;IACtE,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;QACD,UAAU,EAAE;YACV,mBAAmB,EAAE,oBAAoB;YACzC,cAAc,EAAE,gBAAgB;SACjC;KACF;IACD,wEAAwE;IACxE,uDAAuD;IACvD,+EAA+E;IAC/E,+EAA+E;IAC/E,8EAA8E;IAC9E,8EAA8E;IAC9E,+DAA+D;IAC/D,oBAAoB,EAAE,UAAU;IAChC,SAAS,EAAE,QAAQ;IACnB,+EAA+E;IAC/E,6EAA6E;IAC7E,6EAA6E;IAC7E,6EAA6E;IAC7E,+EAA+E;IAC/E,yBAAyB;IACzB,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI;IAC5B,8EAA8E;IAC9E,+EAA+E;IAC/E,4EAA4E;IAC5E,6EAA6E;IAC7E,0CAA0C;CAC3C,CAAC"}