Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ A dated, sanitized offline fallback snapshot is available as [HTML](docs/model-c
Explain the failing test
!npm test
/shell-result
Help me fix it
/shell-result lines
/shell-result drop 8-10
/shell-result send
```

`!commands` run locally with **zero model API calls**. Output streams into the console with an exit code. `/shell-result` explicitly shares the latest result with the next model turn; review it for secrets first.
`!commands` run locally with **zero model API calls**. Output streams into the console with an exit code. `/shell-result` stages and previews the exact bounded attachment, including the command, captured directory, exit status, and any omitted bytes. Edit it with `drop`, `replace`, `mask`, or `redact`, then choose `/shell-result send` or `/shell-result cancel`. Redaction helps identify common secrets but does not guarantee their removal. A later shell command cannot change the staged attachment. For pipes and JSON sessions, `/shell-result send` is the explicit one-step send form.

| Platform | Local shell | Interactive programs |
|---|---|---|
Expand Down
4 changes: 2 additions & 2 deletions docs/generated/commands.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- GENERATED FILE: run `npm run docs:generate`; do not edit by hand. -->
<!-- manifest-digest: sha256:c9524e6cca9e6dc684b59996bcd16fdab2b5a0a0a8007ff3be2b8bff5d7cdc8e -->
<!-- manifest-digest: sha256:528c55f5f15ba474ef4fb931cea11a1f1275bd261aa7503c08bd9ad09b230e51 -->
# Generated command reference

This reference is generated from the validated, versioned command manifest. Availability is evaluated at runtime; a listed command may still require authentication, a hosted capability, or local tooling.
Expand Down Expand Up @@ -485,7 +485,7 @@ Permission: `unknown` · Availability: `runtime-dependent` · Telemetry: `slash.

#### `/shell-result`

explicitly share the last local shell result with chat \(bounded\)
preview and edit a bounded local shell result before sharing it

Permission: `network` · Availability: `runtime-dependent` · Telemetry: `slash.shell-result`

Expand Down
12 changes: 6 additions & 6 deletions src/commands/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1354,7 +1354,7 @@ export async function repl(ctx: AppContext, skillOpts: TurnSkillOptions = {}): P
/** Run one turn without sacrificing an existing type-ahead draft. */
const runQueuedTurn = async (input: ConsoleInput, authContinuation = false): Promise<"completed" | "aborted" | "failed"> => {
const sharedShellResult = input.kind === "share";
if (input.kind === "share") input = consoleShell.share();
if (input.kind === "share") input = consoleShell.share(input);
if (input.kind === "error") { process.stdout.write(input.message + "\n"); return "completed"; }
if (input.kind === "empty") return "completed";
if (input.kind !== "chat") {
Expand Down Expand Up @@ -1468,7 +1468,7 @@ export async function repl(ctx: AppContext, skillOpts: TurnSkillOptions = {}): P
// server's error frame (frame() runs before runTurn throws) — only
// genuinely unrendered failures (network, fallback-leg errors) need
// printError's own "✗" line, or the user sees the error twice.
const authFailure = await resolveBackend(ctx) === "cloud"
const authFailure = !sharedShellResult && await resolveBackend(ctx) === "cloud"
&& authRepair.noteFailure(err, submittedPrompt, turnOutcomeForError(err), receipts);
if (authFailure && buf.value) heldDraft = buf.value;
if (authFailure && !ctx.flags.json) {
Expand All @@ -1488,7 +1488,7 @@ export async function repl(ctx: AppContext, skillOpts: TurnSkillOptions = {}): P
// commit() clears the submitted line before the request starts. Put it
// back only when the user has not typed ahead; otherwise preserve their
// newer draft and leave the failed submission in history for recall.
const recovered = authFailure ? buf.value : recoverSubmittedPrompt(text, buf.value);
const recovered = sharedShellResult || authFailure ? buf.value : recoverSubmittedPrompt(text, buf.value);
if (recovered !== buf.value) {
buf.clear();
buf.insert(recovered);
Expand Down Expand Up @@ -2037,7 +2037,7 @@ export async function replLines(ctx: AppContext, skillOpts: TurnSkillOptions = {
if (ConsoleShell.isTerminalCommand(line)) { await consoleShell.terminalCommand(line); continue; }
let input = classifyConsoleInput(line);
const sharedShellResult = input.kind === "share";
if (input.kind === "share") input = consoleShell.share();
if (input.kind === "share") input = consoleShell.share(input, true);
if (input.kind === "error") { process.stdout.write(input.message + "\n"); if (p) process.stdout.write(p + consoleShell.prompt()); continue; }
let t = input.kind === "chat" ? input.text : "";
let authReplay = false;
Expand Down Expand Up @@ -2085,7 +2085,7 @@ export async function replLines(ctx: AppContext, skillOpts: TurnSkillOptions = {
if (p) process.stdout.write(p + consoleShell.prompt());
continue;
}
if (historyEnabled() && line.trim() !== "/shell-result") appendHistory(line.trim(), historyPath(ctx.flags.cwd));
if (historyEnabled() && !sharedShellResult) appendHistory(line.trim(), historyPath(ctx.flags.cwd));
if (t === "/auth" || t.startsWith("/auth ")) {
inflight = new AbortController();
try {
Expand Down Expand Up @@ -2166,7 +2166,7 @@ export async function replLines(ctx: AppContext, skillOpts: TurnSkillOptions = {
const outcome = turnOutcomeForError(err);
if (ctx.flags.json && outcome) process.stdout.write(turnOutcomeJson(outcome) + "\n");
else process.stderr.write("\n" + errTheme.dim("✗ canceled — turn discarded") + "\n");
} else if (await resolveBackend(ctx) === "cloud" && authRepair.noteFailure(err, submittedPrompt, turnOutcomeForError(err), receipts)) {
} else if (!sharedShellResult && await resolveBackend(ctx) === "cloud" && authRepair.noteFailure(err, submittedPrompt, turnOutcomeForError(err), receipts)) {
if (!ctx.flags.json) process.stderr.write("✗ Hosted credential rejected (401). Task saved. Use /auth login; /auth status shows the credential source.\n");
printed = true;
} else if (err instanceof ChatTurnError) {
Expand Down
8 changes: 4 additions & 4 deletions src/commands/command_manifest_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4056,8 +4056,8 @@ export const COMMAND_MANIFEST_SOURCE: readonly CommandManifestEntry[] = [
"aliases": [],
"compatibilityAliases": [],
"deprecatedAliases": [],
"summary": "explicitly share the last local shell result with chat (bounded)",
"detailedHelp": "/shell-result\nexplicitly share the last local shell result with chat (bounded)",
"summary": "preview and edit a bounded local shell result before sharing it",
"detailedHelp": "/shell-result previews the exact bounded attachment without contacting a model. Use /shell-result lines to inspect numbered rows; drop <first>[-<last>], replace <line> <text>, mask <literal>, or redact to edit the staged snapshot. /shell-result send shares that snapshot; /shell-result cancel discards it. In pipes or JSON sessions, /shell-result send is the explicit one-step send form. A newer command never replaces a staged preview. Redaction is an aid, not a guarantee.",
"section": "Steering",
"hidden": false,
"permissionClass": "network",
Expand All @@ -4084,8 +4084,8 @@ export const COMMAND_MANIFEST_SOURCE: readonly CommandManifestEntry[] = [
"disposition": "generated"
},
"release": {
"disposition": "new",
"note": "Coding console only; explicit sharing of at most 8 KiB of untrusted local output."
"disposition": "changed",
"note": "Coding console now previews and edits the command-bound bounded capture before explicit sharing."
}
},
{
Expand Down
Loading
Loading