sigit-code 0.1.1__tar.gz → 0.1.2__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.
Files changed (37) hide show
  1. sigit_code-0.1.2/.agents/skills/tool-calling/SKILL.md +290 -0
  2. {sigit_code-0.1.1 → sigit_code-0.1.2}/.github/workflows/release-homebrew.yml +1 -1
  3. {sigit_code-0.1.1 → sigit_code-0.1.2}/Cargo.lock +9 -8
  4. {sigit_code-0.1.1 → sigit_code-0.1.2}/Cargo.toml +4 -3
  5. {sigit_code-0.1.1 → sigit_code-0.1.2}/PKG-INFO +1 -1
  6. {sigit_code-0.1.1 → sigit_code-0.1.2}/README.md +13 -0
  7. {sigit_code-0.1.1 → sigit_code-0.1.2}/src/chat.rs +27 -5
  8. sigit_code-0.1.2/src/main.rs +874 -0
  9. sigit_code-0.1.2/src/setup.rs +728 -0
  10. {sigit_code-0.1.1 → sigit_code-0.1.2}/src/tools.rs +472 -76
  11. sigit_code-0.1.1/.agents/skills/tool-calling/SKILL.md +0 -283
  12. sigit_code-0.1.1/src/main.rs +0 -527
  13. sigit_code-0.1.1/src/setup.rs +0 -89
  14. {sigit_code-0.1.1 → sigit_code-0.1.2}/.agents/AGENTS.md +0 -0
  15. {sigit_code-0.1.1 → sigit_code-0.1.2}/.agents/skills/agent-client-protocol/SKILL.md +0 -0
  16. {sigit_code-0.1.1 → sigit_code-0.1.2}/.agents/skills/ai-assisted-coding/SKILL.md +0 -0
  17. {sigit_code-0.1.1 → sigit_code-0.1.2}/.github/workflows/ci.yml +0 -0
  18. {sigit_code-0.1.1 → sigit_code-0.1.2}/.github/workflows/release-github.yml +0 -0
  19. {sigit_code-0.1.1 → sigit_code-0.1.2}/.github/workflows/release-npm.yml +0 -0
  20. {sigit_code-0.1.1 → sigit_code-0.1.2}/.github/workflows/release-pypi.yml +0 -0
  21. {sigit_code-0.1.1 → sigit_code-0.1.2}/.gitignore +0 -0
  22. {sigit_code-0.1.1 → sigit_code-0.1.2}/.nvmrc +0 -0
  23. {sigit_code-0.1.1 → sigit_code-0.1.2}/LICENSE +0 -0
  24. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/README.md.tmpl +0 -0
  25. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/package-main.json.tmpl +0 -0
  26. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/package.json.tmpl +0 -0
  27. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/scripts/render-main-package.cjs +0 -0
  28. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/scripts/render-platform-package.cjs +0 -0
  29. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/sigit/.gitignore +0 -0
  30. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/sigit/README.md +0 -0
  31. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/sigit/package.json +0 -0
  32. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/sigit/src/index.ts +0 -0
  33. {sigit_code-0.1.1 → sigit_code-0.1.2}/npm/sigit/tsconfig.json +0 -0
  34. {sigit_code-0.1.1 → sigit_code-0.1.2}/pypi/README.md +0 -0
  35. {sigit_code-0.1.1 → sigit_code-0.1.2}/pypi/pyproject.toml +0 -0
  36. {sigit_code-0.1.1 → sigit_code-0.1.2}/pyproject.toml +0 -0
  37. {sigit_code-0.1.1 → sigit_code-0.1.2}/rust-toolchain.toml +0 -0
