praxis-agent 0.43.0 → 0.44.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 (59) hide show
  1. package/README.md +48 -47
  2. package/dist/application/native-session-projection.d.ts +4 -0
  3. package/dist/application/native-session-projection.js +135 -0
  4. package/dist/application/session-service.d.ts +0 -1
  5. package/dist/application/session-service.js +9 -170
  6. package/dist/application/top-level-agent-manager.d.ts +7 -0
  7. package/dist/application/top-level-agent-manager.js +21 -3
  8. package/dist/cli/controls.js +4 -0
  9. package/dist/cli/interactive.js +112 -156
  10. package/dist/cli/protocol.d.ts +5 -0
  11. package/dist/cli/protocol.js +45 -0
  12. package/dist/cli/provider-auth-command.d.ts +32 -0
  13. package/dist/cli/provider-auth-command.js +249 -0
  14. package/dist/cli/tui/ansi-frame-renderer.d.ts +2 -0
  15. package/dist/cli/tui/ansi-frame-renderer.js +109 -21
  16. package/dist/cli/tui/ansi-surface.d.ts +6 -22
  17. package/dist/cli/tui/ansi-surface.js +6 -126
  18. package/dist/cli/tui/ansi-theme.d.ts +3 -2
  19. package/dist/cli/tui/ansi-theme.js +22 -1
  20. package/dist/cli/tui/quiet-choice-rows.d.ts +26 -0
  21. package/dist/cli/tui/quiet-choice-rows.js +302 -0
  22. package/dist/cli/tui/quiet-frame-adapter.d.ts +18 -0
  23. package/dist/cli/tui/quiet-frame-adapter.js +90 -0
  24. package/dist/cli/tui/quiet-frame.d.ts +43 -0
  25. package/dist/cli/tui/quiet-frame.js +346 -0
  26. package/dist/cli/tui/quiet-operational-rows.d.ts +17 -0
  27. package/dist/cli/tui/quiet-operational-rows.js +413 -0
  28. package/dist/cli/tui/quiet-screen-projector.d.ts +25 -0
  29. package/dist/cli/tui/quiet-screen-projector.js +110 -0
  30. package/dist/cli/tui/quiet-settings-rows.d.ts +17 -0
  31. package/dist/cli/tui/quiet-settings-rows.js +490 -0
  32. package/dist/cli/tui/status-line.d.ts +15 -9
  33. package/dist/cli/tui/status-line.js +15 -6
  34. package/dist/cli/tui/theme.js +20 -20
  35. package/dist/cli/tui/transcript-viewport.d.ts +5 -0
  36. package/dist/cli/tui/transcript-viewport.js +213 -78
  37. package/dist/cli/tui/tui-row-ir.js +4 -4
  38. package/dist/cli/workspace-trust-prompt.js +2 -2
  39. package/dist/cli-runtime.d.ts +34 -1
  40. package/dist/cli-runtime.js +587 -184
  41. package/dist/maintenance/doctor.js +100 -22
  42. package/dist/persistence/in-memory-transcript-store.js +1 -7
  43. package/dist/persistence/native-transcript-store.d.ts +0 -9
  44. package/dist/persistence/native-transcript-store.js +4 -38
  45. package/dist/persistence/provider-credential-vault.d.ts +72 -0
  46. package/dist/persistence/provider-credential-vault.js +498 -0
  47. package/dist/providers/codex-oauth.d.ts +104 -0
  48. package/dist/providers/codex-oauth.js +770 -0
  49. package/dist/providers/codex-subscription.d.ts +33 -0
  50. package/dist/providers/codex-subscription.js +849 -0
  51. package/dist/providers/provider-auth.d.ts +71 -0
  52. package/dist/providers/provider-auth.js +149 -0
  53. package/dist/providers/provider-registry.d.ts +52 -0
  54. package/dist/providers/provider-registry.js +187 -0
  55. package/dist/providers/provider-settings.d.ts +38 -0
  56. package/dist/providers/provider-settings.js +334 -0
  57. package/dist/security/workspace-trust.d.ts +4 -3
  58. package/dist/security/workspace-trust.js +35 -1
  59. package/package.json +8 -4
