Skip to content

Commit 487d5ad

Browse files
committed
feat: show command preview in spinner instead of filename
The spinner now displays the inferred command with flags (e.g., "claude --model opus --print") instead of the generic "Starting filename..." message. This gives users immediate feedback about what command will be executed based on their agent file configuration.
1 parent 05d4e1e commit 487d5ad

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

src/cli-runner.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
resolveCommand, buildArgs, runCommand, extractPositionalMappings,
1616
extractEnvVars, killCurrentChildProcess, hasInteractiveMarker,
1717
} from "./command";
18+
import { startSpinner } from "./spinner";
1819
import { getProcessManager } from "./process-manager";
1920
import {
2021
expandImports, hasImports,
@@ -323,6 +324,10 @@ export class CliRunner {
323324

324325
getCommandLogger().info({ command, argsCount: finalRunArgs.length, promptLength: finalBody.length }, "Executing command");
325326

327+
// Start spinner with command preview (will be stopped when first output arrives)
328+
const preview = formatCommandPreview(command, finalRunArgs);
329+
startSpinner(preview);
330+
326331
const runResult = await runCommand({
327332
command, args: finalRunArgs, positionals: [finalBody], positionalMappings, captureOutput: false, env: extractEnvVars(frontmatter),
328333
});
@@ -628,6 +633,53 @@ export class CliRunner {
628633
}
629634
}
630635

636+
/**
637+
* Format a command preview for the spinner message
638+
* Shows command + subcommands + key flags in a concise format
639+
*/
640+
function formatCommandPreview(command: string, args: string[], maxLength = 60): string {
641+
// Build a representation: command subcommand flag1 flag2 ...
642+
const parts = [command];
643+
644+
// First, add any leading non-flag args (subcommands like "exec")
645+
let i = 0;
646+
while (i < args.length) {
647+
const arg = args[i];
648+
if (!arg || arg.startsWith("-")) break;
649+
// Include subcommands (short non-flag args)
650+
if (arg.length <= 20) {
651+
parts.push(arg);
652+
}
653+
i++;
654+
}
655+
656+
// Then add flags and their short values
657+
for (; i < args.length; i++) {
658+
const arg = args[i];
659+
if (!arg) continue;
660+
661+
if (arg.startsWith("-")) {
662+
// It's a flag - add it
663+
parts.push(arg);
664+
// Check if next arg is a short value (not another flag)
665+
const nextArg = args[i + 1];
666+
if (nextArg && !nextArg.startsWith("-") && nextArg.length <= 20) {
667+
parts.push(nextArg);
668+
i++;
669+
}
670+
}
671+
}
672+
673+
let preview = parts.join(" ");
674+
675+
// Truncate if too long
676+
if (preview.length > maxLength) {
677+
preview = preview.slice(0, maxLength - 3) + "...";
678+
}
679+
680+
return preview;
681+
}
682+
631683
/** Create a CliRunner with the given environment */
632684
export function createCliRunner(env: SystemEnvironment, options?: Partial<Omit<CliRunnerOptions, "env">>): CliRunner {
633685
return new CliRunner({ env, ...options });

src/cli.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import type { FileSelectorSelection } from "./file-selector";
88

99
// Deferred imports for cold start optimization
1010
let _showFileSelectorWithPreview: typeof import("./file-selector").showFileSelectorWithPreview | null = null;
11-
let _startSpinner: typeof import("./spinner").startSpinner | null = null;
1211
let _loadHistory: typeof import("./history").loadHistory | null = null;
1312
let _getFrecencyScore: typeof import("./history").getFrecencyScore | null = null;
1413

@@ -20,14 +19,6 @@ async function getFileSelector() {
2019
return _showFileSelectorWithPreview;
2120
}
2221

23-
async function getSpinner() {
24-
if (!_startSpinner) {
25-
const mod = await import("./spinner");
26-
_startSpinner = mod.startSpinner;
27-
}
28-
return _startSpinner;
29-
}
30-
3122
async function getHistory() {
3223
if (!_loadHistory || !_getFrecencyScore) {
3324
const mod = await import("./history");
@@ -329,9 +320,7 @@ export async function handleMaCommands(args: CliArgs): Promise<HandleMaCommandsR
329320
if (mdFiles.length > 0) {
330321
const selection = await showInteractiveSelector(mdFiles);
331322
if (selection) {
332-
// Start spinner to show activity while preparing the agent (lazy-loaded)
333-
const startSpinner = await getSpinner();
334-
startSpinner(`Starting ${basename(selection.path)}...`);
323+
// Spinner will be started in cli-runner.ts with command preview
335324
return { handled: true, selectedFile: selection.path, dryRun: selection.dryRun };
336325
}
337326
// User cancelled - throw error for clean exit

0 commit comments

Comments
 (0)