Skip to content

feat: a command request explains itself in plain words, with the command behind an arrow - #61

Merged
httpsphl merged 16 commits into
mainfrom
feat/command-explanation
Sep 30, 2026
Merged

httpsphl merged 16 commits into
mainfrom
feat/command-explanation

Conversation

@httpsphl

Copy link
Copy Markdown
Owner

What changes for the owner

When a bot asks to run a command, the card in the chat used to show the raw command (a long cd ... && python - <<'EOF' ... line) with "Full input" folded under it. Someone who does not read commands allowed it without knowing what it was.

Now the card leads with what the bot says the command is for, in plain words, and keeps the command itself behind an arrow:

  • Scout wants to run a command
  • Opens every link in the report draft and lists the ones that no longer work
  • Scout wrote this. What actually runs is the command below.
  • ▸ See the command (the whole command, with its line breaks)

When the bot gave no explanation, the card says so ("Scout did not say what this command is for. If you are not sure, deny it and ask.") and the command is already open.

The tool line above the card follows the same idea: "Run a command" and the bot's words; a click opens the command and its output. The conversation list shows the same text.

Where the explanation comes from

Claude Code's Bash and PowerShell tools take an optional description next to command, written by the model (documented in the hooks reference). It reaches our permission_prompt tool unchanged. The daemon passes it along as explanation on the tool and approval chat items; summary is now always the command itself.

The generated rules (.claude/rules/botloft.md) now ask every bot to describe each command for a non-technical owner, in the language the owner writes in. Existing bots get the rule on their next start.

Security

The explanation is written by the bot, so it never stands in for the command:

  • it is shown as plain text (no markdown, no links), with a line saying the bot wrote it and that what runs is the command;
  • the command is one click away and whole: a command's input is now kept up to 32 KB (it was 4 KB, and real commands were being cut), and a longer one says it is cut;
  • Claude Code gets back the original input, as before.

What was checked with the real Claude Code (2.1.284)

Recorded in spec section 19.

  • Docs: description is optional in both shell tools.
  • A probe permission tool that logs its input and denies, on Haiku 4.5, Sonnet 5.5 and the plan default (Opus 5.5): all 32 requests carried description, the same as the tool_use block.
  • Language without the rule: short and technical; with the owner writing in Portuguese, Haiku and Opus described in Portuguese, Sonnet in English.
  • With the rule: all three wrote plain Portuguese with the reason ("Instala o pacote requests, que ajuda o Python a acessar sites").
  • Through the dev daemon and the app preview (?live), a Sonnet bot in Manual mode: the card showed the explanation, "Ver o comando" opened the whole heredoc script, Allow ran that command, and the debug log had none of the text.
  • Not guaranteed: in real use, two Sonnet bots made 28 commands with no description at all, while the other bots always sent one. I could not reproduce that in a test, so whether the new rule fixes it is not proven; the card covers the case.

Manual test

  1. cargo run -p botloftd -- serve with a dev BOTLOFT_HOME, and pnpm dev with ?live=<port> (or pnpm tauri dev).
  2. Create a crew, keep its chief in Manual mode, and ask in your own language for something that needs a command, for example "crie uma pasta chamada relatorios e grave nela um arquivo com os números de 1 a 5 usando um script Python".
  3. The card shows the explanation in your language, then "See the command". Open it: the full command is there. Allow: the command runs.
  4. After updating, restart an older bot running on Sonnet and ask for a command: check that it now explains. If it still does not, the card should say the bot did not say what the command is for, with the command open.
  5. pnpm dev alone (fake data): the Revisão bot in the Research crew shows both cards.

Not in this PR

  • Other tools keep their current look (the file or URL, with the input under "Details"). Claude Code's Monitor tool also takes a command and a description, but it was not checked and is shown like any unknown tool.

Delete is the last action of the bot's menu (header and right-click) and of the crew's menu. The confirmation names the bot or crew and says what goes (the conversation, routines and tasks, for good) and what stays (the folder, with its path). A message from a bot that left, archived or deleted, reads as from a bot no longer in the crew.
The other lists leave archived crews and bots out, so nothing showed them. archive.list returns both, the latest archived first, for the app to list and delete.
Settings gets an Archived part: the crews and the bots the owner archived, which the rest of the app does not show, with the same delete confirmation as the menus. The confirmation is now a component of its own (DeleteBot, DeleteCrew) and says "leaves" instead of "stops now" for what was already stopped.
…or good

