little-coder 1.9.0 → 1.9.1
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.
- package/.pi/extensions/plan-mode/index.ts +10 -13
- package/CHANGELOG.md +10 -0
- package/README.md +2 -2
- package/bin/little-coder.mjs +19 -19
- package/package.json +1 -1
|
@@ -12,8 +12,8 @@ import { PlanStatus } from "./status.ts";
|
|
|
12
12
|
|
|
13
13
|
// Plan Mode — a Claude-Code-style "research, ask, then plan" flow.
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
15
|
+
// alt+p toggles plan mode (an indicator appears below the input). While it is
|
|
16
|
+
// on, submitting a prompt does NOT run a normal coding turn; instead the
|
|
17
17
|
// extension orchestrates:
|
|
18
18
|
// 1. decompose the request into 1-4 exploration tasks (a reasoning sub-coder),
|
|
19
19
|
// 2. dispatch those as read-only explorer sub-coders (isolated context; only
|
|
@@ -27,9 +27,8 @@ import { PlanStatus } from "./status.ts";
|
|
|
27
27
|
// child little-coder (spawned via ../subagent/spawn.ts), and the final plan is
|
|
28
28
|
// injected as a normal turn via pi.sendUserMessage so it lands in the chat.
|
|
29
29
|
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
// thinking-level cycle to alt+t to keep it reachable.
|
|
30
|
+
// alt+p is unbound by pi, so the extension can claim it cleanly without
|
|
31
|
+
// shadowing any built-in (shift+tab stays pi's thinking-level cycle — issue #47).
|
|
33
32
|
|
|
34
33
|
const honey = (s: string) => `\x1b[38;2;225;90;31m${s}\x1b[39m`;
|
|
35
34
|
const gray = (s: string) => `\x1b[90m${s}\x1b[39m`;
|
|
@@ -50,7 +49,7 @@ let pendingSynthesis: { digest: string; answers: string } | null = null;
|
|
|
50
49
|
let synthesisActive = false;
|
|
51
50
|
|
|
52
51
|
function indicatorLines(): string[] {
|
|
53
|
-
return [`${honey("◆")} ${honey("PLAN MODE")} ${gray("(
|
|
52
|
+
return [`${honey("◆")} ${honey("PLAN MODE")} ${gray("(alt+p to exit)")}`];
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
function setIndicator(ctx: any, on: boolean): void {
|
|
@@ -269,8 +268,10 @@ async function orchestrate(pi: ExtensionAPI, ctx: any, prompt: string): Promise<
|
|
|
269
268
|
}
|
|
270
269
|
|
|
271
270
|
export default function (pi: ExtensionAPI) {
|
|
272
|
-
//
|
|
273
|
-
|
|
271
|
+
// alt+p toggles plan mode. pi leaves alt+p unbound, so this doesn't collide
|
|
272
|
+
// with any built-in and shift+tab stays bound to pi's thinking-level cycle
|
|
273
|
+
// (issue #47).
|
|
274
|
+
pi.registerShortcut("alt+p", {
|
|
274
275
|
description: "Toggle plan mode",
|
|
275
276
|
handler: (ctx: any) => {
|
|
276
277
|
if (orchestrating) return; // mid-plan: ignore toggles
|
|
@@ -280,10 +281,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
280
281
|
},
|
|
281
282
|
});
|
|
282
283
|
|
|
283
|
-
// The thinking-level cycle keeps working on alt+t: the launcher rebinds pi's
|
|
284
|
-
// built-in `app.thinking.cycle` from shift+tab to alt+t so shift+tab is free
|
|
285
|
-
// for plan mode (see bin/little-coder.mjs §8b). No extension shortcut needed.
|
|
286
|
-
|
|
287
284
|
// Intercept a submitted prompt while plan mode is on and run the orchestration
|
|
288
285
|
// instead of a normal coding turn.
|
|
289
286
|
pi.on("input", async (event, ctx) => {
|
|
@@ -357,7 +354,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
357
354
|
});
|
|
358
355
|
} else {
|
|
359
356
|
(ctx as any).ui?.notify?.(
|
|
360
|
-
"plan not implemented — refine your request, or
|
|
357
|
+
"plan not implemented — refine your request, or alt+p to leave plan mode",
|
|
361
358
|
"info",
|
|
362
359
|
);
|
|
363
360
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
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.9.1] — 2026-06-08
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **Plan Mode shortcut moved to `alt+p` so `shift+tab` stays pi's thinking-level cycle** ([#47](https://github.com/itayinbarr/little-coder/issues/47)). v1.9.0 claimed `shift+tab` for Plan Mode by rebinding pi's built-in `app.thinking.cycle` to `alt+t` in `~/.pi/agent/keybindings.json`. That collided with the muscle memory of every existing pi user — `shift+tab` is the documented thinking cycle — and pi (≥ 0.79) also surfaced an `[Extension issues]` warning whenever the rebind hadn't taken yet. Plan Mode now registers on **`alt+p`** instead (unbound by pi, so the extension claims it cleanly with no shadowing), and `shift+tab` returns to pi's default behavior. The launcher also performs a **one-time cleanup**: on first run after upgrade, if `~/.pi/agent/keybindings.json` still has the v1.9.0 rewrite (`app.thinking.cycle: "alt+t"` exactly), it is removed; any binding you set yourself is preserved untouched. README and the Plan-Mode indicator (`(alt+p to exit)`) updated to match.
|
|
9
|
+
|
|
10
|
+
### Notes for upgraders
|
|
11
|
+
- No CLI-flag or public-API changes. **Plan Mode is now `alt+p`** (was `shift+tab` in v1.9.0). `shift+tab` is again pi's thinking-level cycle. If you customized `app.thinking.cycle` yourself in `~/.pi/agent/keybindings.json`, your binding is left alone.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
5
15
|
## [v1.9.0] — 2026-06-15
|
|
6
16
|
|
|
7
17
|
### Added
|
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ The agent uses the directory you launched it from as its working directory — `
|
|
|
62
62
|
|
|
63
63
|
### Interactive features
|
|
64
64
|
|
|
65
|
-
- **Plan Mode** — press **
|
|
65
|
+
- **Plan Mode** — press **alt+p** to toggle (a `◆ PLAN MODE` indicator shows below the input). Submit a request and little-coder researches it with sub-coders, asks you 1-3 clarifying questions (each with suggested answers and a free-text option), then writes a plan in the chat instead of editing anything. **Esc** cancels a plan mid-run. (**shift+tab** stays pi's thinking-level cycle.)
|
|
66
66
|
- **Prompt history** — from an empty input, **↑** recalls your recent prompts (most-recent first), **↓** walks forward. History persists across sessions, so a fresh session can recall prompts from earlier runs.
|
|
67
67
|
- **Sub-coders (`dispatch`)** — little-coder can spawn isolated child sessions to research a question (read the repo + browse online, read-only) and report back concisely, without cluttering the main conversation. A live panel above the input tracks them. Tune parallelism with `LITTLE_CODER_SUBCODER_CONCURRENCY` (default 2).
|
|
68
68
|
- **Sessions** — each session is auto-named from your first prompt (rename with `/name`) and shown in the terminal tab title. Use `/resume` to list and reopen past sessions for the current directory.
|
|
@@ -342,7 +342,7 @@ little-coder/
|
|
|
342
342
|
│ ├── settings.json # per-model profiles + benchmark_overrides (terminal_bench, gaia)
|
|
343
343
|
│ └── extensions/ # 27 TypeScript extensions, auto-discovered by pi
|
|
344
344
|
│ ├── branding/ # little-coder startup header + terminal title + session auto-naming
|
|
345
|
-
│ ├── plan-mode/ #
|
|
345
|
+
│ ├── plan-mode/ # alt+p "research → ask → plan" flow (sub-coders + clarifying questions → written plan)
|
|
346
346
|
│ ├── subagent/ # `dispatch` tool: isolated read/browse-only sub-coders + live tracker (spawn.ts engine)
|
|
347
347
|
│ ├── prompt-history/ # up-arrow recall of recent prompts (from an empty input)
|
|
348
348
|
│ ├── llama-cpp-provider/ # data-driven provider registration from models.json — ships llamacpp, ollama, lmstudio (+ user override file)
|
package/bin/little-coder.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
mkdirSync,
|
|
10
10
|
readdirSync,
|
|
11
11
|
readFileSync,
|
|
12
|
+
rmSync,
|
|
12
13
|
statSync,
|
|
13
14
|
writeFileSync,
|
|
14
15
|
} from "node:fs";
|
|
@@ -244,32 +245,31 @@ if (!isSubagent) try {
|
|
|
244
245
|
writeFileSync(globalSettingsPath, JSON.stringify(globalSettings, null, 2));
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
// ---- 8b.
|
|
248
|
-
//
|
|
249
|
-
// extension
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
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.
|
|
248
|
+
// ---- 8b. One-time cleanup of the v1.9.0 keybinding rewrite ----
|
|
249
|
+
// v1.9.0 wrote `app.thinking.cycle: "alt+t"` into ~/.pi/agent/keybindings.json
|
|
250
|
+
// so the plan-mode extension could claim shift+tab (issue #47). Plan mode now
|
|
251
|
+
// lives on alt+p, so shift+tab should go back to pi's default thinking-cycle
|
|
252
|
+
// binding — but only if the value is *exactly* the one we wrote. A user who
|
|
253
|
+
// chose their own binding (anything ≠ "alt+t") wins.
|
|
259
254
|
const keybindingsPath = join(agentDir, "keybindings.json");
|
|
260
|
-
let keybindings = {};
|
|
261
255
|
if (existsSync(keybindingsPath)) {
|
|
262
256
|
try {
|
|
263
257
|
const parsed = JSON.parse(readFileSync(keybindingsPath, "utf-8"));
|
|
264
|
-
if (parsed && typeof parsed === "object"
|
|
258
|
+
if (parsed && typeof parsed === "object" && parsed["app.thinking.cycle"] === "alt+t") {
|
|
259
|
+
delete parsed["app.thinking.cycle"];
|
|
260
|
+
if (Object.keys(parsed).length === 0) {
|
|
261
|
+
// Don't leave an empty {} sitting around — remove the file so pi
|
|
262
|
+
// reads its defaults cleanly.
|
|
263
|
+
rmSync(keybindingsPath);
|
|
264
|
+
} else {
|
|
265
|
+
writeFileSync(keybindingsPath, JSON.stringify(parsed, null, 2));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
265
268
|
} catch {
|
|
266
|
-
|
|
269
|
+
// Corrupted JSON or unreadable — leave it alone; pi will surface its own error.
|
|
267
270
|
}
|
|
268
271
|
}
|
|
269
|
-
|
|
270
|
-
keybindings["app.thinking.cycle"] = "alt+t";
|
|
271
|
-
writeFileSync(keybindingsPath, JSON.stringify(keybindings, null, 2));
|
|
272
|
-
}
|
|
272
|
+
|
|
273
273
|
} catch {
|
|
274
274
|
// Best-effort. If we can't write the settings (read-only HOME, etc.) pi
|
|
275
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.9.
|
|
3
|
+
"version": "1.9.1",
|
|
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": {
|