@promptctl/cc-candybar 1.42.0 → 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/README.md +4 -0
  2. package/dist/index.mjs +81 -80
  3. package/package.json +5 -6
  4. package/src/check.ts +0 -478
  5. package/src/cli-flags.ts +0 -8
  6. package/src/click/wire.ts +0 -158
  7. package/src/config/action.ts +0 -329
  8. package/src/config/cli.ts +0 -71
  9. package/src/config/default-dsl-config.ts +0 -1645
  10. package/src/config/disclosure.ts +0 -170
  11. package/src/config/dsl-loader.ts +0 -339
  12. package/src/config/dsl-types.ts +0 -581
  13. package/src/config/edit-chrome.ts +0 -559
  14. package/src/config/help.ts +0 -151
  15. package/src/config/ident.ts +0 -22
  16. package/src/config/layout-ops.ts +0 -177
  17. package/src/config/loader/actions.ts +0 -972
  18. package/src/config/loader/cache.ts +0 -206
  19. package/src/config/loader/cross-ref.ts +0 -714
  20. package/src/config/loader/cycles.ts +0 -148
  21. package/src/config/loader/diagnostics.ts +0 -99
  22. package/src/config/loader/discovery.ts +0 -182
  23. package/src/config/loader/edit-mode.ts +0 -137
  24. package/src/config/loader/emit-schema.ts +0 -68
  25. package/src/config/loader/globals.ts +0 -269
  26. package/src/config/loader/helpers.ts +0 -48
  27. package/src/config/loader/layout.ts +0 -693
  28. package/src/config/loader/looks.ts +0 -96
  29. package/src/config/loader/menu-synth.ts +0 -435
  30. package/src/config/loader/merge.ts +0 -115
  31. package/src/config/loader/persist-target.ts +0 -67
  32. package/src/config/loader/presets.ts +0 -119
  33. package/src/config/loader/refs.ts +0 -100
  34. package/src/config/loader/reserved-namespace.ts +0 -38
  35. package/src/config/loader/segments.ts +0 -120
  36. package/src/config/loader/validate-core.ts +0 -737
  37. package/src/config/loader/variables.ts +0 -260
  38. package/src/config/menu-keys.ts +0 -139
  39. package/src/config/option-domain.ts +0 -164
  40. package/src/config/presets.ts +0 -326
  41. package/src/config/settings-menu.ts +0 -775
  42. package/src/daemon/acquire.ts +0 -684
  43. package/src/daemon/cache/git.ts +0 -649
  44. package/src/daemon/cache/render.ts +0 -623
  45. package/src/daemon/cache/session-usage-store.ts +0 -720
  46. package/src/daemon/cache/watchers.ts +0 -249
  47. package/src/daemon/client-debug.ts +0 -120
  48. package/src/daemon/client-stats.ts +0 -130
  49. package/src/daemon/client-transport.ts +0 -273
  50. package/src/daemon/client.ts +0 -78
  51. package/src/daemon/config-overrides-store.ts +0 -663
  52. package/src/daemon/debug-types.ts +0 -91
  53. package/src/daemon/debug.ts +0 -264
  54. package/src/daemon/fork-bomb-breaker.ts +0 -351
  55. package/src/daemon/limits.ts +0 -211
  56. package/src/daemon/log.ts +0 -81
  57. package/src/daemon/parent-watchdog.ts +0 -87
  58. package/src/daemon/paths.ts +0 -211
  59. package/src/daemon/process-fingerprint.ts +0 -146
  60. package/src/daemon/protocol.ts +0 -292
  61. package/src/daemon/render-payload.ts +0 -1256
  62. package/src/daemon/server.ts +0 -1330
  63. package/src/daemon/session-state-file.ts +0 -108
  64. package/src/daemon/session-state.ts +0 -237
  65. package/src/daemon/socket-lease.ts +0 -209
  66. package/src/daemon/socket-ownership.ts +0 -209
  67. package/src/daemon/stats.ts +0 -235
  68. package/src/daemon/verbs/config-validators.ts +0 -250
  69. package/src/daemon/verbs/index.ts +0 -706
  70. package/src/daemon/verbs/state-validators.ts +0 -249
  71. package/src/daemon/verbs/validator-registry.ts +0 -457
  72. package/src/demo/dsl.ts +0 -143
  73. package/src/demo/mock-data.ts +0 -67
  74. package/src/demo/statusline.json5 +0 -94
  75. package/src/dsl/node-registry.ts +0 -374
  76. package/src/dsl/render.ts +0 -803
  77. package/src/help-text.ts +0 -90
  78. package/src/index.ts +0 -210
  79. package/src/install/index.ts +0 -541
  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";