@@ -0,0 +1,490 @@
1
+ import { createQuietFrameRow, } from './quiet-frame.js';
2
+ import { configSettingValue, CLAUDE_2_1_208_CONFIG_SETTINGS, } from './config-settings.js';
3
+ import { formatCostSummary } from './cost-summary.js';
4
+ const full = (d) => d === 'full' || d === 'standard';
5
+ const max = (o) => Number.isInteger(o.maxItems) && (o.maxItems ?? 0) > 0
6
+ ? o.maxItems
7
+ : 20;
8
+ const clean = (v) => typeof v === 'string' ? v.slice(0, 32768).replace(/\s+/gu, ' ').trim() : '';
9
+ const excerpt = (v, length = 240) => clean(v).slice(0, length);
10
+ const row = (k, t, role = 'body', a) => createQuietFrameRow(k, t, 'focus', role, a);
11
+ const selected = (yes, text, sr) => sr ? `${yes ? 'Selected' : 'Option'}: ${text}` : `${yes ? '❯ ' : ' '}${text}`;
12
+ const index = (n, i) => n ? Math.max(0, Math.min(n - 1, Number.isFinite(i) ? Math.trunc(i) : 0)) : 0;
13
+ function window(n, i, size) {
14
+ if (!n)
15
+ return [0, 0];
16
+ const x = index(n, i);
17
+ const start = Math.max(0, Math.min(Math.max(0, n - size), x - Math.floor(size / 2)));
18
+ return [start, Math.min(n, start + size)];
19
+ }
20
+ const footer = (visual, accessible, sr) => row('quiet:settings:footer', sr ? accessible : visual, 'muted', accessible);
21
+ const unsupported = (v) => {
22
+ throw new Error(`Unsupported quiet settings surface: ${String(v)}`);
23
+ };
24
+ const statusRole = (s) => /fail|error|denied|invalid/iu.test(s)
25
+ ? 'error'
26
+ : /warn|pending|running/iu.test(s)
27
+ ? 'warning'
28
+ : /pass|complete|ready|enabled/iu.test(s)
29
+ ? 'success'
30
+ : 'muted';
31
+ const marker = (s) => /fail|error|denied/iu.test(s)
32
+ ? '!'
33
+ : /warn|pending|running/iu.test(s)
34
+ ? '…'
35
+ : /pass|complete|ready|enabled/iu.test(s)
36
+ ? '✓'
37
+ : '○';
38
+ const value = (v) => typeof v === 'boolean'
39
+ ? v
40
+ ? 'On'
41
+ : 'Off'
42
+ : Array.isArray(v)
43
+ ? excerpt(v
44
+ .slice(0, 20)
45
+ .map((item) => excerpt(item, 80))
46
+ .join(', ')) || 'none'
47
+ : excerpt(v) || 'Not set';
48
+ const closeFooter = (sr) => footer('Esc close', 'Press Escape to close this surface.', sr);
49
+ function doctor(s, o) {
50
+ const out = [
51
+ row('quiet:doctor:heading', 'Doctor · Diagnostics', 'heading', 'Doctor diagnostics'),
52
+ ];
53
+ if (s.loading)
54
+ out.push(row('quiet:doctor:loading', '… Running diagnostics…', 'warning', 'Diagnostics in progress'));
55
+ if (s.error)
56
+ out.push(row('quiet:doctor:error', `! ${clean(s.error)}`, 'error', `Error: ${clean(s.error)}`));
57
+ const report = s.report;
58
+ if (!report) {
59
+ out.push(row('quiet:doctor:empty', '○ No diagnostic report available', 'muted'), closeFooter(!!o.screenReader));
60
+ return out;
61
+ }
62
+ out.push(row('quiet:doctor:version', `Praxis ${clean(report.praxisVersion)}`, 'muted'));
63
+ out.push(row('quiet:doctor:summary', `✓ ${report.summary.passed} passed · ! ${report.summary.failed} failed · ${report.summary.warnings} warnings`, report.summary.failed
64
+ ? 'error'
65
+ : report.summary.warnings
66
+ ? 'warning'
67
+ : 'success'));
68
+ const checks = [...report.checks];
69
+ if (o.density === 'minimal')
70
+ checks.sort((a, b) => (a.status === 'pass' ? 1 : 0) - (b.status === 'pass' ? 1 : 0));
71
+ for (const c of checks.slice(0, max(o)))
72
+ out.push(row(`quiet:doctor:check:${c.id}`, `${marker(c.status)} ${c.id}: ${excerpt(c.summary)}`, statusRole(c.status), `${c.status === 'pass' ? 'Pass' : c.status === 'fail' ? 'Failure' : 'Warning'}: ${c.id}. ${excerpt(c.summary)}`));
73
+ out.push(closeFooter(!!o.screenReader));
74
+ return out;
75
+ }
76
+ function memory(s, o) {
77
+ const sr = !!o.screenReader;
78
+ const out = [
79
+ row('quiet:memory:heading', 'Memory · Native data plane', 'heading'),
80
+ ];
81
+ out.push(row('quiet:memory:auto', `${s.autoMemoryEnabled ? '✓' : '○'} Auto-memory ${s.autoMemoryEnabled ? 'enabled' : 'disabled'}`, s.autoMemoryEnabled ? 'success' : 'muted'));
82
+ if (s.loading)
83
+ out.push(row('quiet:memory:loading', '… Loading memory files', 'warning'));
84
+ if (!s.entries.length) {
85
+ out.push(row('quiet:memory:empty', '○ No memory entries', 'muted'));
86
+ out.push(closeFooter(sr));
87
+ return out;
88
+ }
89
+ if (s.openedIndex !== null) {
90
+ const e = s.entries[index(s.entries.length, s.openedIndex)];
91
+ if (!e)
92
+ return out;
93
+ if (e) {
94
+ out.push(row(`quiet:memory:detail:${e.path}`, `${e.kind === 'folder' ? 'Folder' : 'File'} · ${excerpt(e.displayPath)}`, 'body'));
95
+ if (full(o.density) && e.annotation)
96
+ out.push(row(`quiet:memory:annotation:${e.path}`, excerpt(e.annotation), 'muted'));
97
+ }
98
+ out.push(footer('Esc back', 'Press Escape to go back to memory entries', sr));
99
+ return out;
100
+ }
101
+ const [a, b] = window(s.entries.length, s.selectedIndex, max(o));
102
+ for (let i = a; i < b; i++) {
103
+ const e = s.entries[i];
104
+ if (!e)
105
+ continue;
106
+ const text = `${e.kind === 'folder' ? 'Folder' : 'File'} · ${excerpt(e.label || e.displayPath)}${full(o.density) && e.annotation ? ` — ${excerpt(e.annotation)}` : ''}`;
107
+ out.push(row(`quiet:memory:${e.path}`, selected(i === index(s.entries.length, s.selectedIndex), text, sr), i === index(s.entries.length, s.selectedIndex) ? 'selection' : 'body', `${i === index(s.entries.length, s.selectedIndex) ? 'Selected' : 'Option'}: ${text}`));
108
+ }
109
+ out.push(footer('↑/↓ browse Enter open Esc close', 'Use Up and Down to browse. Press Enter to open. Escape closes.', sr));
110
+ return out;
111
+ }
112
+ function hooks(s, o) {
113
+ const sr = !!o.screenReader;
114
+ const c = s.configuration;
115
+ const out = [
116
+ row('quiet:hooks:heading', `Hooks · ${s.depth}`, 'heading'),
117
+ row('quiet:hooks:notice', '○ Read-only hook settings', 'muted'),
118
+ ];
119
+ const ev = c.events[index(c.events.length, s.eventIndex)];
120
+ if (!ev) {
121
+ out.push(row('quiet:hooks:empty', '○ No hooks configured', 'muted'), closeFooter(sr));
122
+ return out;
123
+ }
124
+ if (s.depth === 'events') {
125
+ const [start, end] = window(c.events.length, s.eventIndex, max(o));
126
+ for (let i = start; i < end; i++) {
127
+ const e = c.events[i];
128
+ if (!e)
129
+ continue;
130
+ const isSelected = i === index(c.events.length, s.eventIndex);
131
+ const label = `${e.name}${full(o.density) ? ` — ${excerpt(e.description)}` : ''}`;
132
+ out.push(row(`quiet:hooks:event:${e.name}`, selected(isSelected, label, sr), isSelected ? 'selection' : 'body', `${isSelected ? 'Selected' : 'Option'}: ${label}`));
133
+ }
134
+ }
135
+ else {
136
+ out.push(row(`quiet:hooks:event:${ev.name}`, `Event · ${ev.name} — ${excerpt(ev.description)}`, 'muted'));
137
+ const m = ev.matchers[index(ev.matchers.length, s.matcherIndex)];
138
+ if (s.depth === 'matchers') {
139
+ const [start, end] = window(ev.matchers.length, s.matcherIndex, max(o));
140
+ for (let i = start; i < end; i++) {
141
+ const x = ev.matchers[i];
142
+ if (!x)
143
+ continue;
144
+ const isSelected = i === index(ev.matchers.length, s.matcherIndex);
145
+ const label = `${x.scopeLabel}: ${excerpt(x.matcher) || 'All tools'}`;
146
+ out.push(row(`quiet:hooks:matcher:${ev.name}:${x.scope}:${x.matcher}`, selected(isSelected, label, sr), isSelected ? 'selection' : 'body', `${isSelected ? 'Selected' : 'Option'}: ${label}`));
147
+ }
148
+ if (!ev.matchers.length)
149
+ out.push(row('quiet:hooks:matchers:empty', '○ No matchers', 'muted'));
150
+ }
151
+ else if (m) {
152
+ out.push(row(`quiet:hooks:matcher:${ev.name}:${m.scope}:${m.matcher}`, `Matcher · ${m.scopeLabel}: ${excerpt(m.matcher) || 'All tools'}`, 'muted'));
153
+ if (s.depth === 'hooks') {
154
+ const [start, end] = window(m.hooks.length, s.hookIndex, max(o));
155
+ for (let i = start; i < end; i++) {
156
+ const h = m.hooks[i];
157
+ if (!h)
158
+ continue;
159
+ const isSelected = i === index(m.hooks.length, s.hookIndex);
160
+ const label = `${h.type} · ${h.scopeLabel} · ${excerpt(h.path)}`;
161
+ out.push(row(`quiet:hooks:hook:${h.path}:${h.type}`, selected(isSelected, label, sr), isSelected ? 'selection' : 'body', `${isSelected ? 'Selected' : 'Option'}: ${label}`));
162
+ }
163
+ if (!m.hooks.length)
164
+ out.push(row('quiet:hooks:hooks:empty', '○ No hooks', 'muted'));
165
+ }
166
+ else {
167
+ const h = m.hooks[index(m.hooks.length, s.hookIndex)];
168
+ if (h)
169
+ out.push(row(`quiet:hooks:hook:${h.path}:${h.type}`, `Hook · ${h.type} · ${h.scopeLabel} · ${excerpt(h.path)}`, 'body'));
170
+ else
171
+ for (const [detailIndex, d] of ev.detail.slice(0, max(o)).entries())
172
+ out.push(row(`quiet:hooks:detail:${ev.name}:${detailIndex}`, excerpt(d), 'muted'));
173
+ }
174
+ }
175
+ else
176
+ out.push(row(`quiet:hooks:${s.depth}:empty`, s.depth === 'hooks' ? '○ No hooks' : '○ No hook details', 'muted'));
177
+ }
178
+ out.push(footer('↑/↓ browse Enter open Esc back', 'Use Up and Down to browse. Press Enter to open. Escape goes back or closes.', sr));
179
+ return out;
180
+ }
181
+ function config(s, o) {
182
+ const sr = !!o.screenReader;
183
+ const tabLabel = s.tab === 'status' ? 'Status' : s.tab === 'config' ? 'Config' : 'Usage';
184
+ const out = [
185
+ row('quiet:config:heading', `Settings · ${tabLabel}`, 'heading'),
186
+ row('quiet:config:tabs', ['Status', 'Config', 'Usage']
187
+ .map((label) => (label === tabLabel ? `[${label}]` : label))
188
+ .join(' '), 'muted', `Current tab: ${tabLabel}`),
189
+ ];
190
+ if (s.tab === 'status') {
191
+ const x = s.status;
192
+ if (!x) {
193
+ out.push(row('quiet:config:status:empty', '○ No status available', 'muted'), footer('←/→ tabs Esc close', 'Use Left and Right to change tabs. Press Escape to close.', sr));
194
+ return out;
195
+ }
196
+ const fields = [
197
+ ['version', x.version],
198
+ ['session', x.sessionName],
199
+ ['session-id', x.sessionId],
200
+ ['cwd', x.cwd],
201
+ ['auth', x.authSource],
202
+ ['base-url', x.baseUrl],
203
+ ['proxy', x.proxy],
204
+ ['mcp', x.mcpSummary],
205
+ ['model', x.model],
206
+ ['sources', x.settingSources.join(', ')],
207
+ ];
208
+ for (const [k, v] of fields.slice(0, max(o)))
209
+ if (v !== undefined)
210
+ out.push(row(`quiet:config:status:${k}`, `${k}: ${value(v)}`, 'body'));
211
+ out.push(footer('←/→ tabs Esc close', 'Use Left and Right to change tabs. Press Escape to close.', sr));
212
+ return out;
213
+ }
214
+ if (s.tab === 'usage') {
215
+ if (!s.usage)
216
+ out.push(row('quiet:config:usage:empty', '○ No usage summary available', 'muted'));
217
+ else {
218
+ try {
219
+ const source = formatCostSummary(s.usage).slice(0, 32768);
220
+ const lines = source.split('\n');
221
+ const truncated = lines.length > max(o);
222
+ const visible = lines.slice(0, truncated ? Math.max(0, max(o) - 1) : max(o));
223
+ for (const [i, line] of visible.entries())
224
+ out.push(row(`quiet:config:usage:${i}`, clean(line), 'body'));
225
+ if (truncated)
226
+ out.push(row('quiet:config:usage:truncated', '… Usage truncated', 'muted'));
227
+ }
228
+ catch {
229
+ out.push(row('quiet:config:usage:error', '! Usage summary unavailable', 'error'));
230
+ }
231
+ }
232
+ out.push(footer('←/→ tabs Esc close', 'Use Left and Right to change tabs. Press Escape to close.', sr));
233
+ return out;
234
+ }
235
+ const q = excerpt(s.query).toLowerCase();
236
+ const defs = CLAUDE_2_1_208_CONFIG_SETTINGS.filter((d) => !q ||
237
+ d.label.toLowerCase().includes(q) ||
238
+ d.nativeKey.toLowerCase().includes(q));
239
+ const [a, b] = window(defs.length, s.selectedIndex, max(o));
240
+ for (let i = a; i < b; i++) {
241
+ const d = defs[i];
242
+ if (!d)
243
+ continue;
244
+ const v = s.effectiveValues?.[d.id] ?? configSettingValue(s.snapshot, d);
245
+ const t = `${d.label}: ${value(v)}`;
246
+ out.push(row(`quiet:config:setting:${d.id}`, selected(i === index(defs.length, s.selectedIndex), t, sr), i === index(defs.length, s.selectedIndex) ? 'selection' : 'body', `${i === index(defs.length, s.selectedIndex) ? 'Selected' : 'Option'}: ${t}`));
247
+ }
248
+ if (!defs.length)
249
+ out.push(row('quiet:config:empty', '○ No matching settings', 'muted'));
250
+ out.push(row('quiet:config:search', `Search: ${excerpt(s.query) || '(all settings)'}`, s.searchFocused ? 'input' : 'muted', s.searchFocused
251
+ ? `Search input: ${excerpt(s.query) || 'all settings'}`
252
+ : undefined));
253
+ out.push(footer('←/→ tabs / search ↑/↓ browse Enter change Esc close', 'Use Left and Right to change tabs. Press slash to search. Use Up and Down to browse. Press Enter to change. Press Escape to close.', sr));
254
+ return out;
255
+ }
256
+ function sandbox(s, o) {
257
+ const x = s.snapshot;
258
+ const sr = !!o.screenReader;
259
+ const out = [
260
+ row('quiet:sandbox:heading', `Sandbox · ${s.tab}`, 'heading'),
261
+ row('quiet:sandbox:tab', `Current tab: ${s.tab}`, 'muted', `Current tab: ${s.tab}`),
262
+ row('quiet:sandbox:support', `${x.supported ? '✓' : '!'} ${x.supported ? 'Supported' : 'Unavailable'} on ${x.platform}${x.unavailableReason ? `: ${excerpt(x.unavailableReason)}` : ''}`, x.supported ? 'success' : 'error', `${x.supported ? 'Supported' : 'Unavailable'} on ${x.platform}${x.unavailableReason ? `: ${excerpt(x.unavailableReason)}` : ''}`),
263
+ ];
264
+ if (s.tab === 'mode') {
265
+ const modes = [
266
+ ['auto-allow', 'Auto-allow'],
267
+ ['regular', 'Regular permissions'],
268
+ ['disabled', 'Disabled'],
269
+ ];
270
+ const current = x.settings.enabled
271
+ ? x.settings.autoAllowBashIfSandboxed
272
+ ? 'auto-allow'
273
+ : 'regular'
274
+ : 'disabled';
275
+ const [start, end] = window(modes.length, s.selectedIndex, max(o));
276
+ for (let i = start; i < end; i++) {
277
+ const candidate = modes[i];
278
+ if (!candidate)
279
+ continue;
280
+ const [id, label] = candidate;
281
+ const isSelected = i === index(modes.length, s.selectedIndex);
282
+ const optionText = `${label}${id === current ? ' (current)' : ''}`;
283
+ out.push(row(`quiet:sandbox:mode:${id}`, selected(isSelected, optionText, sr), isSelected ? 'selection' : 'body', `${isSelected ? 'Selected' : 'Option'}: ${label}${id === current ? ', current mode' : ''}`));
284
+ }
285
+ }
286
+ else if (s.tab === 'dependencies') {
287
+ const d = x.dependencies;
288
+ const errors = (Array.isArray(d.errors) ? d.errors : [])
289
+ .map((item) => excerpt(item))
290
+ .filter(Boolean);
291
+ const warnings = (Array.isArray(d.warnings) ? d.warnings : [])
292
+ .map((item) => excerpt(item))
293
+ .filter(Boolean);
294
+ const diagnostics = [
295
+ ...errors.map((message, sourceIndex) => ({
296
+ key: `quiet:sandbox:dependency:error:${sourceIndex}`,
297
+ text: `! ${message}`,
298
+ role: 'error',
299
+ accessible: `Dependency error: ${message}`,
300
+ })),
301
+ ...warnings.map((message, sourceIndex) => ({
302
+ key: `quiet:sandbox:dependency:warning:${sourceIndex}`,
303
+ text: `… ${message}`,
304
+ role: 'warning',
305
+ accessible: `Dependency warning: ${message}`,
306
+ })),
307
+ ];
308
+ for (const item of diagnostics.slice(0, max(o)))
309
+ out.push(row(item.key, item.text, item.role, item.accessible));
310
+ if (!errors.length && !warnings.length && !x.unavailableReason)
311
+ out.push(row('quiet:sandbox:ready', '✓ Sandbox dependencies ready', 'success'));
312
+ }
313
+ else if (s.tab === 'overrides') {
314
+ const items = [
315
+ [
316
+ 'unsandboxed',
317
+ 'Allow unsandboxed fallback',
318
+ x.settings.allowUnsandboxedCommands,
319
+ ],
320
+ ['strict', 'Strict sandbox mode', x.settings.failIfUnavailable],
321
+ ];
322
+ const [start, end] = window(items.length, s.selectedIndex, max(o));
323
+ for (let i = start; i < end; i++) {
324
+ const candidate = items[i];
325
+ if (!candidate)
326
+ continue;
327
+ const [id, label, on] = candidate;
328
+ const isSelected = i === index(items.length, s.selectedIndex);
329
+ out.push(row(`quiet:sandbox:override:${id}`, selected(isSelected, `${label}${on ? ' (current)' : ''}`, sr), isSelected ? 'selection' : 'body', `${isSelected ? 'Selected' : 'Option'}: ${label}${on ? ', current' : ''}`));
330
+ }
331
+ }
332
+ else {
333
+ const runtime = s.snapshot.settings.runtimeConfig;
334
+ const filesystem = (runtime.filesystem ?? {});
335
+ const network = (runtime.network ?? {});
336
+ const fields = [
337
+ ['excluded commands', x.settings.excludedCommands],
338
+ ['allow write', filesystem.allowWrite],
339
+ ['deny write', filesystem.denyWrite],
340
+ ['allow read', filesystem.allowRead],
341
+ ['deny read', filesystem.denyRead],
342
+ ['allow domains', network.allowedDomains],
343
+ ['deny domains', network.deniedDomains],
344
+ ];
345
+ const warnings = (x.globPatternWarnings ?? []).map((warning, warningIndex) => ({
346
+ key: `quiet:sandbox:warning:${JSON.stringify([warningIndex, excerpt(warning, 120)])}`,
347
+ text: `! Glob warning: ${excerpt(warning)}`,
348
+ role: 'warning',
349
+ accessible: `Glob warning: ${excerpt(warning)}`,
350
+ }));
351
+ const detail = fields.map(([key, fieldValue]) => ({
352
+ key: `quiet:sandbox:config:${key}`,
353
+ text: `${key}: ${Array.isArray(fieldValue) ? fieldValue.slice(0, max(o)).map(excerpt).join(', ') || 'none' : value(fieldValue)}`,
354
+ role: 'body',
355
+ accessible: undefined,
356
+ }));
357
+ const projected = full(o.density) ? [...warnings, ...detail] : warnings;
358
+ for (const item of projected.slice(0, max(o)))
359
+ out.push(row(item.key, item.text, item.role, item.accessible));
360
+ if (projected.length === 0)
361
+ out.push(row('quiet:sandbox:config:empty', '○ No sandbox overrides', 'muted'));
362
+ }
363
+ out.push(footer('←/→ tabs ↑/↓ select Enter confirm Esc close', 'Use Left and Right to change tabs. Use Up and Down to select. Press Enter to confirm. Press Escape to close sandbox settings.', sr));
364
+ return out;
365
+ }
366
+ function optionsSurface(title, options, selectedIndex, o, extra) {
367
+ const sr = !!o.screenReader;
368
+ const out = [
369
+ row(`quiet:${title}:heading`, (title.at(0) ?? '').toUpperCase() + title.slice(1), 'heading'),
370
+ ];
371
+ const occurrences = new Map();
372
+ const identities = options.map((option) => {
373
+ const identity = excerpt(option.id || option.model || option.label) || 'option';
374
+ const occurrence = occurrences.get(identity) ?? 0;
375
+ occurrences.set(identity, occurrence + 1);
376
+ return JSON.stringify([identity, occurrence]);
377
+ });
378
+ if (extra)
379
+ out.push(row(`quiet:${title}:state`, excerpt(extra), 'muted'));
380
+ const [a, b] = window(options.length, selectedIndex, max(o));
381
+ for (let i = a; i < b; i++) {
382
+ const x = options[i];
383
+ if (!x)
384
+ continue;
385
+ const identity = identities[i] ?? `option:${i}`;
386
+ const t = `${excerpt(x.label)}${x.selected ? ' (current)' : ''}${full(o.density) && x.description ? ` — ${excerpt(x.description)}` : ''}`;
387
+ out.push(row(`quiet:${title}:option:${identity}`, selected(i === index(options.length, selectedIndex), t, sr), title === 'theme-delete' && x.id === 'delete'
388
+ ? 'error'
389
+ : i === index(options.length, selectedIndex)
390
+ ? 'selection'
391
+ : 'body', `${i === index(options.length, selectedIndex) ? 'Selected' : 'Option'}: ${t}`));
392
+ }
393
+ if (!options.length)
394
+ out.push(row(`quiet:${title}:empty`, '○ No options available', 'muted'));
395
+ out.push(footer('↑/↓ browse Enter select Esc back', 'Use Up and Down to browse. Press Enter to select. Escape goes back.', sr));
396
+ return out;
397
+ }
398
+ function model(s, o) {
399
+ return optionsSurface('model', s.options, s.selectedIndex, o, `Effort: ${clean(s.effort)}`);
400
+ }
401
+ function effort(s, o) {
402
+ return optionsSurface('effort', s.options, s.selectedIndex, o);
403
+ }
404
+ function theme(s, o) {
405
+ if (s.kind === 'theme-panel')
406
+ return optionsSurface('theme', s.options.map((option) => ({
407
+ ...option,
408
+ selected: option.current,
409
+ })), s.selectedIndex, o, `${s.currentTheme}${s.syntaxHighlightingDisabled ? ' · Syntax highlighting disabled' : ''}`);
410
+ if (s.kind === 'custom-theme-create')
411
+ return [
412
+ row('quiet:theme:create:heading', 'Create custom theme', 'heading'),
413
+ row('quiet:theme:create:base', `Base: ${s.base}`),
414
+ row('quiet:theme:create:name', `Name: ${excerpt(s.name) || '(enter name)'}`),
415
+ footer('Enter save Esc back', 'Press Enter to save. Escape goes back.', !!o.screenReader),
416
+ ];
417
+ if (s.kind === 'custom-theme-editor')
418
+ return optionsSurface('theme-editor', s.tokens.map((token) => ({
419
+ id: token,
420
+ label: token,
421
+ description: s.theme.overrides[token] ?? 'Inherited from base',
422
+ })), s.selectedIndex, o, `Theme: ${s.theme.name}`);
423
+ if (s.kind === 'custom-theme-token')
424
+ return [
425
+ row('quiet:theme:token:heading', `Theme token · ${s.token}`, 'heading'),
426
+ row(`quiet:theme:token:${s.token}:value`, `Value: ${excerpt(s.value) || '(enter value)'}`),
427
+ footer('Enter save Esc back', 'Press Enter to save. Escape goes back.', !!o.screenReader),
428
+ ];
429
+ if (s.kind === 'custom-theme-delete')
430
+ return optionsSurface('theme-delete', [
431
+ {
432
+ id: 'delete',
433
+ label: 'Delete theme',
434
+ description: `Warning: permanently delete ${s.theme.name}`,
435
+ },
436
+ { id: 'cancel', label: 'Cancel', description: 'Keep this theme' },
437
+ ], s.selectedIndex, o, '! Destructive action');
438
+ return unsupported(s);
439
+ }
440
+ function leaf(s, o) {
441
+ if (s.kind === 'model-input' || s.kind === 'export-filename')
442
+ return [
443
+ row(`quiet:leaf:${s.kind}:heading`, s.kind === 'model-input' ? 'Model input' : 'Export filename', 'heading'),
444
+ row(`quiet:leaf:${s.kind}:value`, `Input: ${excerpt(s.value)}`, 'input'),
445
+ footer('Enter submit Esc back', 'Press Enter to submit. Escape goes back.', !!o.screenReader),
446
+ ];
447
+ if (s.kind === 'compact-progress') {
448
+ const p = Math.max(0, Math.min(100, Number.isFinite(s.progress) ? s.progress : 0));
449
+ return [
450
+ row('quiet:leaf:progress:heading', 'Compaction progress', 'heading'),
451
+ row('quiet:leaf:progress', `${p >= 100 ? '✓' : '…'} ${Math.round(p)}%`, p >= 100 ? 'success' : 'warning'),
452
+ ];
453
+ }
454
+ return optionsSurface(s.kind, s.options.map((x) => ({ ...x, id: x.id })), s.selectedIndex, o, s.kind === 'copy' && full(o.density)
455
+ ? `Message age: ${Number.isFinite(s.messageAge) ? s.messageAge : 'unknown'}`
456
+ : undefined);
457
+ }
458
+ export function projectQuietSettingsRows(surface, o) {
459
+ switch (surface.kind) {
460
+ case 'doctor-panel':
461
+ return doctor(surface, o);
462
+ case 'memory-panel':
463
+ return memory(surface, o);
464
+ case 'hooks-panel':
465
+ return hooks(surface, o);
466
+ case 'config-panel':
467
+ return config(surface, o);
468
+ case 'sandbox-panel':
469
+ return sandbox(surface, o);
470
+ case 'model-panel':
471
+ return model(surface, o);
472
+ case 'effort-panel':
473
+ return effort(surface, o);
474
+ case 'theme-panel':
475
+ case 'custom-theme-create':
476
+ case 'custom-theme-editor':
477
+ case 'custom-theme-token':
478
+ case 'custom-theme-delete':
479
+ return theme(surface, o);
480
+ case 'model-input':
481
+ case 'export':
482
+ case 'copy':
483
+ case 'export-filename':
484
+ case 'compact-progress':
485
+ return leaf(surface, o);
486
+ default:
487
+ return unsupported(surface);
488
+ }
489
+ }
490
+ //# sourceMappingURL=quiet-settings-rows.js.map
@@ -82,13 +82,19 @@ export declare function createClaudeStatusLineInput(options: {
82
82
  vimMode?: 'INSERT' | 'NORMAL';
83
83
  dataPlane?: DataPlane;
84
84
  }): ClaudeStatusLineInput;