With the shell's IFileOperation on a thread of its own (it needs a single-threaded apartment). A progress sink aborts any item the shell would destroy instead of recycling, so what the bin cannot take stays where it is. The tests use the real Recycle Bin, check the folder is in it and take it out again.
…n when asked

bots.delete and crews.delete take recycleFolder. The method answers at once; the folder moves afterwards, off the server's workers, tried again while the process that was just killed still holds its files, and folder.recycled tells the apps how it ended. Only a folder under the workspaces goes, and never one that holds a work folder the owner chose.
… Recycle Bin

Unchecked by default: the folder stays. Checked, the confirmation says the folder goes to the Recycle Bin, where it can still be got back, and a notice tells the owner if it could not go.
The bin records the long path, and the temporary folder of the CI runner comes in the short one (RUNNER~1), so the tests did not find the folder they had just moved.
Claude Code's Bash and PowerShell tools take an optional `description`
next to `command`, written by the model, and it reaches the permission
tool unchanged (checked with 2.1.284, spec 19). The daemon now passes it
along as `explanation` on the tool and approval chat items, so the app
can put plain words before the command. `summary` is the command itself
again, as the spec says.

The bot writes the explanation, so it never replaces the command: a
command's input is kept up to 32 KB instead of 4 KB, for the owner to
read all of what they allow, and Claude Code still gets back the input
it asked about.

The generated rules ask each bot to describe every command for an owner
who may not be technical, in the language the owner writes in. Without
the rule the text was terse and, on Sonnet, in English. The field stays
optional: real Sonnet bots were seen leaving it out.
The card for a request to run a command used to show the raw command,
which an owner who does not read commands allowed without knowing what
it did. It now shows what the bot says the command is for as its main
text, says the bot wrote it and that what runs is the command, and keeps
the whole command behind "See the command". With no explanation the card
says so and opens the command.

The tool line above the card shows the same words, and opens to the
command itself instead of its JSON. Other tools keep their summary, with
the input under "Details".

The preview's seed has a long heredoc command with an explanation and a
plain one without.
…onversation

Spec 7.4, 8.6, 9.2, 11.2, 11.3, 12 and the findings in section 19.
bots.setEffort restarts the bot with --effort when nothing is in progress;
the daemon asks Claude Code what the session applies and how full the
conversation is (control requests on stdin), follows each model request,
and bots.compact writes /compact. The fake daemon of the app knows the
new fields and methods; the UI comes next.
Brings in main (#52, #53, #54, #55, #59, #62, #67) and feat/delete-recycle (#56, #58, #60), so this PR merges in any order with them.

- app/src/lib/fakeBots.ts, crates/botloftd/src/state.rs, docs/spec.md: both sides kept (bot.context next to bot.deleted, crew.deleted and folder.recycled; the context store next to the trash).
- crates/botloftd/src/supervisor/spawn.rs: both sides kept, without recording the session at launch (#67 records it from the first turn).
- tests/bot_effort.rs and tests/compaction.rs: the bot has a turn before its conversation is resumed, as #67 described.
…mmand-explanation

Brings in feat/bot-effort-context (#63) with main and the delete stack, so this PR merges in any order with them.

- crates/botloft-core/src/chat.rs: a command's line in the list shows the bot's explanation, and a compaction notice still leaves the list alone.
- docs/spec.md: both sides kept (settings.json and rules\botloft.md in the bot's folder, the fields of Bot, the date of the section 19 check).
- crates/botloft-store/src/delete/tests.rs: the approval item the delete tests build has the new explanation field.
httpsphl added a commit that referenced this pull request Sep 30, 2026
Brings in feat/effort-context-app (#64), which carries #61, #63, main and the delete stack with their conflicts resolved, so this PR merges in any order with them.

- docs/spec.md: the bot folder's settings.json keeps what Claude Code does not load in the bot (this branch), and rules\botloft.md how to explain each command to the owner (#61).
@httpsphl
httpsphl enabled auto-merge September 30, 2026 17:23
@httpsphl
httpsphl merged commit 233adfe into main Sep 30, 2026
3 checks passed
@httpsphl httpsphl mentioned this pull request Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant