You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+9-5Lines changed: 9 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -108,7 +108,7 @@ aish c "docker ps" --model gpt-4o # Use specific model
108
108
-`--provider <provider>` - Override default AI provider
109
109
-`--model <model>` - Override provider's preferred model
110
110
-`--max-tries <n>` - Abort after n failed executions (default 3)
111
-
-`--json` - Output final structured JSON summary (combine with `--yes` for scripting)
111
+
-`--json` - Output ONLY the final structured JSON summary (implies `--yes`; suppresses all intermediate and live output; captured stdout/stderr provided via `finalStdout` / `finalStderr`).
112
112
113
113
#### Non-Interactive & JSON Mode
114
114
Use these flags together for scripting / automation:
"explanation": "Shows the current system date and time",
143
143
"attempts": 0,
144
144
"failures": [],
145
-
"alternativesTried": 0
145
+
"alternativesTried": 0,
146
+
"finalStdout": "Sat Jan 04 12:34:56 UTC 2025\n",
147
+
"finalStderr": ""
146
148
}
147
149
```
148
-
All fields appear on a single line in actual output to simplify parsing (pretty-printed here for readability).
150
+
All fields are emitted as a single compact one-line JSON object in real execution. `finalStdout` / `finalStderr` appear only after a command attempt (success or failure). They are omitted if no execution occurred (e.g. dangerous-command abort).
149
151
150
152
#### JSON Fields
151
153
| Field | Description |
@@ -160,6 +162,8 @@ All fields appear on a single line in actual output to simplify parsing (pretty-
160
162
|`attempts`| Number of failed command executions (non-zero exits) |
161
163
|`failures[]`| Details per failed execution (stdout, stderr, explanation, solution) |
162
164
|`alternativesTried`| Count of failures where an alternative command was executed |
165
+
|`finalStdout`| Captured stdout of the last command attempt (success or failure) |
166
+
|`finalStderr`| Captured stderr of the last command attempt (success or failure) |
163
167
164
168
#### Aborted Reasons
165
169
| Reason | Meaning |
@@ -177,10 +181,10 @@ All fields appear on a single line in actual output to simplify parsing (pretty-
-Use`--json` for CI pipelines (auto-approves and suppresses live output).
181
185
- Pipe to `jq` for assertions: `aish c "show date" -y --json | jq -r '.finalCommand'`.
182
186
- Use `--max-tries 1` to fail fast for deterministic scripting.
183
-
-In `--json`mode spinners and intermediate analysis/failure messages are suppressed (quiet output).
187
+
-`--json`implies non-interactive mode and suppresses ALL non-JSON output (analysis, prompts, live stdout/stderr); final stdout/stderr are only available via `finalStdout` / `finalStderr` in the JSON line.
184
188
185
189
#### JSON Mode Test Guidance
186
190
Previously a helper script existed for deterministic JSON checks. That script has been removed. Use direct invocations combining `--yes`, `--json`, and optional `--max-tries` and validate the final line with `jq`.
// Auto-approve path (non-interactive OR json mode)
284
+
if(context.autoApprove||context.jsonMode){
271
285
context.state=CommandState.EXECUTING;
272
286
return;
273
287
}
@@ -317,6 +331,12 @@ class CommandExecutor {
317
331
context.isSudoRetry=false;
318
332
319
333
if(result.exitCode===0){
334
+
// Record success output for JSON summary (especially when suppressed)
335
+
context.lastSuccess={
336
+
command: context.currentAnalysis.command,
337
+
stdout: result.stdout,
338
+
stderr: result.stderr,
339
+
};
320
340
// Reset sudo attempts on success
321
341
context.sudoAttempts=0;
322
342
context.state=CommandState.SUCCESS;
@@ -499,51 +519,13 @@ class CommandExecutor {
499
519
query: string,
500
520
conversationHistory: ModelMessage[],
501
521
): Promise<CommandAnalysis>{
502
-
constnormalized=query.toLowerCase();
503
522
constsystemPrompt=
504
523
"You are a shell command expert. You MUST respond with valid JSON only, no other text or formatting.";
505
524
506
525
constuserContent=
507
526
conversationHistory.length>0
508
-
? `Based on our conversation, analyze this request and generate an appropriate shell command: "${query}"
509
-
510
-
Return a JSON object with these exact fields:
511
-
{
512
-
"command": "the shell command to execute",
513
-
"explanation": "brief explanation of what the command does",
514
-
"isDangerous": false,
515
-
"requiresExternalPackages": false,
516
-
"externalPackages": [],
517
-
"needsInteractiveMode": false
518
-
}
519
-
520
-
Set isDangerous to true ONLY for commands that could cause irreversible system damage or data loss (like rm -rf /, format, dd, etc.).
521
-
Common development operations like removing lock files, node_modules, build artifacts, or temporary files are NOT dangerous.
522
-
Set requiresExternalPackages to true and list packages if external tools are needed.
523
-
Set needsInteractiveMode to true if the user's intent is clearly to open/run an interactive program (like "open vim", "start nano", "run python interactively", "launch htop", etc.) or if the program they're trying to run is interactive in your knowledge. If unsure, assume false.
524
-
525
-
For file searches, prefer searching in user directories (~) rather than system-wide (/) to avoid permission issues and long execution times.
526
-
527
-
JSON only:`
528
-
: `Analyze this user query and generate an appropriate shell command: "${query}"
529
-
530
-
Return a JSON object with these exact fields:
531
-
{
532
-
"command": "the shell command to execute",
533
-
"explanation": "brief explanation of what the command does",
534
-
"isDangerous": false,
535
-
"requiresExternalPackages": false,
536
-
"externalPackages": [],
537
-
"needsInteractiveMode": false
538
-
}
539
-
540
-
Set isDangerous to true ONLY for commands that could cause irreversible system damage or data loss.
541
-
Common development operations are NOT dangerous.
542
-
Set needsInteractiveMode to true if the user's intent is clearly to open/run an interactive program (like "open vim", "start nano", "run python interactively", "launch htop", etc.).
543
-
544
-
For file searches, prefer searching in user directories (~) rather than system-wide (/).
545
-
546
-
JSON only:`;
527
+
? `Based on our conversation, analyze this request and generate an appropriate shell command: "${query}"\n\nReturn a JSON object with these exact fields:\n{\n "command": "the shell command to execute",\n "explanation": "brief explanation of what the command does",\n "isDangerous": false,\n "requiresExternalPackages": false,\n "externalPackages": [],\n "needsInteractiveMode": false\n}\n\nSet isDangerous to true ONLY for commands that could cause irreversible system damage or data loss (like rm -rf /, format, dd, etc.).\nCommon development operations like removing lock files, node_modules, build artifacts, or temporary files are NOT dangerous.\nSet requiresExternalPackages to true and list packages if external tools are needed.\nSet needsInteractiveMode to true if the user's intent is clearly to open/run an interactive program (like "open vim", "start nano", "run python interactively", "launch htop", etc.) or if the program they're trying to run is interactive in your knowledge. If unsure, assume false.\n\nFor file searches, prefer searching in user directories (~) rather than system-wide (/) to avoid permission issues and long execution times.\n\nJSON only:`
528
+
: `Analyze this user query and generate an appropriate shell command: "${query}"\n\nReturn a JSON object with these exact fields:\n{\n "command": "the shell command to execute",\n "explanation": "brief explanation of what the command does",\n "isDangerous": false,\n "requiresExternalPackages": false,\n "externalPackages": [],\n "needsInteractiveMode": false\n}\n\nSet isDangerous to true ONLY for commands that could cause irreversible system damage or data loss.\nCommon development operations are NOT dangerous.\nSet needsInteractiveMode to true if the user's intent is clearly to open/run an interactive program (like "open vim", "start nano", "run python interactively", "launch htop", etc.).\n\nFor file searches, prefer searching in user directories (~) rather than system-wide (/).\n\nJSON only:`;
547
529
548
530
constenvironmentInfo=`Environment Context:\nOS: ${os.type()}${os.release()} (${os.platform()}${os.arch()})\nDate: ${newDate().toISOString().split('T')[0]}\nCWD: ${process.cwd()}\n\nInstructions:\n- Only propose commands valid for this OS.\n- Avoid Linux-specific /proc paths on macOS (darwin).\n- Prefer portable POSIX utilities when possible.\n- If the user's request is impossible without additional tools or privileges, still produce a safe explanatory command (e.g., an echo) and keep isDangerous=false.`;
549
531
@@ -582,43 +564,7 @@ JSON only:`;
582
564
constsystemPrompt=
583
565
"You are a shell command expert. You MUST respond with valid JSON only, no other text or formatting.";
584
566
585
-
constuserPrompt=`A command failed with the following details:
586
-
587
-
Command: ${error.command}
588
-
Exit Code: ${error.exitCode}
589
-
Standard Output: ${error.stdout||"(none)"}
590
-
Standard Error: ${error.stderr||"(none)"}
591
-
Original User Query: "${query}"
592
-
593
-
Based on our conversation history (if any) and these details, analyze the failure.
594
-
595
-
Return a JSON object with these exact fields:
596
-
{
597
-
"explanation": "brief explanation of why the command failed (1-2 sentences max)",
598
-
"solution": "how to fix the issue or what the user should do (1-2 sentences max)",
599
-
"alternativeCommand": "alternative command to try (or null ONLY if absolutely no alternative exists)",
600
-
"needsInteractiveMode": false
601
-
}
602
-
603
-
IMPORTANT: You should ALMOST ALWAYS provide an alternativeCommand that attempts to fulfill the user's original request. Look at the error message and suggest a command that will work. For example:
604
-
- If a flag isn't recognized, suggest the command without that flag or with an equivalent
605
-
- If permission denied, suggest with sudo or in a different directory
606
-
- If a tool doesn't exist, suggest an alternative tool that achieves the same goal
607
-
- If a file/directory doesn't exist, suggest creating it or using a different path
608
-
609
-
Only return null for alternativeCommand in cases where:
610
-
- The user needs to install software first (but even then, try to suggest the install command)
611
-
- The request is physically impossible (e.g., accessing hardware that doesn't exist)
612
-
- The command requires user-specific information you don't have
613
-
614
-
IMPORTANT: Set needsInteractiveMode to true ONLY if ALL of these conditions are met:
615
-
1. The failure was clearly caused by missing TTY/terminal (errors like "not a terminal", "no tty", input/output redirection issues)
616
-
2. The alternative command you're suggesting is an interactive program (vim, nano, htop, etc.)
617
-
3. Double-check: Does this alternative command actually need TTY to function properly?
618
-
619
-
If you're unsure about ANY of these conditions, set needsInteractiveMode to false. Be extremely conservative.
620
-
621
-
Be very concise and helpful. Keep explanations short. JSON only:`;
567
+
constuserPrompt=`A command failed with the following details:\n\nCommand: ${error.command}\nExit Code: ${error.exitCode}\nStandard Output: ${error.stdout||"(none)"}\nStandard Error: ${error.stderr||"(none)"}\nOriginal User Query: "${query}"\n\nBased on our conversation history (if any) and these details, analyze the failure.\n\nReturn a JSON object with these exact fields:\n{\n "explanation": "brief explanation of why the command failed (1-2 sentences max)",\n "solution": "how to fix the issue or what the user should do (1-2 sentences max)",\n "alternativeCommand": "alternative command to try (or null ONLY if absolutely no alternative exists)",\n "needsInteractiveMode": false\n}\n\nIMPORTANT: You should ALMOST ALWAYS provide an alternativeCommand that attempts to fulfill the user's original request. Look at the error message and suggest a command that will work. For example:\n- If a flag isn't recognized, suggest the command without that flag or with an equivalent\n- If permission denied, suggest with sudo or in a different directory\n- If a tool doesn't exist, suggest an alternative tool that achieves the same goal\n- If a file/directory doesn't exist, suggest creating it or using a different path\n\nOnly return null for alternativeCommand in cases where:\n- The user needs to install software first (but even then, try to suggest the install command)\n- The request is physically impossible (e.g., accessing hardware that doesn't exist)\n- The command requires user-specific information you don't have\n\nIMPORTANT: Set needsInteractiveMode to true ONLY if ALL of these conditions are met:\n1. The failure was clearly caused by missing TTY/terminal (errors like "not a terminal", "no tty", input/output redirection issues)\n2. The alternative command you're suggesting is an interactive program (vim, nano, htop, etc.)\n3. Double-check: Does this alternative command actually need TTY to function properly?\n\nIf you're unsure about ANY of these conditions, set needsInteractiveMode to false. Be extremely conservative.\n\nBe very concise and helpful. Keep explanations short. JSON only:`;
622
568
623
569
constfailureEnvInfo=`Environment Context:\nOS: ${os.type()}${os.release()} (${os.platform()}${os.arch()})\nDate: ${newDate().toISOString().split('T')[0]}\nCWD: ${process.cwd()}\n\nValidation Rules:\n- Suggest only commands valid for this OS.\n- Avoid Linux-specific /proc paths on macOS.\n- If hardware metrics or privileged data are requested and unavailable without new tools, return alternativeCommand null with a concise explanation unless a standard built-in utility suffices.\n- Prefer safe existence checks (test -f, test -d) before operations.\n- Never hallucinate files or system paths.`;
624
570
@@ -693,7 +639,7 @@ Be very concise and helpful. Keep explanations short. JSON only:`;
693
639
child.on("close",(code)=>{
694
640
if(!resolved){
695
641
resolved=true;
696
-
resolve({
642
+
resolve({
697
643
exitCode: code||0,
698
644
stdout: "",
699
645
stderr: stderr,
@@ -758,6 +704,7 @@ Be very concise and helpful. Keep explanations short. JSON only:`;
0 commit comments