85
- export declare function StatusLine({ configRoot, cwd, input, refreshKey, width, settingSources, dataPlane: _dataPlane, }: {
86
- configRoot: string;
87
- cwd: string;
88
- input: ClaudeStatusLineInput;
89
- refreshKey: string;
90
- width?: number;
91
- settingSources?: readonly ResourceScope[];
92
- dataPlane?: DataPlane;
93
- }): import("react").JSX.Element | null;
85
+ export interface ClaudeStatusLineState {
86
+ readonly text?: string;
87
+ readonly padding: number;
88
+ }
89
+ export declare function formatClaudeStatusLineState(state: ClaudeStatusLineState, width?: number): string | undefined;
90
+ export interface UseClaudeStatusLineOptions {
91
+ readonly configRoot: string;
92
+ readonly cwd: string;
93
+ readonly input: ClaudeStatusLineInput;
94
+ readonly refreshKey: string;
95
+ readonly width?: number;
96
+ readonly settingSources?: readonly ResourceScope[];
97
+ readonly dataPlane?: DataPlane;
98
+ }
99
+ export declare function useClaudeStatusLine({ configRoot, cwd, input, refreshKey, width, settingSources, dataPlane: _dataPlane, }: UseClaudeStatusLineOptions): ClaudeStatusLineState;
94
100
  //# sourceMappingURL=status-line.d.ts.map
