@msdavid/pi-distro 0.2.0 → 0.3.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.
- package/CHANGELOG.md +21 -1
- package/README.md +23 -12
- package/docs/authoring.md +2 -2
- package/extensions/catalogue.ts +42 -33
- package/extensions/deploy.ts +5 -4
- package/extensions/github.ts +124 -0
- package/extensions/info.ts +30 -12
- package/extensions/pick.ts +5 -4
- package/extensions/resolve.ts +33 -6
- package/extensions/save.ts +4 -3
- package/extensions/show.ts +22 -5
- package/extensions/update.ts +1 -1
- package/package.json +1 -2
- package/skills/pi-distro/SKILL.md +8 -7
- package/harnesses/minimal/README.md +0 -21
- package/harnesses/minimal/files/AGENTS.md +0 -20
- package/harnesses/minimal/files/settings.json +0 -4
- package/harnesses/minimal/harness.md +0 -24
- package/harnesses/pi-distro-one/README.md +0 -50
- package/harnesses/pi-distro-one/files/.pi/extensions/claude-statusline.ts +0 -220
- package/harnesses/pi-distro-one/files/AGENTS.md +0 -166
- package/harnesses/pi-distro-one/files/settings.json +0 -9
- package/harnesses/pi-distro-one/harness.md +0 -79
- package/harnesses/web-fullstack/README.md +0 -25
- package/harnesses/web-fullstack/files/.pi/prompts/review.md +0 -12
- package/harnesses/web-fullstack/files/AGENTS.md +0 -37
- package/harnesses/web-fullstack/files/settings.json +0 -11
- package/harnesses/web-fullstack/harness.md +0 -40
package/extensions/show.ts
CHANGED
|
@@ -7,15 +7,15 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
7
7
|
import { readFileSync, existsSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
|
|
10
|
-
import { readFullHarnessMd, listBundledFiles, readCatalogue, findHarness, parsePackageList } from "./catalogue.ts";
|
|
10
|
+
import { readFullHarnessMd, listBundledFiles, readCatalogue, findHarness, parsePackageList, sourceLabel } from "./catalogue.ts";
|
|
11
11
|
import type { HarnessEntry } from "./catalogue.ts";
|
|
12
12
|
import { parseFrontmatter, extractBody } from "./frontmatter.ts";
|
|
13
|
-
import { looksLikeGithubRef, parseGithubRef, fetchGithubDistro } from "./github.ts";
|
|
13
|
+
import { looksLikeGithubRef, parseGithubRef, fetchGithubDistro, fetchOfficialDistro } from "./github.ts";
|
|
14
14
|
import { display } from "./util.ts";
|
|
15
15
|
|
|
16
16
|
/** Build the dry-run preview for a harness (used by local and GitHub show). */
|
|
17
17
|
export async function buildShowPreview(harness: HarnessEntry): Promise<string> {
|
|
18
|
-
const fullMd = await readFullHarnessMd(harness.harnessMdPath);
|
|
18
|
+
const fullMd = await readFullHarnessMd(harness.harnessMdPath!);
|
|
19
19
|
const fm = parseFrontmatter(fullMd);
|
|
20
20
|
const body = extractBody(fullMd);
|
|
21
21
|
const packages = parsePackageList(body);
|
|
@@ -41,7 +41,7 @@ export async function buildShowPreview(harness: HarnessEntry): Promise<string> {
|
|
|
41
41
|
- **Description:** ${fm?.description ?? harness.description}
|
|
42
42
|
- **Version:** ${fm?.version ?? harness.version}
|
|
43
43
|
- **Tags:** ${tags}
|
|
44
|
-
- **Source:** ${harness.source}
|
|
44
|
+
- **Source:** ${sourceLabel(harness.source)}
|
|
45
45
|
|
|
46
46
|
### pi packages to install
|
|
47
47
|
${packages.length > 0 ? packages.map((p) => `- \`${p}\``).join("\n") : "_(none)_"}
|
|
@@ -84,7 +84,8 @@ export async function handleShow(pi: ExtensionAPI, name?: string): Promise<void>
|
|
|
84
84
|
return;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
// Local catalogue
|
|
87
|
+
// Local catalogue (official distros appear here as needsFetch listing entries;
|
|
88
|
+
// user distros are read straight from disk).
|
|
88
89
|
const catalogue = await readCatalogue();
|
|
89
90
|
const harness = findHarness(name, catalogue);
|
|
90
91
|
if (!harness) {
|
|
@@ -92,5 +93,21 @@ export async function handleShow(pi: ExtensionAPI, name?: string): Promise<void>
|
|
|
92
93
|
display(pi, `Distro '${name}' not found. Available: ${available}`);
|
|
93
94
|
return;
|
|
94
95
|
}
|
|
96
|
+
// Official listing entry — fetch the actual distro from GitHub before previewing.
|
|
97
|
+
if (harness.needsFetch) {
|
|
98
|
+
let fetched;
|
|
99
|
+
try {
|
|
100
|
+
fetched = fetchOfficialDistro(harness.name);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
display(pi, `**Error:** ${err instanceof Error ? err.message : String(err)}`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
display(pi, await buildShowPreview(fetched.entry));
|
|
107
|
+
} finally {
|
|
108
|
+
fetched.cleanup();
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
95
112
|
display(pi, await buildShowPreview(harness));
|
|
96
113
|
}
|
package/extensions/update.ts
CHANGED
|
@@ -55,7 +55,7 @@ export async function handleUpdate(pi: ExtensionAPI, ctx: ExtensionCommandContex
|
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
} else {
|
|
58
|
-
// Local catalogue (
|
|
58
|
+
// Local catalogue (user distros only — official distros use the github: branch above).
|
|
59
59
|
current = await resolveCatalogueEntry(prov.appliedHarness);
|
|
60
60
|
if (!current) {
|
|
61
61
|
ctx.ui.notify(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@msdavid/pi-distro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Reusable, composable configurations for the pi coding agent — seed distros, project snapshots, and GitHub sharing with version-aware updates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,7 +28,6 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"extensions",
|
|
30
30
|
"skills",
|
|
31
|
-
"harnesses",
|
|
32
31
|
"README.md",
|
|
33
32
|
"docs",
|
|
34
33
|
"LICENSE",
|
|
@@ -40,7 +40,8 @@ When you receive a kickoff message from the `deploy` command, it contains:
|
|
|
40
40
|
- The merge-don't-clobber rule.
|
|
41
41
|
- An instruction to write/update provenance.
|
|
42
42
|
|
|
43
|
-
The distro may come from the local catalogue (
|
|
43
|
+
The distro may come from the local catalogue (official, fetched from GitHub, or
|
|
44
|
+
user-saved) or directly from another GitHub repo
|
|
44
45
|
(`/pi-distro deploy owner/repo`). For GitHub distros, the extension has already
|
|
45
46
|
cloned the repo, displayed a security warning + preview, and obtained the user's
|
|
46
47
|
explicit confirmation before sending the kickoff — so you can proceed normally.
|
|
@@ -130,15 +131,15 @@ them from there as usual.
|
|
|
130
131
|
<!-- pi-distro provenance
|
|
131
132
|
appliedHarness: <name>
|
|
132
133
|
appliedVersion: <version>
|
|
133
|
-
sourceCatalogue: <user|
|
|
134
|
+
sourceCatalogue: <user|github:owner/repo[/subpath]>
|
|
134
135
|
lastUpdated: <ISO8601>
|
|
135
136
|
-->
|
|
136
137
|
```
|
|
137
138
|
|
|
138
139
|
- `appliedHarness`: the name of the distro that was deployed.
|
|
139
140
|
- `appliedVersion`: the version from the distro frontmatter.
|
|
140
|
-
- `sourceCatalogue`: `
|
|
141
|
-
|
|
141
|
+
- `sourceCatalogue`: `user` if from `~/.pi/harnesses/`, or
|
|
142
|
+
`github:owner/repo[/subpath]` (official distros are `github:msdavid/pi-distro/harnesses/<name>`).
|
|
142
143
|
- `lastUpdated`: current timestamp in ISO 8601 format.
|
|
143
144
|
|
|
144
145
|
6. **Report.** Summarise what was deployed, merged, skipped, and any packages installed.
|
|
@@ -296,10 +297,10 @@ be bundled.
|
|
|
296
297
|
the kickoff message exactly:
|
|
297
298
|
- Ask the user whether to **save as a new distro** or **update an existing distro**.
|
|
298
299
|
- If **save as new**: validate the slug; warn on collisions with existing user
|
|
299
|
-
distros or
|
|
300
|
+
distros or official distro names (fetched from GitHub) and ask whether to overwrite.
|
|
300
301
|
- If **update existing**: let the user pick from the existing user distros listed in
|
|
301
|
-
the kickoff. Refuse to update names not in that list (
|
|
302
|
-
|
|
302
|
+
the kickoff. Refuse to update names not in that list (official/GitHub distros are
|
|
303
|
+
read-only locally). Back the old distro up to `~/.pi/harnesses/.trash/<name>-<timestamp>/` before
|
|
303
304
|
overwriting. **Bump the version**: read the old distro's `version` and increment it
|
|
304
305
|
with semver — **patch** for small tweaks/bug fixes, **minor** for new capabilities or
|
|
305
306
|
config additions, **major** for breaking changes (removed packages, changed
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# minimal
|
|
2
|
-
|
|
3
|
-
A clean starting point for any project. Drops in a basic `AGENTS.md` and a sensible
|
|
4
|
-
`.pi/settings.json` — nothing more, nothing less.
|
|
5
|
-
|
|
6
|
-
## What it sets up
|
|
7
|
-
|
|
8
|
-
- **`AGENTS.md`** — a minimal explore-before-acting methodology: investigate before
|
|
9
|
-
implementing, make surgical changes, keep solutions simple.
|
|
10
|
-
- **`.pi/settings.json`** — a sensible default thinking level (pi's default
|
|
11
|
-
one-at-a-time steering already applies).
|
|
12
|
-
|
|
13
|
-
No pi packages are installed. Use this as a blank canvas — tailor the `AGENTS.md` to your
|
|
14
|
-
project's build/test commands and conventions, then layer on packages and skills as you go.
|
|
15
|
-
|
|
16
|
-
## When to use
|
|
17
|
-
|
|
18
|
-
- You want a clean baseline without opinions about which packages to install.
|
|
19
|
-
- You're starting a new project and just want the agent oriented with good defaults.
|
|
20
|
-
- You want to build your own distro from a minimal starting point (deploy this, then
|
|
21
|
-
`/pi-distro save` after customizing).
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# Project Instructions
|
|
2
|
-
|
|
3
|
-
Brief guidance for working in this project. Add your own rules below.
|
|
4
|
-
|
|
5
|
-
## Build & Test Commands
|
|
6
|
-
|
|
7
|
-
<!-- e.g. npm run build, npm test, npm run lint -->
|
|
8
|
-
- Build:
|
|
9
|
-
- Test:
|
|
10
|
-
- Lint:
|
|
11
|
-
|
|
12
|
-
## Code Style
|
|
13
|
-
|
|
14
|
-
<!-- e.g. use TypeScript, 2-space indent, prefer named exports -->
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
## Project Structure Notes
|
|
18
|
-
|
|
19
|
-
<!-- e.g. src/ for source, tests/ for tests, docs/ for documentation -->
|
|
20
|
-
-
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: minimal
|
|
3
|
-
title: Minimal
|
|
4
|
-
description: Clean starting point: a basic AGENTS.md and .pi/settings.json.
|
|
5
|
-
version: 0.1.0
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Minimal
|
|
9
|
-
|
|
10
|
-
## Bundled files
|
|
11
|
-
The following bundled files are provided under `files/` and should be placed into the
|
|
12
|
-
target project. For any path that already exists, do NOT overwrite — show the user a diff
|
|
13
|
-
and ask whether to overwrite, keep theirs, or merge. Merge JSON settings objects field by
|
|
14
|
-
field. Append (not replace) `AGENTS.md` content under a clearly-delimited section.
|
|
15
|
-
|
|
16
|
-
- `files/AGENTS.md` → `./AGENTS.md`
|
|
17
|
-
- `files/settings.json` → `./.pi/settings.json` (merge with existing settings)
|
|
18
|
-
|
|
19
|
-
## Context
|
|
20
|
-
This harness provides a minimal starting point for any project. After placing the bundled
|
|
21
|
-
files, ensure the `AGENTS.md` is tailored to the project's actual build/test commands and
|
|
22
|
-
conventions. The `settings.json` sets a sensible default thinking level — adjust as needed.
|
|
23
|
-
|
|
24
|
-
No additional pi packages are required for this harness.
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# pi-distro-one
|
|
2
|
-
|
|
3
|
-
A complete Claude Code–style coding agent configuration for pi. The primary capability is
|
|
4
|
-
spawning and coordinating autonomous sub-agents; everything else — web research, browser
|
|
5
|
-
automation, live shell, model routing, task management — is integrated in support of that.
|
|
6
|
-
|
|
7
|
-
This is a personal draft shot at using the most popular pi coding packages to closely
|
|
8
|
-
resemble the capabilities of Claude Code. It's opinionated and not for everyone — but it
|
|
9
|
-
makes a great starting point to fork and make your own.
|
|
10
|
-
|
|
11
|
-
## What it sets up
|
|
12
|
-
|
|
13
|
-
- **`AGENTS.md`** — an explore-before-acting working methodology: investigate before
|
|
14
|
-
implementing, surface interpretations and tradeoffs, make surgical changes, keep
|
|
15
|
-
solutions simple, execute against verifiable goals, and treat documentation as part of
|
|
16
|
-
the implementation.
|
|
17
|
-
- **`.pi/settings.json`** — high thinking level, one-at-a-time steering, hidden thinking
|
|
18
|
-
blocks, and hardware cursor.
|
|
19
|
-
- **`.pi/extensions/claude-statusline.ts`** — a Claude-style status-line footer
|
|
20
|
-
(model | dir | thinking level | context-window bar gauge + cache % | git branch status).
|
|
21
|
-
Auto-enables on session start and **auto-expands tool outputs** (the Ctrl+O effect) so
|
|
22
|
-
full output is visible by default. Toggle with the `/claude-statusline` command.
|
|
23
|
-
|
|
24
|
-
## Packages installed (all project-local)
|
|
25
|
-
|
|
26
|
-
| Package | What it does |
|
|
27
|
-
|---------|-------------|
|
|
28
|
-
| `npm:@tintinweb/pi-subagents` | Claude Code–style autonomous sub-agents — spawn specialized agents in isolated sessions with own tools/prompt/model/thinking; parallel execution, live widget, mid-run steering, resumption (**primary capability**) |
|
|
29
|
-
| `npm:pi-web-access` | Web search, URL fetching, GitHub cloning, PDF/video extraction |
|
|
30
|
-
| `npm:pi-agent-browser-native` | Real browser automation and web interaction |
|
|
31
|
-
| `npm:pi-bash-live-view` | Live terminal rendering for shell commands |
|
|
32
|
-
| `npm:@yeliu84/pi-model-router` | Model routing / fallback across providers |
|
|
33
|
-
| `npm:@robhowley/pi-openrouter` | OpenRouter provider integration |
|
|
34
|
-
| `npm:@juicesharp/rpiv-todo` | Task list management |
|
|
35
|
-
| `npm:pi-goal` | Persistent autonomous goals — `/goal` loops until complete, paused, or budget-limited |
|
|
36
|
-
| `npm:@trevonistrevon/pi-loop` | Cron/event-based agent re-wake loops + background process monitoring — schedule agents to re-wake on time/events |
|
|
37
|
-
| `npm:@juicesharp/rpiv-ask-user-question` | Structured questionnaire the model puts to the user (typed options instead of guessing) |
|
|
38
|
-
|
|
39
|
-
## Prerequisites
|
|
40
|
-
|
|
41
|
-
- **Auth and model routing** are not part of this distro — configure those independently
|
|
42
|
-
(e.g. set `defaultProvider` and `defaultModel` in your global `~/.pi/settings.json`,
|
|
43
|
-
or use OpenRouter via the `@robhowley/pi-openrouter` package installed above).
|
|
44
|
-
- **Restart pi** after deploying — packages and extensions load at startup.
|
|
45
|
-
|
|
46
|
-
## When to use
|
|
47
|
-
|
|
48
|
-
- You want a Claude Code–like experience in pi.
|
|
49
|
-
- You work on complex tasks that benefit from multi-agent coordination.
|
|
50
|
-
- You want an opinionated, batteries-included setup to fork and customize.
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Claude-style status line for pi.
|
|
3
|
-
*
|
|
4
|
-
* Clones the Claude Code footer format:
|
|
5
|
-
* <model> | <dir> | <style> | [████████░░] 80% | 45% cached | <branch (status)>
|
|
6
|
-
*
|
|
7
|
-
* - model : active model name/id
|
|
8
|
-
* - dir : basename of ctx.cwd
|
|
9
|
-
* - style : current thinking level (pi's analog of Claude's "output style")
|
|
10
|
-
* - context : UTF-8 bar gauge of context-window usage (eighths resolution),
|
|
11
|
-
* colored by level (accent → warning → error), plus usage % and
|
|
12
|
-
* cache-read % (same token math as the Claude Code statusLine
|
|
13
|
-
* command in ~/.claude/settings.json)
|
|
14
|
-
* - git : branch from footerData + dirty(!)/untracked(?) markers, refreshed
|
|
15
|
-
* in the background so render() never spawns a process
|
|
16
|
-
*
|
|
17
|
-
* Auto-enables on session start. Also auto-expands tool outputs on session start
|
|
18
|
-
* (the Ctrl+O effect) so full output is visible by default. Toggle the status line
|
|
19
|
-
* with the /claude-statusline command.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { execSync } from "node:child_process";
|
|
23
|
-
import { basename } from "node:path";
|
|
24
|
-
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
25
|
-
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
26
|
-
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
27
|
-
|
|
28
|
-
const REFRESH_MS = 2000;
|
|
29
|
-
|
|
30
|
-
/** Eighth-block characters for sub-cell bar precision (index = eighths, 0 = empty). */
|
|
31
|
-
const EIGHTHS = ["", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"];
|
|
32
|
-
const BAR_WIDTH = 10;
|
|
33
|
-
|
|
34
|
-
interface GitCache {
|
|
35
|
-
branch: string | null;
|
|
36
|
-
dirty: boolean;
|
|
37
|
-
untracked: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default function (pi: ExtensionAPI) {
|
|
41
|
-
let enabled = false;
|
|
42
|
-
let timer: ReturnType<typeof setInterval> | undefined;
|
|
43
|
-
let gitCache: GitCache = { branch: null, dirty: false, untracked: false };
|
|
44
|
-
|
|
45
|
-
function refreshGit(cwd: string): void {
|
|
46
|
-
try {
|
|
47
|
-
// One porcelain call gives us both dirty (staged/modified) and untracked flags.
|
|
48
|
-
const out = execSync("git status --porcelain=v1 -z", {
|
|
49
|
-
cwd,
|
|
50
|
-
encoding: "utf8",
|
|
51
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
52
|
-
timeout: 1500,
|
|
53
|
-
});
|
|
54
|
-
let dirty = false;
|
|
55
|
-
let untracked = false;
|
|
56
|
-
// -z separates records with NUL. Each record: <XY><space><path>\0...
|
|
57
|
-
const records = out.split("\0").filter((r) => r.length > 0);
|
|
58
|
-
for (const rec of records) {
|
|
59
|
-
const xy = rec.slice(0, 2);
|
|
60
|
-
if (xy === "??") {
|
|
61
|
-
untracked = true;
|
|
62
|
-
} else {
|
|
63
|
-
dirty = true;
|
|
64
|
-
}
|
|
65
|
-
if (dirty && untracked) break;
|
|
66
|
-
}
|
|
67
|
-
gitCache = { ...gitCache, dirty, untracked };
|
|
68
|
-
} catch {
|
|
69
|
-
// Not a git repo, or git unavailable — clear flags.
|
|
70
|
-
gitCache = { ...gitCache, dirty: false, untracked: false };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function startTimer(ctx: ExtensionContext): void {
|
|
75
|
-
stopTimer();
|
|
76
|
-
refreshGit(ctx.cwd);
|
|
77
|
-
timer = setInterval(() => refreshGit(ctx.cwd), REFRESH_MS);
|
|
78
|
-
// Don't keep the event loop alive solely for footer refreshes.
|
|
79
|
-
if (timer && typeof timer.unref === "function") timer.unref();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function stopTimer(): void {
|
|
83
|
-
if (timer) {
|
|
84
|
-
clearInterval(timer);
|
|
85
|
-
timer = undefined;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Render a fixed-width UTF-8 bar gauge for a fraction in [0,1].
|
|
91
|
-
* Filled cells use full blocks; the boundary cell uses an eighth-block for
|
|
92
|
-
* sub-cell precision. Returns [filled, empty] strings for separate coloring.
|
|
93
|
-
*/
|
|
94
|
-
function barGauge(fraction: number): { filled: string; empty: string } {
|
|
95
|
-
const clamped = Math.max(0, Math.min(1, fraction));
|
|
96
|
-
const totalEighths = Math.round(clamped * BAR_WIDTH * 8);
|
|
97
|
-
const fullCells = Math.floor(totalEighths / 8);
|
|
98
|
-
const remainder = totalEighths % 8;
|
|
99
|
-
const filled = "█".repeat(fullCells) + (remainder > 0 ? EIGHTHS[remainder] : "");
|
|
100
|
-
const filledWidth = fullCells + (remainder > 0 ? 1 : 0);
|
|
101
|
-
const empty = "░".repeat(Math.max(0, BAR_WIDTH - filledWidth));
|
|
102
|
-
return { filled, empty };
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function enable(ctx: ExtensionContext): void {
|
|
106
|
-
enabled = true;
|
|
107
|
-
startTimer(ctx);
|
|
108
|
-
|
|
109
|
-
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
110
|
-
const unsub = footerData.onBranchChange(() => {
|
|
111
|
-
gitCache = { ...gitCache, branch: footerData.getGitBranch() };
|
|
112
|
-
tui.requestRender();
|
|
113
|
-
});
|
|
114
|
-
gitCache = { ...gitCache, branch: footerData.getGitBranch() };
|
|
115
|
-
|
|
116
|
-
return {
|
|
117
|
-
dispose: () => {
|
|
118
|
-
unsub();
|
|
119
|
-
stopTimer();
|
|
120
|
-
},
|
|
121
|
-
invalidate() {},
|
|
122
|
-
render(width: number): string[] {
|
|
123
|
-
const segments: string[] = [];
|
|
124
|
-
|
|
125
|
-
// 1. Model
|
|
126
|
-
const model = ctx.model;
|
|
127
|
-
segments.push(theme.fg("accent", model?.name ?? model?.id ?? "no-model"));
|
|
128
|
-
|
|
129
|
-
// 2. Cwd basename
|
|
130
|
-
segments.push(theme.fg("dim", basename(ctx.cwd) || ctx.cwd));
|
|
131
|
-
|
|
132
|
-
// 3. Style -> thinking level (pi's analog of output style).
|
|
133
|
-
// getThinkingLevel() lives on the ExtensionAPI (pi), not on ctx.
|
|
134
|
-
segments.push(theme.fg("dim", pi.getThinkingLevel()));
|
|
135
|
-
|
|
136
|
-
// 4. Context: bar gauge + usage % + cache %.
|
|
137
|
-
// Use pi's own context estimate (ctx.getContextUsage) — it represents the
|
|
138
|
-
// current context size of the most recent request, matching Claude Code's
|
|
139
|
-
// `context_window.current_usage`. Summing per-message usage would double-
|
|
140
|
-
// count, since each request's input already includes prior context.
|
|
141
|
-
const usage = ctx.getContextUsage();
|
|
142
|
-
const usedPct = usage?.percent ?? 0;
|
|
143
|
-
|
|
144
|
-
// Cache-read % comes from the last assistant message's usage (the most
|
|
145
|
-
// recent request): cacheRead / (input + cacheWrite + cacheRead).
|
|
146
|
-
let cachePct = 0;
|
|
147
|
-
const branch = ctx.sessionManager.getBranch();
|
|
148
|
-
for (let i = branch.length - 1; i >= 0; i--) {
|
|
149
|
-
const e = branch[i];
|
|
150
|
-
if (e.type === "message" && e.message.role === "assistant") {
|
|
151
|
-
const u = (e.message as AssistantMessage).usage;
|
|
152
|
-
const totalInput = u.input + u.cacheWrite + u.cacheRead;
|
|
153
|
-
cachePct = totalInput > 0 ? Math.floor((u.cacheRead * 100) / totalInput) : 0;
|
|
154
|
-
break;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const { filled, empty } = barGauge(usedPct / 100);
|
|
159
|
-
const barColor = usedPct >= 95 ? "error" : usedPct >= 80 ? "warning" : "accent";
|
|
160
|
-
const bar = theme.fg(barColor, filled) + theme.fg("dim", empty);
|
|
161
|
-
const pctLabel = theme.fg("dim", `${Math.floor(usedPct)}%`);
|
|
162
|
-
|
|
163
|
-
let ctxInfo = `${bar} ${pctLabel}`;
|
|
164
|
-
if (cachePct > 0) ctxInfo += theme.fg("dim", ` | ${cachePct}% cached`);
|
|
165
|
-
segments.push(ctxInfo);
|
|
166
|
-
|
|
167
|
-
// 5. Git: branch (!dirty ?untracked)
|
|
168
|
-
if (gitCache.branch) {
|
|
169
|
-
let status = "";
|
|
170
|
-
if (gitCache.dirty) status += "!";
|
|
171
|
-
if (gitCache.untracked) status += "?";
|
|
172
|
-
const gitInfo = status ? `${gitCache.branch} (${status})` : gitCache.branch;
|
|
173
|
-
segments.push(theme.fg("dim", gitInfo));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Join with dim separators.
|
|
177
|
-
const sep = theme.fg("muted", " | ");
|
|
178
|
-
const line = segments.join(sep);
|
|
179
|
-
|
|
180
|
-
return [truncateToWidth(line, width)];
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function disable(ctx: ExtensionContext): void {
|
|
187
|
-
enabled = false;
|
|
188
|
-
stopTimer();
|
|
189
|
-
ctx.ui.setFooter(undefined);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Auto-enable on every (re)start, and auto-expand tool outputs (the Ctrl+O effect).
|
|
193
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
194
|
-
enable(ctx);
|
|
195
|
-
ctx.ui.setToolsExpanded(true);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
pi.on("session_shutdown", async (_event, _ctx) => {
|
|
199
|
-
stopTimer();
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// Refresh git right after a turn settles (catches file writes from the agent).
|
|
203
|
-
pi.on("turn_end", async (_event, ctx) => {
|
|
204
|
-
if (enabled) refreshGit(ctx.cwd);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
// Manual toggle.
|
|
208
|
-
pi.registerCommand("claude-statusline", {
|
|
209
|
-
description: "Toggle the Claude-style status line footer",
|
|
210
|
-
handler: async (_args, ctx) => {
|
|
211
|
-
if (enabled) {
|
|
212
|
-
disable(ctx);
|
|
213
|
-
ctx.ui.notify("Claude status line: off", "info");
|
|
214
|
-
} else {
|
|
215
|
-
enable(ctx);
|
|
216
|
-
ctx.ui.notify("Claude status line: on", "info");
|
|
217
|
-
}
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# Quick Rules
|
|
2
|
-
|
|
3
|
-
- Never commit or push until I explicitly ask
|
|
4
|
-
- **Always update documentation** when planning, implementing, or refactoring
|
|
5
|
-
- Update README, docs/, inline comments, docstrings, and any related docs
|
|
6
|
-
- Include documentation updates as explicit steps in plans
|
|
7
|
-
- **EYU** = Explain Your Understanding of the request and wait for approval
|
|
8
|
-
- Example: User says "EYU" → Summarize what you understood, then stop and wait
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Approach to Changes
|
|
13
|
-
|
|
14
|
-
Before planning or implementing any changes, adopt a "deep researcher" mindset.
|
|
15
|
-
|
|
16
|
-
## Core Principle
|
|
17
|
-
|
|
18
|
-
**Understand before you act.** Don't jump to implementation. Your first instinct should be to explore the codebase, not to write code.
|
|
19
|
-
|
|
20
|
-
## Surface, Don't Hide
|
|
21
|
-
|
|
22
|
-
- **Present interpretations, don't pick silently.** If a request has more than one reasonable reading, lay them out and let me choose.
|
|
23
|
-
- **Surface tradeoffs and push back.** If a simpler approach exists, say so. If the request seems wrong or overcomplicated, challenge it before building it.
|
|
24
|
-
- **Name confusion instead of papering over it.** If something is still unclear after investigating, stop and say exactly what's confusing.
|
|
25
|
-
- Investigate what the code can answer (see "When uncertain, investigate more"); *ask* only about genuine intent or scope ambiguity the code can't resolve.
|
|
26
|
-
|
|
27
|
-
## When to Explore vs Plan
|
|
28
|
-
|
|
29
|
-
- **Always explore first** before any plan or implementation
|
|
30
|
-
- **Default behavior**: Explore → Implement
|
|
31
|
-
- **When user says "EYU"**: Explain understanding → Wait for approval → Explore → Plan/Implement
|
|
32
|
-
|
|
33
|
-
## Before Any Task
|
|
34
|
-
|
|
35
|
-
1. **Find existing examples first.** Use grep, glob, or read files to locate similar features. Study at least 2-3 examples of how comparable things are implemented before proposing changes.
|
|
36
|
-
|
|
37
|
-
2. **Map the full surface area.** For any change, actively investigate:
|
|
38
|
-
- How are similar things structured and named? (conventions, patterns)
|
|
39
|
-
- What's the full lifecycle? (creation, registration, configuration, persistence, cleanup)
|
|
40
|
-
- What are all integration points? (UI, menus, context menus, commands, keybindings, APIs)
|
|
41
|
-
- What files document this? (README, docs/, inline comments, docstrings)
|
|
42
|
-
- What tests exist for similar features? How do they test this kind of thing?
|
|
43
|
-
- What registries, configs, or manifests need updating?
|
|
44
|
-
|
|
45
|
-
3. **Learn the local conventions.** Every codebase has its own patterns. Discover them before writing new code. Match the existing style exactly.
|
|
46
|
-
|
|
47
|
-
4. **Think in systems.** A request to "add X" means "integrate X into the existing system." Understand the system's architecture first.
|
|
48
|
-
|
|
49
|
-
5. **When uncertain, investigate more.** Read more files. Run the code. Check tests. Don't guess—know.
|
|
50
|
-
|
|
51
|
-
6. **Update all documentation.** Treat documentation as part of the implementation, not an afterthought. Update README, docs/, docstrings, inline comments, and any related documentation files. If the change affects public APIs, update their documentation first.
|
|
52
|
-
|
|
53
|
-
## In Practice
|
|
54
|
-
|
|
55
|
-
When given a task:
|
|
56
|
-
- Start by exploring, not planning (unless SYP is requested)
|
|
57
|
-
- Use tools to read related code before proposing changes
|
|
58
|
-
- List all affected files/areas before making edits
|
|
59
|
-
- Identify all documentation that needs updating (README, docs/, docstrings, comments)
|
|
60
|
-
- Ask clarifying questions if the scope seems larger than implied
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
# Implementation Principles
|
|
65
|
-
|
|
66
|
-
Once you understand the task, these govern how you write the code.
|
|
67
|
-
|
|
68
|
-
## Simplicity First
|
|
69
|
-
|
|
70
|
-
Write the minimum code that solves the problem. Nothing speculative.
|
|
71
|
-
|
|
72
|
-
- No features beyond what was asked.
|
|
73
|
-
- No abstractions for single-use code.
|
|
74
|
-
- No "flexibility" or "configurability" I didn't request.
|
|
75
|
-
- No error handling for scenarios that can't actually happen.
|
|
76
|
-
- If you wrote 200 lines and it could be 50, rewrite it.
|
|
77
|
-
- Gut check: *"Would a senior engineer call this overcomplicated?"* If yes, simplify.
|
|
78
|
-
|
|
79
|
-
(This is about not writing unnecessary code; the File Size Limits below are about splitting code that has already grown too large — a different problem.)
|
|
80
|
-
|
|
81
|
-
## Surgical Changes
|
|
82
|
-
|
|
83
|
-
Touch only what you must. Clean up only your own mess.
|
|
84
|
-
|
|
85
|
-
When editing existing code:
|
|
86
|
-
- Don't "improve" adjacent code, comments, or formatting.
|
|
87
|
-
- Don't refactor things that aren't broken.
|
|
88
|
-
- If you notice unrelated dead code, *mention* it — don't delete it.
|
|
89
|
-
- (Matching existing style is already required under "Learn the local conventions" — it holds here too, even when you'd do it differently.)
|
|
90
|
-
|
|
91
|
-
When your changes create orphans:
|
|
92
|
-
- Remove imports/variables/functions that *your* changes made unused.
|
|
93
|
-
- Don't remove pre-existing dead code unless I ask.
|
|
94
|
-
|
|
95
|
-
**The test:** every changed line should trace directly to my request.
|
|
96
|
-
|
|
97
|
-
## Goal-Driven Execution
|
|
98
|
-
|
|
99
|
-
Define success criteria up front, then loop until they're verified.
|
|
100
|
-
|
|
101
|
-
Turn vague tasks into verifiable goals:
|
|
102
|
-
- "Add validation" → write tests for invalid inputs, then make them pass.
|
|
103
|
-
- "Fix the bug" → write a test that reproduces it, then make it pass.
|
|
104
|
-
- "Refactor X" → confirm tests pass before and after.
|
|
105
|
-
|
|
106
|
-
For multi-step work, state a brief plan with a check per step:
|
|
107
|
-
1. [Step] → verify: [check]
|
|
108
|
-
2. [Step] → verify: [check]
|
|
109
|
-
|
|
110
|
-
Strong success criteria let you loop independently; weak ones ("make it work") force constant clarification — so define them precisely. (those gate *whether* you start; success criteria define *when you're done*.)
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
# Code Quality Guidelines
|
|
115
|
-
|
|
116
|
-
## File Size Limits
|
|
117
|
-
|
|
118
|
-
| Type | Warning | Consider Split | Must Split |
|
|
119
|
-
|------|---------|----------------|------------|
|
|
120
|
-
| Python | 500 | 600 | 700 |
|
|
121
|
-
| TypeScript (general) | 400 | 500 | 600 |
|
|
122
|
-
| React components | 200 | 300 | 400 |
|
|
123
|
-
| Next.js pages/routes | 300 | 400 | 500 |
|
|
124
|
-
| Custom hooks | 100 | 150 | 200 |
|
|
125
|
-
| Utility/service files | 400 | 500 | 600 |
|
|
126
|
-
|
|
127
|
-
## Test File Size Limits
|
|
128
|
-
| Type | Warning | Consider Split | Must Split |
|
|
129
|
-
|------|---------|----------------|------------|
|
|
130
|
-
| Python tests | 800 | 1000 | 1200 |
|
|
131
|
-
| TypeScript tests | 600 | 800 | 1000 |
|
|
132
|
-
| React component tests | 400 | 500 | 600 |
|
|
133
|
-
| Hook tests | 200 | 300 | 400 |
|
|
134
|
-
| E2E/integration tests | 800 | 1000 | 1200 |
|
|
135
|
-
|
|
136
|
-
When a test file approaches these limits, first consider whether the module under test should be split before splitting the test file.
|
|
137
|
-
|
|
138
|
-
## Verify, don't guess
|
|
139
|
-
|
|
140
|
-
- If you find yourself reconstructing an API, function signature, config key, or
|
|
141
|
-
command flag from memory, stop and confirm it before writing it. Memory of these
|
|
142
|
-
is the single biggest source of confident-but-wrong code.
|
|
143
|
-
- Check the right source for the kind of doubt:
|
|
144
|
-
- Project conventions, types, helpers, existing patterns → search the codebase.
|
|
145
|
-
- External library/framework behavior, versions, deprecations → search online,
|
|
146
|
-
preferring official docs and the library's changelog over blog posts or older
|
|
147
|
-
Stack Overflow answers.
|
|
148
|
-
- A search is cheap; a wrong assumption is expensive to debug. When unsure whether
|
|
149
|
-
it's worth checking, check.
|
|
150
|
-
- When a non-obvious choice rests on something you looked up, note the source in one
|
|
151
|
-
line (e.g. "per React 19 docs") so it can be verified.
|
|
152
|
-
- Treat anything that may have changed since your training cutoff — latest versions,
|
|
153
|
-
new APIs, deprecations — as something to look up, not recall.
|
|
154
|
-
|
|
155
|
-
## When debugging
|
|
156
|
-
|
|
157
|
-
- State your current hypothesis for the cause before each fix attempt. If it's the
|
|
158
|
-
same hypothesis as the last failed attempt, do not try another variation — the
|
|
159
|
-
diagnosis is suspect, not just the fix. Gather new information instead.
|
|
160
|
-
- After 2 failed attempts, stop iterating and treat your understanding of the problem
|
|
161
|
-
as the thing that's wrong. Re-examine the premise.
|
|
162
|
-
- Get fresh ground truth instead of reasoning from memory: read the actual error
|
|
163
|
-
message in full, read the actual source of the function involved, and search the
|
|
164
|
-
literal error text online — others have usually hit it.
|
|
165
|
-
- Never repeat a fix that already failed.
|
|
166
|
-
- If you're stuck after re-examining, surface the competing hypotheses to me rather than continuing to guess.
|