@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,109 @@
1
+ /**
2
+ * Where the failure policy's decisions are remembered — CR-018.
3
+ *
4
+ * `policy.ts` decides; this module persists. The split is what keeps every
5
+ * classification and both backlog caps testable without a filesystem.
6
+ *
7
+ * ## The binding key — `CR-017d`
8
+ *
9
+ * `(repoKey, sessionId)`, and the repo half is REQUIRED. `CR-018` shipped this
10
+ * module keyed on the session alone, deliberately and one dimension short; D58
11
+ * names what that costs: *"The server key gains repo; the client offset state did
12
+ * not, so a `prefix_sha256` resync would duplicate repo 1's turns into repo 2."*
13
+ * The key lives in the path (`sessionStatePath`, which refuses to produce one
14
+ * without a repo), so there is no bucket for a caller that cannot say which repo
15
+ * it is in. `files` is untouched — it is already keyed by `FileKey`, which is the
16
+ * `file_key` of the server's four-column key.
17
+ *
18
+ * What `CR-018` needed, and the only reason this module exists a wave early, is
19
+ * that its three classes are *defined* in terms of an offset: `later` holds one,
20
+ * `never` advances one, and neither means anything without somewhere to keep it.
21
+ * `CR-016` left a bootstrap that re-sent from byte 0 every time and said,
22
+ * correctly, not to build a backlog policy on that. This is the alternative it was
23
+ * pointing at, kept as thin as the policy allows.
24
+ *
25
+ * ## Failure posture
26
+ *
27
+ * Every read failure yields the empty state. That direction is a RESYNC — the
28
+ * client re-sends from offset 0 and the server is idempotent on
29
+ * `(repo, session, offset)` — rather than a silent skip, which would be data
30
+ * loss. The one thing lost with it is a recorded `fatal` stop, and a revoked
31
+ * credential simply 401s again on the next attempt and re-stops.
32
+ *
33
+ * Two hooks racing in one session can lose an update to each other. The
34
+ * consequence is a re-send from a stale offset, which the server treats as a
35
+ * resync — so this is left unlocked rather than paying for a lockfile on the
36
+ * hook's wall-clock budget.
37
+ */
38
+ import { type SessionKey } from "./paths.js";
39
+ import { type FileState } from "./policy.js";
40
+ /** The terminal stop set by the `fatal` class. */
41
+ export interface Stop {
42
+ readonly at: string;
43
+ /**
44
+ * Which credential was refused. Not the credential — a one-way digest of it
45
+ * (`credentialFingerprint`, `post.ts`).
46
+ *
47
+ * This is what makes "stopped until the user reconnects" hold WITHOUT this
48
+ * module knowing anything about `connect`: reconnecting mints a different
49
+ * credential, its fingerprint does not match, and capture resumes on its own.
50
+ * A stop keyed on the session alone would either wedge capture until the
51
+ * session ended or need a clearing call from a file this task does not own.
52
+ */
53
+ readonly fingerprint: string;
54
+ }
55
+ export interface SessionState {
56
+ /** Monotonic per session, across the main transcript and every sub-agent's. */
57
+ readonly seq: number;
58
+ readonly stop: Stop | null;
59
+ /**
60
+ * Keyed by `FileKey` — `main`, or `agent-<id>` for a delegated transcript.
61
+ *
62
+ * `CR-124` (W6, D76) fills the `agent-*` half. The comment here credited
63
+ * `CR-020` for a wave; that was wrong — `CR-020`'s finding (D57 §DX7) is
64
+ * SessionEnd timing only and says nothing about sub-agent files, which is how
65
+ * the work stayed believed-owned while nothing produced it.
66
+ *
67
+ * The SHAPE needed no change to accommodate them, which is why this is a
68
+ * comment fix and not a migration.
69
+ */
70
+ readonly files: Readonly<Record<string, FileState>>;
71
+ }
72
+ export declare const EMPTY_SESSION_STATE: SessionState;
73
+ export declare function loadSessionState(home: string, key: SessionKey): SessionState;
74
+ /** The most recent delivery for one repo, across every session and file. */
75
+ export interface LastSend {
76
+ /** Epoch ms. */
77
+ readonly at: number;
78
+ /** `seq` of the session that delivery belongs to. */
79
+ readonly seq: number;
80
+ }
81
+ /**
82
+ * When did this REPO last deliver anything, and at what `seq` — `CR-021`.
83
+ *
84
+ * A scan, deliberately, and not a second index. `status` is interactive and has
85
+ * no `session_id`, so it cannot address a session file the way a hook can; it
86
+ * knows only the repo. The alternative — a per-repo "latest" pointer written on
87
+ * every successful send — is a second source of truth for a fact the session
88
+ * files already hold, and it would be the one that goes stale.
89
+ *
90
+ * The directory holds one small JSON per session for one repo, read once per
91
+ * `status` invocation on a human's keypress. There is no budget here worth
92
+ * trading correctness for.
93
+ *
94
+ * Null means nothing has ever been delivered for this repo. That is distinct
95
+ * from `at: 0`, which a pre-`CR-021` file parses to — both render as "nothing
96
+ * recorded yet" rather than as a date.
97
+ */
98
+ export declare function lastSendForRepo(home: string, repoKey: string): LastSend | null;
99
+ /** Persist. Returns false on any failure — the caller must not throw from a hook. */
100
+ export declare function saveSessionState(home: string, key: SessionKey, next: SessionState): boolean;
101
+ export declare function fileState(session: SessionState, fileKey: string): FileState;
102
+ export declare function withFileState(session: SessionState, fileKey: string, next: FileState): SessionState;
103
+ /**
104
+ * Is this session stopped for THIS credential?
105
+ *
106
+ * A stop recorded against a different fingerprint does not apply: that is the
107
+ * user having reconnected.
108
+ */
109
+ export declare function isStopped(session: SessionState, fingerprint: string): boolean;
package/dist/state.js ADDED
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Where the failure policy's decisions are remembered — CR-018.
3
+ *
4
+ * `policy.ts` decides; this module persists. The split is what keeps every
5
+ * classification and both backlog caps testable without a filesystem.
6
+ *
7
+ * ## The binding key — `CR-017d`
8
+ *
9
+ * `(repoKey, sessionId)`, and the repo half is REQUIRED. `CR-018` shipped this
10
+ * module keyed on the session alone, deliberately and one dimension short; D58
11
+ * names what that costs: *"The server key gains repo; the client offset state did
12
+ * not, so a `prefix_sha256` resync would duplicate repo 1's turns into repo 2."*
13
+ * The key lives in the path (`sessionStatePath`, which refuses to produce one
14
+ * without a repo), so there is no bucket for a caller that cannot say which repo
15
+ * it is in. `files` is untouched — it is already keyed by `FileKey`, which is the
16
+ * `file_key` of the server's four-column key.
17
+ *
18
+ * What `CR-018` needed, and the only reason this module exists a wave early, is
19
+ * that its three classes are *defined* in terms of an offset: `later` holds one,
20
+ * `never` advances one, and neither means anything without somewhere to keep it.
21
+ * `CR-016` left a bootstrap that re-sent from byte 0 every time and said,
22
+ * correctly, not to build a backlog policy on that. This is the alternative it was
23
+ * pointing at, kept as thin as the policy allows.
24
+ *
25
+ * ## Failure posture
26
+ *
27
+ * Every read failure yields the empty state. That direction is a RESYNC — the
28
+ * client re-sends from offset 0 and the server is idempotent on
29
+ * `(repo, session, offset)` — rather than a silent skip, which would be data
30
+ * loss. The one thing lost with it is a recorded `fatal` stop, and a revoked
31
+ * credential simply 401s again on the next attempt and re-stops.
32
+ *
33
+ * Two hooks racing in one session can lose an update to each other. The
34
+ * consequence is a re-send from a stale offset, which the server treats as a
35
+ * resync — so this is left unlocked rather than paying for a lockfile on the
36
+ * hook's wall-clock budget.
37
+ */
38
+ import { chmodSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
39
+ import { dirname, join } from "node:path";
40
+ import { repoSessionsDir, sessionStatePath } from "./paths.js";
41
+ import { EMPTY_FILE_STATE } from "./policy.js";
42
+ export const EMPTY_SESSION_STATE = { seq: 0, stop: null, files: {} };
43
+ export function loadSessionState(home, key) {
44
+ const path = sessionStatePath(home, key);
45
+ // No repo, no key, no read. Falling back to a session-only file here is the
46
+ // whole of D58: it is the one path on which two repos share an offset ledger.
47
+ if (path === null)
48
+ return EMPTY_SESSION_STATE;
49
+ return readStateAt(path);
50
+ }
51
+ /** One state file, or the empty state. Every failure yields empty — a RESYNC. */
52
+ function readStateAt(path) {
53
+ let raw;
54
+ try {
55
+ raw = readFileSync(path, "utf8");
56
+ }
57
+ catch {
58
+ return EMPTY_SESSION_STATE;
59
+ }
60
+ try {
61
+ return parseSessionState(JSON.parse(raw));
62
+ }
63
+ catch {
64
+ return EMPTY_SESSION_STATE;
65
+ }
66
+ }
67
+ /**
68
+ * When did this REPO last deliver anything, and at what `seq` — `CR-021`.
69
+ *
70
+ * A scan, deliberately, and not a second index. `status` is interactive and has
71
+ * no `session_id`, so it cannot address a session file the way a hook can; it
72
+ * knows only the repo. The alternative — a per-repo "latest" pointer written on
73
+ * every successful send — is a second source of truth for a fact the session
74
+ * files already hold, and it would be the one that goes stale.
75
+ *
76
+ * The directory holds one small JSON per session for one repo, read once per
77
+ * `status` invocation on a human's keypress. There is no budget here worth
78
+ * trading correctness for.
79
+ *
80
+ * Null means nothing has ever been delivered for this repo. That is distinct
81
+ * from `at: 0`, which a pre-`CR-021` file parses to — both render as "nothing
82
+ * recorded yet" rather than as a date.
83
+ */
84
+ export function lastSendForRepo(home, repoKey) {
85
+ const dir = repoSessionsDir(home, repoKey);
86
+ if (dir === null)
87
+ return null;
88
+ let entries;
89
+ try {
90
+ entries = readdirSync(dir);
91
+ }
92
+ catch {
93
+ // No directory means nothing was ever written for this repo.
94
+ return null;
95
+ }
96
+ let best = null;
97
+ for (const entry of entries) {
98
+ if (!entry.endsWith(".json"))
99
+ continue;
100
+ const session = readStateAt(join(dir, entry));
101
+ for (const file of Object.values(session.files)) {
102
+ if (file.lastSentAt <= 0)
103
+ continue;
104
+ if (best === null || file.lastSentAt > best.at) {
105
+ best = { at: file.lastSentAt, seq: session.seq };
106
+ }
107
+ }
108
+ }
109
+ return best;
110
+ }
111
+ /** Persist. Returns false on any failure — the caller must not throw from a hook. */
112
+ export function saveSessionState(home, key, next) {
113
+ const path = sessionStatePath(home, key);
114
+ // Refusing to write is safe in the direction this module already fails: the
115
+ // next invocation resyncs from 0. Writing under a placeholder key is not.
116
+ if (path === null)
117
+ return false;
118
+ try {
119
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
120
+ writeFileSync(path, `${JSON.stringify(next)}\n`, { mode: 0o600 });
121
+ // `writeFileSync`'s `mode` is ignored when the file already exists.
122
+ chmodSync(path, 0o600);
123
+ return true;
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ }
129
+ export function fileState(session, fileKey) {
130
+ return session.files[fileKey] ?? EMPTY_FILE_STATE;
131
+ }
132
+ export function withFileState(session, fileKey, next) {
133
+ return { ...session, files: { ...session.files, [fileKey]: next } };
134
+ }
135
+ /**
136
+ * Is this session stopped for THIS credential?
137
+ *
138
+ * A stop recorded against a different fingerprint does not apply: that is the
139
+ * user having reconnected.
140
+ */
141
+ export function isStopped(session, fingerprint) {
142
+ return session.stop !== null && session.stop.fingerprint === fingerprint;
143
+ }
144
+ /**
145
+ * Read back a persisted state defensively.
146
+ *
147
+ * Hand-written rather than trusted, for the same reason `consent.ts` validates
148
+ * its allow list: this file lives in the user's home, is edited by nothing but
149
+ * us, and would still be believed if something else wrote nonsense into it. A
150
+ * bogus `sentOffset` would silently skip real turns.
151
+ */
152
+ function parseSessionState(parsed) {
153
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
154
+ return EMPTY_SESSION_STATE;
155
+ }
156
+ const o = parsed;
157
+ const files = {};
158
+ const rawFiles = o.files;
159
+ if (rawFiles !== null && typeof rawFiles === "object" && !Array.isArray(rawFiles)) {
160
+ for (const [key, value] of Object.entries(rawFiles)) {
161
+ const state = parseFileState(value);
162
+ if (state !== null)
163
+ files[key] = state;
164
+ }
165
+ }
166
+ return { seq: nonNegative(o.seq), stop: parseStop(o.stop), files };
167
+ }
168
+ function parseStop(value) {
169
+ if (value === null || typeof value !== "object" || Array.isArray(value))
170
+ return null;
171
+ const o = value;
172
+ if (typeof o.at !== "string" || typeof o.fingerprint !== "string")
173
+ return null;
174
+ if (o.fingerprint === "")
175
+ return null;
176
+ return { at: o.at, fingerprint: o.fingerprint };
177
+ }
178
+ function parseFileState(value) {
179
+ if (value === null || typeof value !== "object" || Array.isArray(value))
180
+ return null;
181
+ const o = value;
182
+ const sentOffset = nonNegative(o.sentOffset);
183
+ const backlog = [];
184
+ if (Array.isArray(o.backlog)) {
185
+ let cursor = sentOffset;
186
+ for (const entry of o.backlog) {
187
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry))
188
+ continue;
189
+ const e = entry;
190
+ const from = nonNegative(e.from);
191
+ const to = nonNegative(e.to);
192
+ const at = nonNegative(e.at);
193
+ // Contiguity is an invariant of the writer, so a file that breaks it has
194
+ // been tampered with or half-written. Stop at the break rather than trust
195
+ // the rest: a non-contiguous backlog makes `pendingBytes` a fiction.
196
+ if (from !== cursor || to <= from)
197
+ break;
198
+ backlog.push({ from, to, at });
199
+ cursor = to;
200
+ }
201
+ }
202
+ return {
203
+ sentOffset,
204
+ backlog,
205
+ gapBytes: nonNegative(o.gapBytes),
206
+ gapCount: nonNegative(o.gapCount),
207
+ // Absent in any file written before `CR-021`. `nonNegative` yields 0, which
208
+ // this codebase reads as "never sent" rather than as 1 January 1970.
209
+ lastSentAt: nonNegative(o.lastSentAt),
210
+ };
211
+ }
212
+ function nonNegative(value) {
213
+ return typeof value === "number" && Number.isFinite(value) && value >= 0
214
+ ? Math.floor(value)
215
+ : 0;
216
+ }
217
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The `systemMessage` channel — D57 (plan §DX3), style guide §10.1.
3
+ *
4
+ * The only sanctioned way a hook speaks to a human. It renders to the user
5
+ * **without** entering the agent's context, which is why it is safe and why it
6
+ * must stay short.
7
+ *
8
+ * FOUR states — `CR-023d` added the fourth, and it inverts an assumption the
9
+ * other three shared.
10
+ *
11
+ * **"stdout empty on the happy path" is no longer universally true.** Every
12
+ * populated-stdout case before `capture_confirmed` was a FAILURE signal, so a
13
+ * consumer could read "any `systemMessage`" as "something is wrong". That
14
+ * consumer is now wrong. This is an inversion, not a shape-preserving addition,
15
+ * which is why it is called out here rather than left to the type.
16
+ *
17
+ * **The fourth state is keyed per `(repo, day)`, the other three per session.**
18
+ * `PreCompact` mints a new `session_id` mid-task, so a per-session key would fire
19
+ * the one positive message twice in an afternoon and read as a bug; a key with no
20
+ * day at all would fire once ever, which under-communicates on a daily tool. See
21
+ * `repoDayNoticePath`.
22
+ *
23
+ * ⚠ Two hard constraints from DESIGN.md §13.7/§13.9:
24
+ * - stdout carries **exactly one** well-formed JSON object, or nothing.
25
+ * **If construction fails for ANY reason → empty stdout. The fallback is
26
+ * always silence.**
27
+ * - **No SGR escapes inside the string.** It is a JSON field rendered by Claude
28
+ * Code's own UI; embedded escapes are undefined behaviour.
29
+ */
30
+ export type NoticeState = "not_connected" | "credential_revoked" | "unsupported_runtime"
31
+ /** The only POSITIVE state, and the only one not keyed on the session. */
32
+ | "capture_confirmed";
33
+ export interface NoticeContext {
34
+ readonly home: string;
35
+ readonly sessionId: string;
36
+ /** `process.versions.node`, for the unsupported-runtime state. */
37
+ readonly nodeVersion: string;
38
+ /**
39
+ * `resolveProjectKey()`'s git toplevel — half of `capture_confirmed`'s key.
40
+ *
41
+ * Null for the three session-keyed states, which is not a placeholder: two of
42
+ * them can fire BEFORE the consent gate has resolved a project at all
43
+ * (`unsupported_runtime` fires before anything else runs). `capture_confirmed`
44
+ * cannot fire without one and refuses rather than falling back — the same
45
+ * discipline `CR-017d` applied to the state key, for the same reason.
46
+ */
47
+ readonly repoKey: string | null;
48
+ /**
49
+ * Injected so the day boundary is reachable from a test. Following
50
+ * `CommandContext.now`'s precedent; a module that read the clock directly
51
+ * could only be tested by waiting for midnight.
52
+ */
53
+ readonly now: () => Date;
54
+ }
55
+ /**
56
+ * The UTC calendar day, `YYYY-MM-DD`.
57
+ *
58
+ * UTC and not local: a local key silently depends on the machine's timezone and
59
+ * changes twice a year at a DST boundary, which would fire the message a second
60
+ * time on one day in spring and skip it on one in autumn. It also matches how
61
+ * this file already timestamps (`new Date().toISOString()`).
62
+ */
63
+ export declare function utcDay(now: Date): string;
64
+ /** The one-line text for a state. Never contains an escape sequence. */
65
+ export declare function noticeText(state: NoticeState, nodeVersion: string): string;
66
+ /**
67
+ * Where this state's once-per-key marker lives, or null if it cannot have one.
68
+ *
69
+ * The whole difference between the four states is here: three are keyed on the
70
+ * session, and `capture_confirmed` is keyed on `(repo, day)`.
71
+ *
72
+ * Null when `capture_confirmed` has no repo. **Refuse, do not guess** — a
73
+ * fallback key would be shared by every repo on the machine, so the first repo
74
+ * to capture today would silence all the others. Null becomes silence, which is
75
+ * this channel's fallback everywhere else too.
76
+ */
77
+ export declare function noticeClaimPath(ctx: NoticeContext, state: NoticeState): string | null;
78
+ /**
79
+ * Claim the once-per-KEY slot for `state`.
80
+ *
81
+ * `wx` is doing real work here: it creates-or-fails atomically, so two hooks
82
+ * firing concurrently under the same key cannot both win. A read-then-write
83
+ * check would let both through and print the message twice. That mattered for
84
+ * one session; it matters more now, because `capture_confirmed`'s key spans a
85
+ * whole day and therefore many more concurrent hooks.
86
+ *
87
+ * Returns false on ANY failure, including an unwritable home. A message we cannot
88
+ * record having sent is a message we do not send — repeating it every turn is
89
+ * worse than losing it once.
90
+ */
91
+ export declare function claimSessionNotice(ctx: NoticeContext, state: NoticeState): boolean;
92
+ /**
93
+ * Render the hook's stdout payload for a state, or null.
94
+ *
95
+ * Null means "write nothing", and every failure resolves to null. The caller
96
+ * writes the string verbatim and adds a trailing newline; it never builds JSON
97
+ * itself, so there is exactly one place this contract can be broken.
98
+ */
99
+ export declare function renderNotice(ctx: NoticeContext, state: NoticeState): string | null;
100
+ /** True if `text` holds any C0 control character (including ESC) or DEL. */
101
+ export declare function hasControlCharacter(text: string): boolean;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * The `systemMessage` channel — D57 (plan §DX3), style guide §10.1.
3
+ *
4
+ * The only sanctioned way a hook speaks to a human. It renders to the user
5
+ * **without** entering the agent's context, which is why it is safe and why it
6
+ * must stay short.
7
+ *
8
+ * FOUR states — `CR-023d` added the fourth, and it inverts an assumption the
9
+ * other three shared.
10
+ *
11
+ * **"stdout empty on the happy path" is no longer universally true.** Every
12
+ * populated-stdout case before `capture_confirmed` was a FAILURE signal, so a
13
+ * consumer could read "any `systemMessage`" as "something is wrong". That
14
+ * consumer is now wrong. This is an inversion, not a shape-preserving addition,
15
+ * which is why it is called out here rather than left to the type.
16
+ *
17
+ * **The fourth state is keyed per `(repo, day)`, the other three per session.**
18
+ * `PreCompact` mints a new `session_id` mid-task, so a per-session key would fire
19
+ * the one positive message twice in an afternoon and read as a bug; a key with no
20
+ * day at all would fire once ever, which under-communicates on a daily tool. See
21
+ * `repoDayNoticePath`.
22
+ *
23
+ * ⚠ Two hard constraints from DESIGN.md §13.7/§13.9:
24
+ * - stdout carries **exactly one** well-formed JSON object, or nothing.
25
+ * **If construction fails for ANY reason → empty stdout. The fallback is
26
+ * always silence.**
27
+ * - **No SGR escapes inside the string.** It is a JSON field rendered by Claude
28
+ * Code's own UI; embedded escapes are undefined behaviour.
29
+ */
30
+ import { mkdirSync, writeFileSync } from "node:fs";
31
+ import { dirname } from "node:path";
32
+ import { SYSTEM_MESSAGE, URLS } from "./copy/index.js";
33
+ import { repoDayNoticePath, sessionNoticePath } from "./paths.js";
34
+ import { NODE_FLOOR_TEXT } from "./runtime.js";
35
+ /**
36
+ * The UTC calendar day, `YYYY-MM-DD`.
37
+ *
38
+ * UTC and not local: a local key silently depends on the machine's timezone and
39
+ * changes twice a year at a DST boundary, which would fire the message a second
40
+ * time on one day in spring and skip it on one in autumn. It also matches how
41
+ * this file already timestamps (`new Date().toISOString()`).
42
+ */
43
+ export function utcDay(now) {
44
+ return now.toISOString().slice(0, 10);
45
+ }
46
+ /** The one-line text for a state. Never contains an escape sequence. */
47
+ export function noticeText(state, nodeVersion) {
48
+ switch (state) {
49
+ case "not_connected":
50
+ return SYSTEM_MESSAGE.notConnected;
51
+ case "credential_revoked":
52
+ return SYSTEM_MESSAGE.credentialRevoked;
53
+ case "unsupported_runtime":
54
+ return SYSTEM_MESSAGE.unsupportedRuntime(NODE_FLOOR_TEXT, nodeVersion);
55
+ case "capture_confirmed":
56
+ return SYSTEM_MESSAGE.captureConfirmed(URLS.dashboard);
57
+ }
58
+ }
59
+ /**
60
+ * Where this state's once-per-key marker lives, or null if it cannot have one.
61
+ *
62
+ * The whole difference between the four states is here: three are keyed on the
63
+ * session, and `capture_confirmed` is keyed on `(repo, day)`.
64
+ *
65
+ * Null when `capture_confirmed` has no repo. **Refuse, do not guess** — a
66
+ * fallback key would be shared by every repo on the machine, so the first repo
67
+ * to capture today would silence all the others. Null becomes silence, which is
68
+ * this channel's fallback everywhere else too.
69
+ */
70
+ export function noticeClaimPath(ctx, state) {
71
+ if (state !== "capture_confirmed") {
72
+ return sessionNoticePath(ctx.home, ctx.sessionId, state);
73
+ }
74
+ if (ctx.repoKey === null || ctx.repoKey === "")
75
+ return null;
76
+ return repoDayNoticePath(ctx.home, ctx.repoKey, utcDay(ctx.now()), state);
77
+ }
78
+ /**
79
+ * Claim the once-per-KEY slot for `state`.
80
+ *
81
+ * `wx` is doing real work here: it creates-or-fails atomically, so two hooks
82
+ * firing concurrently under the same key cannot both win. A read-then-write
83
+ * check would let both through and print the message twice. That mattered for
84
+ * one session; it matters more now, because `capture_confirmed`'s key spans a
85
+ * whole day and therefore many more concurrent hooks.
86
+ *
87
+ * Returns false on ANY failure, including an unwritable home. A message we cannot
88
+ * record having sent is a message we do not send — repeating it every turn is
89
+ * worse than losing it once.
90
+ */
91
+ export function claimSessionNotice(ctx, state) {
92
+ const path = noticeClaimPath(ctx, state);
93
+ if (path === null)
94
+ return false;
95
+ try {
96
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
97
+ writeFileSync(path, `${JSON.stringify({ at: new Date().toISOString() })}\n`, {
98
+ flag: "wx",
99
+ mode: 0o600,
100
+ });
101
+ return true;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
107
+ /**
108
+ * Render the hook's stdout payload for a state, or null.
109
+ *
110
+ * Null means "write nothing", and every failure resolves to null. The caller
111
+ * writes the string verbatim and adds a trailing newline; it never builds JSON
112
+ * itself, so there is exactly one place this contract can be broken.
113
+ */
114
+ export function renderNotice(ctx, state) {
115
+ try {
116
+ const text = noticeText(state, ctx.nodeVersion);
117
+ // A message carrying an escape sequence or a newline is undefined behaviour
118
+ // in Claude Code's renderer, so it is dropped rather than sent — silence is
119
+ // the fallback. Tested by char code, not by a regex holding a literal
120
+ // control character: DESIGN.md §13.2 puts every escape sequence in one
121
+ // module, and a literal one here would be both a violation and unreadable.
122
+ if (hasControlCharacter(text))
123
+ return null;
124
+ if (!claimSessionNotice(ctx, state))
125
+ return null;
126
+ return JSON.stringify({ systemMessage: text });
127
+ }
128
+ catch {
129
+ return null;
130
+ }
131
+ }
132
+ /** True if `text` holds any C0 control character (including ESC) or DEL. */
133
+ export function hasControlCharacter(text) {
134
+ for (let i = 0; i < text.length; i += 1) {
135
+ const code = text.charCodeAt(i);
136
+ if (code < 0x20 || code === 0x7f)
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ //# sourceMappingURL=system_message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system_message.js","sourceRoot":"","sources":["../src/system_message.ts"],"names":[],"mappings":"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"}