@@ -1,8 +1,6 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
1
  import { spawn } from 'node:child_process';
3
2
  import { watchFile, unwatchFile } from 'node:fs';
4
3
  import { join, resolve } from 'node:path';
5
- import { Box, Text } from 'ink';
6
4
  import { useCallback, useEffect, useRef, useState } from 'react';
7
5
  import { loadNativeSharedResources } from '../../persistence/native-resources.js';
8
6
  import { commandShell, commandShellArguments, } from '../../platform/command-shell.js';
@@ -172,7 +170,20 @@ export function createClaudeStatusLineInput(options) {
172
170
  ...(options.vimMode ? { vim: { mode: options.vimMode } } : {}),
173
171
  };
174
172
  }
175
- export function StatusLine({ configRoot, cwd, input, refreshKey, width, settingSources, dataPlane: _dataPlane = 'native', }) {
173
+ export function formatClaudeStatusLineState(state, width) {
174
+ if (!state.text)
175
+ return undefined;
176
+ const padding = Number.isFinite(state.padding)
177
+ ? Math.max(0, Math.trunc(state.padding))
178
+ : 0;
179
+ const maxPadding = width !== undefined && Number.isFinite(width) && width > 0
180
+ ? Math.floor((width - 1) / 2)
181
+ : 256;
182
+ const boundedPadding = Math.min(padding, maxPadding);
183
+ const spaces = ' '.repeat(boundedPadding);
184
+ return `${spaces}${state.text}${spaces}`;
185
+ }
186
+ export function useClaudeStatusLine({ configRoot, cwd, input, refreshKey, width, settingSources, dataPlane: _dataPlane = 'native', }) {
176
187
  void _dataPlane;
177
188
  const [text, setText] = useState();
178
189
  const [padding, setPadding] = useState(0);
@@ -235,8 +246,6 @@ export function StatusLine({ configRoot, cwd, input, refreshKey, width, settingS
235
246
  clearTimeout(timer.current);
236
247
  };
237
248
  }, [configRoot, cwd, schedule]);
238
- if (!text)
239
- return null;
240
- return (_jsx(Box, { paddingX: padding, flexShrink: 0, children: _jsx(Text, { dimColor: true, wrap: "truncate", children: text }) }));
249
+ return text === undefined ? { padding } : { text, padding };
241
250
  }
