little-coder 1.1.0 → 1.2.0

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.
@@ -1,7 +1,8 @@
1
1
  import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
2
  import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
- import { join } from "node:path";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
5
6
  import { applyEnvOverrides, loadProviders, mergeProviders, resolveOverridePath, type ProviderEntry } from "./config.ts";
6
7
 
7
8
  const sampleProvider = (baseUrl: string, modelId: string): ProviderEntry => ({
@@ -76,6 +77,11 @@ describe("applyEnvOverrides", () => {
76
77
  const out = applyEnvOverrides(providers, { OLLAMA_BASE_URL: "http://env/v1" });
77
78
  expect(out.ollama.baseUrl).toBe("http://env/v1");
78
79
  });
80
+ it("LMSTUDIO_BASE_URL overrides lmstudio baseUrl", () => {
81
+ const providers = { lmstudio: sampleProvider("http://127.0.0.1:1234/v1", "local-model") };
82
+ const out = applyEnvOverrides(providers, { LMSTUDIO_BASE_URL: "http://127.0.0.1:5678/v1" });
83
+ expect(out.lmstudio.baseUrl).toBe("http://127.0.0.1:5678/v1");
84
+ });
79
85
  it("does not alter providers without a known env knob", () => {
80
86
  const providers = { custom: sampleProvider("http://file/v1", "m") };
81
87
  const out = applyEnvOverrides(providers, { LLAMACPP_BASE_URL: "http://env/v1" });
@@ -159,3 +165,23 @@ describe("loadProviders (filesystem)", () => {
159
165
  expect(result.providers.llamacpp.models[0].id).toBe("via-xdg");
160
166
  });
161
167
  });
168
+
169
+ describe("shipped models.json", () => {
170
+ const here = dirname(fileURLToPath(import.meta.url));
171
+ const pkgRoot = resolve(here, "..", "..", "..");
172
+
173
+ it("registers lmstudio/local-model on http://127.0.0.1:1234/v1", () => {
174
+ const result = loadProviders(pkgRoot, {});
175
+ const lmstudio = result.providers.lmstudio;
176
+ expect(lmstudio, "lmstudio provider should be present in shipped models.json").toBeDefined();
177
+ expect(lmstudio.baseUrl).toBe("http://127.0.0.1:1234/v1");
178
+ expect(lmstudio.api).toBe("openai-completions");
179
+ expect(lmstudio.apiKey).toBe("LMSTUDIO_API_KEY");
180
+ expect(lmstudio.models.find((m) => m.id === "local-model")).toBeDefined();
181
+ });
182
+
183
+ it("still registers llamacpp and ollama alongside lmstudio", () => {
184
+ const result = loadProviders(pkgRoot, {});
185
+ expect(Object.keys(result.providers).sort()).toEqual(["llamacpp", "lmstudio", "ollama"]);
186
+ });
187
+ });
@@ -43,11 +43,14 @@ export interface LoadResult {
43
43
  sources: { path: string; status: "ok" | "missing" | "invalid"; error?: string }[];
44
44
  }
45
45
 
46
- /** Provider env knob: if set, overrides the provider's baseUrl. Legacy compat
47
- * for the two providers we shipped before the data-driven refactor. */
46
+ /** Provider env knob: if set, overrides the provider's baseUrl. Originally a
47
+ * back-compat shim for the two providers we shipped before the data-driven
48
+ * refactor; kept as the per-provider env-override pattern for any provider
49
+ * whose baseUrl changes between deployments. */
48
50
  const LEGACY_BASE_URL_ENV: Record<string, string> = {
49
51
  llamacpp: "LLAMACPP_BASE_URL",
50
52
  ollama: "OLLAMA_BASE_URL",
53
+ lmstudio: "LMSTUDIO_BASE_URL",
51
54
  };
52
55
 
53
56
  /** Resolution order for the user-override file. First existing path wins. */
@@ -56,7 +56,6 @@ const INTENT_MAP: Record<string, string[]> = {
56
56
  browse: ["BrowserNavigate", "BrowserExtract"],
57
57
  page: ["BrowserExtract"],
58
58
  click: ["BrowserClick"],
59
- agent: ["Agent"], delegate: ["Agent"], spawn: ["Agent"],
60
59
  };
61
60
 
62
61
  function skillsDir(): string {
@@ -21,7 +21,6 @@ const INTENT_MAP: Record<string, string[]> = {
21
21
  grep: ["Grep"], glob: ["Glob"],
22
22
  fetch: ["WebFetch"], download: ["WebFetch"], url: ["WebFetch"],
23
23
  web: ["WebSearch"],
24
- agent: ["Agent"], delegate: ["Agent"], spawn: ["Agent"],
25
24
  };
26
25
 
27
26
  function predictTools(userText: string): string[] {
@@ -61,10 +60,10 @@ describe("skills directory loads from repo", () => {
61
60
  const here = dirname(fileURLToPath(import.meta.url));
62
61
  const toolsDir = join(here, "..", "..", "..", "skills", "tools");
63
62
 
64
- it("exists and has 14 markdown files", () => {
63
+ it("exists and has 13 markdown files", () => {
65
64
  expect(existsSync(toolsDir)).toBe(true);
66
65
  const files = readdirSync(toolsDir).filter((f) => f.endsWith(".md"));
67
- expect(files.length).toBe(14);
66
+ expect(files.length).toBe(13);
68
67
  });
69
68
 
70
69
  it("every tool skill has target_tool in frontmatter", () => {
package/.pi/settings.json CHANGED
@@ -70,6 +70,14 @@
70
70
  "skill_token_budget": 300,
71
71
  "knowledge_token_budget": 200,
72
72
  "temperature": 0.3
73
+ },
74
+ "lmstudio/local-model": {
75
+ "context_limit": 32768,
76
+ "max_tokens": 4096,
77
+ "thinking_budget": 2048,
78
+ "skill_token_budget": 300,
79
+ "knowledge_token_budget": 200,
80
+ "temperature": 0.3
73
81
  }
74
82
  }
75
83
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to little-coder are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and little-coder's public interface (CLI, providers, tools, skills) follows semver starting at `v0.0.1` post-rename.
4
4
 
5
+ ## [v1.2.0] — 2026-05-10
6
+
7
+ Issue-cleanup release that also ships built-in LM Studio support. Closes [#17](https://github.com/itayinbarr/little-coder/issues/17) (Windows), [#19](https://github.com/itayinbarr/little-coder/issues/19) (phantom Agent tool), [#21](https://github.com/itayinbarr/little-coder/issues/21) (skill param mismatch).
8
+
9
+ ### Added
10
+ - **Built-in `lmstudio/local-model` provider.** [LM Studio](https://lmstudio.ai/) exposes an OpenAI-compatible server on `http://127.0.0.1:1234/v1` by default, and previously the only way to use it was to overload `LLAMACPP_BASE_URL`. Now you can run `little-coder --model lmstudio/local-model` and it routes to whatever model LM Studio currently has loaded — no extra config for the single-model case. New env knobs `LMSTUDIO_BASE_URL` (overrides baseUrl, parity with `LLAMACPP_BASE_URL`/`OLLAMA_BASE_URL`) and `LMSTUDIO_API_KEY` (any value; LM Studio ignores it locally but pi requires the env var to exist). README has a new **Option C — LM Studio** under *Local model setup*. `.pi/settings.json` ships a `lmstudio/local-model` profile so the same context/thinking-budget tuning as the llamacpp profiles applies.
11
+
12
+ ### Fixed
13
+ - **Windows launch ([#17](https://github.com/itayinbarr/little-coder/issues/17), thanks @Grogger for [PR #18](https://github.com/itayinbarr/little-coder/pull/18)).** On Windows, `node_modules/.bin/pi` is a `.cmd` shim that Node 20's `spawn()` can't execute directly without `shell: true`, and `shell: true` reintroduces the CVE-2024-27980 / DEP0190 shell-injection class. The launcher now resolves `pi.cmd` on Windows and invokes `cmd.exe /c pi.cmd ...` with args as an array — works on Windows 11, no Linux/macOS regression.
14
+ - **Edit skill documentation ([#21](https://github.com/itayinbarr/little-coder/issues/21)).** `skills/tools/edit.md` advertised `old_string` / `new_string`, but pi's Edit tool only accepts `oldText` / `newText` (single-edit form) or `edits: [{oldText, newText}]` (array form). Rewritten to show the canonical array form *and* the single-edit back-compat form. While in there, also corrected `skills/tools/read.md` and `skills/tools/write.md` (`file_path` → `path` — pi aliases both, but the canonical name is now in the docs) and `skills/tools/grep.md` (`include` → `glob`, `max_results` → `limit`; pi does not alias these, so the old skill could genuinely produce tool-call errors on the grep path the same way Edit did).
15
+
16
+ ### Changed
17
+ - **Removed phantom `Agent` skill ([#19](https://github.com/itayinbarr/little-coder/issues/19)).** `skills/tools/agent.md` documented an `Agent` tool that little-coder never actually registered — pi ships `examples/extensions/subagent/` as a reference impl, but it was not wired up by default. Deleted the skill card and the `agent` / `delegate` / `spawn` keys from `.pi/extensions/skill-inject/index.ts`'s `INTENT_MAP` so the model is no longer told it has a delegation tool. The `skills/protocols/task_decomposition.md` cheatsheet is untouched — decomposition guidance does not depend on a delegation tool.
18
+
19
+ ### Notes for upgraders
20
+ - No CLI flag, settings, or skill-pack breaks. `--model lmstudio/local-model` works out of the box if LM Studio is serving on its default port 1234 with a model loaded.
21
+ - If you'd been overloading `LLAMACPP_BASE_URL=http://127.0.0.1:1234/v1` to point at LM Studio, that keeps working — but the cleaner path is now `--model lmstudio/local-model` with no env tweaking.
22
+
23
+ ---
24
+
5
25
  ## [v1.1.0] — 2026-05-03
6
26
 
7
27
  Issue-cleanup release. Three small features and one bug fix, driven by GitHub issues #12 / #13 / #15 / #16.
package/README.md CHANGED
@@ -51,19 +51,21 @@ Cloud models work the same way:
51
51
  little-coder --model anthropic/claude-haiku-4-5
52
52
  little-coder --model openai/gpt-4o-mini "What does this codebase do?"
53
53
  little-coder --model ollama/qwen3.5 # local Ollama
54
+ little-coder --model lmstudio/local-model # local LM Studio (whatever model you have loaded)
54
55
  little-coder --list-models # see everything pi knows about
55
56
  ```
56
57
 
57
58
  The agent uses the directory you launched it from as its working directory — `Read` / `Write` / `Edit` / `Bash` operate on your project, not on little-coder's install path.
58
59
 
59
- For local providers (llama.cpp, Ollama) pi expects *some* value in the API-key env even though local servers ignore it:
60
+ For local providers (llama.cpp, Ollama, LM Studio) pi expects *some* value in the API-key env even though local servers ignore it:
60
61
 
61
62
  ```bash
62
63
  export LLAMACPP_API_KEY=noop
63
64
  export OLLAMA_API_KEY=noop
65
+ export LMSTUDIO_API_KEY=noop
64
66
  ```
65
67
 
66
- `LLAMACPP_BASE_URL` and `OLLAMA_BASE_URL` override the defaults (`http://127.0.0.1:8888/v1`, `http://127.0.0.1:11434/v1`).
68
+ `LLAMACPP_BASE_URL`, `OLLAMA_BASE_URL`, and `LMSTUDIO_BASE_URL` override the defaults (`http://127.0.0.1:8888/v1`, `http://127.0.0.1:11434/v1`, `http://127.0.0.1:1234/v1`).
67
69
 
68
70
  For cloud providers, set the standard env (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.) and pi will discover it.
69
71
 
@@ -97,6 +99,17 @@ ollama pull qwen3.5 # 9.7B — the paper's model
97
99
  # or: ollama pull qwen3.6-35b-a3b
98
100
  ```
99
101
 
102
+ **Option C — LM Studio** (GUI; OpenAI-compatible server on port 1234):
103
+
104
+ 1. Install [LM Studio](https://lmstudio.ai/) and download a model (e.g. Qwen3.6 35B A3B GGUF).
105
+ 2. Open the **Developer** / **Local Server** tab, load the model, and click **Start Server** (default `http://127.0.0.1:1234`).
106
+ 3. Run little-coder:
107
+ ```bash
108
+ export LMSTUDIO_API_KEY=noop
109
+ little-coder --model lmstudio/local-model
110
+ ```
111
+ The shipped `lmstudio/local-model` id routes to whatever model LM Studio currently has loaded — no extra config needed for the single-model case. If you serve on a non-default port, set `LMSTUDIO_BASE_URL=http://127.0.0.1:<port>/v1`. To target a specific model when you have several loaded, add an entry to `~/.config/little-coder/models.json` (see **Configuring models** below).
112
+
100
113
  All small-model-specific extensions auto-disable for large/cloud models so they don't interfere.
101
114
 
102
115
  ---
@@ -140,7 +153,7 @@ Example — switch the llama.cpp port and bump `qwen3.6-35b-a3b` to a 150K conte
140
153
 
141
154
  Then verify with `little-coder --list-models` — you should see your overridden entry.
142
155
 
143
- `LLAMACPP_BASE_URL` and `OLLAMA_BASE_URL` env vars still beat both files for those two providers (legacy compat).
156
+ `LLAMACPP_BASE_URL`, `OLLAMA_BASE_URL`, and `LMSTUDIO_BASE_URL` env vars still beat both files for those three providers.
144
157
 
145
158
  `.pi/settings.json` is a separate concern: it controls per-model **profiles** (context_limit, thinking_budget, temperature, benchmark_overrides) referenced by the `<provider>/<id>` key. Profiles don't register or describe models — they only tune how little-coder runs against models that are already registered.
146
159
 
@@ -241,7 +254,7 @@ little-coder/
241
254
  ├── .pi/
242
255
  │ ├── settings.json # per-model profiles + benchmark_overrides (terminal_bench, gaia)
243
256
  │ └── extensions/ # 20 TypeScript extensions, auto-discovered by pi
244
- │ ├── llama-cpp-provider/ # data-driven provider registration from models.json (+ user override file)
257
+ │ ├── llama-cpp-provider/ # data-driven provider registration from models.json — ships llamacpp, ollama, lmstudio (+ user override file)
245
258
  │ ├── write-guard/ # Write refuses on existing files — the whitepaper invariant
246
259
  │ ├── extra-tools/ # glob, webfetch, websearch (pi ships grep/find)
247
260
  │ ├── skill-inject/ # per-turn tool-skill selection (error > recency > intent)
@@ -29,7 +29,9 @@ const here = dirname(fileURLToPath(import.meta.url));
29
29
  const pkgRoot = resolve(here, "..");
30
30
 
31
31
  // ---- 3. Resolve the bundled pi binary ----
32
- const piBin = join(pkgRoot, "node_modules", ".bin", "pi");
32
+ const isWindows = process.platform === "win32";
33
+ const piBinBase = join(pkgRoot, "node_modules", ".bin", "pi");
34
+ const piBin = isWindows && existsSync(`${piBinBase}.cmd`) ? `${piBinBase}.cmd` : piBinBase;
33
35
  if (!existsSync(piBin)) {
34
36
  console.error(
35
37
  `little-coder: cannot find pi at ${piBin}.\n` +
@@ -86,7 +88,11 @@ const piArgs = [
86
88
  ];
87
89
 
88
90
  // ---- 7. Spawn pi in the user's cwd ----
89
- const child = spawn(piBin, piArgs, {
91
+ const [spawnCmd, spawnArgs] = isWindows
92
+ ? ["cmd.exe", ["/c", piBin, ...piArgs]]
93
+ : [piBin, piArgs];
94
+
95
+ const child = spawn(spawnCmd, spawnArgs, {
90
96
  stdio: "inherit",
91
97
  cwd: process.cwd(),
92
98
  env: process.env,
package/models.json CHANGED
@@ -49,6 +49,22 @@
49
49
  "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
50
50
  }
51
51
  ]
52
+ },
53
+ "lmstudio": {
54
+ "api": "openai-completions",
55
+ "baseUrl": "http://127.0.0.1:1234/v1",
56
+ "apiKey": "LMSTUDIO_API_KEY",
57
+ "models": [
58
+ {
59
+ "id": "local-model",
60
+ "name": "LM Studio (currently-loaded local model)",
61
+ "reasoning": true,
62
+ "input": ["text"],
63
+ "contextWindow": 32768,
64
+ "maxTokens": 4096,
65
+ "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
66
+ }
67
+ ]
52
68
  }
53
69
  }
54
70
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "little-coder",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A pi-based coding agent optimized for small local language models. Reproduces the whitepaper's scaffold-model-fit adaptations as pi extensions.",
5
5
  "homepage": "https://github.com/itayinbarr/little-coder",
6
6
  "repository": {
@@ -9,22 +9,28 @@ user-invocable: false
9
9
  ## Edit Tool
10
10
  Replace exact text in a file. This is the **default tool for changing any existing file** — prefer it over Write for anything except creating a new file from scratch.
11
11
 
12
- REQUIRED: file_path (absolute), old_string (exact text to find), new_string (replacement)
13
- OPTIONAL: replace_all (boolean, replace all occurrences)
12
+ REQUIRED: path (absolute), edits (array of {oldText, newText})
13
+ OPTIONAL: none
14
14
 
15
15
  RULES:
16
- - old_string must match EXACTLY (whitespace, indentation, line endings all matter)
17
- - old_string must appear exactly ONCE unless replace_all=true
18
- - Include enough surrounding context (2-3 lines) to make old_string unique
19
- - To delete text: set new_string to ""
16
+ - Each `oldText` must match EXACTLY (whitespace, indentation, line endings all matter)
17
+ - Each `oldText` must be unique in the file — include 2-3 lines of surrounding context if needed
18
+ - `edits` is matched against the **original** file, not after earlier edits apply — do not overlap or nest
19
+ - To delete text: set `newText` to ""
20
20
  - Read the file first if you do not already have its current content
21
+ - Batch multiple disjoint changes in one call by passing multiple `edits[]` entries
21
22
 
22
- EXAMPLE:
23
+ EXAMPLE (single change):
23
24
  ```tool
24
- {"name": "Edit", "input": {"file_path": "/absolute/path/file.py", "old_string": "def hello():\n return 1", "new_string": "def hello():\n return 2"}}
25
+ {"name": "Edit", "input": {"path": "/absolute/path/file.py", "edits": [{"oldText": "def hello():\n return 1", "newText": "def hello():\n return 2"}]}}
26
+ ```
27
+
28
+ EXAMPLE (two changes in one call):
29
+ ```tool
30
+ {"name": "Edit", "input": {"path": "/absolute/path/file.py", "edits": [{"oldText": "MAX = 10", "newText": "MAX = 20"}, {"oldText": "TIMEOUT = 5", "newText": "TIMEOUT = 30"}]}}
25
31
  ```
26
32
 
27
33
  RECOVERY WHEN Edit FAILS:
28
34
  - "String not found" → Read the file to get the exact current content (whitespace often differs), then retry Edit with the exact string
29
- - "Found multiple times" → include more surrounding context so old_string is unique, then retry Edit
30
- - Do NOT fall back to Write just because Edit failed once — re-read, fix old_string, retry. Write is almost always the wrong recovery here for an existing file.
35
+ - "Found multiple times" → include more surrounding context so `oldText` is unique, then retry Edit
36
+ - Do NOT fall back to Write just because Edit failed once — re-read, fix `oldText`, retry. Write is almost always the wrong recovery here for an existing file.
@@ -10,17 +10,18 @@ user-invocable: false
10
10
  Search file contents with regex. Uses ripgrep.
11
11
 
12
12
  REQUIRED: pattern (regex pattern)
13
- OPTIONAL: path (directory/file), include (file glob filter like "*.py"), max_results (limit)
13
+ OPTIONAL: path (directory/file), glob (file glob filter like "*.py"), ignoreCase (bool), literal (bool — treat pattern as literal text), context (lines of context before/after), limit (max matches, default 100)
14
14
 
15
15
  RULES:
16
- - Supports full regex syntax
17
- - Use include to filter by file type (e.g. "*.py", "*.js")
16
+ - Supports full regex syntax (unless `literal: true`)
17
+ - Use `glob` to filter by file type (e.g. "*.py", "*.js")
18
+ - Use `limit` to cap results; default 100
18
19
  - Returns matching lines with file path and line number
19
20
  - Good for finding function definitions, imports, references
20
21
 
21
22
  EXAMPLE:
22
23
  ```tool
23
- {"name": "Grep", "input": {"pattern": "def main", "include": "*.py"}}
24
+ {"name": "Grep", "input": {"pattern": "def main", "glob": "*.py"}}
24
25
  ```
25
26
 
26
27
  EXAMPLE with path:
@@ -9,7 +9,7 @@ user-invocable: false
9
9
  ## Read Tool
10
10
  Read a file's contents with line numbers.
11
11
 
12
- REQUIRED: file_path (absolute path)
12
+ REQUIRED: path (absolute path)
13
13
  OPTIONAL: limit (max lines), offset (start line, 0-indexed)
14
14
 
15
15
  RULES:
@@ -19,10 +19,10 @@ RULES:
19
19
 
20
20
  EXAMPLE:
21
21
  ```tool
22
- {"name": "Read", "input": {"file_path": "/absolute/path/to/file.py"}}
22
+ {"name": "Read", "input": {"path": "/absolute/path/to/file.py"}}
23
23
  ```
24
24
 
25
25
  EXAMPLE with range:
26
26
  ```tool
27
- {"name": "Read", "input": {"file_path": "/absolute/path/to/file.py", "limit": 50, "offset": 100}}
27
+ {"name": "Read", "input": {"path": "/absolute/path/to/file.py", "limit": 50, "offset": 100}}
28
28
  ```
@@ -9,7 +9,7 @@ user-invocable: false
9
9
  ## Write Tool
10
10
  Create a **new** file with the given content. Creates parent directories automatically.
11
11
 
12
- REQUIRED: file_path (absolute), content (full file content)
12
+ REQUIRED: path (absolute), content (full file content)
13
13
 
14
14
  **Write is for creating new files only.** If the file already exists, Write will be **refused** by the tool and return an error telling you to use Edit instead. Do not retry Write on the same path — it will be refused again.
15
15
 
@@ -17,13 +17,13 @@ WHEN TO USE Write:
17
17
  - The file does not exist yet and you are creating it from scratch
18
18
 
19
19
  WHEN TO USE Edit INSTEAD:
20
- - ANY change to an existing file — bug fixes, refactors, format tweaks, adding a function, renaming a variable, everything. Edit takes old_string + new_string and patches in place.
20
+ - ANY change to an existing file — bug fixes, refactors, format tweaks, adding a function, renaming a variable, everything. Edit takes `path` + `edits: [{oldText, newText}]` and patches in place.
21
21
  - Iterating after a failed test — never retype the whole file
22
22
 
23
- If you need to completely replace an existing file's content, Edit can still do that: pass the entire current content as old_string and the full new content as new_string. Read the file first if you don't already have its current content.
23
+ If you need to completely replace an existing file's content, Edit can still do that: pass the entire current content as `oldText` and the full new content as `newText`. Read the file first if you don't already have its current content.
24
24
 
25
25
  EXAMPLE:
26
26
  ```tool
27
- {"name": "Write", "input": {"file_path": "/tmp/example/new_module.py", "content": "def hello():\n return 'hi'\n"}}
27
+ {"name": "Write", "input": {"path": "/tmp/example/new_module.py", "content": "def hello():\n return 'hi'\n"}}
28
28
  ```
29
29
  NOTE: Always use the EXACT file path given in the task, never a placeholder.
@@ -1,24 +0,0 @@
1
- ---
2
- name: agent-guidance
3
- type: tool-guidance
4
- target_tool: Agent
5
- priority: 6
6
- token_cost: 120
7
- user-invocable: false
8
- ---
9
- ## Agent Tool
10
- Spawn a sub-agent to handle a task autonomously.
11
-
12
- REQUIRED: prompt (task description for the sub-agent)
13
- OPTIONAL: subagent_type (coder/reviewer/researcher/tester/general-purpose), name (for messaging), isolation ("worktree" for git isolation)
14
-
15
- RULES:
16
- - Use for independent tasks that don't need your direct attention
17
- - Sub-agents get their own context and can use all tools
18
- - Use subagent_type to get specialized behavior
19
- - Use isolation="worktree" when the agent needs to modify files independently
20
-
21
- EXAMPLE:
22
- ```tool
23
- {"name": "Agent", "input": {"prompt": "Find all Python files that import requests and list them", "subagent_type": "researcher"}}
24
- ```