@promptctl/cc-candybar 1.42.1 → 1.43.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 (126) hide show
  1. package/dist/index.mjs +72 -71
  2. package/package.json +5 -6
  3. package/src/check.ts +0 -478
  4. package/src/cli-flags.ts +0 -8
  5. package/src/click/wire.ts +0 -158
  6. package/src/config/action.ts +0 -329
  7. package/src/config/cli.ts +0 -71
  8. package/src/config/default-dsl-config.ts +0 -1645
  9. package/src/config/disclosure.ts +0 -170
  10. package/src/config/dsl-loader.ts +0 -339
  11. package/src/config/dsl-types.ts +0 -581
  12. package/src/config/edit-chrome.ts +0 -559
  13. package/src/config/help.ts +0 -151
  14. package/src/config/ident.ts +0 -22
  15. package/src/config/layout-ops.ts +0 -177
  16. package/src/config/loader/actions.ts +0 -972
  17. package/src/config/loader/cache.ts +0 -206
  18. package/src/config/loader/cross-ref.ts +0 -714
  19. package/src/config/loader/cycles.ts +0 -148
  20. package/src/config/loader/diagnostics.ts +0 -99
  21. package/src/config/loader/discovery.ts +0 -182
  22. package/src/config/loader/edit-mode.ts +0 -137
  23. package/src/config/loader/emit-schema.ts +0 -68
  24. package/src/config/loader/globals.ts +0 -269
  25. package/src/config/loader/helpers.ts +0 -48
  26. package/src/config/loader/layout.ts +0 -693
  27. package/src/config/loader/looks.ts +0 -96
  28. package/src/config/loader/menu-synth.ts +0 -435
  29. package/src/config/loader/merge.ts +0 -115
  30. package/src/config/loader/persist-target.ts +0 -67
  31. package/src/config/loader/presets.ts +0 -119
  32. package/src/config/loader/refs.ts +0 -100
  33. package/src/config/loader/reserved-namespace.ts +0 -38
  34. package/src/config/loader/segments.ts +0 -120
  35. package/src/config/loader/validate-core.ts +0 -737
  36. package/src/config/loader/variables.ts +0 -260
  37. package/src/config/menu-keys.ts +0 -139
  38. package/src/config/option-domain.ts +0 -164
  39. package/src/config/presets.ts +0 -326
  40. package/src/config/settings-menu.ts +0 -775
  41. package/src/daemon/acquire.ts +0 -684
  42. package/src/daemon/cache/git.ts +0 -649
  43. package/src/daemon/cache/render.ts +0 -623
  44. package/src/daemon/cache/session-usage-store.ts +0 -720
  45. package/src/daemon/cache/watchers.ts +0 -249
  46. package/src/daemon/client-debug.ts +0 -120
  47. package/src/daemon/client-stats.ts +0 -130
  48. package/src/daemon/client-transport.ts +0 -273
  49. package/src/daemon/client.ts +0 -78
  50. package/src/daemon/config-overrides-store.ts +0 -663
  51. package/src/daemon/debug-types.ts +0 -91
  52. package/src/daemon/debug.ts +0 -264
  53. package/src/daemon/fork-bomb-breaker.ts +0 -351
  54. package/src/daemon/limits.ts +0 -211
  55. package/src/daemon/log.ts +0 -81
  56. package/src/daemon/parent-watchdog.ts +0 -87
  57. package/src/daemon/paths.ts +0 -211
  58. package/src/daemon/process-fingerprint.ts +0 -146
  59. package/src/daemon/protocol.ts +0 -292
  60. package/src/daemon/render-payload.ts +0 -1256
  61. package/src/daemon/server.ts +0 -1330
  62. package/src/daemon/session-state-file.ts +0 -108
  63. package/src/daemon/session-state.ts +0 -237
  64. package/src/daemon/socket-lease.ts +0 -209
  65. package/src/daemon/socket-ownership.ts +0 -209
  66. package/src/daemon/stats.ts +0 -235
  67. package/src/daemon/verbs/config-validators.ts +0 -250
  68. package/src/daemon/verbs/index.ts +0 -706
  69. package/src/daemon/verbs/state-validators.ts +0 -249
  70. package/src/daemon/verbs/validator-registry.ts +0 -457
  71. package/src/demo/dsl.ts +0 -143
  72. package/src/demo/mock-data.ts +0 -67
  73. package/src/demo/statusline.json5 +0 -94
  74. package/src/dsl/node-registry.ts +0 -374
  75. package/src/dsl/render.ts +0 -803
  76. package/src/help-text.ts +0 -90
  77. package/src/index.ts +0 -210
  78. package/src/install/currency.ts +0 -197
  79. package/src/install/index.ts +0 -557
  80. package/src/proc/launch.ts +0 -459
  81. package/src/proc/stats-handle.ts +0 -13
  82. package/src/render/action.ts +0 -883
  83. package/src/render/active-segment.ts +0 -78
  84. package/src/render/diagnostic-style.ts +0 -23
  85. package/src/render/diagnostic-text.ts +0 -77
  86. package/src/render/error-glyph.ts +0 -53
  87. package/src/render/menu.ts +0 -257
  88. package/src/render/outcome-plan.ts +0 -45
  89. package/src/render/picker.ts +0 -372
  90. package/src/render/segment-color.ts +0 -74
  91. package/src/render/split-lines.ts +0 -51
  92. package/src/render/strip.ts +0 -228
  93. package/src/segments/cache.ts +0 -131
  94. package/src/segments/context.ts +0 -190
  95. package/src/segments/git.ts +0 -1084
  96. package/src/segments/metrics.ts +0 -187
  97. package/src/segments/pricing.ts +0 -452
  98. package/src/segments/session.ts +0 -23
  99. package/src/segments/tmux.ts +0 -74
  100. package/src/template-engine/cells.ts +0 -90
  101. package/src/template-engine/colors.ts +0 -124
  102. package/src/template-engine/engine.ts +0 -108
  103. package/src/template-engine/funcs.ts +0 -232
  104. package/src/template-engine/index.ts +0 -11
  105. package/src/template-engine/layout.ts +0 -133
  106. package/src/template-engine/scope.ts +0 -62
  107. package/src/template-engine/sparkline.ts +0 -79
  108. package/src/themes/index.ts +0 -20
  109. package/src/themes/palette-resolvers.ts +0 -84
  110. package/src/themes/policy.ts +0 -393
  111. package/src/utils/cache.ts +0 -206
  112. package/src/utils/claude.ts +0 -683
  113. package/src/utils/color-support.ts +0 -118
  114. package/src/utils/formatters.ts +0 -99
  115. package/src/utils/logger.ts +0 -5
  116. package/src/utils/outcome.ts +0 -33
  117. package/src/utils/schema-validator.ts +0 -126
  118. package/src/utils/single-flight.ts +0 -57
  119. package/src/utils/terminal-width.ts +0 -51
  120. package/src/utils/terminal.ts +0 -11
  121. package/src/utils/transcript-fs.ts +0 -279
  122. package/src/var-system/index.ts +0 -24
  123. package/src/var-system/sources.ts +0 -1047
  124. package/src/var-system/store.ts +0 -223
  125. package/src/var-system/types.ts +0 -57
  126. package/src/version.ts +0 -17
