compact-agent 1.32.2 → 1.33.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.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Braille spinner — 10-frame rotation. The smooth gradient between
3
+ * adjacent frames gives it a more "tech" feel than ASCII spinners
4
+ * (| / - \) and renders in any UTF-8 terminal.
5
+ */
6
+ export declare const BRAILLE_SPINNER: string[];
7
+ /**
8
+ * Quarter-arc spinner — heavier visual weight than Braille, fits
9
+ * better next to text labels when we want the spinner to read as
10
+ * "active state" rather than ambient indicator.
11
+ */
12
+ export declare const ARC_SPINNER: string[];
13
+ /**
14
+ * Filling-dot sequence — used by transition animations to suggest
15
+ * "powering up" or "settling" without needing rotation.
16
+ */
17
+ export declare const POWER_UP: string[];
18
+ export declare const POWER_DOWN: string[];
19
+ /**
20
+ * Configure animations globally. Called by index.ts after config
21
+ * load + when the user toggles screen-reader mode.
22
+ */
23
+ export declare function setAnimationConfig(opts: {
24
+ enabled?: boolean;
25
+ screenReader?: boolean;
26
+ }): void;
27
+ /**
28
+ * Resolved "should I animate" check. Combines the static config,
29
+ * the screen-reader flag, the env override, and the TTY check.
30
+ */
31
+ export declare function animationsEnabled(): boolean;
32
+ /**
33
+ * Paint a frame in-place: `\r` (col 0 of current line) + `\x1b[K`
34
+ * (clear to end of line) + the new content. No trailing newline —
35
+ * the next paint or commitFrame() handles that.
36
+ *
37
+ * Exported because some callers (cancel paths, error fallbacks) need
38
+ * to forcibly repaint a line even when animations are off.
39
+ */
40
+ export declare function paintFrame(text: string): void;
41
+ /**
42
+ * Commit the current in-place frame by emitting a newline. After
43
+ * this the cursor is on a fresh row and the painted frame is locked
44
+ * into the transcript.
45
+ */
46
+ export declare function commitFrame(): void;
47
+ /**
48
+ * Play a sequence of frames in place at the given period. The
49
+ * promise resolves once the last frame has been painted and the
50
+ * period for it has elapsed.
51
+ *
52
+ * When animations are disabled the function paints ONLY the last
53
+ * frame (no waiting, no intermediate frames) so the screen still
54
+ * settles on the correct final visual.
55
+ *
56
+ * The caller controls whether to commit the final frame: if you
57
+ * want subsequent prints to start on a new row, call commitFrame()
58
+ * after playFrames() resolves. If you want a spinner to take over
59
+ * the same row, leave the frame uncommitted.
60
+ */
61
+ export declare function playFrames(frames: string[], periodMs?: number): Promise<void>;
62
+ export interface Spinner {
63
+ /** Stop ticking; leave whatever frame is on screen in place. */
64
+ stop(): void;
65
+ /**
66
+ * Stop ticking, overwrite the spinner line with `line`, then emit
67
+ * a newline. Use when a result is ready and you want the row to
68
+ * settle to the final value before moving on.
69
+ */
70
+ stopAndCommit(line: string): void;
71
+ }
72
+ /**
73
+ * Start an in-place spinner. The `prefixTemplate` is painted each
74
+ * tick with the literal substring "{S}" replaced by the current
75
+ * spinner frame char. Example:
76
+ *
77
+ * startSpinner(" {S} running bash")
78
+ *
79
+ * paints, in sequence, " ⠋ running bash", " ⠙ running bash", …
80
+ *
81
+ * Returns a handle with stop() and stopAndCommit(). Caller MUST
82
+ * invoke one of them — leaving the interval running leaks an event-
83
+ * loop ref and pins the process open.
84
+ *
85
+ * When animations are off: paints the template once with "●" in
86
+ * place of "{S}" and returns a no-op handle. Callers keep working
87
+ * without changes.
88
+ */
89
+ export declare function startSpinner(prefixTemplate: string, periodMs?: number): Spinner;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Animation primitives — terminal in-place rendering for tight,
3
+ * "futuristic"-feeling transitions on state changes (thinking
4
+ * open/close, tool run/result, banner boot).
5
+ *
6
+ * Two patterns:
7
+ *
8
+ * playFrames() Render a sequence of frames in place
9
+ * (`\r\x1b[K` + content), then settle on the
10
+ * last frame. Async — caller awaits the brief
11
+ * transition (~150–250 ms typical). Use for
12
+ * state-transition moments: opening a section,
13
+ * closing/collapsing a section, settling a
14
+ * result.
15
+ *
16
+ * startSpinner() Kick off a setInterval that overwrites the
17
+ * current line with rotating spinner frames
18
+ * (Braille) until stop() is called. Non-
19
+ * blocking — use for "waiting" indicators when
20
+ * nothing else is writing to that line (e.g.
21
+ * while a tool is executing).
22
+ *
23
+ * Both bail to no-op when:
24
+ * - stdout is not a TTY (CI, log capture, piped output)
25
+ * - screen-reader mode is on (in-place repaints become a flood
26
+ * of new content events for the screen reader)
27
+ * - the env override CROWCODER_ANIMATIONS=0 is set
28
+ * - setAnimationConfig({ enabled: false }) was called
29
+ *
30
+ * Disabled-mode behavior: `playFrames()` paints only the LAST frame
31
+ * instantly (settles to the final visual state without the
32
+ * sequence), and `startSpinner()` paints a static "●" placeholder
33
+ * and returns no-op stop()/stopAndCommit() helpers. Callers that
34
+ * use the spinner result keep working — they just don't see motion.
35
+ */
36
+ import { stdout } from 'node:process';
37
+ /**
38
+ * Braille spinner — 10-frame rotation. The smooth gradient between
39
+ * adjacent frames gives it a more "tech" feel than ASCII spinners
40
+ * (| / - \) and renders in any UTF-8 terminal.
41
+ */
42
+ export const BRAILLE_SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
43
+ /**
44
+ * Quarter-arc spinner — heavier visual weight than Braille, fits
45
+ * better next to text labels when we want the spinner to read as
46
+ * "active state" rather than ambient indicator.
47
+ */
48
+ export const ARC_SPINNER = ['◐', '◓', '◑', '◒'];
49
+ /**
50
+ * Filling-dot sequence — used by transition animations to suggest
51
+ * "powering up" or "settling" without needing rotation.
52
+ */
53
+ export const POWER_UP = ['◌', '◍', '◉'];
54
+ export const POWER_DOWN = ['◉', '◍', '◌'];
55
+ let _enabled = true;
56
+ let _screenReader = false;
57
+ /**
58
+ * Configure animations globally. Called by index.ts after config
59
+ * load + when the user toggles screen-reader mode.
60
+ */
61
+ export function setAnimationConfig(opts) {
62
+ if (opts.enabled !== undefined)
63
+ _enabled = opts.enabled;
64
+ if (opts.screenReader !== undefined)
65
+ _screenReader = opts.screenReader;
66
+ }
67
+ /**
68
+ * Resolved "should I animate" check. Combines the static config,
69
+ * the screen-reader flag, the env override, and the TTY check.
70
+ */
71
+ export function animationsEnabled() {
72
+ if (!stdout.isTTY)
73
+ return false;
74
+ if (process.env.CROWCODER_ANIMATIONS === '0')
75
+ return false;
76
+ if (_screenReader)
77
+ return false;
78
+ return _enabled;
79
+ }
80
+ /**
81
+ * Paint a frame in-place: `\r` (col 0 of current line) + `\x1b[K`
82
+ * (clear to end of line) + the new content. No trailing newline —
83
+ * the next paint or commitFrame() handles that.
84
+ *
85
+ * Exported because some callers (cancel paths, error fallbacks) need
86
+ * to forcibly repaint a line even when animations are off.
87
+ */
88
+ export function paintFrame(text) {
89
+ stdout.write('\r\x1b[K' + text);
90
+ }
91
+ /**
92
+ * Commit the current in-place frame by emitting a newline. After
93
+ * this the cursor is on a fresh row and the painted frame is locked
94
+ * into the transcript.
95
+ */
96
+ export function commitFrame() {
97
+ stdout.write('\n');
98
+ }
99
+ /**
100
+ * Play a sequence of frames in place at the given period. The
101
+ * promise resolves once the last frame has been painted and the
102
+ * period for it has elapsed.
103
+ *
104
+ * When animations are disabled the function paints ONLY the last
105
+ * frame (no waiting, no intermediate frames) so the screen still
106
+ * settles on the correct final visual.
107
+ *
108
+ * The caller controls whether to commit the final frame: if you
109
+ * want subsequent prints to start on a new row, call commitFrame()
110
+ * after playFrames() resolves. If you want a spinner to take over
111
+ * the same row, leave the frame uncommitted.
112
+ */
113
+ export async function playFrames(frames, periodMs = 50) {
114
+ if (frames.length === 0)
115
+ return;
116
+ if (!animationsEnabled()) {
117
+ paintFrame(frames[frames.length - 1]);
118
+ return;
119
+ }
120
+ for (const f of frames) {
121
+ paintFrame(f);
122
+ await sleep(periodMs);
123
+ }
124
+ }
125
+ /**
126
+ * Start an in-place spinner. The `prefixTemplate` is painted each
127
+ * tick with the literal substring "{S}" replaced by the current
128
+ * spinner frame char. Example:
129
+ *
130
+ * startSpinner(" {S} running bash")
131
+ *
132
+ * paints, in sequence, " ⠋ running bash", " ⠙ running bash", …
133
+ *
134
+ * Returns a handle with stop() and stopAndCommit(). Caller MUST
135
+ * invoke one of them — leaving the interval running leaks an event-
136
+ * loop ref and pins the process open.
137
+ *
138
+ * When animations are off: paints the template once with "●" in
139
+ * place of "{S}" and returns a no-op handle. Callers keep working
140
+ * without changes.
141
+ */
142
+ export function startSpinner(prefixTemplate, periodMs = 80) {
143
+ if (!animationsEnabled()) {
144
+ paintFrame(prefixTemplate.replace('{S}', '●'));
145
+ return {
146
+ stop: () => { },
147
+ stopAndCommit: (line) => {
148
+ paintFrame(line);
149
+ commitFrame();
150
+ },
151
+ };
152
+ }
153
+ let i = 0;
154
+ const id = setInterval(() => {
155
+ paintFrame(prefixTemplate.replace('{S}', BRAILLE_SPINNER[i % BRAILLE_SPINNER.length]));
156
+ i++;
157
+ }, periodMs);
158
+ // Don't ref the interval — if the process is otherwise idle the
159
+ // spinner shouldn't keep it alive. unref() is a no-op on intervals
160
+ // in Node 18+, but safe to call.
161
+ if (typeof id.unref === 'function')
162
+ id.unref();
163
+ return {
164
+ stop: () => {
165
+ clearInterval(id);
166
+ },
167
+ stopAndCommit: (line) => {
168
+ clearInterval(id);
169
+ paintFrame(line);
170
+ commitFrame();
171
+ },
172
+ };
173
+ }
174
+ function sleep(ms) {
175
+ return new Promise((r) => setTimeout(r, ms));
176
+ }
177
+ //# sourceMappingURL=animations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animations.js","sourceRoot":"","sources":["../src/animations.ts"],"names":[],"mappings":"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"}
@@ -70,7 +70,8 @@ export const COMMAND_CATALOG = [
70
70
  { command: '/perm', description: 'Permission mode (ask/auto/yolo)', category: 'Config' },
71
71
  { command: '/sandbox', description: 'OS-native bash sandbox (off/standard/strict)', category: 'Config' },
72
72
  { command: '/dry-run', description: 'Toggle dry-run mode (preview tool calls)', category: 'Config' },
73
- { command: '/thinking', description: 'Toggle thinking/reasoning display', category: 'Config' },
73
+ { command: '/thinking', description: 'Toggle thinking/reasoning display (live + auto-collapse)', category: 'Config' },
74
+ { command: '/think', description: 'Re-expand the most recent collapsed thinking block', category: 'Config' },
74
75
  { command: '/cd', description: 'Change working directory', category: 'Config' },
75
76
  { command: '/hooks', description: 'List configured hooks', category: 'Config' },
76
77
  // ── Planning ──
@@ -1 +1 @@
1
- {"version":3,"file":"command-palette.js","sourceRoot":"","sources":["../src/command-palette.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"command-palette.js","sourceRoot":"","sources":["../src/command-palette.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import { buildCommitPrompt, buildPRPrompt, printDiff, printLog } from './git-wor
24
24
  import { buildReviewPrompt, buildTDDPrompt, buildSecurityReviewPrompt, runAudit, printAuditReport, buildPlanPrompt, buildE2EPrompt, buildBuildFixPrompt, buildEvalPrompt } from './evaluation.js';
25
25
  import { printRules } from './rules.js';
26
26
  import { buildOrchestrationPrompt } from './orchestration.js';
27
- import { printBanner as printThemedBanner, theme, sym, formatDuration, installScreenReaderDispatch, uninstallScreenReaderDispatch, setPalette, getPaletteId, listPalettes, isPaletteId, PALETTES } from './theme.js';
27
+ import { printBanner as printThemedBanner, theme, sym, formatDuration, installScreenReaderDispatch, uninstallScreenReaderDispatch, setPalette, getPaletteId, listPalettes, isPaletteId, PALETTES, expandLastThinking } from './theme.js';
28
28
  import { saveExport } from './export.js';
29
29
  // New feature modules
30
30
  import { buildVerifyPrompt, saveCheckpoint, listCheckpoints } from './verification.js';
@@ -472,7 +472,8 @@ export function handleSlashCommand(input, config, messages, session, mode) {
472
472
  console.log(d(' ') + c('/perm-reset') + d(' — clear the per-tool always-allow list'));
473
473
  console.log(d(' ') + c('/sandbox [level]') + d(' — OS-native bash sandbox (off / standard / strict)'));
474
474
  console.log(d(' ') + c('/dry-run') + d(' — toggle dry-run mode'));
475
- console.log(d(' ') + c('/thinking') + d(' — toggle thinking/reasoning display'));
475
+ console.log(d(' ') + c('/thinking') + d(' — toggle thinking display (live + auto-collapse)'));
476
+ console.log(d(' ') + c('/think') + d(' — re-expand the most recent collapsed thinking'));
476
477
  console.log(d(' ') + c('/cd <path>') + d(' — change directory'));
477
478
  console.log(d(' ') + c('/hooks') + d(' — list configured hooks'));
478
479
  console.log(d(' ') + c('/reset-hooks') + d(' — wipe hooks.json and re-seed ECC hooks for current install'));
@@ -1211,10 +1212,53 @@ export function handleSlashCommand(input, config, messages, session, mode) {
1211
1212
  console.log(chalk.green(` Show thinking: ${thinkingStatus}`));
1212
1213
  if (config.showThinking) {
1213
1214
  console.log(chalk.dim(' Model reasoning/chain-of-thought will be displayed when available.'));
1215
+ console.log(chalk.dim(' Streams live in a │-bordered panel, then collapses to a one-liner.'));
1216
+ console.log(chalk.dim(' Re-expand the most recent thinking block with /think.'));
1214
1217
  console.log(chalk.dim(' Works with DeepSeek, OpenRouter reasoning models, and others.'));
1215
1218
  }
1216
1219
  return { handled: true };
1217
1220
  }
1221
+ case '/think': {
1222
+ // /think (no args) → re-expand the most recent thinking block
1223
+ // (the one that just collapsed to a one-liner footer)
1224
+ // /think on|off → alias for /thinking (toggles show-thinking)
1225
+ //
1226
+ // Mental model: /thinking is the *setting* (display reasoning
1227
+ // at all? yes/no), /think is the *action* (show me that
1228
+ // reasoning again now). Both names appear in the wild — Claude
1229
+ // Code uses /think, other CLIs use /thinking — so we support
1230
+ // both rather than picking a winner.
1231
+ const sub = (args || '').trim().toLowerCase();
1232
+ if (sub === 'on' || sub === 'off') {
1233
+ const wantOn = sub === 'on';
1234
+ if (config.showThinking !== wantOn) {
1235
+ config.showThinking = wantOn;
1236
+ saveConfig(config);
1237
+ }
1238
+ console.log(chalk.green(` Show thinking: ${wantOn ? chalk.yellow('ON') : chalk.green('OFF')}`));
1239
+ return { handled: true };
1240
+ }
1241
+ if (sub === 'toggle' || sub === '') {
1242
+ // Empty args → expand last thinking. If there is none yet
1243
+ // (no model turn this session has emitted reasoning),
1244
+ // surface a helpful hint instead of silently no-op'ing.
1245
+ const ok = expandLastThinking();
1246
+ if (!ok) {
1247
+ console.log(chalk.dim(' No thinking captured yet this session.'));
1248
+ if (config.showThinking === false) {
1249
+ console.log(chalk.dim(' /thinking is currently OFF — run /thinking to enable.'));
1250
+ }
1251
+ else {
1252
+ console.log(chalk.dim(' The current model may not emit reasoning tokens.'));
1253
+ console.log(chalk.dim(' Try a reasoning model: deepseek-r1, o1-mini, etc.'));
1254
+ }
1255
+ }
1256
+ return { handled: true };
1257
+ }
1258
+ console.log(chalk.dim(' /think — re-expand the most recent thinking'));
1259
+ console.log(chalk.dim(' /think on | off — enable/disable thinking display'));
1260
+ return { handled: true };
1261
+ }
1218
1262
  case '/cd':
1219
1263
  if (args) {
1220
1264
  try {
@@ -2492,11 +2536,17 @@ export function handleSlashCommand(input, config, messages, session, mode) {
2492
2536
  saveConfig(config);
2493
2537
  // Screen-reader mode is special: install/uninstall the stdout filter
2494
2538
  // immediately so the toggle takes effect for the very next log line.
2539
+ // Also flip animations off when SR is on — in-place ANSI repaints
2540
+ // (spinners + collapse transitions) read as a flood of new content
2541
+ // events to NVDA/JAWS and drown out actual response text.
2495
2542
  if (field === 'screenReader') {
2496
2543
  if (v === 'on')
2497
2544
  installScreenReaderDispatch(applyScreenReader);
2498
2545
  else
2499
2546
  uninstallScreenReaderDispatch();
2547
+ void import('./animations.js').then(({ setAnimationConfig }) => {
2548
+ setAnimationConfig({ screenReader: v === 'on' });
2549
+ });
2500
2550
  }
2501
2551
  console.log(chalk.green(` ${label}: ${v.toUpperCase()}`));
2502
2552
  };
@@ -2653,6 +2703,20 @@ async function main() {
2653
2703
  installScreenReaderDispatch(applyScreenReader);
2654
2704
  console.log('[notice] screen-reader mode is ON — ANSI colors are stripped for NVDA/JAWS compatibility. Turn off with: /accessibility screen-reader off');
2655
2705
  }
2706
+ // ── Animation config ─────────────────────────────────────
2707
+ // Wire the global animation flag now that we know the screen-reader
2708
+ // setting. In-place ANSI repaints (used by tool/thinking spinners and
2709
+ // collapse/settle transitions) generate a flood of new content events
2710
+ // for screen readers, so they're force-off in that mode. Sighted
2711
+ // users get them by default; the CROWCODER_ANIMATIONS=0 env var still
2712
+ // overrides for users who specifically don't want the motion.
2713
+ {
2714
+ const { setAnimationConfig } = await import('./animations.js');
2715
+ setAnimationConfig({
2716
+ enabled: process.env.CROWCODER_ANIMATIONS !== '0',
2717
+ screenReader: config.voice?.accessibility?.screenReader === true,
2718
+ });
2719
+ }
2656
2720
  // Create session
2657
2721
  const mode = { current: 'dev' };
2658
2722
  const session = createSession(process.cwd(), config.model, config.provider, mode.current);
@@ -2809,6 +2873,15 @@ async function main() {
2809
2873
  const lookup = name || seq;
2810
2874
  if (!INTERCEPT.has(lookup))
2811
2875
  return;
2876
+ // While the command palette / inline-suggest is open it takes
2877
+ // exclusive control of stdin via its own `data` listener. The
2878
+ // hotkey listener must bail entirely — otherwise Esc would
2879
+ // trigger the rewind chord, Tab/F-keys would print status
2880
+ // overlays, and Space/'/' would try to open a second picker on
2881
+ // top of the first. The data-level handler in the picker sees
2882
+ // the bytes first and finishes its work; we just stand down.
2883
+ if (pickerActive)
2884
+ return;
2812
2885
  const shift = !!key.shift;
2813
2886
  const meta = !!key.meta;
2814
2887
  const ctrl = !!key.ctrl;
@@ -3006,14 +3079,25 @@ async function main() {
3006
3079
  // Any other shifted F-key: no-op (don't fall through to bare).
3007
3080
  return;
3008
3081
  }
3009
- // ── Space (bare): command palette at empty prompt ──
3010
- // Pressing Space when the input buffer is empty opens the
3011
- // command palette — an alt-screen picker showing every slash
3012
- // command, arrow-key navigable, type-to-filter, Enter to run.
3013
- // When the buffer has content (the user is typing a real
3014
- // message that begins with a space-separated word) the keypress
3015
- // listener stays out of the way and lets readline handle the
3016
- // space normally.
3082
+ // ── Space (bare) / '/' (bare): command palette at empty prompt ──
3083
+ // Two distinct UX shapes that share the same trigger guards:
3084
+ //
3085
+ // Space → full-screen browse picker (alt-screen takeover).
3086
+ // User explicitly asked to browse every command, so
3087
+ // a big sortable list with descriptions, category
3088
+ // hints, and a footer is exactly what they want.
3089
+ //
3090
+ // '/' → inline dropdown rendered directly below the
3091
+ // prompt (no alt-screen). The user is in the middle
3092
+ // of typing a command — they need to KEEP seeing
3093
+ // their chat history and the prompt context, not
3094
+ // have a full-screen widget yanked over them. The
3095
+ // dropdown narrows as they type and disappears on
3096
+ // Esc or Backspace-to-empty.
3097
+ //
3098
+ // Both branches share the trigger guards (no buffer content,
3099
+ // no active stream) and the pickerActive interlock that
3100
+ // prevents stacking two pickers.
3017
3101
  if (name === 'space' || lookup === '/') {
3018
3102
  if (pickerActive)
3019
3103
  return;
@@ -3035,14 +3119,15 @@ async function main() {
3035
3119
  const turnCtl = globalThis.__turnAbortCtl;
3036
3120
  if (turnCtl && !turnCtl.signal.aborted)
3037
3121
  return;
3038
- const triggerChar = lookup === '/' ? '/' : ' ';
3039
- // Open the palette. The picker is async and takes stdin into
3040
- // raw mode for its lifetime we fire-and-forget here.
3122
+ const isSlash = lookup === '/';
3123
+ // Take the interlock. The async branch sets pickerActive=false
3124
+ // in a finally so any error path still releases it.
3041
3125
  pickerActive = true;
3042
- // The trigger character is already in readline's buffer at
3043
- // this point (the keypress listener is an observer, not a
3044
- // gate). Clear it so the prompt is clean once the picker
3045
- // exits.
3126
+ // Clear the trigger char from readline's buffer so the prompt
3127
+ // is clean. For '/' we'll re-render it ourselves at the
3128
+ // inline-suggest anchor; for Space we don't need any
3129
+ // character on the prompt because the alt-screen picker
3130
+ // covers everything.
3046
3131
  try {
3047
3132
  const rlAny = rl;
3048
3133
  rlAny.line = '';
@@ -3051,50 +3136,81 @@ async function main() {
3051
3136
  catch { /* noop */ }
3052
3137
  void (async () => {
3053
3138
  try {
3054
- const { pick } = await import('./picker.js');
3055
3139
  const { COMMAND_CATALOG } = await import('./command-palette.js');
3056
- const items = COMMAND_CATALOG.map((c) => ({
3057
- label: c.command,
3058
- hint: c.category,
3059
- description: c.description,
3060
- value: c.command,
3061
- }));
3062
- const selected = await pick(items, {
3063
- title: 'compact-agent · command palette',
3064
- footer: 'type to filter · ↑↓ to navigate · Enter to run · Esc to cancel',
3065
- // '/' trigger: pre-fill filter so the user's mental
3066
- // model ("I typed / and now I see slash commands") is
3067
- // preserved. Space trigger: blank filter, browse all.
3068
- initialFilter: lookup === '/' ? '/' : undefined,
3069
- });
3070
- if (selected) {
3071
- globalThis.__crowcoderQueuedInput = selected + '\n';
3072
- try {
3073
- rl.emit('line', '');
3140
+ if (isSlash) {
3141
+ // '/' → inline dropdown below the prompt.
3142
+ const { inlineSuggest } = await import('./inline-suggest.js');
3143
+ const result = await inlineSuggest(rl, COMMAND_CATALOG.map((c) => ({
3144
+ command: c.command,
3145
+ description: c.description,
3146
+ })), '/');
3147
+ if (result.accepted && result.command) {
3148
+ // Trailing space "fill but don't submit" (Tab
3149
+ // pathway): plant the command in rl.line so the
3150
+ // user can type args before Enter.
3151
+ if (result.command.endsWith(' ')) {
3152
+ const cmd = result.command;
3153
+ try {
3154
+ const rlAny = rl;
3155
+ rlAny.line = cmd;
3156
+ rlAny.cursor = cmd.length;
3157
+ rlAny._refreshLine?.();
3158
+ }
3159
+ catch { /* noop */ }
3160
+ }
3161
+ else {
3162
+ // Enter → submit immediately via the queued-input
3163
+ // sentinel pattern (mirrors how the space picker
3164
+ // submits).
3165
+ globalThis.__crowcoderQueuedInput = result.command + '\n';
3166
+ try {
3167
+ rl.emit('line', '');
3168
+ }
3169
+ catch { /* noop */ }
3170
+ }
3074
3171
  }
3075
- catch { /* noop */ }
3076
- }
3077
- else if (triggerChar === '/') {
3078
- // Cancel from '/'-triggered picker: restore the '/'
3079
- // to the buffer so the user can keep typing the
3080
- // command manually (e.g. they wanted /model claude-
3081
- // sonnet-4 directly, not the picker). Don't resolve
3082
- // rl.question leave readline waiting for input.
3083
- try {
3084
- const rlAny = rl;
3085
- rlAny.line = '/';
3086
- rlAny._refreshLine?.();
3172
+ else {
3173
+ // Cancelled. Restore rl.line to whatever the user
3174
+ // had typed so they can keep editing (could be '/',
3175
+ // '/he', or '' if they backspaced all the way out).
3176
+ try {
3177
+ const rlAny = rl;
3178
+ rlAny.line = result.filter;
3179
+ rlAny.cursor = result.filter.length;
3180
+ rlAny._refreshLine?.();
3181
+ }
3182
+ catch { /* noop */ }
3087
3183
  }
3088
- catch { /* noop */ }
3089
3184
  }
3090
3185
  else {
3091
- // Cancel from space-triggered picker: prompt is clean,
3092
- // resolve readline with empty so the loop iterates
3093
- // back to a fresh prompt.
3094
- try {
3095
- rl.emit('line', '');
3186
+ // Space full-screen browse picker (unchanged).
3187
+ const { pick } = await import('./picker.js');
3188
+ const items = COMMAND_CATALOG.map((c) => ({
3189
+ label: c.command,
3190
+ hint: c.category,
3191
+ description: c.description,
3192
+ value: c.command,
3193
+ }));
3194
+ const selected = await pick(items, {
3195
+ title: 'compact-agent · command palette',
3196
+ footer: 'type to filter · ↑↓ to navigate · Enter to run · Esc to cancel',
3197
+ });
3198
+ if (selected) {
3199
+ globalThis.__crowcoderQueuedInput = selected + '\n';
3200
+ try {
3201
+ rl.emit('line', '');
3202
+ }
3203
+ catch { /* noop */ }
3204
+ }
3205
+ else {
3206
+ // Cancel from space-triggered picker: prompt is
3207
+ // clean, resolve readline with empty so the loop
3208
+ // iterates back to a fresh prompt.
3209
+ try {
3210
+ rl.emit('line', '');
3211
+ }
3212
+ catch { /* noop */ }
3096
3213
  }
3097
- catch { /* noop */ }
3098
3214
  }
3099
3215
  }
3100
3216
  finally {