sigit-code 1.4.1__tar.gz → 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/AGENTS.md +64 -12
- sigit_code-1.5.0/.github/workflows/release-mcp-registry.yml +79 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/AGENTS.md +64 -12
- {sigit_code-1.4.1 → sigit_code-1.5.0}/CHANGELOG.md +59 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/CLAUDE.md +64 -12
- {sigit_code-1.4.1 → sigit_code-1.5.0}/Cargo.lock +1 -1
- {sigit_code-1.4.1 → sigit_code-1.5.0}/Cargo.toml +1 -1
- {sigit_code-1.4.1 → sigit_code-1.5.0}/PKG-INFO +1 -1
- sigit_code-1.5.0/docs/hooks.md +92 -0
- sigit_code-1.5.0/docs/mcp.md +68 -0
- sigit_code-1.5.0/examples/settings-with-hooks.toml +49 -0
- sigit_code-1.5.0/server.json +28 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/chat.rs +923 -38
- sigit_code-1.5.0/src/commands.rs +436 -0
- sigit_code-1.5.0/src/frontmatter.rs +200 -0
- sigit_code-1.5.0/src/hooks.rs +507 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/main.rs +165 -19
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/mcp.rs +148 -5
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/permissions.rs +179 -18
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/settings.rs +17 -2
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/skills.rs +6 -149
- sigit_code-1.5.0/src/subagents.rs +327 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/tools.rs +530 -71
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/agent-client-protocol/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/branding/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/sigit-code-release/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.agents/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/agent-client-protocol/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/ai-assisted-coding/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/branding/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/run-sigit/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/run-sigit/driver.mjs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/run-sigit/tui-smoke.sh +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/sigit-code-release/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.claude/skills/tool-calling/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.github/workflows/ci.yml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.github/workflows/release-crates.yml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.github/workflows/release-github.yml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.github/workflows/release-homebrew.yml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.github/workflows/release-npm.yml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.github/workflows/release-pypi.yml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.gitignore +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/.nvmrc +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/LICENSE +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/README.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/examples/skills/README.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/examples/skills/commit-message/SKILL.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/README.md.tmpl +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/package-main.json.tmpl +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/package.json.tmpl +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/scripts/render-main-package.cjs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/scripts/render-platform-package.cjs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/sigit/.gitignore +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/sigit/README.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/sigit/package.json +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/sigit/src/index.ts +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/npm/sigit/tsconfig.json +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/pypi/README.md +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/pypi/pyproject.toml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/pyproject.toml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/rust-toolchain.toml +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/account.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/backend.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/credentials.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/headless.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/instructions.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/models.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/provider.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/session_store.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/src/setup.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/tests/acp_permissions.rs +0 -0
- {sigit_code-1.4.1 → sigit_code-1.5.0}/tests/headless_mode.rs +0 -0
|
@@ -110,7 +110,23 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
110
110
|
spec list (`all_tools`) and the execute `match` (`execute_tool`). `run_command` also enforces
|
|
111
111
|
commit attribution: when a command creates a new commit that lacks the
|
|
112
112
|
`Co-Authored-By: siGit Code` trailer (`COMMIT_CO_AUTHOR_TRAILER`), it amends the trailer in —
|
|
113
|
-
unless the commit already exists on a remote, which is never rewritten.
|
|
113
|
+
unless the commit already exists on a remote, which is never rewritten. Also owns the `task`
|
|
114
|
+
tool: a nested agent loop in a fresh conversation, offered only when `subagent_available()`
|
|
115
|
+
(a subagent factory is registered — see `register_subagent_factory_for` in `main.rs`; on-device
|
|
116
|
+
registers a `None`-returning factory since onde has a single shared history). A subagent's
|
|
117
|
+
toolset is a hard-gated read-only allow-list (`SUBAGENT_TOOL_NAMES`) that is never expanded by a
|
|
118
|
+
configurable subagent type (see `src/subagents.rs`) — only ever narrowed — so a `.sigit/agents/*.md`
|
|
119
|
+
file can't grant itself `edit_file`/`run_command` and bypass the permission system. Also owns
|
|
120
|
+
`web_search`: a thin native wrapper around the official MCP server's Brave-Search-backed
|
|
121
|
+
`web_search` tool (`mcp__sigit__web_search`, implemented server-side in `sigit-si`'s
|
|
122
|
+
`WebSearchService` + `Mcp::Tools::WebSearch`), offered only when that MCP tool was actually
|
|
123
|
+
discovered (i.e. the user is signed in to siGit Code Cloud — see `web_search_available`).
|
|
124
|
+
Wrapping it natively rather than leaving it as a raw `mcp__*` tool matters for two reasons:
|
|
125
|
+
`permissions::classify` treats every `mcp__*` tool as mutating (an "ask" prompt on every call),
|
|
126
|
+
while `web_search` is read-only like `read_website`; and the raw delegate name is filtered out
|
|
127
|
+
of the assembled tool list (`is_web_search_delegate`) so the model sees one clean option, not
|
|
128
|
+
two names for the same tool. Execution still forwards verbatim to `mcp::call_tool` — no second
|
|
129
|
+
HTTP/JSON-RPC implementation.
|
|
114
130
|
- **`src/skills.rs`** — [Agent Skills](https://agentskills.io) support. Discovers skill
|
|
115
131
|
folders (each with a `SKILL.md`: YAML frontmatter `name` + `description`, then Markdown
|
|
116
132
|
instructions) from `.sigit/skills/` and `.claude/skills/` in the cwd, `$SIGIT_CONFIG_DIR/skills/`,
|
|
@@ -119,6 +135,31 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
119
135
|
calls `skill` with a name) loads the full `SKILL.md` body. The `skill` tool is appended in the
|
|
120
136
|
`*_as_specs`/`build_tool_specs` layer (not in `all_tools()`) so its description can be dynamic,
|
|
121
137
|
and only when at least one skill exists.
|
|
138
|
+
- **`src/commands.rs`** — user-defined slash commands. Discovers Markdown files (each an
|
|
139
|
+
optional YAML frontmatter block — `description`, `argument-hint` — followed by a prompt-template
|
|
140
|
+
body) from `.sigit/commands/` and `.claude/commands/` in the cwd, `$SIGIT_CONFIG_DIR/commands/`,
|
|
141
|
+
and `~/.claude/commands/`. A subdirectory namespaces the command with `:`
|
|
142
|
+
(`.sigit/commands/git/commit.md` → `/git:commit`). Unlike skills there's no tool-call
|
|
143
|
+
indirection: invoking one works exactly like the built-in `/init` — `commands::render`
|
|
144
|
+
substitutes `$ARGUMENTS`/`$1..$9` in the body against whatever followed the command on the
|
|
145
|
+
input line, and the result is fed to the model as a normal turn through the ordinary tools and
|
|
146
|
+
permission checks. Resolution happens where each surface (`main.rs`/`chat.rs`) already
|
|
147
|
+
special-cases `/init`: an unrecognized slash command is tried against `commands::resolve_command`
|
|
148
|
+
before falling back to "unknown command". A custom command sharing a name with a built-in is
|
|
149
|
+
unreachable (built-ins match first in `parse_slash`) and is skipped when advertised to ACP
|
|
150
|
+
clients, with a warning logged.
|
|
151
|
+
- **`src/subagents.rs`** — configurable subagent types for the `task` tool. Discovers Markdown
|
|
152
|
+
files (YAML frontmatter `name` + `description`, optional comma-separated `tools:` allow-list,
|
|
153
|
+
then a Markdown body that becomes the subagent's system prompt) from `.sigit/agents/` and
|
|
154
|
+
`.claude/agents/` in the cwd, `$SIGIT_CONFIG_DIR/agents/`, and `~/.claude/agents/`. Passing a
|
|
155
|
+
type's `name` as `task`'s `subagent_type` argument swaps in that system prompt and, if `tools:`
|
|
156
|
+
is set, narrows the offered toolset to its *intersection* with `SUBAGENT_TOOL_NAMES` — the
|
|
157
|
+
security-relevant narrowing logic lives in `tools.rs` next to that constant, not here; this
|
|
158
|
+
module only discovers and parses files. `SubagentFactory` (in `tools.rs`) takes the resolved
|
|
159
|
+
system prompt per call rather than baking one in at registration, so a single registered
|
|
160
|
+
factory serves both the default research subagent and every configured type.
|
|
161
|
+
- **`src/frontmatter.rs`** — shared "YAML frontmatter + Markdown body" parsing used by both
|
|
162
|
+
`src/skills.rs` (`SKILL.md`) and `src/commands.rs` (`.sigit/commands/*.md`).
|
|
122
163
|
- **`src/mcp.rs`** — [Model Context Protocol](https://modelcontextprotocol.io) *client*. Two
|
|
123
164
|
transports: **Streamable HTTP** (one JSON-RPC POST endpoint, `url` in `mcp.toml`; replies are
|
|
124
165
|
`application/json` or SSE) and **stdio** (`command` + optional `args`/`[server.env]` in
|
|
@@ -131,12 +172,20 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
131
172
|
both read it; `/reload` does *not* re-run it, so config changes need a restart. stdio children
|
|
132
173
|
live for the process; a dead child fails calls with an in-band error string (no auto-restart).
|
|
133
174
|
Tools are namespaced `mcp__<server>__<tool>`, appended in the `*_as_specs`/`build_tool_specs`
|
|
134
|
-
layer and routed in `tools::execute_tool` via `mcp::is_mcp_tool`.
|
|
135
|
-
(`<cloud>/mcp`, default `https://sigit.si/api/v1/mcp
|
|
136
|
-
with the cloud session token
|
|
175
|
+
layer and routed in `tools::execute_tool` via `mcp::is_mcp_tool`. Two servers are baked in:
|
|
176
|
+
the official server (`<cloud>/mcp`, default `https://sigit.si/api/v1/mcp`, always HTTP, authed
|
|
177
|
+
with the cloud session token) and the smbCloud CLI server (`smb --mcp`, stdio, added only when
|
|
178
|
+
the `smb` binary is on `PATH`; opt out with `smbcloud = false` in `mcp.toml` or
|
|
179
|
+
`SIGIT_MCP_SMBCLOUD=off`). A user-defined entry named `sigit` or `smbcloud` overrides the
|
|
180
|
+
corresponding baked-in one. Extra servers live in `mcp.toml` (global
|
|
137
181
|
`$SIGIT_CONFIG_DIR/mcp.toml` and project-local `.sigit/mcp.toml`). The stdio path is covered by
|
|
138
182
|
`tests/mcp_stdio.rs`, driven by the test-only `src/bin/mcp_stdio_stub.rs` helper binary
|
|
139
|
-
(excluded from the published crate via `exclude` in `Cargo.toml`).
|
|
183
|
+
(excluded from the published crate via `exclude` in `Cargo.toml`). The baked-in official
|
|
184
|
+
server is *also* listed in the public MCP Registry as `si.sigit/sigit` — a **remote**
|
|
185
|
+
Streamable-HTTP listing (`server.json` at the repo root, published by
|
|
186
|
+
`release-mcp-registry.yml`). Because it's a remote server, the registry's URL-match rule
|
|
187
|
+
forces a domain namespace (`si.sigit` ↔ `sigit.si`) verified by a DNS TXT record, not the
|
|
188
|
+
GitHub-OIDC scheme `smbcloud-cli` uses for its package listing.
|
|
140
189
|
- **`src/permissions.rs`** — tool permission policy. Every tool call passes through
|
|
141
190
|
`decision_for` before executing: read-only tools always run; mutating tools (and all
|
|
142
191
|
`mcp__*`/unknown tools) are governed by, in order: per-session plan mode (`/plan` — deny all
|
|
@@ -165,12 +214,14 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
165
214
|
- **`src/credentials.rs`** — local session-token store (TOML, `0600` on Unix).
|
|
166
215
|
- **`src/models.rs`** — model-picker types shared across platforms.
|
|
167
216
|
|
|
168
|
-
Slash commands (`/help`, `/models`, `/skills`, `/
|
|
169
|
-
`/plan`, `/permissions`, `/init`, `/clear`, `/status`) are advertised via
|
|
170
|
-
`main.rs` and handled in both the TUI and ACP sessions. `/init` is
|
|
171
|
-
directly it substitutes `instructions::INIT_PROMPT` for the user
|
|
172
|
-
turn that explores the repo and writes (or improves) `AGENTS.md`
|
|
173
|
-
permission checks.
|
|
217
|
+
Slash commands (`/help`, `/models`, `/skills`, `/agents`, `/commands`, `/mcp`, `/login`, `/logout`,
|
|
218
|
+
`/whoami`, `/reload`, `/plan`, `/permissions`, `/init`, `/clear`, `/status`) are advertised via
|
|
219
|
+
`advertise_commands` in `main.rs` and handled in both the TUI and ACP sessions. `/init` is
|
|
220
|
+
special: instead of replying directly it substitutes `instructions::INIT_PROMPT` for the user
|
|
221
|
+
text and runs a normal agent turn that explores the repo and writes (or improves) `AGENTS.md`
|
|
222
|
+
through the ordinary tools and permission checks. User-defined commands (see `src/commands.rs`)
|
|
223
|
+
get the same treatment via the `SlashCommand::Unknown` fallback path, so anyone can add their own
|
|
224
|
+
`/name` commands without touching the built-in command list.
|
|
174
225
|
|
|
175
226
|
## Model cache (macOS)
|
|
176
227
|
|
|
@@ -190,7 +241,8 @@ verbosity with `RUST_LOG`.
|
|
|
190
241
|
|
|
191
242
|
`OPENAI_BASE_URL` / `OPENAI_API_KEY` (provider override), `SIGIT_API_URL` (account API base,
|
|
192
243
|
default `https://sigit.si`), `SIGIT_CLOUD_URL`, `SIGIT_CONFIG_DIR` (default `~/.config/sigit`),
|
|
193
|
-
`SIGIT_MODEL`, `SIGIT_MCP` (`off` disables MCP), `
|
|
244
|
+
`SIGIT_MODEL`, `SIGIT_MCP` (`off` disables MCP), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
|
|
245
|
+
smbCloud CLI server), `SIGIT_MCP_OFFICIAL` (`off` drops the baked-in
|
|
194
246
|
server), `SIGIT_PERMISSIONS` (`allow`/`ask`/`deny` — overrides the default permission mode for
|
|
195
247
|
mutating tools; the escape hatch for clients without permission-request support),
|
|
196
248
|
`HF_HOME` / `HF_HUB_CACHE`, `RUST_LOG`.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: MCP Registry Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version:
|
|
7
|
+
description: "Override the version in server.json (e.g. 0.2.0); leave blank to publish as-is"
|
|
8
|
+
required: false
|
|
9
|
+
|
|
10
|
+
# A remote server proves namespace ownership through domain (DNS) verification,
|
|
11
|
+
# not GitHub OIDC: the registry checks a v=MCPv1 TXT record on sigit.si against
|
|
12
|
+
# a request signed with the matching Ed25519 private key. See the MCP Registry
|
|
13
|
+
# doc in the sigit-si repo for the one-time key/DNS setup.
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
publish:
|
|
19
|
+
name: Publish server.json to the MCP Registry
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout
|
|
23
|
+
uses: actions/checkout@v6
|
|
24
|
+
|
|
25
|
+
- name: Optionally override the version in server.json
|
|
26
|
+
shell: bash
|
|
27
|
+
run: |
|
|
28
|
+
version="${{ github.event.inputs.version }}"
|
|
29
|
+
if [ -n "${version}" ]; then
|
|
30
|
+
jq --arg v "${version}" '.version = $v' server.json > server.tmp
|
|
31
|
+
mv server.tmp server.json
|
|
32
|
+
fi
|
|
33
|
+
cat server.json
|
|
34
|
+
|
|
35
|
+
- name: Check the remote URL matches the namespace domain
|
|
36
|
+
shell: bash
|
|
37
|
+
run: |
|
|
38
|
+
# The registry rejects a remote server whose URL isn't under the
|
|
39
|
+
# namespace's domain. si.sigit -> sigit.si, so every listed remote
|
|
40
|
+
# must live on sigit.si (or a subdomain).
|
|
41
|
+
server_name="$(jq -r '.name' server.json)"
|
|
42
|
+
domain="$(echo "${server_name%%/*}" | awk -F. '{ for (i=NF; i>=1; i--) printf "%s%s", $i, (i>1 ? "." : "") }')"
|
|
43
|
+
echo "Server: ${server_name} Domain: ${domain}"
|
|
44
|
+
|
|
45
|
+
bad="$(jq -r --arg d "${domain}" '
|
|
46
|
+
.remotes // []
|
|
47
|
+
| map(.url | sub("^[a-z]+://"; "") | sub("/.*$"; ""))
|
|
48
|
+
| map(select(. != $d and (endswith("." + $d) | not)))
|
|
49
|
+
| .[]' server.json)"
|
|
50
|
+
if [ -n "${bad}" ]; then
|
|
51
|
+
echo "Remote URL host(s) not under ${domain}: ${bad}" >&2
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
- name: Install mcp-publisher
|
|
56
|
+
shell: bash
|
|
57
|
+
run: |
|
|
58
|
+
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
|
|
59
|
+
|
|
60
|
+
- name: Authenticate to the MCP Registry
|
|
61
|
+
shell: bash
|
|
62
|
+
env:
|
|
63
|
+
MCP_REGISTRY_DNS_PRIVATE_KEY: ${{ secrets.MCP_REGISTRY_DNS_PRIVATE_KEY }}
|
|
64
|
+
run: |
|
|
65
|
+
if [ -z "${MCP_REGISTRY_DNS_PRIVATE_KEY}" ]; then
|
|
66
|
+
echo "MCP_REGISTRY_DNS_PRIVATE_KEY secret is not set. See the MCP Registry doc in the sigit-si repo." >&2
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
69
|
+
./mcp-publisher login dns --domain=sigit.si --private-key="${MCP_REGISTRY_DNS_PRIVATE_KEY}"
|
|
70
|
+
|
|
71
|
+
- name: Publish
|
|
72
|
+
shell: bash
|
|
73
|
+
run: ./mcp-publisher publish
|
|
74
|
+
|
|
75
|
+
- name: Verify the server is listed
|
|
76
|
+
shell: bash
|
|
77
|
+
run: |
|
|
78
|
+
server_name="$(jq -r '.name' server.json)"
|
|
79
|
+
curl -fsSL "https://registry.modelcontextprotocol.io/v0.1/servers?search=${server_name}" | jq .
|
|
@@ -110,7 +110,23 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
110
110
|
spec list (`all_tools`) and the execute `match` (`execute_tool`). `run_command` also enforces
|
|
111
111
|
commit attribution: when a command creates a new commit that lacks the
|
|
112
112
|
`Co-Authored-By: siGit Code` trailer (`COMMIT_CO_AUTHOR_TRAILER`), it amends the trailer in —
|
|
113
|
-
unless the commit already exists on a remote, which is never rewritten.
|
|
113
|
+
unless the commit already exists on a remote, which is never rewritten. Also owns the `task`
|
|
114
|
+
tool: a nested agent loop in a fresh conversation, offered only when `subagent_available()`
|
|
115
|
+
(a subagent factory is registered — see `register_subagent_factory_for` in `main.rs`; on-device
|
|
116
|
+
registers a `None`-returning factory since onde has a single shared history). A subagent's
|
|
117
|
+
toolset is a hard-gated read-only allow-list (`SUBAGENT_TOOL_NAMES`) that is never expanded by a
|
|
118
|
+
configurable subagent type (see `src/subagents.rs`) — only ever narrowed — so a `.sigit/agents/*.md`
|
|
119
|
+
file can't grant itself `edit_file`/`run_command` and bypass the permission system. Also owns
|
|
120
|
+
`web_search`: a thin native wrapper around the official MCP server's Brave-Search-backed
|
|
121
|
+
`web_search` tool (`mcp__sigit__web_search`, implemented server-side in `sigit-si`'s
|
|
122
|
+
`WebSearchService` + `Mcp::Tools::WebSearch`), offered only when that MCP tool was actually
|
|
123
|
+
discovered (i.e. the user is signed in to siGit Code Cloud — see `web_search_available`).
|
|
124
|
+
Wrapping it natively rather than leaving it as a raw `mcp__*` tool matters for two reasons:
|
|
125
|
+
`permissions::classify` treats every `mcp__*` tool as mutating (an "ask" prompt on every call),
|
|
126
|
+
while `web_search` is read-only like `read_website`; and the raw delegate name is filtered out
|
|
127
|
+
of the assembled tool list (`is_web_search_delegate`) so the model sees one clean option, not
|
|
128
|
+
two names for the same tool. Execution still forwards verbatim to `mcp::call_tool` — no second
|
|
129
|
+
HTTP/JSON-RPC implementation.
|
|
114
130
|
- **`src/skills.rs`** — [Agent Skills](https://agentskills.io) support. Discovers skill
|
|
115
131
|
folders (each with a `SKILL.md`: YAML frontmatter `name` + `description`, then Markdown
|
|
116
132
|
instructions) from `.sigit/skills/` and `.claude/skills/` in the cwd, `$SIGIT_CONFIG_DIR/skills/`,
|
|
@@ -119,6 +135,31 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
119
135
|
calls `skill` with a name) loads the full `SKILL.md` body. The `skill` tool is appended in the
|
|
120
136
|
`*_as_specs`/`build_tool_specs` layer (not in `all_tools()`) so its description can be dynamic,
|
|
121
137
|
and only when at least one skill exists.
|
|
138
|
+
- **`src/commands.rs`** — user-defined slash commands. Discovers Markdown files (each an
|
|
139
|
+
optional YAML frontmatter block — `description`, `argument-hint` — followed by a prompt-template
|
|
140
|
+
body) from `.sigit/commands/` and `.claude/commands/` in the cwd, `$SIGIT_CONFIG_DIR/commands/`,
|
|
141
|
+
and `~/.claude/commands/`. A subdirectory namespaces the command with `:`
|
|
142
|
+
(`.sigit/commands/git/commit.md` → `/git:commit`). Unlike skills there's no tool-call
|
|
143
|
+
indirection: invoking one works exactly like the built-in `/init` — `commands::render`
|
|
144
|
+
substitutes `$ARGUMENTS`/`$1..$9` in the body against whatever followed the command on the
|
|
145
|
+
input line, and the result is fed to the model as a normal turn through the ordinary tools and
|
|
146
|
+
permission checks. Resolution happens where each surface (`main.rs`/`chat.rs`) already
|
|
147
|
+
special-cases `/init`: an unrecognized slash command is tried against `commands::resolve_command`
|
|
148
|
+
before falling back to "unknown command". A custom command sharing a name with a built-in is
|
|
149
|
+
unreachable (built-ins match first in `parse_slash`) and is skipped when advertised to ACP
|
|
150
|
+
clients, with a warning logged.
|
|
151
|
+
- **`src/subagents.rs`** — configurable subagent types for the `task` tool. Discovers Markdown
|
|
152
|
+
files (YAML frontmatter `name` + `description`, optional comma-separated `tools:` allow-list,
|
|
153
|
+
then a Markdown body that becomes the subagent's system prompt) from `.sigit/agents/` and
|
|
154
|
+
`.claude/agents/` in the cwd, `$SIGIT_CONFIG_DIR/agents/`, and `~/.claude/agents/`. Passing a
|
|
155
|
+
type's `name` as `task`'s `subagent_type` argument swaps in that system prompt and, if `tools:`
|
|
156
|
+
is set, narrows the offered toolset to its *intersection* with `SUBAGENT_TOOL_NAMES` — the
|
|
157
|
+
security-relevant narrowing logic lives in `tools.rs` next to that constant, not here; this
|
|
158
|
+
module only discovers and parses files. `SubagentFactory` (in `tools.rs`) takes the resolved
|
|
159
|
+
system prompt per call rather than baking one in at registration, so a single registered
|
|
160
|
+
factory serves both the default research subagent and every configured type.
|
|
161
|
+
- **`src/frontmatter.rs`** — shared "YAML frontmatter + Markdown body" parsing used by both
|
|
162
|
+
`src/skills.rs` (`SKILL.md`) and `src/commands.rs` (`.sigit/commands/*.md`).
|
|
122
163
|
- **`src/mcp.rs`** — [Model Context Protocol](https://modelcontextprotocol.io) *client*. Two
|
|
123
164
|
transports: **Streamable HTTP** (one JSON-RPC POST endpoint, `url` in `mcp.toml`; replies are
|
|
124
165
|
`application/json` or SSE) and **stdio** (`command` + optional `args`/`[server.env]` in
|
|
@@ -131,12 +172,20 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
131
172
|
both read it; `/reload` does *not* re-run it, so config changes need a restart. stdio children
|
|
132
173
|
live for the process; a dead child fails calls with an in-band error string (no auto-restart).
|
|
133
174
|
Tools are namespaced `mcp__<server>__<tool>`, appended in the `*_as_specs`/`build_tool_specs`
|
|
134
|
-
layer and routed in `tools::execute_tool` via `mcp::is_mcp_tool`.
|
|
135
|
-
(`<cloud>/mcp`, default `https://sigit.si/api/v1/mcp
|
|
136
|
-
with the cloud session token
|
|
175
|
+
layer and routed in `tools::execute_tool` via `mcp::is_mcp_tool`. Two servers are baked in:
|
|
176
|
+
the official server (`<cloud>/mcp`, default `https://sigit.si/api/v1/mcp`, always HTTP, authed
|
|
177
|
+
with the cloud session token) and the smbCloud CLI server (`smb --mcp`, stdio, added only when
|
|
178
|
+
the `smb` binary is on `PATH`; opt out with `smbcloud = false` in `mcp.toml` or
|
|
179
|
+
`SIGIT_MCP_SMBCLOUD=off`). A user-defined entry named `sigit` or `smbcloud` overrides the
|
|
180
|
+
corresponding baked-in one. Extra servers live in `mcp.toml` (global
|
|
137
181
|
`$SIGIT_CONFIG_DIR/mcp.toml` and project-local `.sigit/mcp.toml`). The stdio path is covered by
|
|
138
182
|
`tests/mcp_stdio.rs`, driven by the test-only `src/bin/mcp_stdio_stub.rs` helper binary
|
|
139
|
-
(excluded from the published crate via `exclude` in `Cargo.toml`).
|
|
183
|
+
(excluded from the published crate via `exclude` in `Cargo.toml`). The baked-in official
|
|
184
|
+
server is *also* listed in the public MCP Registry as `si.sigit/sigit` — a **remote**
|
|
185
|
+
Streamable-HTTP listing (`server.json` at the repo root, published by
|
|
186
|
+
`release-mcp-registry.yml`). Because it's a remote server, the registry's URL-match rule
|
|
187
|
+
forces a domain namespace (`si.sigit` ↔ `sigit.si`) verified by a DNS TXT record, not the
|
|
188
|
+
GitHub-OIDC scheme `smbcloud-cli` uses for its package listing.
|
|
140
189
|
- **`src/permissions.rs`** — tool permission policy. Every tool call passes through
|
|
141
190
|
`decision_for` before executing: read-only tools always run; mutating tools (and all
|
|
142
191
|
`mcp__*`/unknown tools) are governed by, in order: per-session plan mode (`/plan` — deny all
|
|
@@ -165,12 +214,14 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
165
214
|
- **`src/credentials.rs`** — local session-token store (TOML, `0600` on Unix).
|
|
166
215
|
- **`src/models.rs`** — model-picker types shared across platforms.
|
|
167
216
|
|
|
168
|
-
Slash commands (`/help`, `/models`, `/skills`, `/
|
|
169
|
-
`/plan`, `/permissions`, `/init`, `/clear`, `/status`) are advertised via
|
|
170
|
-
`main.rs` and handled in both the TUI and ACP sessions. `/init` is
|
|
171
|
-
directly it substitutes `instructions::INIT_PROMPT` for the user
|
|
172
|
-
turn that explores the repo and writes (or improves) `AGENTS.md`
|
|
173
|
-
permission checks.
|
|
217
|
+
Slash commands (`/help`, `/models`, `/skills`, `/agents`, `/commands`, `/mcp`, `/login`, `/logout`,
|
|
218
|
+
`/whoami`, `/reload`, `/plan`, `/permissions`, `/init`, `/clear`, `/status`) are advertised via
|
|
219
|
+
`advertise_commands` in `main.rs` and handled in both the TUI and ACP sessions. `/init` is
|
|
220
|
+
special: instead of replying directly it substitutes `instructions::INIT_PROMPT` for the user
|
|
221
|
+
text and runs a normal agent turn that explores the repo and writes (or improves) `AGENTS.md`
|
|
222
|
+
through the ordinary tools and permission checks. User-defined commands (see `src/commands.rs`)
|
|
223
|
+
get the same treatment via the `SlashCommand::Unknown` fallback path, so anyone can add their own
|
|
224
|
+
`/name` commands without touching the built-in command list.
|
|
174
225
|
|
|
175
226
|
## Model cache (macOS)
|
|
176
227
|
|
|
@@ -190,7 +241,8 @@ verbosity with `RUST_LOG`.
|
|
|
190
241
|
|
|
191
242
|
`OPENAI_BASE_URL` / `OPENAI_API_KEY` (provider override), `SIGIT_API_URL` (account API base,
|
|
192
243
|
default `https://sigit.si`), `SIGIT_CLOUD_URL`, `SIGIT_CONFIG_DIR` (default `~/.config/sigit`),
|
|
193
|
-
`SIGIT_MODEL`, `SIGIT_MCP` (`off` disables MCP), `
|
|
244
|
+
`SIGIT_MODEL`, `SIGIT_MCP` (`off` disables MCP), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
|
|
245
|
+
smbCloud CLI server), `SIGIT_MCP_OFFICIAL` (`off` drops the baked-in
|
|
194
246
|
server), `SIGIT_PERMISSIONS` (`allow`/`ask`/`deny` — overrides the default permission mode for
|
|
195
247
|
mutating tools; the escape hatch for clients without permission-request support),
|
|
196
248
|
`HF_HOME` / `HF_HUB_CACHE`, `RUST_LOG`.
|
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
Opens the agent up to user extension: lifecycle hooks, custom slash commands,
|
|
6
|
+
and configurable subagent types. Also adds web search, a Repo tab in the TUI,
|
|
7
|
+
and a baked-in smbCloud MCP server.
|
|
8
|
+
|
|
9
|
+
### What changed
|
|
10
|
+
|
|
11
|
+
- Hooks let you run shell commands at three points in the agent's lifecycle:
|
|
12
|
+
`session_start`, `pre_tool_use`, and `post_tool_use`, configured under
|
|
13
|
+
`[hooks]` in `settings.toml`. Commands get context through variable
|
|
14
|
+
substitution (`{cwd}`, `{tool_name}`, `{tool_result_len}`) and run in the
|
|
15
|
+
session working directory. A hook that fails is logged and the session
|
|
16
|
+
continues; a hook that hangs is killed on a timeout, along with its whole
|
|
17
|
+
process group
|
|
18
|
+
- User-defined slash commands: drop a Markdown file in `.sigit/commands/` or
|
|
19
|
+
`.claude/commands/` (or the personal `$SIGIT_CONFIG_DIR/commands/` and
|
|
20
|
+
`~/.claude/commands/`) with optional `description` and `argument-hint`
|
|
21
|
+
frontmatter, and the body becomes a prompt template. `$ARGUMENTS` takes the
|
|
22
|
+
whole argument string and `$1`..`$9` the positional words. A subdirectory
|
|
23
|
+
namespaces the command with `:`, so `.sigit/commands/git/commit.md` becomes
|
|
24
|
+
`/git:commit`. They run as ordinary agent turns through the usual tools and
|
|
25
|
+
permission checks, and are advertised to ACP clients like Zed. `/commands`
|
|
26
|
+
lists what was discovered
|
|
27
|
+
- Configurable subagent types for the `task` tool: a Markdown file in
|
|
28
|
+
`.sigit/agents/` or `.claude/agents/` with `name`, `description`, and an
|
|
29
|
+
optional `tools:` allow-list, whose body becomes that subagent's system
|
|
30
|
+
prompt. Pass its name as `subagent_type` to swap the prompt in. The
|
|
31
|
+
`tools:` list can only narrow the subagent's read-only ceiling, never widen
|
|
32
|
+
it, so a config file cannot grant itself `edit_file` or `run_command` and
|
|
33
|
+
route around the permission system. `/agents` lists the discovered types
|
|
34
|
+
- New `web_search` tool, backed by the Brave-Search-backed search on siGit
|
|
35
|
+
Code Cloud's MCP server. It is offered only when you are signed in, and it
|
|
36
|
+
is classified read-only, so searching never triggers a permission prompt
|
|
37
|
+
- The TUI gains a Repo tab, shown when the session's `origin` remote points at
|
|
38
|
+
the sigit.si host. It lists issues and pull requests fetched through the
|
|
39
|
+
official MCP server: Up and Down select, Enter opens a scrollable detail,
|
|
40
|
+
`i`, `p`, Left and Right switch sections, `r` refreshes. The tab is hidden
|
|
41
|
+
and skipped in the cycle for any other remote
|
|
42
|
+
- The smbCloud CLI's stdio MCP server (`smb --mcp`) is now baked in, so
|
|
43
|
+
smbCloud project and deployment tools work with no `mcp.toml` setup. It is
|
|
44
|
+
added only when the `smb` binary is on `PATH`, its read-only tools (`me`,
|
|
45
|
+
`deployments`, `project_list`, `project_show`) skip permission prompts, and
|
|
46
|
+
you can opt out with `smbcloud = false` in `mcp.toml` or
|
|
47
|
+
`SIGIT_MCP_SMBCLOUD=off`
|
|
48
|
+
- siGit Code Cloud's MCP server is now listed in the public MCP Registry as
|
|
49
|
+
`si.sigit/sigit`, published from `server.json` at the repository root
|
|
50
|
+
|
|
51
|
+
### Fixes
|
|
52
|
+
|
|
53
|
+
- An explicit `deny` rule now applies to read-only first-party tools instead
|
|
54
|
+
of being skipped
|
|
55
|
+
- A subagent type whose `tools:` list resolves to an empty set is rejected
|
|
56
|
+
rather than producing a subagent with no tools
|
|
57
|
+
- Command templates render in a single substitution pass, so an argument that
|
|
58
|
+
contains something like `$1` is no longer re-substituted
|
|
59
|
+
- Frontmatter is stripped with the same leniency it is parsed with
|
|
60
|
+
- The Repo tab's detail view no longer shows stale data after a refresh
|
|
61
|
+
|
|
3
62
|
## 1.4.1
|
|
4
63
|
|
|
5
64
|
Two small fixes: correct co-author attribution and a Homebrew tap fix.
|
|
@@ -110,7 +110,23 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
110
110
|
spec list (`all_tools`) and the execute `match` (`execute_tool`). `run_command` also enforces
|
|
111
111
|
commit attribution: when a command creates a new commit that lacks the
|
|
112
112
|
`Co-Authored-By: siGit Code` trailer (`COMMIT_CO_AUTHOR_TRAILER`), it amends the trailer in —
|
|
113
|
-
unless the commit already exists on a remote, which is never rewritten.
|
|
113
|
+
unless the commit already exists on a remote, which is never rewritten. Also owns the `task`
|
|
114
|
+
tool: a nested agent loop in a fresh conversation, offered only when `subagent_available()`
|
|
115
|
+
(a subagent factory is registered — see `register_subagent_factory_for` in `main.rs`; on-device
|
|
116
|
+
registers a `None`-returning factory since onde has a single shared history). A subagent's
|
|
117
|
+
toolset is a hard-gated read-only allow-list (`SUBAGENT_TOOL_NAMES`) that is never expanded by a
|
|
118
|
+
configurable subagent type (see `src/subagents.rs`) — only ever narrowed — so a `.sigit/agents/*.md`
|
|
119
|
+
file can't grant itself `edit_file`/`run_command` and bypass the permission system. Also owns
|
|
120
|
+
`web_search`: a thin native wrapper around the official MCP server's Brave-Search-backed
|
|
121
|
+
`web_search` tool (`mcp__sigit__web_search`, implemented server-side in `sigit-si`'s
|
|
122
|
+
`WebSearchService` + `Mcp::Tools::WebSearch`), offered only when that MCP tool was actually
|
|
123
|
+
discovered (i.e. the user is signed in to siGit Code Cloud — see `web_search_available`).
|
|
124
|
+
Wrapping it natively rather than leaving it as a raw `mcp__*` tool matters for two reasons:
|
|
125
|
+
`permissions::classify` treats every `mcp__*` tool as mutating (an "ask" prompt on every call),
|
|
126
|
+
while `web_search` is read-only like `read_website`; and the raw delegate name is filtered out
|
|
127
|
+
of the assembled tool list (`is_web_search_delegate`) so the model sees one clean option, not
|
|
128
|
+
two names for the same tool. Execution still forwards verbatim to `mcp::call_tool` — no second
|
|
129
|
+
HTTP/JSON-RPC implementation.
|
|
114
130
|
- **`src/skills.rs`** — [Agent Skills](https://agentskills.io) support. Discovers skill
|
|
115
131
|
folders (each with a `SKILL.md`: YAML frontmatter `name` + `description`, then Markdown
|
|
116
132
|
instructions) from `.sigit/skills/` and `.claude/skills/` in the cwd, `$SIGIT_CONFIG_DIR/skills/`,
|
|
@@ -119,6 +135,31 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
119
135
|
calls `skill` with a name) loads the full `SKILL.md` body. The `skill` tool is appended in the
|
|
120
136
|
`*_as_specs`/`build_tool_specs` layer (not in `all_tools()`) so its description can be dynamic,
|
|
121
137
|
and only when at least one skill exists.
|
|
138
|
+
- **`src/commands.rs`** — user-defined slash commands. Discovers Markdown files (each an
|
|
139
|
+
optional YAML frontmatter block — `description`, `argument-hint` — followed by a prompt-template
|
|
140
|
+
body) from `.sigit/commands/` and `.claude/commands/` in the cwd, `$SIGIT_CONFIG_DIR/commands/`,
|
|
141
|
+
and `~/.claude/commands/`. A subdirectory namespaces the command with `:`
|
|
142
|
+
(`.sigit/commands/git/commit.md` → `/git:commit`). Unlike skills there's no tool-call
|
|
143
|
+
indirection: invoking one works exactly like the built-in `/init` — `commands::render`
|
|
144
|
+
substitutes `$ARGUMENTS`/`$1..$9` in the body against whatever followed the command on the
|
|
145
|
+
input line, and the result is fed to the model as a normal turn through the ordinary tools and
|
|
146
|
+
permission checks. Resolution happens where each surface (`main.rs`/`chat.rs`) already
|
|
147
|
+
special-cases `/init`: an unrecognized slash command is tried against `commands::resolve_command`
|
|
148
|
+
before falling back to "unknown command". A custom command sharing a name with a built-in is
|
|
149
|
+
unreachable (built-ins match first in `parse_slash`) and is skipped when advertised to ACP
|
|
150
|
+
clients, with a warning logged.
|
|
151
|
+
- **`src/subagents.rs`** — configurable subagent types for the `task` tool. Discovers Markdown
|
|
152
|
+
files (YAML frontmatter `name` + `description`, optional comma-separated `tools:` allow-list,
|
|
153
|
+
then a Markdown body that becomes the subagent's system prompt) from `.sigit/agents/` and
|
|
154
|
+
`.claude/agents/` in the cwd, `$SIGIT_CONFIG_DIR/agents/`, and `~/.claude/agents/`. Passing a
|
|
155
|
+
type's `name` as `task`'s `subagent_type` argument swaps in that system prompt and, if `tools:`
|
|
156
|
+
is set, narrows the offered toolset to its *intersection* with `SUBAGENT_TOOL_NAMES` — the
|
|
157
|
+
security-relevant narrowing logic lives in `tools.rs` next to that constant, not here; this
|
|
158
|
+
module only discovers and parses files. `SubagentFactory` (in `tools.rs`) takes the resolved
|
|
159
|
+
system prompt per call rather than baking one in at registration, so a single registered
|
|
160
|
+
factory serves both the default research subagent and every configured type.
|
|
161
|
+
- **`src/frontmatter.rs`** — shared "YAML frontmatter + Markdown body" parsing used by both
|
|
162
|
+
`src/skills.rs` (`SKILL.md`) and `src/commands.rs` (`.sigit/commands/*.md`).
|
|
122
163
|
- **`src/mcp.rs`** — [Model Context Protocol](https://modelcontextprotocol.io) *client*. Two
|
|
123
164
|
transports: **Streamable HTTP** (one JSON-RPC POST endpoint, `url` in `mcp.toml`; replies are
|
|
124
165
|
`application/json` or SSE) and **stdio** (`command` + optional `args`/`[server.env]` in
|
|
@@ -131,12 +172,20 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
131
172
|
both read it; `/reload` does *not* re-run it, so config changes need a restart. stdio children
|
|
132
173
|
live for the process; a dead child fails calls with an in-band error string (no auto-restart).
|
|
133
174
|
Tools are namespaced `mcp__<server>__<tool>`, appended in the `*_as_specs`/`build_tool_specs`
|
|
134
|
-
layer and routed in `tools::execute_tool` via `mcp::is_mcp_tool`.
|
|
135
|
-
(`<cloud>/mcp`, default `https://sigit.si/api/v1/mcp
|
|
136
|
-
with the cloud session token
|
|
175
|
+
layer and routed in `tools::execute_tool` via `mcp::is_mcp_tool`. Two servers are baked in:
|
|
176
|
+
the official server (`<cloud>/mcp`, default `https://sigit.si/api/v1/mcp`, always HTTP, authed
|
|
177
|
+
with the cloud session token) and the smbCloud CLI server (`smb --mcp`, stdio, added only when
|
|
178
|
+
the `smb` binary is on `PATH`; opt out with `smbcloud = false` in `mcp.toml` or
|
|
179
|
+
`SIGIT_MCP_SMBCLOUD=off`). A user-defined entry named `sigit` or `smbcloud` overrides the
|
|
180
|
+
corresponding baked-in one. Extra servers live in `mcp.toml` (global
|
|
137
181
|
`$SIGIT_CONFIG_DIR/mcp.toml` and project-local `.sigit/mcp.toml`). The stdio path is covered by
|
|
138
182
|
`tests/mcp_stdio.rs`, driven by the test-only `src/bin/mcp_stdio_stub.rs` helper binary
|
|
139
|
-
(excluded from the published crate via `exclude` in `Cargo.toml`).
|
|
183
|
+
(excluded from the published crate via `exclude` in `Cargo.toml`). The baked-in official
|
|
184
|
+
server is *also* listed in the public MCP Registry as `si.sigit/sigit` — a **remote**
|
|
185
|
+
Streamable-HTTP listing (`server.json` at the repo root, published by
|
|
186
|
+
`release-mcp-registry.yml`). Because it's a remote server, the registry's URL-match rule
|
|
187
|
+
forces a domain namespace (`si.sigit` ↔ `sigit.si`) verified by a DNS TXT record, not the
|
|
188
|
+
GitHub-OIDC scheme `smbcloud-cli` uses for its package listing.
|
|
140
189
|
- **`src/permissions.rs`** — tool permission policy. Every tool call passes through
|
|
141
190
|
`decision_for` before executing: read-only tools always run; mutating tools (and all
|
|
142
191
|
`mcp__*`/unknown tools) are governed by, in order: per-session plan mode (`/plan` — deny all
|
|
@@ -165,12 +214,14 @@ feeds results back. Neither the loop nor ACP/TUI surfaces depend on a concrete b
|
|
|
165
214
|
- **`src/credentials.rs`** — local session-token store (TOML, `0600` on Unix).
|
|
166
215
|
- **`src/models.rs`** — model-picker types shared across platforms.
|
|
167
216
|
|
|
168
|
-
Slash commands (`/help`, `/models`, `/skills`, `/
|
|
169
|
-
`/plan`, `/permissions`, `/init`, `/clear`, `/status`) are advertised via
|
|
170
|
-
`main.rs` and handled in both the TUI and ACP sessions. `/init` is
|
|
171
|
-
directly it substitutes `instructions::INIT_PROMPT` for the user
|
|
172
|
-
turn that explores the repo and writes (or improves) `AGENTS.md`
|
|
173
|
-
permission checks.
|
|
217
|
+
Slash commands (`/help`, `/models`, `/skills`, `/agents`, `/commands`, `/mcp`, `/login`, `/logout`,
|
|
218
|
+
`/whoami`, `/reload`, `/plan`, `/permissions`, `/init`, `/clear`, `/status`) are advertised via
|
|
219
|
+
`advertise_commands` in `main.rs` and handled in both the TUI and ACP sessions. `/init` is
|
|
220
|
+
special: instead of replying directly it substitutes `instructions::INIT_PROMPT` for the user
|
|
221
|
+
text and runs a normal agent turn that explores the repo and writes (or improves) `AGENTS.md`
|
|
222
|
+
through the ordinary tools and permission checks. User-defined commands (see `src/commands.rs`)
|
|
223
|
+
get the same treatment via the `SlashCommand::Unknown` fallback path, so anyone can add their own
|
|
224
|
+
`/name` commands without touching the built-in command list.
|
|
174
225
|
|
|
175
226
|
## Model cache (macOS)
|
|
176
227
|
|
|
@@ -190,7 +241,8 @@ verbosity with `RUST_LOG`.
|
|
|
190
241
|
|
|
191
242
|
`OPENAI_BASE_URL` / `OPENAI_API_KEY` (provider override), `SIGIT_API_URL` (account API base,
|
|
192
243
|
default `https://sigit.si`), `SIGIT_CLOUD_URL`, `SIGIT_CONFIG_DIR` (default `~/.config/sigit`),
|
|
193
|
-
`SIGIT_MODEL`, `SIGIT_MCP` (`off` disables MCP), `
|
|
244
|
+
`SIGIT_MODEL`, `SIGIT_MCP` (`off` disables MCP), `SIGIT_MCP_SMBCLOUD` (`off` drops the baked-in
|
|
245
|
+
smbCloud CLI server), `SIGIT_MCP_OFFICIAL` (`off` drops the baked-in
|
|
194
246
|
server), `SIGIT_PERMISSIONS` (`allow`/`ask`/`deny` — overrides the default permission mode for
|
|
195
247
|
mutating tools; the escape hatch for clients without permission-request support),
|
|
196
248
|
`HF_HOME` / `HF_HUB_CACHE`, `RUST_LOG`.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# siGit Code Hooks
|
|
2
|
+
|
|
3
|
+
Hooks allow you to run custom scripts at key moments in the agent lifecycle:
|
|
4
|
+
- **SessionStart**: When a session begins (new/load/fork)
|
|
5
|
+
- **PreToolUse**: Before a tool is executed
|
|
6
|
+
- **PostToolUse**: After a tool is executed
|
|
7
|
+
|
|
8
|
+
## Configuration
|
|
9
|
+
|
|
10
|
+
Hooks are configured in `~/.config/sigit/settings.toml` under the `[hooks]` section:
|
|
11
|
+
|
|
12
|
+
```toml
|
|
13
|
+
[hooks]
|
|
14
|
+
session_start = ["echo 'Starting siGit in {cwd}'"]
|
|
15
|
+
pre_tool_use = ["echo 'About to run {tool_name} with {tool_args_len} bytes of args'"]
|
|
16
|
+
post_tool_use = ["echo 'Finished {tool_name}, result was {tool_result_len} bytes'"]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Each hook is a list of shell commands. Commands run in the session working directory and inherit the environment.
|
|
20
|
+
|
|
21
|
+
## Variable Substitution
|
|
22
|
+
|
|
23
|
+
Hooks support substitution for context variables:
|
|
24
|
+
|
|
25
|
+
### SessionStart
|
|
26
|
+
- `{cwd}` - The session working directory
|
|
27
|
+
- `{session_id}` - The unique session identifier
|
|
28
|
+
|
|
29
|
+
### PreToolUse
|
|
30
|
+
- `{tool_name}` - The name of the tool being called
|
|
31
|
+
- `{tool_args_len}` - Length of the tool arguments in bytes
|
|
32
|
+
- `{cwd}` - The session working directory
|
|
33
|
+
|
|
34
|
+
### PostToolUse
|
|
35
|
+
- `{tool_name}` - The name of the tool that ran
|
|
36
|
+
- `{tool_result_len}` - Length of the tool result in bytes
|
|
37
|
+
- `{cwd}` - The session working directory
|
|
38
|
+
|
|
39
|
+
Every `{var}` value is quote-escaped for the platform shell before it's spliced
|
|
40
|
+
into the command line, so a directory name or an MCP-server-supplied tool name
|
|
41
|
+
that happens to contain shell metacharacters is always treated as literal text,
|
|
42
|
+
never executed. Windows `cmd.exe` quoting is best-effort (`cmd` has no fully
|
|
43
|
+
safe quoting story); the same values are also exported as environment
|
|
44
|
+
variables — `SIGIT_HOOK_CWD`, `SIGIT_HOOK_SESSION_ID`, `SIGIT_HOOK_TOOL_NAME`,
|
|
45
|
+
`SIGIT_HOOK_TOOL_ARGS_LEN`, `SIGIT_HOOK_TOOL_RESULT_LEN` — and reading those
|
|
46
|
+
in your script instead of `{var}` sidesteps quoting entirely on any platform.
|
|
47
|
+
|
|
48
|
+
## Examples
|
|
49
|
+
|
|
50
|
+
### Log all tool usage to a file
|
|
51
|
+
|
|
52
|
+
```toml
|
|
53
|
+
[hooks]
|
|
54
|
+
pre_tool_use = ["echo '[{tool_name}]' >> /tmp/sigit-tools.log"]
|
|
55
|
+
post_tool_use = ["echo ' → {tool_result_len} bytes' >> /tmp/sigit-tools.log"]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Send notifications
|
|
59
|
+
|
|
60
|
+
```toml
|
|
61
|
+
[hooks]
|
|
62
|
+
session_start = ["notify-send 'siGit started in {cwd}'"]
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Build on session start
|
|
66
|
+
|
|
67
|
+
```toml
|
|
68
|
+
[hooks]
|
|
69
|
+
session_start = ["cd {cwd} && cargo build"]
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Track metrics
|
|
73
|
+
|
|
74
|
+
```toml
|
|
75
|
+
[hooks]
|
|
76
|
+
pre_tool_use = ["echo {tool_name} | tee -a ~/.sigit/tool_usage.txt"]
|
|
77
|
+
post_tool_use = ["wc -c <<< {tool_result_len} >> ~/.sigit/result_sizes.txt"]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Notes
|
|
81
|
+
|
|
82
|
+
- Hooks are optional. When none are configured, each tool call still does one
|
|
83
|
+
lightweight settings-file read to check for that; there is no shell process
|
|
84
|
+
spawned and no other measurable overhead.
|
|
85
|
+
- Hook failures (non-zero exit codes) are logged as warnings but do not interrupt the session.
|
|
86
|
+
- Hooks are run synchronously, so slow hooks will impact agent responsiveness — this applies
|
|
87
|
+
in the interactive TUI too, not just ACP/headless mode.
|
|
88
|
+
- `PreToolUse` and `PostToolUse` hooks fire in every mode (TUI, ACP, headless) and for every
|
|
89
|
+
call to the shared tool-execution path — including tool calls made by a `task` subagent, not
|
|
90
|
+
just the top-level agent.
|
|
91
|
+
- `SessionStart` hooks currently only fire on ACP session lifecycle events (new/load/fork
|
|
92
|
+
session). The interactive TUI does not have an equivalent session-start hook point yet.
|