Initial commit: shiro-neko 0.1.0-beta.1
Agentic coding CLI on Bun, Ink, and the AI SDK. Core: streamText loop with SDK-level tool approval so a denied call provably never executes; endpoint fallback for OpenAI reasoning models; retry with backoff. Tools: read/write/edit/glob/grep/bash, path-jailed, gitignore-aware, ripgrep with a JS fallback, binary rejection, live bash streaming. Agents: five variants crossing thinking level with tool restriction; plan and review withhold mutating tools from the model. Extensibility: frontmatter skills with on-demand bodies, plugin host with blocking hooks, MCP stdio and HTTP, read-only subagents. State: durable per-project memory, session task lists, session persistence, compaction that repairs provider-item dependencies. Distribution: five-platform cross-compiled binaries with checksums, install scripts, CI on three operating systems. 404 tests, typecheck clean.
This commit is contained in:
+172
@@ -0,0 +1,172 @@
|
||||
# Tools
|
||||
|
||||
## The approval model
|
||||
|
||||
Three categories.
|
||||
|
||||
**Free.** Read-only, no prompt: `read_file`, `glob`, `grep`, `task`.
|
||||
|
||||
**Session tools.** Also free, because they touch the agent's own state rather than your
|
||||
files: `todo_write`, `remember`, `recall`, `forget`, `skill`, `ask`, and anything a plugin
|
||||
marks auto-approved.
|
||||
|
||||
**Gated.** Every call stops for a decision: `write_file`, `edit_file`, `bash`, and every
|
||||
`mcp__*` tool.
|
||||
|
||||
```
|
||||
edit_file wants to run
|
||||
src/users.ts +2 -1
|
||||
export function paginate(offset: number, total: number) {
|
||||
- if (offset < total) return next();
|
||||
+ if (offset <= total) return next();
|
||||
}
|
||||
y allow once | a always allow edit_file | n deny
|
||||
```
|
||||
|
||||
`a` whitelists that tool for the rest of the session. `n` tells the model it was denied and
|
||||
to ask what to do instead. `--yolo` skips all prompts.
|
||||
|
||||
**The guard runs before all of this.** It is not an approval — it is a refusal, and `--yolo`
|
||||
does not reach it. See [plugins](plugins.md).
|
||||
|
||||
## File tools
|
||||
|
||||
### `read_file`
|
||||
|
||||
```
|
||||
path file path relative to the workspace root
|
||||
offset first line, 1-based
|
||||
limit max lines, default 2000
|
||||
```
|
||||
|
||||
Returns contents with 1-based line numbers. Refuses binaries: a NUL byte in the first 8 KB
|
||||
means the file is not text, and a model that reads a 90 MB executable has burned its whole
|
||||
context on nothing.
|
||||
|
||||
### `write_file`
|
||||
|
||||
```
|
||||
path file path
|
||||
content full contents
|
||||
```
|
||||
|
||||
New files and full rewrites only. Creates parent directories.
|
||||
|
||||
### `edit_file`
|
||||
|
||||
```
|
||||
path file path
|
||||
oldString exact text to find, whitespace and indentation included
|
||||
newString replacement
|
||||
replaceAll replace every occurrence instead of requiring exactly one
|
||||
```
|
||||
|
||||
`oldString` must match byte-for-byte and appear exactly once unless `replaceAll` is set.
|
||||
An ambiguous match is an error naming the count, which pushes the model to add surrounding
|
||||
context rather than guessing which occurrence it meant.
|
||||
|
||||
### `glob`
|
||||
|
||||
```
|
||||
pattern e.g. "src/**/*.ts"
|
||||
limit max paths, default 200
|
||||
includeIgnored also return files git ignores
|
||||
```
|
||||
|
||||
Walks the tree honouring `.gitignore` and `.shiroignore`, skipping `.git` and
|
||||
`node_modules` unconditionally. Nested ignore files apply only within their own directory,
|
||||
as git does. Returns posix paths relative to the workspace root.
|
||||
|
||||
### `grep`
|
||||
|
||||
```
|
||||
pattern regex source
|
||||
include glob limiting the search, default "**/*"
|
||||
ignoreCase case-insensitive
|
||||
includeIgnored also search files git ignores
|
||||
```
|
||||
|
||||
Shells out to ripgrep when it is on PATH — roughly 15x faster on a real repo — and falls
|
||||
back to a JavaScript walker otherwise. Output is `path:line: text` either way, so the model
|
||||
sees one format regardless. Skips binaries. Caps at 200 hits.
|
||||
|
||||
### `bash`
|
||||
|
||||
```
|
||||
command shell command
|
||||
timeout ms, default 120000, max 600000
|
||||
```
|
||||
|
||||
Runs in the workspace root through `bash -lc` or `cmd /c`. Output streams live to the panel
|
||||
above the input rather than appearing all at once when the command exits — a two-minute test
|
||||
run is otherwise indistinguishable from a hang. Both pipes are drained concurrently, since a
|
||||
command that fills one while you block on the other deadlocks.
|
||||
|
||||
Returns exit code, stdout, stderr, and a note if a signal killed it.
|
||||
|
||||
## Agent tools
|
||||
|
||||
### `task`
|
||||
|
||||
```
|
||||
description short label shown to you
|
||||
prompt self-contained instructions
|
||||
kind "explore" (default) or "review"
|
||||
```
|
||||
|
||||
Spawns a read-only subagent with `read_file`, `glob`, and `grep` only. It returns one
|
||||
report, so the parent pays for findings rather than the whole search transcript. It sees
|
||||
none of the parent conversation, so its prompt has to stand alone.
|
||||
|
||||
`explore` finds and reports. `review` critiques code in severity order. Progress streams to
|
||||
the subagent panel.
|
||||
|
||||
### `ask`
|
||||
|
||||
```
|
||||
question one specific question
|
||||
options choices, recommendation first, each with an optional detail
|
||||
multiple allow more than one
|
||||
```
|
||||
|
||||
Stops the turn and puts the question on screen. With options it is a picker; without, free
|
||||
text. `esc` skips, which tells the model to decide and state its assumption.
|
||||
|
||||
Withheld entirely in headless mode — a question with no one to answer it would hang.
|
||||
|
||||
### `todo_write`
|
||||
|
||||
```
|
||||
todos the complete list: content, status, optional note
|
||||
```
|
||||
|
||||
Statuses: `pending`, `in_progress`, `done`, `blocked`. Send the whole list each time; it
|
||||
replaces the previous one. Warns when more than one task is `in_progress`, when nothing is
|
||||
`in_progress` while work remains, or when a `blocked` task has no note.
|
||||
|
||||
### `remember`, `recall`, `forget`
|
||||
|
||||
Durable per-project notes. See [memory](memory.md).
|
||||
|
||||
### `skill`
|
||||
|
||||
```
|
||||
name skill name from the catalogue
|
||||
```
|
||||
|
||||
Loads the body of a skill. See [skills](skills.md).
|
||||
|
||||
## Path safety
|
||||
|
||||
Every path a tool receives goes through a jail: resolved against the workspace root, then
|
||||
checked that it did not escape. `../../etc/passwd` and absolute paths outside the root are
|
||||
both refused before any filesystem call.
|
||||
|
||||
The model's output is a trust boundary. It can emit any string, so the check happens on
|
||||
every call rather than being assumed.
|
||||
|
||||
## Output caps
|
||||
|
||||
Any single tool result is truncated at 30,000 characters with a note saying how much was
|
||||
cut. `grep` stops at 200 hits, `glob` at 200 paths, `read_file` at 2000 lines by default.
|
||||
Without caps one `grep` for `function` can end a session.
|
||||
Reference in New Issue
Block a user