@@ -0,0 +1,290 @@
1
+ # Skill: Tool Calling in siGit Code
2
+
3
+ ## Overview
4
+
5
+ siGit Code supports **agentic tool calling** — the LLM invokes tools (read/write files, run commands, read websites) to operate on the user's codebase. This works in both **interactive TUI mode** and **ACP server mode** (Zed editor).
6
+
7
+ Tool calling spans three layers:
8
+
9
+ ```
10
+ siGit (agent loop + tool execution)
11
+ → onde (ChatEngine with tool-aware API)
12
+ → mistral.rs (model inference + tool call parsing)
13
+ ```
14
+
15
+ ---
16
+
17
+ ## Model Requirement
18
+
19
+ **Only Qwen 3 supports tool calling.** Qwen 2.5 does NOT — mistral.rs only has a parser for Qwen 3's `<tool_call>...</tool_call>` XML format.
20
+
21
+ | Model | Constructor | Size | Tool calling | Default |
22
+ |-------|-----------|------|:---:|:---:|
23
+ | Qwen 3 8B (Q4_K_M) | `GgufModelConfig::qwen3_8b()` | ~5 GB | ✅ | ✅ **default** |
24
+ | Qwen 3 4B (Q4_K_M) | `GgufModelConfig::qwen3_4b()` | ~2.7 GB | ✅ | |
25
+ | Qwen 3 1.7B (Q4_K_M) | `GgufModelConfig::qwen3_1_7b()` | ~1.3 GB | ✅ | |
26
+ | Qwen 2.5 Coder 3B | `GgufModelConfig::qwen25_coder_3b()` | ~1.93 GB | ❌ | |
27
+ | Qwen 2.5 Coder 1.5B | `GgufModelConfig::qwen25_coder_1_5b()` | ~941 MB | ❌ | |
28
+
29
+ siGit uses **Qwen 3 8B** by default with `max_tokens: 8192` (set in `main.rs` for both TUI and ACP modes).
30
+
31
+ ### Why 8B over 4B
32
+
33
+ 4B can't do `edit_file` reliably. It reads a file, then fails to reproduce the exact `old_text` it just saw. This spirals into 7+ retry rounds that burn through `max_tokens` on `<think>` blocks and return nothing. 8B is the smallest model that actually lands edits.
34
+
35
+ ### bartowski GGUF naming convention
36
+
37
+ bartowski's repos use the publisher name as a prefix with an underscore:
38
+
39
+ | Constant | Value |
40
+ |----------|-------|
41
+ | `BARTOWSKI_QWEN3_8B_GGUF` | `"bartowski/Qwen_Qwen3-8B-GGUF"` |
42
+ | `QWEN3_8B_GGUF_FILE` | `"Qwen_Qwen3-8B-Q4_K_M.gguf"` |
43
+ | `BARTOWSKI_QWEN3_4B_GGUF` | `"bartowski/Qwen_Qwen3-4B-GGUF"` |
44
+ | `QWEN3_4B_GGUF_FILE` | `"Qwen_Qwen3-4B-Q4_K_M.gguf"` |
45
+
46
+ These constants live in `onde/src/inference/models.rs`.
47
+
48
+ ---
49
+
50
+ ## Tools (9 total)
51
+
52
+ Defined in `sigit/src/tools.rs` via `all_tools()`:
53
+
54
+ | # | Tool | Parameters | Behavior |
55
+ |---|------|-----------|----------|
56
+ | 1 | `read_file` | `path` | Reads file contents, truncates at 10,000 chars |
57
+ | 2 | `create_directory` | `path` | Creates directory and all parents |
58
+ | 3 | `list_directory` | `path` | Lists entries with `[DIR]`/`[FILE]` prefix, dirs first |
59
+ | 4 | `search_files` | `pattern`, `path` (optional) | Recursive regex search, max 50 matches |
60
+ | 5 | `read_website` | `url` | Fetches HTTP/HTTPS, strips HTML, returns text |
61
+ | 6 | `create_file` | `path`, `content` | Creates new file (fails if exists) |
62
+ | 7 | `edit_file` | `path`, `old_text`, `new_text` | Find-and-replace (must match exactly once) |
63
+ | 8 | `delete_file` | `path` | Deletes file or empty directory |
64
+ | 9 | `run_command` | `command`, `cwd` (optional) | Shell command with 120s timeout |
65
+
66
+ ### Async handling
67
+
68
+ `execute_tool()` is `async`. Most tools run synchronously, except:
69
+
70
+ - **`read_website`** — uses `tokio::task::spawn_blocking` because `reqwest::blocking::Client` panics inside a tokio runtime ("Cannot start a runtime from within a runtime")
71
+
72
+ ### Tool gating by model
73
+
74
+ In TUI mode, `run_inference_task()` takes a `tools_enabled: bool` parameter. When the model's `ModelOption.tool_calling` is `false` (Qwen 2.5), an empty tool list is passed so the model doesn't receive tool schemas it can't use.
75
+
76
+ ---
77
+
78
+ ## Architecture
79
+
80
+ ### Layer 1: mistral.rs (model-level)
81
+
82
+ - `RequestBuilder::set_tools(Vec<Tool>)` — attach tool definitions
83
+ - `RequestBuilder::set_tool_choice(ToolChoice::Auto)` — let model decide
84
+ - `QwenParser` detects `<tool_call>...</tool_call>` tags in output
85
+ - Grammar-constrained decoding forces valid JSON inside tool calls
86
+ - `<think>...</think>` reasoning is separated from tool calls by the reasoning parser
87
+ - Works identically for GGUF and full-precision models
88
+
89
+ ### Layer 2: onde (engine-level)
90
+
91
+ #### Key types (`onde/src/inference/types.rs`)
92
+
93
+ | Type | Purpose |
94
+ |------|---------|
95
+ | `ToolDefinition` | `{ name, description, parameters_schema: String }` |
96
+ | `ToolCallRequest` | `{ id, function_name, arguments: String }` |
97
+ | `ToolResult` | `{ tool_call_id, content: String }` |
98
+ | `ToolAwareResult` | `{ text, tool_calls: Vec<ToolCallRequest>, duration_secs, ... }` |
99
+
100
+ #### Key methods (`onde/src/inference/engine.rs`)
101
+
102
+ | Method | Purpose |
103
+ |--------|---------|
104
+ | `send_message_with_tools(msg, &[ToolDefinition])` | Returns `ToolAwareResult` with possible tool calls |
105
+ | `send_tool_results(Vec<ToolResult>, Option<&[ToolDefinition]>)` | Feed results back; `None` forces text response |
106
+
107
+ #### Internal details
108
+
109
+ - `attach_tools()` converts `ToolDefinition` → mistral.rs `Tool`, sets `ToolChoice::Auto` and `strict: Some(true)`
110
+ - `parse_tool_calls()` extracts tool calls from `choice.message.tool_calls`, generates fallback IDs if empty
111
+ - `replay_history_with_tools()` uses `.enumerate()` for correct sequential `index` values
112
+ - Malformed `parameters_schema` JSON logs a warning instead of silently producing empty params
113
+ - Malformed tool call `arguments` JSON logs a warning for debugging
114
+
115
+ ### Layer 3: siGit (agent-level)
116
+
117
+ #### ACP session handling (`src/main.rs`)
118
+
119
+ All session handlers (`load_session`, `fork_session`, `new_session`) do:
120
+
121
+ 1. **Store `args.cwd`** in `session_cwd: Mutex<Option<PathBuf>>`
122
+ 2. **`std::env::set_current_dir(&args.cwd)`** — so relative paths in tool calls resolve correctly
123
+ 3. **`engine.clear_history()`** — siGit doesn't persist sessions
124
+ 4. **`engine.push_history(ChatMessage::system(...))`** — injects: *"The user's project working directory is {cwd}. Always use absolute paths..."*
125
+
126
+ Without step 4, the model uses the process `cwd` (often `$HOME`) and creates files in the wrong directory.
127
+
128
+ #### ACP content block handling (`prompt()`)
129
+
130
+ The `prompt()` handler processes all ACP content block types:
131
+
132
+ - **`ContentBlock::Text`** — passed through as-is
133
+ - **`ContentBlock::Resource` (EmbeddedResource)** — `TextResourceContents` inlined as `--- {uri} ---\n{text}\n--- end ---`
134
+ - **`ContentBlock::ResourceLink`** — `file://` URIs are read from disk. **Line range fragments** like `#L207:219` are parsed: the `#` fragment is stripped from the path, and only lines 207–219 are extracted and sent to the model
135
+
136
+ Example: Zed sends `@ index.html (207:219)` as:
137
+ ```
138
+ ResourceLink(name="index.html (207:219)", uri="file:///path/to/index.html#L207:219")
139
+ ```
140
+ siGit parses this into path `/path/to/index.html` + lines 207–219.
141
+
142
+ ---
143
+
144
+ ## The Agentic Loop
145
+
146
+ Both ACP mode (`SiGitAgent::prompt()`) and TUI mode (`run_inference_task()`) implement:
147
+
148
+ ```
149
+ 1. engine.send_message_with_tools(user_text, &tools) → ToolAwareResult
150
+ 2. while result.tool_calls is non-empty AND round < MAX_TOOL_ROUNDS (10):
151
+ a. For each tool_call:
152
+ - Log: → tool_name(arguments)
153
+ - Execute: tools::execute_tool(name, arguments).await
154
+ - Log: ← N chars
155
+ - Collect ToolResult { tool_call_id, content }
156
+ b. Decide next_tools:
157
+ - round < MAX_TOOL_ROUNDS → Some(&tools) (allow more calls)
158
+ - else → None (force text response)
159
+ c. engine.send_tool_results(results, next_tools) → ToolAwareResult
160
+ 3. Send final result.text to user
161
+ - Empty reply after tool rounds → log warning (ACP) or show error (TUI)
162
+ ```
163
+
164
+ ---
165
+
166
+ ## System Prompt
167
+
168
+ The `SYSTEM_PROMPT` in `main.rs` (~122 lines) includes critical instructions:
169
+
170
+ - **Never tell the user to run commands** — use `run_command` tool instead
171
+ - **Can access websites** — use `read_website` tool (overrides RLHF refusal training)
172
+ - **Prefer absolute paths** in all tool arguments
173
+ - **Git operations** — always use `run_command` with absolute cwd
174
+ - **smbCloud domain knowledge** — auth boundaries, deploy flows, project structure
175
+
176
+ The session `cwd` is injected as a separate system message at session creation time (not part of the static prompt).
177
+
178
+ ---
179
+
180
+ ## Model Cache
181
+
182
+ Models are stored in the shared Onde App Group container on macOS:
183
+
184
+ ```
185
+ ~/Library/Group Containers/group.com.ondeinference.apps/models/hub/
186
+ ```
187
+
188
+ `setup.rs` sets `HF_HOME` and `HF_HUB_CACHE` to point there at startup, so siGit reuses models downloaded by the Onde desktop app (and vice versa).
189
+
190
+ ---
191
+
192
+ ## Adding a New Tool
193
+
194
+ 1. Add an `AgentTool` entry to `all_tools()` in `src/tools.rs`
195
+ 2. Add a match arm to `execute_tool()` — use `spawn_blocking` if the implementation blocks
196
+ 3. Write `exec_your_tool(arguments: &str) -> String`
197
+ 4. Update `test_all_tools_count` test (currently expects 9)
198
+
199
+ No changes needed in onde or mistral.rs — tool definitions are passed dynamically.
200
+
201
+ ---
202
+
203
+ ## Adding a New Model
204
+
205
+ 1. **`onde/src/inference/models.rs`** — add `pub const` for repo ID and GGUF filename, add to `SUPPORTED_MODELS` array and `SUPPORTED_MODEL_INFO`
206
+ 2. **`onde/src/inference/engine.rs`** — add `pub fn model_name() -> Self` constructor to `impl GgufModelConfig`
207
+ 3. **`sigit/src/chat.rs`** — add `ModelOption` entry to `SIGIT_MODELS` with `tool_calling: true/false`
208
+ 4. **`sigit/src/main.rs`** — update `run_interactive()` and `run_acp_server()` if changing the default
209
+
210
+ ---
211
+
212
+ ## Debugging
213
+
214
+ ### Log locations
215
+
216
+ - **TUI mode:** `$TMPDIR/sigit.log` (e.g. `/var/folders/.../sigit.log`)
217
+ - **ACP mode (Zed):** `~/Library/Logs/Zed/Zed.log` — grep for `agent stderr:.*sigit`
218
+
219
+ ### Key log patterns
220
+
221
+ ```
222
+ # Model loaded successfully
223
+ ChatEngine: model Qwen 3 8B loaded in 6.9s
224
+
225
+ # Session cwd captured
226
+ load_session: id=..., cwd=/path/to/project, additional_directories=[...]
227
+
228
+ # Tool call parsed by mistral.rs
229
+ ChatEngine: tool inference END — 12.3s — tool_calls: 1
230
+
231
+ # Tool executed
232
+ → read_file({"path":"/absolute/path/to/file.rs"})
233
+ ← 6506 chars
234
+
235
+ # Tool result sent back
236
+ ChatEngine: tool results inference START — 1 results
237
+
238
+ # Model returned empty (exhausted max_tokens on thinking)
239
+ model returned empty reply after 7 tool round(s)
240
+
241
+ # ResourceLink received from Zed
242
+ block[1]: ResourceLink(name=index.html (207:219), uri=file:///path/to/index.html#L207:219)
243
+
244
+ # ResourceLink read failed (fragment not stripped — old bug, now fixed)
245
+ could not read ResourceLink file:///path/to/index.html#L207:219: No such file or directory
246
+ ```
247
+
248
+ ### Common issues
249
+
250
+ | Symptom | Cause | Fix |
251
+ |---------|-------|-----|
252
+ | Model says "I cannot access websites" | RLHF refusal override not in system prompt | System prompt now has CRITICAL block about `read_website` |
253
+ | `0 tool call(s)` for every prompt | Wrong model loaded (Qwen 2.5) | Check log for `loading GGUF model` — must be Qwen 3 |
254
+ | `edit_file` returns `← 161 chars` repeatedly | `old_text not found` — model can't match exact text | Use Qwen 3 8B (not 4B); consider line-based edit tool |
255
+ | Files created in wrong directory | `cwd` not captured from ACP session | Session handlers must call `set_current_dir` + `push_history` with cwd |
256
+ | `@ file.html (207:219)` context missing | `#L207:219` fragment not stripped from file path | `prompt()` now parses URI fragments and extracts line ranges |
257
+ | `read_website` panics/hangs | `reqwest::blocking` inside tokio runtime | `exec_read_website` wrapped in `spawn_blocking` |
258
+ | Empty reply after many tool rounds | Model exhausted `max_tokens` on `<think>` blocks | Set `max_tokens: 8192`; 8B model wastes fewer tokens on thinking |
259
+
260
+ ---
261
+
262
+ ## Cargo Dependency Note
263
+
264
+ For local development, `sigit/Cargo.toml` must use the path dependency:
265
+
266
+ ```toml
267
+ onde = { path = "../onde" }
268
+ ```
269
+
270
+ For CI/release, switch to the git dependency (after pushing Onde changes):
271
+
272
+ ```toml
273
+ onde = { git = "https://github.com/ondeinference/onde", branch = "development" }
274
+ ```
275
+
276
+ The `qwen3_8b()` constructor only exists in the local Onde SDK until it's pushed to the `development` branch.
277
+
278
+ ---
279
+
280
+ ## File Map
281
+
282
+ | File | What it does |
283
+ |------|-------------|
284
+ | `sigit/src/tools.rs` | 9 tool schemas (`all_tools()`), `execute_tool()` dispatch, all `exec_*` implementations |
285
+ | `sigit/src/main.rs` | `SYSTEM_PROMPT`, `SiGitAgent` struct with `session_cwd`, ACP session handlers (cwd + push_history), `prompt()` with content block parsing, model selection (`qwen3_8b`), `MAX_TOOL_ROUNDS` |
286
+ | `sigit/src/chat.rs` | `SIGIT_MODELS` array (4 models), `run_inference_task()` with `tools_enabled` gate, TUI tool loop |
287
+ | `sigit/src/setup.rs` | HF cache setup pointing to shared App Group container |
288
+ | `onde/src/inference/types.rs` | `ToolDefinition`, `ToolCallRequest`, `ToolResult`, `ToolAwareResult` |
289
+ | `onde/src/inference/engine.rs` | `send_message_with_tools()`, `send_tool_results()`, `attach_tools()`, `parse_tool_calls()`, `replay_history_with_tools()`, `GgufModelConfig::qwen3_8b()` |
290
+ | `onde/src/inference/models.rs` | Model constants and `SUPPORTED_MODELS` array |
@@ -54,7 +54,7 @@ jobs:
54
54
  - name: Checkout Homebrew tap
