little-coder 1.8.3 → 1.9.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.
@@ -36,6 +36,13 @@ if (tooOld) {
36
36
  const here = dirname(fileURLToPath(import.meta.url));
37
37
  const pkgRoot = resolve(here, "..");
38
38
 
39
+ // Headless sub-coder fast-path. When the subagent extension re-invokes this
40
+ // launcher to spawn a child little-coder (--mode json -p), the update check and
41
+ // the global-settings merge below are pointless per-child overhead (network +
42
+ // disk) — and we want children to start fast. The env flag is set by
43
+ // .pi/extensions/subagent/spawn.ts::buildChildEnv.
44
+ const isSubagent = process.env.LITTLE_CODER_SUBAGENT === "1";
45
+
39
46
  // ---- 3. Resolve the bundled pi CLI entry point ----
40
47
  // We invoke pi's JS entry directly under the current Node binary instead of
41
48
  // the `node_modules/.bin/pi` shim. Two reasons:
@@ -110,10 +117,12 @@ try {
110
117
  } catch {
111
118
  // ignore — update-check just won't fire if we can't read the version
112
119
  }
113
- const exitAfterCheck = await checkForUpdate(currentVersion);
114
- if (exitAfterCheck) {
115
- // Successful update happened; user needs to re-run the new binary.
116
- process.exit(0);
120
+ if (!isSubagent) {
121
+ const exitAfterCheck = await checkForUpdate(currentVersion);
122
+ if (exitAfterCheck) {
123
+ // Successful update happened; user needs to re-run the new binary.
124
+ process.exit(0);
125
+ }
117
126
  }
118
127
 
119
128
  // ---- 6. Compose pi argv ----
@@ -124,10 +133,22 @@ if (exitAfterCheck) {
124
133
  // Strip our own flags before forwarding to pi so it doesn't reject them.
125
134
  const userArgs = process.argv.slice(2).filter((a) => a !== "--no-update-check");
126
135
  const agentsMd = join(pkgRoot, "AGENTS.md");
136
+
137
+ // Default the thinking level to "medium" for interactive sessions (pi's own
138
+ // default is "minimal"). Only when the user hasn't asked for a level themselves
139
+ // (--thinking, or the --model "provider/id:level" shorthand) and this isn't a
140
+ // headless/sub-coder run (--mode rpc/json) where the caller controls thinking.
141
+ const userPickedThinking =
142
+ userArgs.includes("--thinking") ||
143
+ userArgs.some((a, i) => a === "--model" && /:/.test(userArgs[i + 1] || ""));
144
+ const headless = isSubagent || userArgs.includes("--mode") || userArgs.includes("-p");
145
+ const thinkingArgs = !userPickedThinking && !headless ? ["--thinking", "medium"] : [];
146
+
127
147
  const piArgs = [
128
148
  "--no-context-files",
129
149
  "--no-extensions",
130
150
  ...(existsSync(agentsMd) ? ["--system-prompt", agentsMd] : []),
151
+ ...thinkingArgs,
131
152
  ...extArgs,
132
153
  ...userArgs,
133
154
  ];
@@ -167,7 +188,10 @@ if (process.env.PI_SKIP_VERSION_CHECK === undefined) {
167
188
  //
168
189
  // Existing keys are preserved. We only write when the desired value differs
169
190
  // from what's already on disk, so this is a no-op on warm launches.
170
- try {
191
+ //
192
+ // Skipped for headless sub-coders: they share the user's settings (already
193
+ // written by the interactive parent) and shouldn't each re-do the merge.
194
+ if (!isSubagent) try {
171
195
  const agentDirEnv = process.env.PI_CODING_AGENT_DIR;
172
196
  let agentDir;
173
197
  if (agentDirEnv && agentDirEnv.trim().length > 0) {
@@ -219,6 +243,33 @@ try {
219
243
  if (mutated) {
220
244
  writeFileSync(globalSettingsPath, JSON.stringify(globalSettings, null, 2));
221
245
  }
246
+
247
+ // ---- 8b. Free shift+tab for Plan Mode ----
248
+ // little-coder binds shift+tab to its plan-mode toggle (the plan-mode
249
+ // extension registers it). But shift+tab is pi's built-in "cycle thinking
250
+ // level", and pi (>= 0.79) refuses an extension shortcut that collides with a
251
+ // RESERVED built-in — it skips it with an "[Extension issues]" warning. pi
252
+ // builds its conflict map from the *resolved* keybindings, so moving the
253
+ // thinking-cycle action to another key in the user keybindings file removes
254
+ // shift+tab from that map and lets the extension claim it. We rebind the
255
+ // cycle to alt+t (the key plan-mode used to register itself) — only when the
256
+ // user hasn't already chosen their own binding for it, so a real user
257
+ // customization always wins. Non-destructive: every other binding is left
258
+ // untouched.
259
+ const keybindingsPath = join(agentDir, "keybindings.json");
260
+ let keybindings = {};
261
+ if (existsSync(keybindingsPath)) {
262
+ try {
263
+ const parsed = JSON.parse(readFileSync(keybindingsPath, "utf-8"));
264
+ if (parsed && typeof parsed === "object") keybindings = parsed;
265
+ } catch {
266
+ keybindings = {};
267
+ }
268
+ }
269
+ if (keybindings["app.thinking.cycle"] === undefined) {
270
+ keybindings["app.thinking.cycle"] = "alt+t";
271
+ writeFileSync(keybindingsPath, JSON.stringify(keybindings, null, 2));
272
+ }
222
273
  } catch {
223
274
  // Best-effort. If we can't write the settings (read-only HOME, etc.) pi
224
275
  // falls back to its built-in defaults — the [Extensions] block will show
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "little-coder",
3
- "version": "1.8.3",
3
+ "version": "1.9.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": {
@@ -38,7 +38,7 @@
38
38
  "postinstall": "node scripts/patch-pi.mjs"
39
39
  },
40
40
  "dependencies": {
41
- "@earendil-works/pi-coding-agent": "^0.75.3",
41
+ "@earendil-works/pi-coding-agent": "^0.79.4",
42
42
  "@sinclair/typebox": "^0.34.49",
43
43
  "playwright": "^1.59.1"
44
44
  },
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: dispatch-guidance
3
+ type: tool-guidance
4
+ target_tool: dispatch
5
+ priority: 6
6
+ token_cost: 140
7
+ user-invocable: false
8
+ ---
9
+ ## Dispatch Tool (sub-coders)
10
+ Delegate focused research to isolated child little-coder sessions ("sub-coders").
11
+ Each runs in its own context window, can READ the repo and BROWSE online
12
+ (read, grep, glob, webfetch, websearch, browser, read-only bash) but CANNOT
13
+ edit or write files. Each returns a concise report. Use this to gather
14
+ information without filling your own context with raw file dumps or web pages.
15
+
16
+ SINGLE: `task` (string) — one research question.
17
+ PARALLEL: `tasks` — array of `{label, task}`, up to 4, run concurrently.
18
+ OPTIONAL: `cwd` (defaults to the current working directory).
19
+
20
+ RULES:
21
+ - Give each parallel task a short, distinct `label` — it shows in the live tracker.
22
+ - Ask narrow, answerable questions ("how does auth work in this repo?", not "do everything").
23
+ - You receive only each sub-coder's short report; the full transcript stays in the
24
+ tool's details (not in your context). Act on the reports.
25
+ - Sub-coders can't change files — do the editing yourself after they report back.
26
+
27
+ EXAMPLE (single):
28
+ ```tool
29
+ {"name": "dispatch", "input": {"task": "Find where sessions are persisted in this repo and summarize the format."}}
30
+ ```
31
+
32
+ EXAMPLE (parallel):
33
+ ```tool
34
+ {"name": "dispatch", "input": {"tasks": [
35
+ {"label": "repo auth", "task": "How does authentication work in this codebase? Cite files."},
36
+ {"label": "lib docs", "task": "Look up the current recommended API for the jose JWT library online."}
37
+ ]}}
38
+ ```