Shell manual — router
Use the schema for a short, deterministic command. Read one focused route before
long-lived/coding-CLI work, scheduling, an unfamiliar workflow, or recovery.
The selected dialect, policy, and working-directory sandbox remain boundaries.
A-priori root summarize defaults to true. Use it for anticipated long, bulky
output; set summarize=false for expected short results or required exact text
(for example a mandatory gate, path, field, source or diff). Give CLEAR retention
targets in reasoning: status, key fields, counts and evidence, not extra
interpretation. Narrow the read. Summaries are lossy;
the raw original remains preserved for targeted recovery.
Route by task
| Task |
Read one direct reference |
Long command/CLI; job_id, poll, cancel, reminder, completion, relaunch, or async recovery |
async jobs |
| Recurring or time-triggered work |
scheduled work |
| One future self-wakeup |
notification reminders |
| Silent, duplicated, failed, or retired scheduler |
debugging and cleanup |
| Reading, creating, or editing durable files; large or non-UTF-8 content |
Durable filesystem changes below |
| Unfamiliar dialect, working directory, timeout, or policy boundary |
First success and Settings inventory below |
| Backend-specific CLI flags, environment, or parser behavior |
daemon-manual → reference/cli-backends/SKILL.md |
First success
- For a bounded command, call
shell(action="run", input={"command": "..."}, reasoning="...") synchronously. Keep working_dir inside the sandbox; for
an external checkout, leave it at the granted root and use
cd /absolute/path && ... in the command. Dialect is fixed and policy still
applies.
- For work that may take minutes, set
input.async=true and keep the returned
job_id. On completion or a genuine health-check trigger, poll once for
exact output; do not poll to stay active. Follow up with
shell(action="poll", input={"job_id": "..."}, reasoning="..."); cancel only
within authority with shell(action="cancel", input={"job_id": "..."}, reasoning="...").
A reminder means “may still be running,” not completion; cancellation is not cleanup.
- Judge
exit_code, ok, command_status, and warning; top-level status
only records spawn/terminal handling, not inner command success. status: "error" means Shell could not run it. Prefer bounded rg --files and parse
JSONL line by line.
Unknown or late async state stays unknown: never invent an exit code,
completion, or cancellation before durable terminal truth. Keep the job ID
pollable after lease/return failure. Completion is authoritative; a reminder is
fallback. Retain artifacts unless the human authorizes cleanup. Optional
progress is channel-neutral; use task_card(action="manual", input={}).
Shell creates no watcher.
Durable filesystem changes
Shell is the one filesystem tool: there is no separate file family. The same
sandbox, policy, and authority boundaries apply to every read and write.
- Anchor the target. Work under the authorized working directory, or
cd /absolute/path && ... inside the command for an approved external
checkout. Never let a relative path or .. decide where a write lands.
- Bound every read. Inspect metadata first (
wc -lc, file, ls -l),
then read a window: sed -n '120,200p' path, head -c 20000 path,
rg -n 'pattern' path. A capped window is not the whole file; page by line
range until you reach the end you measured.
- Precondition an exact replacement. Before editing, prove the old text
exists exactly once:
rg -c -F 'old text' path must print 1. Then replace
with a tool that does not reinterpret the text (for example a short
python - <<'PY' block using str.replace(old, new, 1) on the UTF-8
content), never an unescaped sed -i over user data.
- Create or overwrite deliberately. Read the target before a full rewrite;
write with a heredoc (
cat > path <<'EOF' ... EOF) or a Python block, and
create parent directories explicitly (mkdir -p).
- Verify the mutation. Re-read the changed lines (
sed -n/rg -n) or
compare (diff) before reporting success; the write receipt is the
re-read, not the exit code alone.
- Handle binary and non-UTF-8 honestly. Check
file path first. Convert
known encodings explicitly (iconv -f gbk -t utf-8) and treat lossy
errors='replace' output as review material, not as the durable copy. Do
not describe binary, image, or audio content you did not decode.
- Rebuild when the owner requires it. Editing a durable prompt source
(
system/pad.md, system/lingtai.md, pinned references, knowledge, skills)
changes disk only; it takes effect after context(action="rebuild", input={}) or a passive refresh/molt. The owning psyche domain manual and
context(action="manual", input={}) own that procedure.
Keep private local paths out of human-facing results, and retain artifacts
unless the human authorizes cleanup.
Settings inventory
Call shell(action="settings", input={}, reasoning="inspect applied Shell settings") for read-only rows. The live schema/settings response owns values and
configurability; this section owns only procedures. SHOW has no set/reset
authority and an unavailable value fails the whole action without partial rows.
| Row |
Meaning, source and authorized change |
shell_kind |
Setup-selected posix, powershell, cmd, gitbash or wsl; valid capability value wins, then case-insensitive LINGTAI_SHELL, then platform discovery. Invalid values fall through; default is null because discovery has no universal shell. Change the capability/launcher or setup(shell_kind=...), rebuild/relaunch the owning Agent, recheck SHOW. |
sync_timeout_default_seconds |
Built-in 30, immutable; nullable call timeout uses it. A finite non-negative per-call timeout does not reconfigure it. |
sync_timeout_max_seconds |
LINGTAI_TOOL_TIMEOUT_MAX_SECONDS read each call/SHOW: positive finite values win over 120, invalid/missing values use 120, and values below 30 are floored at 30. Change only the authorized process/launcher environment; relaunch if snapshotted. Work above the ceiling uses async. |
result_max_chars |
Per-stream stdout/stderr capture limit, default 50000. Only an authorized embedding owner can pass positive ShellManager(max_output=...) before rebuilding the manager. Normal capability setup exposes no key or environment override. |
async_default |
Built-in false, immutable; input.async selects one call. |
async_reminder_default_seconds |
Built-in 1800, immutable. For one async run, input.reminder must be finite/non-negative within the platform timer bound; it is fallback, not completion. |
command_policy |
Both values stay redacted. Default (omitted config or shell: {}) is yolo. Authorized setup selects explicit yolo=true, then policy_file, then packaged policy for explicit yolo=false; rebuild/relaunch and verify SHOW. No rules/paths are disclosed. |
SHOW is strict-empty and read-only; there is no Shell settings file or mutation
verb. Verify an authorized change through its owning construction procedure and
SHOW. Result limits affect disclosure, not authority; no setting permits a
working-directory escape.
1---2name: shell-manual3description: >4 **Read before running a long-lived agent/coding CLI as a shell subprocess**,5 or before setting up cron/launchd/systemd timers or scheduled reminders.6 Routes shell-side async+poll supervision, host-scheduler setup, LingTai7 wake-by-mailbox-drop, one-shot reminders, safe cleanup, and the bounded,8 verified discipline for durable filesystem reads and edits. Per-backend CLI9 operational detail (command shapes, flags, env contracts) for daemon-backed10 CLIs lives in `daemon-manual` → `reference/cli-backends/SKILL.md`.11version: 1.16.012last_changed_at: 2026-09-14T00:00:00Z13related_files:14- src/lingtai/tools/bash/__init__.py15- src/lingtai/tools/bash/_tool_family.py16- src/lingtai/tools/bash/CONTRACT.md17- src/lingtai/tools/bash/ANATOMY.md18- src/lingtai/tools/bash/manual/reference/async-jobs/SKILL.md19- src/lingtai/tools/bash/manual/reference/scheduled-work/SKILL.md20- src/lingtai/tools/bash/manual/reference/notification-reminders/SKILL.md21- src/lingtai/tools/bash/manual/reference/debugging-cleanup/SKILL.md22- src/lingtai/tools/task_card/manual/SKILL.md23- src/lingtai/tools/daemon/manual/SKILL.md24- src/lingtai/tools/daemon/shell_prompt_events.py25maintenance: |26 Tracks the shell capability's short operational router and its nested27 references; update the route and the owning reference when shell guidance28 changes.29metadata:30 source: https://skillmd.com/skills/lingtai-ai/shell-manual31---3233# Shell manual — router3435Use the schema for a short, deterministic command. Read one focused route before36long-lived/coding-CLI work, scheduling, an unfamiliar workflow, or recovery.37The selected dialect, policy, and working-directory sandbox remain boundaries.38A-priori root `summarize` defaults to true. Use it for anticipated long, bulky39output; set `summarize=false` for expected short results or required exact text40(for example a mandatory gate, path, field, source or diff). Give CLEAR retention41targets in `reasoning`: status, key fields, counts and evidence, not extra42interpretation. Narrow the read. Summaries are lossy;43the raw original remains preserved for targeted recovery.4445## Route by task4647| Task | Read one direct reference |48|---|---|49| Long command/CLI; `job_id`, `poll`, `cancel`, reminder, completion, relaunch, or async recovery | [async jobs](reference/async-jobs/SKILL.md) |50| Recurring or time-triggered work | [scheduled work](reference/scheduled-work/SKILL.md) |51| One future self-wakeup | [notification reminders](reference/notification-reminders/SKILL.md) |52| Silent, duplicated, failed, or retired scheduler | [debugging and cleanup](reference/debugging-cleanup/SKILL.md) |53| Reading, creating, or editing durable files; large or non-UTF-8 content | Durable filesystem changes below |54| Unfamiliar dialect, working directory, timeout, or policy boundary | First success and Settings inventory below |55| Backend-specific CLI flags, environment, or parser behavior | `daemon-manual` → `reference/cli-backends/SKILL.md` |5657## First success58591. For a bounded command, call `shell(action="run", input={"command": "..."},60 reasoning="...")` synchronously. Keep `working_dir` inside the sandbox; for61 an external checkout, leave it at the granted root and use62 `cd /absolute/path && ...` in the command. Dialect is fixed and policy still63 applies.642. For work that may take minutes, set `input.async=true` and keep the returned65 `job_id`. On completion or a genuine health-check trigger, poll once for66 exact output; do not poll to stay active. Follow up with67 `shell(action="poll", input={"job_id": "..."}, reasoning="...")`; cancel only68 within authority with `shell(action="cancel", input={"job_id": "..."}, reasoning="...")`.69 A reminder means “may still be running,” not completion; cancellation is not cleanup.703. Judge `exit_code`, `ok`, `command_status`, and `warning`; top-level `status`71 only records spawn/terminal handling, not inner command success. `status:72 "error"` means Shell could not run it. Prefer bounded `rg --files` and parse73 JSONL line by line.7475Unknown or late async state stays unknown: never invent an exit code,76completion, or cancellation before durable terminal truth. Keep the job ID77pollable after lease/return failure. Completion is authoritative; a reminder is78fallback. Retain artifacts unless the human authorizes cleanup. Optional79progress is channel-neutral; use `task_card(action="manual", input={})`.80Shell creates no watcher.8182## Durable filesystem changes8384Shell is the one filesystem tool: there is no separate file family. The same85sandbox, policy, and authority boundaries apply to every read and write.86871. **Anchor the target.** Work under the authorized working directory, or88 `cd /absolute/path && ...` inside the command for an approved external89 checkout. Never let a relative path or `..` decide where a write lands.902. **Bound every read.** Inspect metadata first (`wc -lc`, `file`, `ls -l`),91 then read a window: `sed -n '120,200p' path`, `head -c 20000 path`,92 `rg -n 'pattern' path`. A capped window is not the whole file; page by line93 range until you reach the end you measured.943. **Precondition an exact replacement.** Before editing, prove the old text95 exists exactly once: `rg -c -F 'old text' path` must print `1`. Then replace96 with a tool that does not reinterpret the text (for example a short97 `python - <<'PY'` block using `str.replace(old, new, 1)` on the UTF-898 content), never an unescaped `sed -i` over user data.994. **Create or overwrite deliberately.** Read the target before a full rewrite;100 write with a heredoc (`cat > path <<'EOF' ... EOF`) or a Python block, and101 create parent directories explicitly (`mkdir -p`).1025. **Verify the mutation.** Re-read the changed lines (`sed -n`/`rg -n`) or103 compare (`diff`) before reporting success; the write receipt is the104 re-read, not the exit code alone.1056. **Handle binary and non-UTF-8 honestly.** Check `file path` first. Convert106 known encodings explicitly (`iconv -f gbk -t utf-8`) and treat lossy107 `errors='replace'` output as review material, not as the durable copy. Do108 not describe binary, image, or audio content you did not decode.1097. **Rebuild when the owner requires it.** Editing a durable prompt source110 (`system/pad.md`, `system/lingtai.md`, pinned references, knowledge, skills)111 changes disk only; it takes effect after `context(action="rebuild",112 input={})` or a passive refresh/molt. The owning `psyche` domain manual and113 `context(action="manual", input={})` own that procedure.114115Keep private local paths out of human-facing results, and retain artifacts116unless the human authorizes cleanup.117118## Settings inventory119120Call `shell(action="settings", input={}, reasoning="inspect applied Shell121settings")` for read-only rows. The live schema/settings response owns values and122configurability; this section owns only procedures. SHOW has no set/reset123authority and an unavailable value fails the whole action without partial rows.124125| Row | Meaning, source and authorized change |126|---|---|127| `shell_kind` | Setup-selected `posix`, `powershell`, `cmd`, `gitbash` or `wsl`; valid capability value wins, then case-insensitive `LINGTAI_SHELL`, then platform discovery. Invalid values fall through; default is null because discovery has no universal shell. Change the capability/launcher or `setup(shell_kind=...)`, rebuild/relaunch the owning Agent, recheck SHOW. |128| `sync_timeout_default_seconds` | Built-in 30, immutable; nullable call timeout uses it. A finite non-negative per-call timeout does not reconfigure it. |129| `sync_timeout_max_seconds` | `LINGTAI_TOOL_TIMEOUT_MAX_SECONDS` read each call/SHOW: positive finite values win over 120, invalid/missing values use 120, and values below 30 are floored at 30. Change only the authorized process/launcher environment; relaunch if snapshotted. Work above the ceiling uses async. |130| `result_max_chars` | Per-stream stdout/stderr capture limit, default 50000. Only an authorized embedding owner can pass positive `ShellManager(max_output=...)` before rebuilding the manager. Normal capability setup exposes no key or environment override. |131| `async_default` | Built-in false, immutable; `input.async` selects one call. |132| `async_reminder_default_seconds` | Built-in 1800, immutable. For one async run, `input.reminder` must be finite/non-negative within the platform timer bound; it is fallback, not completion. |133| `command_policy` | Both values stay redacted. Default (omitted config or `shell: {}`) is yolo. Authorized setup selects explicit `yolo=true`, then `policy_file`, then packaged policy for explicit `yolo=false`; rebuild/relaunch and verify SHOW. No rules/paths are disclosed. |134135SHOW is strict-empty and read-only; there is no Shell settings file or mutation136verb. Verify an authorized change through its owning construction procedure and137SHOW. Result limits affect disclosure, not authority; no setting permits a138working-directory escape.