55
55
  uses: actions/checkout@v6
56
56
  with:
57
- repository: getsigit/sigit-homebrew-tap
57
+ repository: getsigit/homebrew-tap
58
58
  token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
59
59
  path: homebrew-tap
60
60
 
@@ -741,9 +741,9 @@ dependencies = [
741
741
 
742
742
  [[package]]
743
743
  name = "cc"
744
- version = "1.2.60"
744
+ version = "1.2.61"
745
745
  source = "registry+https://github.com/rust-lang/crates.io-index"
746
- checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
746
+ checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d"
747
747
  dependencies = [
748
748
  "find-msvc-tools",
749
749
  "jobserver",
@@ -1264,9 +1264,9 @@ dependencies = [
1264
1264
 
1265
1265
  [[package]]
1266
1266
  name = "data-encoding"
1267
- version = "2.10.0"
1267
+ version = "2.11.0"
1268
1268
  source = "registry+https://github.com/rust-lang/crates.io-index"
1269
- checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea"
1269
+ checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
1270
1270
 
1271
1271
  [[package]]
1272
1272
  name = "defmac"
@@ -3799,7 +3799,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
3799
3799
  [[package]]
3800
3800
  name = "onde"
3801
3801
  version = "0.1.8"
3802
- source = "git+https://github.com/ondeinference/onde?branch=development#f0bb0daad7fb07af951002f1bcae16fbd0bc876d"
3802
+ source = "git+https://github.com/ondeinference/onde?branch=development#8321bc566cfbca8ff1d4b71f187f2b007fd98433"
3803
3803
  dependencies = [
3804
3804
  "anyhow",
3805
3805
  "cc",
@@ -4807,9 +4807,9 @@ dependencies = [
4807
4807
 
4808
4808
  [[package]]
4809
4809
  name = "rustls-pki-types"
4810
- version = "1.14.0"
4810
+ version = "1.14.1"
4811
4811
  source = "registry+https://github.com/rust-lang/crates.io-index"
4812
- checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
4812
+ checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
4813
4813
  dependencies = [
4814
4814
  "web-time",
4815
4815
  "zeroize",
@@ -5271,7 +5271,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
5271
5271
 
5272
5272
  [[package]]
5273
5273
  name = "sigit"
5274
- version = "0.1.1"
5274
+ version = "0.1.2"
5275
5275
  dependencies = [
5276
5276
  "agent-client-protocol",
5277
5277
  "anyhow",
@@ -5283,6 +5283,7 @@ dependencies = [
5283
5283
  "onde",
5284
5284
  "ratatui",
5285
5285
  "regex",
5286
+ "reqwest 0.12.28",
5286
5287
  "serde_json",
5287
5288
  "tokio",
5288
5289
  "tokio-util",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sigit"
3
- version = "0.1.1"
3
+ version = "0.1.2"
4
4
  edition = "2024"
5
5
  description = "siGit Code — ACP-compatible AI coding agent for smbCloud platform."
6
6
  documentation = "https://github.com/getsigit/sigit"
@@ -18,10 +18,10 @@ path = "src/main.rs"
18
18
 
19
19
  [dependencies]
20
20
  # ACP protocol SDK
21
- agent-client-protocol = { version = "0.10.4", features = ["unstable_session_fork"] }
21
+ agent-client-protocol = { version = "0.10.4", features = ["unstable_session_fork", "unstable_session_additional_directories"] }
22
22
 
23
23
  # Onde Inference engine (local LLM)
24
- # For local development: onde = { path = "../onde" }
24
+ # onde = { path = "../onde" }
25
25
  onde = { git = "https://github.com/ondeinference/onde", branch = "development" }
26
26
 
27
27
  # Async runtime
@@ -41,4 +41,5 @@ log = "0.4"
41
41
  tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
42
42
  serde_json = "1"
43
43
  regex = "1"
44
+ reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls"] }
44
45
  uuid = { version = "1", features = ["v4"] }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sigit-code
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -4,6 +4,8 @@
4
4
 
5
5
  A coding agent for [smbCloud](https://smbcloud.xyz/) that runs entirely on your machine. No API keys. No cloud round-trips. The model lives in your local HuggingFace cache.
6
6
 
7
+ siGit is meant to be a general coding agent, but it is especially good in smbCloud codebases. It already knows the rough shape of the platform: Rust workspaces with focused crates, Rails services, deploy flows, auth boundaries, and platform-managed services like GresIQ. In smbCloud repos, that means it can usually give more grounded answers with less back-and-forth.
8
+
7
9
  siGit has two modes:
8
10
 
9
11
  - ACP mode, where Zed or another ACP-compatible editor starts it over stdio
@@ -15,6 +17,17 @@ Current platform support:
15
17
  - Linux: ACP mode and interactive terminal mode
16
18
  - Windows: ACP mode only for now
17
19
 
20
+ ## What siGit knows about smbCloud
21
+
22
+ When siGit is working in an smbCloud repo, it should lean on platform context instead of treating everything like a generic cloud app. That includes things like:
23
+
24
+ - the difference between platform user flows and tenant app auth flows
25
+ - the fact that `Project` is the umbrella workspace, while app-like resources such as `FrontendApp`, `AuthApp`, and GresIQ are separate deployable units
26
+ - the fact that Next.js SSR deploys are not the same as the generic git-push path
27
+ - the fact that smbCloud repos usually prefer existing workspace patterns and crate boundaries over new abstractions
28
+
29
+ Outside smbCloud, it should still behave like a normal coding agent and not force platform-specific advice where it does not belong.
30
+
18
31
  ## Install
19
32
 
20
33
  ```sh
@@ -120,6 +120,8 @@ struct App {
120
120
  load_start: Instant,
121
121
  /// Display name of the model being loaded (shown in the spinner line).
122
122
  load_model_name: String,
123
+ /// Whether the currently loaded model supports tool calling.
124
+ tool_calling: bool,
123
125
  }
124
126
 
125
127
  const BANNER_ART: &str = "\
@@ -142,6 +144,11 @@ const THINKING_FRAMES: &[&str] = &["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "
142
144
 
143
145
  impl App {
144
146
  fn new(load_model_name: String) -> Self {
147
+ let tool_calling = SIGIT_MODELS
148
+ .iter()
149
+ .find(|m| m.name == load_model_name)
150
+ .map(|m| m.tool_calling)
151
+ .unwrap_or(true);
145
152
  Self {
146
153
  messages: Vec::new(),
147
154
  input: String::new(),
@@ -160,6 +167,7 @@ impl App {
160
167
  load_error: None,
161
168
  load_start: Instant::now(),
162
169
  load_model_name,
170
+ tool_calling,
163
171
  }
164
172
  }
165
173
 
@@ -303,6 +311,13 @@ struct ModelOption {
303
311
  }
304
312
 
305
313
  const SIGIT_MODELS: &[ModelOption] = &[
314
+ ModelOption {
315
+ name: "Qwen 3 8B (Q4_K_M)",
316
+ description: "~5 GB",
317
+ tool_calling: true,
318
+ max_tokens: 4096,
319
+ config_fn: GgufModelConfig::qwen3_8b,
320
+ },
306
321
  ModelOption {
307
322
  name: "Qwen 3 4B (Q4_K_M)",
308
323
  description: "~2.7 GB",
@@ -840,6 +855,7 @@ async fn exec_slash<B: ratatui::backend::Backend>(
840
855
  match engine.load_gguf_model(config, None, Some(sampling)).await {
841
856
  Ok(_) => {
842
857
  engine.clear_history().await;
858
+ app.tool_calling = model.tool_calling;
843
859
  app.messages.push(ChatMessage::system(format!(
844
860
  "✓ Switched to {}",
845
861
  model.name
@@ -892,8 +908,13 @@ async fn run_inference_task(
892
908
  engine: Arc<ChatEngine>,
893
909
  text: String,
894
910
  tx: mpsc::Sender<InferenceUpdate>,
911
+ tools_enabled: bool,
895
912
  ) {
896
- let onde_tools = build_onde_tools();
913
+ let onde_tools = if tools_enabled {
914
+ build_onde_tools()
915
+ } else {
916
+ vec![]
917
+ };
897
918
 
898
919
  let mut result = match engine.send_message_with_tools(&text, &onde_tools).await {
899
920
  Ok(r) => r,
@@ -923,8 +944,8 @@ async fn run_inference_task(
923
944
  .send(InferenceUpdate::ToolUse(tc.function_name.clone()))
924
945
  .await;
925
946
 
926
- // Execute the tool (synchronous / blocking-ok for file I/O).
927
- let output = crate::tools::execute_tool(&tc.function_name, &tc.arguments);
947
+ // Execute the tool (async read_website uses spawn_blocking internally).
948
+ let output = crate::tools::execute_tool(&tc.function_name, &tc.arguments).await;
928
949
  log::info!(" ← {} chars", output.len());
929
950
 
930
951
  tool_results.push(ToolResult {
@@ -985,7 +1006,7 @@ pub async fn run_with<B: ratatui::backend::Backend>(
985
1006
  engine: Arc<ChatEngine>,
986
1007
  load_rx: std_mpsc::Receiver<Result<(), String>>,
987
1008
  ) -> Result<()> {
988
- let config = GgufModelConfig::platform_default();
1009
+ let config = GgufModelConfig::qwen3_8b();
989
1010
  let model_name = config.display_name.clone();
990
1011
  event_loop(terminal, engine, load_rx, model_name).await
991
1012
  }
@@ -1149,8 +1170,9 @@ async fn event_loop<B: ratatui::backend::Backend>(
1149
1170
 
1150
1171
  let engine_handle = Arc::clone(&engine);
1151
1172
  let user_text = text.clone();
1173
+ let tools_enabled = app.tool_calling;
1152
1174
  tokio::spawn(async move {
1153
- run_inference_task(engine_handle, user_text, tx).await;
1175
+ run_inference_task(engine_handle, user_text, tx, tools_enabled).await;
1154
1176
  });
1155
1177
  }
1156
1178
  }