release 1.0.0: cost control, 41 tools, 29 skills, custom commands, auto-load

This commit is contained in:
Muhammad Zakir Ramadhan
2026-09-07 19:59:58 +07:00
parent a22d8e13b1
commit ffa9a02c26
97 changed files with 4791 additions and 788 deletions
+24 -3
View File
@@ -20,8 +20,10 @@ Written by `/provider`, editable by hand. Every field is optional.
"agent": "default",
"thinking": "medium",
"maxRetries": 3,
"maxSpendUsd": 5,
"subagentModel": "gpt-5-nano",
"plugins": ["guard", "time"],
"toolSets": ["edit-plus", "git"],
"toolSets": ["edit-plus", "extra", "git"],
"permission": {
"bash": { "*": "ask", "git *": "allow" }
},
@@ -42,12 +44,31 @@ Written by `/provider`, editable by hand. Every field is optional.
| `agent` | default variant: `default`, `quick`, `deep`, `plan`, `review` |
| `thinking` | default level: `off`, `low`, `medium`, `high`, `max` |
| `maxRetries` | retries per model call for transient failures. Default 3 |
| `plugins` | which builtin plugins to enable. Omit for `["guard", "secrets", "protect", "time"]` |
| `toolSets` | optional tool sets beyond `core`: `edit-plus`, `git`, and `net`. Omit for the defaults; `net` is opt-in. See [tools](tools.md) |
| `maxSpendUsd` | session spend ceiling: warn at 80%, refuse the next turn at 100%. Headless exits non-zero naming the ceiling. Only enforced on priced models |
| `subagentModel` | model id for `explore` subagents, which search rather than reason. Omit to share the parent's model. `/cost` reports subagent spend separately |
| `plugins` | which builtin plugins to enable. Omit for `["guard", "secrets", "protect", "time", "no-force-push", "no-net-pipe", "no-root", "no-env-write"]` |
| `toolSets` | optional tool sets beyond `core`: `edit-plus`, `nav`, `extra`, `git`, and `net`. Omit for the defaults; `net` is opt-in. See [tools](tools.md) |
| `permission` | which calls run, ask, or are refused, matched per command or path. See [permissions](permissions.md) |
| `registryUrl` | index for `/registry`. Omit for the default. See [registry](registry.md) |
| `mcpServers` | see [MCP](mcp.md) |
## Directories
Beyond the config file, these locations are read on every start:
| Path | Holds |
|---|---|
| `~/.shiro-neko/config.json` | the config above |
| `~/.shiro-neko/skills/*.md` `.shiro/skills/*.md` | auto-loaded skills — [extensions](extensions.md) |
| `~/.shiro-neko/tools/*.json` `.shiro/tools/*.json` | auto-loaded tool manifests |
| `~/.shiro-neko/plugins/*.json` `.shiro/plugins/*.json` | auto-loaded plugin manifests |
| `~/.shiro-neko/commands/*.md` `.shiro/commands/*.md` | custom slash commands — [custom commands](custom-commands.md) |
| `~/.shiro-neko/registry/{skills,plugins}/` | entries installed with `/registry` |
| `~/.shiro-neko/sessions/` | saved sessions, for `-c` / `-r` |
`SHIRO_HOME` overrides the home directory for all of these, which is also how the test suite
isolates itself.
## Provider presets
`/provider` offers these. Each sets `baseURL` and the wire protocol for you.
+76
View File
@@ -0,0 +1,76 @@
# Custom slash commands
A Markdown file becomes a slash command. Write the prompt once, run it with `/name` any time.
Two directories are scanned, the project shadowing the user by name:
| Origin | Directory |
|---|---|
| user | `~/.shiro-neko/commands/*.md` |
| project | `.shiro/commands/*.md` |
The filename is the command: `.shiro/commands/review-diff.md` becomes `/review-diff`. Names are
letters, digits, dashes, and underscores; anything else is skipped. A custom command can never
shadow a built-in — `/cost` always runs the built-in `/cost`.
## Format
```markdown
---
description: Review the staged diff for defects
agent: review
---
Review the staged changes. For each finding give file, line, what breaks, and the fix.
```
Frontmatter is optional but useful:
- **`description`** — the one line shown in the `/` menu. Without it the first body line is used.
- **`agent`** — run this command under a specific agent variant (`default`, `quick`, `deep`,
`plan`, `review`). The variant is restored afterwards, so one command does not leak its agent
into the rest of the session.
Everything after the frontmatter fence is the prompt. A file with an empty body is skipped, as
is one that fails to parse.
## Arguments
The body is a template, expanded against whatever you type after the command:
- `$ARGUMENTS` — the whole argument string.
- `$1`, `$2`, … — positional arguments. A missing positional expands to nothing.
```markdown
Compare $1 against $2 and report the differences. Context: $ARGUMENTS
```
`/compare src/a.ts src/b.ts` sends `Compare src/a.ts against src/b.ts … Context: src/a.ts src/b.ts`.
## Shell substitution
A `` !`command` `` inline runs the shell command and inlines its output before the prompt is
sent:
```markdown
Review this diff:
!`git diff --staged`
```
Every substitution runs through the **guard** before executing, exactly as a direct `bash` call
is — so a custom command cannot smuggle a destructive command past you. A substitution that
exits non-zero, or one the guard refuses, fails the command with the reason named.
## When to write one
- A prompt you find yourself retyping: a review shape, a release checklist, a project-specific
"how we test".
- A prompt that should pin an agent: a read-only review command that always runs under `review`.
- Project conventions the whole team should share: commit `.shiro/commands/` so everyone gets
the same commands.
For behaviour that must survive across sessions rather than be invoked on demand, use
[memory](memory.md). For instructions the agent loads by task rather than by name, use a
[skill](skills.md). For extensions that add tools or refusal rules rather than prompts, see
[extensions](extensions.md).
+101
View File
@@ -0,0 +1,101 @@
# Extensions: auto-loaded skills, tools, and plugins
External extensions load automatically from two directories on every start, the project
shadowing the user by name:
| Origin | Directories |
|---|---|
| user | `~/.shiro-neko/skills` `~/.shiro-neko/tools` `~/.shiro-neko/plugins` |
| project | `.shiro/skills` `.shiro/tools` `.shiro/plugins` |
Drop a file in and it is live on the next start. No registry, no install command, no restart
of anything but the CLI itself.
**Everything here is data, never code.** That is the same rule the [registry](registry.md)
enforces, and it is the whole security model. An external extension can add instructions, a
bounded tool, or a refusal rule — it cannot run arbitrary code, so it cannot read every file
the agent can read or lie about what it blocks. A malformed file is reported on the welcome
dashboard and skipped, never fatal.
## Skills
A skill is a Markdown file with frontmatter, exactly like a bundled one:
```markdown
---
name: deploy
description: Ship a release. Use when asked to deploy or cut a release.
---
# Deploy
1. Confirm the tests pass. Do not deploy on a red suite.
2. Tag with the version from src/version.ts, not by hand.
```
Skills merge by name with the precedence `builtin < registry < user < project`, so your own
`debug.md` overrides the bundled `debug`. See [skills](skills.md) for the full format.
## Tools
A tool is a JSON manifest describing one bounded operation. Three kinds, each with a ceiling
on what it can do:
```json
{
"name": "recent-changes",
"description": "List the ten most recently changed files",
"kind": "shell",
"command": "git diff --name-only HEAD~10",
"autoApprove": true
}
```
| Field | Meaning |
|---|---|
| `name` | The tool name the model calls. Letters, digits, dashes, underscores. |
| `description` | What the model reads to decide when to use it. |
| `kind` | `shell`, `http`, or `read`. |
| `command` | For `shell`: the template to run, with an optional `{arg}` placeholder. |
| `url` | For `http`: the URL to fetch, with an optional `{arg}` placeholder. HTTPS only. |
| `path` | For `read`: the workspace file to return, with an optional `{arg}` placeholder. |
| `autoApprove` | `false` to require approval before running. Default `true`. |
The model passes a single optional `arg` string, substituted into `{arg}`.
**The limits are the point.** A `shell` tool runs a fixed template through the **guard** and
the platform shell — the same chain a built-in `bash` call goes through, so an installed tool
cannot do what the agent itself may not. An `http` tool fetches one HTTPS URL. A `read` tool
returns one workspace file, jailed to the workspace. None of them executes code from the
manifest.
## Plugins
A plugin is a refusal manifest — the same shape the registry installs — a name, an optional
prompt appendix, and deny rules matched against tool input:
```json
{
"name": "no-prod-config",
"description": "refuses to edit production config",
"appendix": "Production config is changed by hand, never by the agent.",
"deny": [
{ "tools": ["write_file", "edit_file"], "pathPattern": "config/production", "reason": "production config is hand-edited" },
{ "tools": ["bash"], "commandPattern": "kubectl\\s+apply", "reason": "deploys to the cluster" }
]
}
```
A rule names the tools it covers and either a `pathPattern` (matched against the path a file
tool carries) or a `commandPattern` (matched against a `bash` command), both as case-insensitive
regexes, plus the `reason` handed to the model when it blocks. Patterns are validated on load;
an invalid regex is a reported error, not a crash.
Refusal plugins compose with the built-in [plugins](plugins.md) — the first block wins.
## Relationship to the registry
The [registry](registry.md) fetches the same kinds of files over HTTPS with a confirmation
step. Auto-load is for your own and your project's files, which need no confirmation because
you wrote them. The two mechanisms share the loaders and the safety model; they differ only in
where the file comes from.
+44 -1
View File
@@ -19,7 +19,7 @@ the agent can read. That is a sandbox problem, not a loader problem — see
## Enabling
```json
{ "plugins": ["guard", "secrets", "protect", "time"] }
{ "plugins": ["guard", "secrets", "protect", "time", "no-force-push", "no-net-pipe", "no-root", "no-env-write"] }
```
That is also the default when the field is absent, and it lists **builtin** plugins only.
@@ -128,6 +128,49 @@ write normally.
Adds `current_time`, returning ISO 8601 plus the local string. Auto-approved; it reads
nothing. Useful because models are confidently wrong about the date.
### The narrow safety refusals (default on)
Six small plugins, each blocking one irreversible class of mistake. They are on by default for
the same reason the guard is: a safety check you have to opt into is not one. Each is data — a
name, a pattern list, and a refusal message — matched against the `bash` command string (or, for
`confirm-delete`, the path a `delete_file` carries).
| Plugin | Refuses | Why |
|---|---|---|
| `no-force-push` | `git push --force`, `--force-with-lease`, `-f`, `+<ref>` | rewrites remote history |
| `no-net-pipe` | `curl … \| sh`, `wget … \| node`, `iex (iwr …)` | executes a download unseen |
| `no-root` | `sudo …`, elevated `runas` / `Start-Process -Verb RunAs` | nothing the agent does should need root |
| `no-env-write` | `export …KEY/TOKEN/SECRET/PASSWORD=…` | writes a credential into the environment |
| `no-main-commit` | `git commit`/`git merge` naming `main`/`master` | touches the default branch directly |
| `no-git-config` | `git config --global`, identity/runner keys | changes how git identifies or runs |
Normal commands pass: `git push origin feature`, `npm test`, `export NODE_ENV=production`. The
patterns target the irreversible act, not the command family.
### The advisory plugins (opt in)
Three plugins carry only a prompt appendix — no blocking hook — so they shape behaviour without
ever refusing a call. Enable them in config when you want the nudge:
| Plugin | Advises |
|---|---|
| `conventional-commit` | commit subjects as `type(scope): summary`, e.g. `fix(auth): reject expired tokens` |
| `tests-first` | for a bug, pin it with a failing test before fixing; watch it fail, then pass |
| `small-diffs` | one change does one thing; split a diff that is really two |
```json
{ "plugins": ["guard", "secrets", "protect", "time", "conventional-commit", "small-diffs"] }
```
### `confirm-delete` (opt in)
Refuses `delete_file` calls whose path is broad or ambiguous — a wildcard, a trailing slash, or
an empty path — so a delete is always one explicit file:
```
refusing to delete "src/*" (ambiguous or broad). Delete one explicit file.
```
### `bell` (opt in)
Writes `\u0007` to stderr when a turn ends. Off by default — a bell after every turn is
+15 -6
View File
@@ -3,9 +3,9 @@
A skill is a markdown file with instructions for one kind of task. Only its name and
description sit in the system prompt; the body is loaded on demand.
That split matters. The nine bundled skills are roughly 14,000 characters of body against about
1,500 characters of catalogue — paid on every request. Putting every body in the prompt
would cost that on every turn, for instructions relevant to one turn in twenty.
That split matters. The twenty-nine bundled skills are tens of thousands of characters of body
against a small catalogue of names and descriptions — paid on every request. Putting every body
in the prompt would cost that on every turn, for instructions relevant to one turn in twenty.
## Format
@@ -88,9 +88,18 @@ thing at a time, and stop at a target stated up front. Report the baseline along
CI, Dockerfiles, and docs), apply one shape of change rather than improving as you pass, and
never hand-merge a lockfile.
They are string constants in `src/skills-builtin.ts` rather than files, because
`bun build --compile` only embeds modules reachable through imports. A directory of `.md`
files would be missing from the shipped binary.
**`plan`** — break a non-trivial task into an ordered, verifiable sequence before writing code:
order by dependency rather than by file, one step one verifiable outcome, keep it small, and
replan when the ground moves.
**`docs`** — write documentation grounded in the source: verify every claim against the code,
answer the reader's actual question, show a working example before describing one, and match
the house style.
They are Markdown files in `src/skills-md/`, one per skill, loaded by `src/skills-builtin.ts`
as Bun raw-text imports. The `.md` file is the single source of truth — frontmatter and body
in proper Markdown — and Bun inlines every text import into the compiled binary, so the folder
ships with `bun build --compile` rather than being left behind on disk.
## How the agent uses one
+56 -1
View File
@@ -46,7 +46,7 @@ Three more things sit around the rules:
## Tool sets
Each tool costs its name, its description, and its JSON schema on **every request**. The current
registry has nineteen built-ins. `/tools` shows the live set; disabling an optional set removes
registry has forty-one built-ins. `/tools` shows the live set; disabling an optional set removes
its schemas from both the request and the system prompt.
| Tool | Bytes | Tool | Bytes |
@@ -68,6 +68,8 @@ Sets let you switch off what a project does not need:
|---|---|---|
| `core` | `read_file` `write_file` `edit_file` `glob` `grep` `bash` | ~2,993 B |
| `edit-plus` | `multi_edit` `list_dir` `read_many_files` `apply_patch` `move_file` `delete_file` | patch and file ops |
| `nav` | `find_symbol` `json_query` | navigation and structured reads |
| `extra` | 20 tools: line edits, fs inspect, git extensions, code/env reads | on by default |
| `git` | `git_status` `git_diff` `git_log` `git_show` `git_blame` `git_branch` `git_commit_message` | ~2,180 B + message |
| `net` | `web_fetch` | opt in |
@@ -107,6 +109,59 @@ Both extra sets earn their place in most projects, but not all:
- **Reading a lot, editing rarely?** Keep `edit-plus` for `list_dir` and `read_many_files`
alone; they pay for themselves in round trips saved.
## The `extra` set
Twenty tools across four families, on by default. Each follows the same rules as the core
tools: writes are jailed to the workspace, reads honour `.gitignore`, and every git call spawns
the binary with a fixed argument array, never a shell string.
### Line edits
Precise edits by line number, for changes that need no full-file rewrite and no exact-string
match. All refuse a path outside the workspace.
| Tool | Does |
|---|---|
| `insert_lines` | Insert a block before a 1-based line, pushing the rest down. One past the end appends. |
| `delete_lines` | Delete an inclusive line range. Refuses the whole file — that is `delete_file`'s job. |
| `replace_lines` | Replace an inclusive line range with new text in one write. |
| `append_file` | Add text to the end of a file. |
| `prepend_file` | Add text to the top of a file, e.g. a header or import block. |
| `count_lines` | Line count for one file, or per file across a glob. A size read before opening something large. |
### Filesystem
| Tool | Does |
|---|---|
| `tree` | Indented directory tree, ignore-aware, directories first. A broad shape faster to scan than `list_dir`. |
| `file_info` | Size, line count, modified time, text-or-binary for one file. |
| `find_files` | Files whose *name* contains a substring (not a glob), e.g. `auth`. |
| `recent_files` | Files modified most recently, newest first. Find what a tool just touched. |
| `changed_files` | The working-tree delta git reports (modified, staged, untracked). |
### Git extensions (read-only)
Spawned with a fixed argv, so they are auto-approved like the core git tools.
| Tool | Does |
|---|---|
| `git_log_file` | Commits that touched one file, newest first, with hash, date, subject. |
| `git_diff_commits` | Diff between two refs, optionally limited to one path. |
| `git_show_file` | A file's contents at a ref, e.g. `auth.ts` at `HEAD~3`. |
| `git_current_branch` | The current branch with its upstream and ahead/behind count. |
| `git_changed_in_ref` | Files changed between a ref and the working tree, names only. |
### Code and environment
| Tool | Does |
|---|---|
| `find_symbol` | Where a function, class, or type is *defined* across JS/TS, Python, Go, Rust. Matches declarations, not uses. |
| `json_query` | One value from a JSON file by dotted path (`scripts.build`), instead of reading it whole. |
| `outline` | Top-level declarations of a source file as a structural map. Read before opening a large file. |
| `read_symbol` | The full body of one top-level definition by name. |
| `env_info` | Platform, shell, and which runtimes and package managers are installed, before writing a command. |
| `count_tokens` | Estimate the token cost of a file or string (~4 chars per token) before sending it to the model. |
## File tools
### `read_file`