@@ -1,279 +0,0 @@
1
- import {
2
- open as fsOpen,
3
- readdir as fsReaddir,
4
- readFile as fsReadFile,
5
- stat as fsStat,
6
- } from "node:fs/promises";
7
- import type { FileHandle } from "node:fs/promises";
8
- import { statSync } from "node:fs";
9
-
10
- import { ABSENT, failed, ok, type Outcome } from "./outcome";
11
- import { debug } from "./logger";
12
-
13
- // [LAW:single-enforcer] The one per-render freshness probe: a SYNC single-file
14
- // stat's mtimeMs (0 when the file is absent or unreadable). Sync by design — the
15
- // mtime gate must resolve before deciding whether to read, and a sync stat
16
- // consumes no gate slot (the async `readAppended`/`readFile` bulk reads are what
17
- // the limiter bounds). Both the usage store and the metrics provider key their
18
- // incremental fold off this one helper so the ENOENT→0 policy lives in one place.
19
- export function statMtimeMs(filePath: string | undefined): number {
20
- if (!filePath) return 0;
21
- try {
22
- return statSync(filePath).mtimeMs;
23
- } catch {
24
- return 0;
25
- }
26
- }
27
-
28
- // [LAW:single-enforcer] One owner of the transcript-scanning in-flight-I/O
29
- // budget. Every readdir/stat/readFile over the ~/.claude/projects tree passes
30
- // through this module's limiter, so the number of concurrent libuv fs requests
31
- // is bounded by a constant no matter how many renders fan out at once. The OOM
32
- // heap proved the illegal state this forbids: ~3046 FSReqPromise pending at
33
- // once, each pinning a parked await-stack.
34
- //
35
- // [LAW:types-are-the-program] The bound is enforced by ROUTING, not by a
36
- // post-hoc guard: the transcript path imports these gated primitives instead of
37
- // node:fs/promises, so "thousands of stats/reads in flight" is unrepresentable
38
- // rather than merely checked. There is no `if (tooMany)` anywhere — the same
39
- // fan-out runs every render; the limiter only decides *when* each op dispatches.
40
-
41
- // 8 = 2× the libuv default UV_THREADPOOL_SIZE (4). Two dispatched ops per worker
42
- // keeps every threadpool thread fed without a queue-drain stall between syscalls,
43
- // while peak in-flight memory stays O(threadpool) rather than O(transcript count).
44
- const TRANSCRIPT_FS_CONCURRENCY = 8;
45
-
46
- interface Waiter {
47
- readonly wake: () => void;
48
- next: Waiter | null;
49
- }
50
-
51
- // A counting semaphore that runs at most `max` thunks concurrently. Slots are
52
- // handed off directly to the next waiter on release (never incremented while a
53
- // waiter is parked), so admission can never exceed `max` — the over-admission
54
- // race of an increment-then-wake design is structurally absent.
55
- class Limiter {
56
- private slots: number;
57
- // FIFO wait queue as a singly-linked list: enqueue at `tail`, dequeue at
58
- // `head`, both O(1) with no array reindexing (Array.shift would be O(n), so a
59
- // drain of the thousands-of-queued-ops burst this limiter exists to absorb
60
- // would be O(n²) on the render hot path). A dequeued node is immediately
61
- // unreferenced, so a continuously-saturated queue retains only the waiters
62
- // currently parked — no consumed-prefix accumulates, unlike a head-index
63
- // array that only reclaims on full drain.
64
- private head: Waiter | null = null;
65
- private tail: Waiter | null = null;
66
-
67
- constructor(max: number) {
68
- this.slots = max;
69
- }
70
-
71
- async run<T>(task: () => Promise<T>): Promise<T> {
72
- await this.acquire();
73
- try {
74
- return await task();
75
- } finally {
76
- this.release();
77
- }
78
- }
79
-
80
- private async acquire(): Promise<void> {
81
- if (this.slots > 0) {
82
- this.slots--;
83
- return;
84
- }
85
- // No slot free — park until release() hands one to us. The slot is
86
- // transferred directly, so we must NOT decrement again on resume.
87
- await new Promise<void>((wake) => {
88
- const node: Waiter = { wake, next: null };
89
- if (this.tail) this.tail.next = node;
90
- else this.head = node;
91
- this.tail = node;
92
- });
93
- }
94
-
95
- private release(): void {
96
- const node = this.head;
97
- if (!node) {
98
- // No one waiting — return the slot to the pool.
99
- this.slots++;
100
- return;
101
- }
102
- // Hand the slot directly to the next waiter; the dequeued node is dropped.
103
- this.head = node.next;
104
- if (!this.head) this.tail = null;
105
- node.wake();
106
- }
107
- }
108
-
109
- const gate = new Limiter(TRANSCRIPT_FS_CONCURRENCY);
110
-
111
- // Wrap an async fs primitive so every call flows through the shared gate. The
112
- // `(...args: never[])` bound is the maximally-permissive function constraint
113
- // (parameters are contravariant, so `never[]` accepts any arg list) — it admits
114
- // every fs/promises overload, not rejects them. Callers see the original
115
- // overloaded type `F`; the cast is needed because TS can't prove a generic
116
- // wrapper preserves an overload set, but each fs overload is a valid `fn(...)`
117
- // call, so the wrap is sound (verified: `tsc --noEmit` passes against the
118
- // multi-overload call sites in claude.ts/cache.ts).
119
- function gated<F extends (...args: never[]) => Promise<unknown>>(fn: F): F {
120
- return ((...args: Parameters<F>) =>
121
- gate.run(() => fn(...args))) as unknown as F;
122
- }
123
-
124
- export const readdir = gated(fsReaddir);
125
- export const readFile = gated(fsReadFile);
126
- export const stat = gated(fsStat);
127
-
128
- // [LAW:single-enforcer] A bounded tail read through the SAME gate: the last
129
- // `maxBytes` of a file (the whole file when smaller), plus whether that window
130
- // reaches the file start. The open→stat→read→close runs under one gate slot, so
131
- // a tail read counts as one in-flight op exactly like a readFile — a transcript
132
- // scanner that grows its window backward must use THIS, not raw node:fs, or it
133
- // reintroduces the unbounded-fs state the gate forbids.
134
- //
135
- // [LAW:no-silent-failure] The file not existing is the expected, every-render
136
- // case for a fresh session (no transcript yet) — `absent`. Any other error
137
- // (permissions, I/O) is a real read failure — `failed`, carrying its reason
138
- // to whichever boundary owns the log effect. Folding both into one null made
139
- // a broken transcript indistinguishable from a missing one.
140
- // [LAW:single-enforcer] The forward complement of readTail, through the SAME
141
- // gate: read the bytes of an append-only file from `priorOffset` to EOF (the
142
- // whole file when `priorOffset` is undefined). One open→stat→read→close per
143
- // gate slot, exactly like readTail. A transcript scanner that maintains a byte
144
- // cursor to re-read only what was appended MUST use this, not raw node:fs, or it
145
- // reintroduces the unbounded-fs state the gate forbids.
146
- //
147
- // [LAW:one-source-of-truth] `reset` is the single signal that the file shrank
148
- // below the caller's cursor — a truncation or /compact rewrite — so the caller
149
- // discards its prior fold for this file and re-folds from the returned bytes
150
- // (which then start at offset 0). Append-only monotonicity makes every other
151
- // case a pure suffix read: `start..size` is exactly what's new.
152
- //
153
- // [LAW:no-silent-failure] ENOENT (fresh session, no transcript yet) is `absent`;
154
- // any other error is `failed`, carrying its reason to the boundary that logs.
155
- export async function readAppended(
156
- path: string,
157
- prior: { offset: number; ino: number } | undefined,
158
- ): Promise<
159
- Outcome<{
160
- buf: Buffer;
161
- start: number;
162
- size: number;
163
- mtimeMs: number;
164
- ino: number;
165
- reset: boolean;
166
- }>
167
- > {
168
- return gate.run(async () => {
169
- let fh: FileHandle | null = null;
170
- try {
171
- fh = await fsOpen(path, "r");
172
- const { size, mtimeMs, ino } = await fh.stat();
173
- // [LAW:one-source-of-truth] The prior fold is a valid PREFIX of this file
174
- // only while the file stays append-only. Two independent signals break
175
- // that: the inode changed (a rename-based /compact or log rotation swapped
176
- // the file under the path — the realistic rewrite mechanism), or the file
177
- // shrank below our cursor (an in-place truncate). Either ⇒ re-read from 0;
178
- // the suffix-only read would otherwise splice new bytes onto a fold of a
179
- // file that no longer exists. (A cursor-only size check missed a rewrite
180
- // that grew back to ≥ the cursor.)
181
- const reset =
182
- prior !== undefined && (ino !== prior.ino || size < prior.offset);
183
- const start = reset || prior === undefined ? 0 : prior.offset;
184
- if (start >= size) {
185
- return ok({ buf: Buffer.alloc(0), start, size, mtimeMs, ino, reset });
186
- }
187
- const buf = Buffer.alloc(size - start);
188
- // [LAW:no-silent-fallbacks] A single read may return short — parsing a
189
- // zero-padded tail would fabricate entries. Loop until the window fills or
190
- // EOF; on a short final read (the file shrank under us) return only the
191
- // bytes actually read, never the zero padding.
192
- let off = 0;
193
- while (off < buf.length) {
194
- const { bytesRead } = await fh.read(
195
- buf,
196
- off,
197
- buf.length - off,
198
- start + off,
199
- );
200
- if (bytesRead === 0) break;
201
- off += bytesRead;
202
- }
203
- return ok({
204
- buf: off === buf.length ? buf : buf.subarray(0, off),
205
- start,
206
- size,
207
- mtimeMs,
208
- ino,
209
- reset,
210
- });
211
- } catch (e) {
212
- if ((e as NodeJS.ErrnoException).code === "ENOENT") return ABSENT;
213
- return failed(
214
- `readAppended ${path}: ${e instanceof Error ? e.message : String(e)}`,
215
- );
216
- } finally {
217
- // [LAW:no-silent-failure] A close() rejection must NOT override the typed
218
- // Outcome the try returned — callers await this and read `.kind`, so a raw
219
- // rejection would escape every failed-guard. A failed close after a good
220
- // read doesn't invalidate the read, but it can signal a real fs problem
221
- // (ENOSPC on fsync), so log it at debug rather than swallowing it blind.
222
- await fh?.close().catch((e: unknown) => {
223
- debug(
224
- `transcript-fs: close failed: ${e instanceof Error ? e.message : String(e)}`,
225
- );
226
- });
227
- }
228
- });
229
- }
230
-
231
- export async function readTail(
232
- path: string,
233
- maxBytes: number,
234
- ): Promise<Outcome<{ buf: Buffer; fromStart: boolean }>> {
235
- return gate.run(async () => {
236
- let fh: FileHandle | null = null;
237
- try {
238
- fh = await fsOpen(path, "r");
239
- const { size } = await fh.stat();
240
- const start = Math.max(0, size - maxBytes);
241
- const buf = Buffer.alloc(size - start);
242
- // [LAW:no-silent-fallbacks] A single read may return short — the scanner
243
- // would then parse a zero-padded tail and miss cache activity. Loop until
244
- // the window is filled or EOF; on a short final read (the file shrank
245
- // under us) return only the bytes actually read, never the zero padding.
246
- let off = 0;
247
- while (off < buf.length) {
248
- const { bytesRead } = await fh.read(
249
- buf,
250
- off,
251
- buf.length - off,
252
- start + off,
253
- );
254
- if (bytesRead === 0) break;
255
- off += bytesRead;
256
- }
257
- return ok({
258
- buf: off === buf.length ? buf : buf.subarray(0, off),
259
- fromStart: start === 0,
260
- });
261
- } catch (e) {
262
- if ((e as NodeJS.ErrnoException).code === "ENOENT") return ABSENT;
263
- return failed(
264
- `readTail ${path}: ${e instanceof Error ? e.message : String(e)}`,
265
- );
266
- } finally {
267
- // [LAW:no-silent-failure] A close() rejection must NOT override the typed
268
- // Outcome the try returned — callers await this and read `.kind`, so a raw
269
- // rejection would escape every failed-guard. A failed close after a good
270
- // read doesn't invalidate the read, but it can signal a real fs problem
271
- // (ENOSPC on fsync), so log it at debug rather than swallowing it blind.
272
- await fh?.close().catch((e: unknown) => {
273
- debug(
274
- `transcript-fs: close failed: ${e instanceof Error ? e.message : String(e)}`,
275
- );
276
- });
277
- }
278
- });
279
- }
@@ -1,24 +0,0 @@
1
- export { VariableStore, type VarNode } from "./store";
2
- export {
3
- type VarType,
4
- type VarValue,
5
- typeOf,
6
- toString,
7
- toNumber,
8
- toBool,
9
- } from "./types";
10
- export {
11
- SourceRegistry,
12
- parseDuration,
13
- formatGoTime,
14
- MIN_SHELL_TTL_MS,
15
- type CachePolicy,
16
- type ShellOptions,
17
- type FileOptions,
18
- type TemplateOptions,
19
- type TimeOptions,
20
- type GitField,
21
- type GitOptions,
22
- type StateOptions,
23
- type LastError,
24
- } from "./sources";