242
251
  //# sourceMappingURL=status-line.js.map
@@ -391,6 +391,7 @@ function resolveRawPalette(profile, syntaxHighlightingDisabled = false, environm
391
391
  profile === 'dark-daltonized' ||
392
392
  profile === 'dark-ansi';
393
393
  const ansiOnly = profile.endsWith('-ansi');
394
+ const basicColor = terminalColorCapability(environment) === 'ansi16';
394
395
  const daltonized = profile.endsWith('-daltonized');
395
396
  const syntaxBase = ansiOnly
396
397
  ? {
@@ -442,15 +443,21 @@ function resolveRawPalette(profile, syntaxHighlightingDisabled = false, environm
442
443
  ansiOnly,
443
444
  syntaxHighlightingDisabled,
444
445
  sessionColors,
445
- brand: ansiOnly ? 'redBright' : '#D97757',
446
- accent: ansiOnly ? 'redBright' : '#D97757',
447
- info: ansiOnly ? 'cyanBright' : dark ? '#5fd7ff' : '#005f87',
448
- link: ansiOnly ? 'blueBright' : dark ? '#5fafff' : '#005faf',
449
- error: ansiOnly ? 'redBright' : dark ? '#ff5f5f' : '#af0000',
450
- success: ansiOnly ? 'greenBright' : dark ? '#5fd75f' : '#008700',
451
- warning: ansiOnly ? 'yellowBright' : dark ? '#ffd75f' : '#875f00',
452
- muted: ansiOnly ? (dark ? 'white' : 'black') : dark ? '#a8a8a8' : '#585858',
453
- selectionText: dark ? 'black' : 'white',
446
+ brand: ansiOnly || basicColor ? 'cyanBright' : dark ? '#5EE6B5' : '#087F5B',
447
+ accent: ansiOnly || basicColor ? 'cyanBright' : dark ? '#5EE6B5' : '#087F5B',
448
+ info: ansiOnly || basicColor ? 'cyanBright' : dark ? '#9AA7B8' : '#475467',
449
+ link: ansiOnly || basicColor ? 'cyanBright' : dark ? '#5EE6B5' : '#087F5B',
450
+ error: ansiOnly || basicColor ? 'redBright' : dark ? '#FF6B6B' : '#C92A2A',
451
+ success: ansiOnly || basicColor ? 'greenBright' : dark ? '#5EE6B5' : '#087F5B',
452
+ warning: ansiOnly || basicColor ? 'yellowBright' : dark ? '#F2C14E' : '#9A6700',
453
+ muted: ansiOnly || basicColor
454
+ ? dark
455
+ ? 'white'
456
+ : 'black'
457
+ : dark
458
+ ? '#8B949E'
459
+ : '#667085',
460
+ selectionText: ansiOnly || basicColor ? 'cyanBright' : dark ? '#5EE6B5' : '#087F5B',
454
461
  syntaxTheme: ansiOnly ? 'ansi' : dark ? 'Monokai Extended' : 'GitHub',
455
462
  syntax: syntaxBase,
456
463
  };
@@ -538,7 +545,6 @@ function buildSemanticTheme(settings, palette, options = {}, capability = palett
538
545
  const successColor = palette.success;
539
546
  const warningColor = palette.warning;
540
547
  const mutedColor = palette.muted;
541
- const selectionTextColor = palette.selectionText;
542
548
  const syntaxPalette = palette.syntax;
543
549
  const sessionColors = palette.sessionColors;
544
550
  const color = (value) => suppressColor ? undefined : value;
@@ -551,12 +557,10 @@ function buildSemanticTheme(settings, palette, options = {}, capability = palett
551
557
  }),
552
558
  heading: semanticText(undefined, options, { bold: true }),
553
559
  navigation: semanticText(color(focusColor), options, { bold: true }),
554
- selectedRow: semanticText(color(selectionTextColor), options, {
555
- ...(suppressColor ? {} : { backgroundColor: focusColor }),
560
+ selectedRow: semanticText(color(focusColor), options, {
556
561
  bold: true,
557
562
  }),
558
- selectedTab: semanticText(color(selectionTextColor), options, {
559
- ...(suppressColor ? {} : { backgroundColor: focusColor }),
563
+ selectedTab: semanticText(color(focusColor), options, {
560
564
  bold: true,
561
565
  }),
562
566
  focusMarker: semanticText(color(focusColor), options, { bold: true }),
@@ -578,15 +582,11 @@ function buildSemanticTheme(settings, palette, options = {}, capability = palett
578
582
  const resolved = color(value);
579
583
  return resolved === undefined ? {} : { borderColor: resolved };
580
584
  };
581
- const background = (value) => {
582
- const resolved = color(value);
583
- return resolved === undefined ? {} : { backgroundColor: resolved };
584
- };
585
585
  const surface = {
586
586
  neutralBorder: semanticSurface(border(mutedColor), options),
587
587
  separator: semanticSurface(border(mutedColor), options),
588
- selectedRow: semanticSurface(background(focusColor), options),
589
- selectedTab: semanticSurface(background(focusColor), options),
588
+ selectedRow: semanticSurface({}, options),
589
+ selectedTab: semanticSurface({}, options),
590
590
  input: semanticSurface(border(focusColor), options),
591
591
  decision: semanticSurface(border(warningColor), options),
592
592
  error: semanticSurface(border(errorColor), options),
@@ -1,6 +1,11 @@
1
1
  import type { TranscriptPresentationEntry, TranscriptPresentationMode } from './transcript-presentation.js';
2
2
  export declare const FULLSCREEN_TRANSCRIPT_RESERVED_ROWS = 12;
3
3
  export declare const TRANSCRIPT_TRUNCATION_MARKER = "\u2026";
4
+ export declare function terminalGraphemes(text: string): readonly string[];
5
+ export declare function terminalGraphemeWidth(cluster: string): number;
6
+ export declare function terminalTextWidth(text: string): number;
7
+ export declare function terminalTextHead(text: string, budget: number): string;
8
+ export declare function terminalTextTail(text: string, budget: number): string;
4
9
  export declare function estimateTranscriptEntryLines(entry: TranscriptPresentationEntry, width: number, mode: TranscriptPresentationMode): number;
5
10
  export declare function transcriptPresentationLineCount(entries: readonly TranscriptPresentationEntry[], width: number, mode: TranscriptPresentationMode): number;
6
11
  export declare function projectTranscriptEntryRows(entry: TranscriptPresentationEntry, width: number, mode: TranscriptPresentationMode): readonly string[] | undefined;