decorated-pi 0.5.5 → 0.6.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.
Files changed (67) hide show
  1. package/.codegraph/daemon.pid +6 -0
  2. package/AGENTS.md +92 -0
  3. package/README.md +53 -64
  4. package/commands/dp-model.ts +23 -0
  5. package/commands/dp-settings.ts +23 -0
  6. package/commands/mcp-status.ts +62 -0
  7. package/commands/retry.ts +19 -0
  8. package/commands/usage.ts +544 -0
  9. package/hooks/compaction.ts +204 -0
  10. package/hooks/externalize.ts +70 -0
  11. package/hooks/image-vision.ts +132 -0
  12. package/hooks/inject-agents-md.ts +164 -0
  13. package/hooks/mcp.ts +340 -0
  14. package/hooks/normalize-codeblocks.ts +88 -0
  15. package/hooks/pi-tool-filter.ts +28 -0
  16. package/{extensions/safety → hooks/redact}/types.ts +1 -8
  17. package/hooks/redact.ts +104 -0
  18. package/{extensions → hooks}/rtk.ts +92 -115
  19. package/{extensions → hooks}/session-title.ts +17 -28
  20. package/hooks/skeleton.ts +212 -0
  21. package/hooks/smart-at.ts +318 -0
  22. package/{extensions/file-times.ts → hooks/track-mtime.ts} +40 -38
  23. package/{extensions → hooks}/wakatime.ts +120 -122
  24. package/index.ts +155 -1
  25. package/package.json +5 -4
  26. package/{extensions/settings.ts → settings.ts} +32 -0
  27. package/{extensions → tools}/lsp/client.ts +0 -25
  28. package/{extensions → tools}/lsp/format.ts +2 -99
  29. package/{extensions → tools}/lsp/index.ts +1 -1
  30. package/{extensions → tools}/lsp/servers.ts +1 -1
  31. package/{extensions → tools}/lsp/tools.ts +1 -66
  32. package/{extensions → tools}/lsp/types.ts +0 -11
  33. package/tools/mcp/builtin/codegraph.ts +45 -0
  34. package/tools/mcp/builtin/context7.ts +10 -0
  35. package/tools/mcp/builtin/exa.ts +10 -0
  36. package/tools/mcp/builtin/index.ts +19 -0
  37. package/tools/mcp/cache.ts +124 -0
  38. package/{extensions → tools}/mcp/client.ts +2 -2
  39. package/{extensions/mcp/builtin.ts → tools/mcp/config.ts} +21 -181
  40. package/tools/mcp/index.ts +44 -0
  41. package/tools/mcp/tool-definition.ts +112 -0
  42. package/{extensions/patch.ts → tools/patch/core.ts} +135 -43
  43. package/{extensions/io.ts → tools/patch/index.ts} +24 -206
  44. package/tsconfig.json +10 -1
  45. package/ui/mcp-status.ts +202 -0
  46. package/ui/model-picker.ts +162 -0
  47. package/ui/module-settings.ts +83 -0
  48. package/ui/usage.ts +396 -0
  49. package/extensions/index.ts +0 -154
  50. package/extensions/io-tool-output.ts +0 -33
  51. package/extensions/mcp/index.ts +0 -435
  52. package/extensions/model-integration.ts +0 -531
  53. package/extensions/providers/ark-coding.ts +0 -75
  54. package/extensions/providers/index.ts +0 -9
  55. package/extensions/providers/ollama-cloud.ts +0 -101
  56. package/extensions/providers/qianfan-coding.ts +0 -71
  57. package/extensions/safety/index.ts +0 -102
  58. package/extensions/slash.ts +0 -458
  59. package/extensions/smart-at.ts +0 -481
  60. package/extensions/subdir-agents.ts +0 -151
  61. /package/{extensions/safety → hooks/redact}/detect.ts +0 -0
  62. /package/{extensions/safety → hooks/redact}/entropy.ts +0 -0
  63. /package/{extensions/safety → hooks/redact}/patterns.ts +0 -0
  64. /package/{extensions → tools}/lsp/env.ts +0 -0
  65. /package/{extensions → tools}/lsp/manager.ts +0 -0
  66. /package/{extensions → tools}/lsp/prompt.ts +0 -0
  67. /package/{extensions → tools}/lsp/protocol.ts +0 -0
@@ -0,0 +1,6 @@
1
+ {
2
+ "pid": 3967873,
3
+ "version": "0.9.9",
4
+ "socketPath": "/home/liuchang/workspace/pi-extensions/decorated-pi/.codegraph/daemon.sock",
5
+ "startedAt": 1781237871587
6
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,92 @@
1
+ # decorated-pi — pi extension
2
+
3
+ ## Architecture
4
+
5
+ ### Three top-level categories
6
+
7
+ ```
8
+ decorated-pi/
9
+ ├── AGENTS.md
10
+ ├── package.json
11
+ ├── settings.ts # ~/.pi/agent/decorated-pi.json read/write
12
+ ├── tools/ # LLM-callable tools
13
+ ├── hooks/ # agent-loop event handlers
14
+ ├── commands/ # slash commands
15
+ ├── providers/ # LLM providers
16
+ └── test/ # vitest specs
17
+ ```
18
+
19
+ | Category | Role | Knows about |
20
+ |----------|------|-------------|
21
+ | `tools/` | Endpoints the LLM calls | other modules via import only |
22
+ | `hooks/` | Reacts to agent-loop events | primitives, other hooks via skeleton |
23
+ | `commands/` | User-typed `/...` commands | `settings.ts` only |
24
+
25
+ **Hard rules**:
26
+ - A tool never registers a hook (no `pi.on(...)` in `tools/*.ts`).
27
+ - A hook does not care whether the triggering tool was registered by us or by pi core.
28
+ - A command does not participate in the agent loop. Its only shared state with tools is `settings.ts`.
29
+ - The skeleton (`hooks/skeleton.ts`) is the only place that calls `pi.on(...)` for hooks.
30
+
31
+ ### Skeleton — `hooks/skeleton.ts`
32
+
33
+ ```
34
+ pi core
35
+
36
+ │ events
37
+
38
+ ┌──────────────┐
39
+ │ skeleton │ ← only place that calls pi.on(...)
40
+ │ │
41
+ │ · collect │
42
+ │ · order │
43
+ │ · dispatch │
44
+ │ · owns: │
45
+ │ - deps │
46
+ │ - prompt │
47
+ └──────┬───────┘
48
+
49
+
50
+ registered hooks
51
+ ```
52
+
53
+ **Rules**:
54
+ - Registration order = execution order.
55
+ - Two handler modes: **parallel** (return values ignored) for lifecycle events; **compose** (next handler sees previous return) for transformation chains (`before_agent_start` mutates `systemPrompt`, `tool_call` mutates `input.command`, `tool_result` mutates `content`).
56
+ - The skeleton also owns dependency checking and system-prompt guideline injection — hooks declare them, skeleton enforces them at the right event.
57
+
58
+ ### `dp-settings`
59
+
60
+ The only shared state is `settings.ts`. Commands write; `index.ts` reads on `/reload` to decide which tools to register. Neither side imports the other.
61
+
62
+ ### Adding a new feature
63
+
64
+ **New tool**:
65
+ 1. `tools/<name>.ts` exporting `register<Name>Tool(pi)`.
66
+ 2. In `index.ts`: `if (isModuleEnabled("<name>")) register<Name>Tool(pi);`
67
+ 3. *(Optional)* In `commands/dp-settings.ts`: add the module label so users can toggle it via `/dp-settings`. Without this the tool is always on; users would have to edit `settings.json` directly to disable.
68
+
69
+ If the tool has its own state, protocol client, or dynamic sub-tools, organize it as a directory instead of a single file: `tools/<name>/{client,manager,...}.ts` plus `tools/<name>/index.ts` exporting `register<Name>Tools(pi)`. See `tools/mcp/` and `tools/lsp/` for examples.
70
+
71
+ **New hook**:
72
+ 1. `hooks/<name>.ts` exporting `<name>Module` and optionally `setup<X>(sk)`.
73
+ 2. In `hooks/index.ts`: export the setup wrapper.
74
+ 3. In `index.ts`: call `setup<X>(sk)` in the right slot (order = execution order).
75
+ 4. Inside the setup, call `sk.declareDependency` / `sk.declareGuideline` if needed.
76
+
77
+ **New command**:
78
+ 1. `commands/<name>.ts` exporting `register<Name>Command(pi)`.
79
+ 2. In `index.ts`: call `register<Name>Command(pi)`.
80
+
81
+ ## Test
82
+
83
+ ```bash
84
+ npm test
85
+ ```
86
+
87
+ Organization rules:
88
+
89
+ - Tests mirror the source layout one-to-one: `extensions/<area>/<name>.ts` → `test/<name>.test.ts`.
90
+ - All spec files live flat in `test/` (no nested folders). For a tool that is a directory (e.g. `tools/mcp/`), the spec is `test/mcp.test.ts` covering the whole module.
91
+ - Sub-features of the same module may get their own file: `test/mcp-externalize.test.ts` for a specific concern of MCP, `test/patch.test.ts` for the patch tool.
92
+ - Every new feature or bug fix ships with a test — run `npm test` before considering the change done.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # decorated-pi
2
2
 
3
- `decorated-pi` is a practical enhancement pack for [Pi](https://github.com/earendil-works/pi) — smarter tools that are token efficient and cache friendly.
3
+ `decorated-pi` is a practical enhancement pack for [Pi](https://github.com/earendil-works/pi) — token-efficient workflow, cache-friendly design, and smarter tools.
4
4
 
5
5
  ## Install
6
6
 
@@ -12,21 +12,41 @@ pi install /path/to/decorated-pi
12
12
 
13
13
  ## Features
14
14
 
15
- ### 1. Patch Tool
15
+ ### 1. Token Efficiency
16
16
 
17
- Replaces Pi's built-in `edit` with a stronger `patch` tool that adds unique safety and usability improvements on top of the native tools.
17
+ Multiple layers of token savings that compound across every session.
18
+
19
+ **RTK** — integrates [RTK](https://github.com/rtk-ai/rtk) to compress bash output into structured summaries, so the LLM never sees raw noise.
20
+
21
+ **codegraph** — integrates [codegraph](https://github.com/colbymchenry/codegraph) to offer a code map of your project, so the LLM can navigate symbols and call graphs without chaining `ls` → `grep` → `read`.
22
+
23
+ **Auxiliary Models** — offloads heavy-but-dumb tasks to cheaper models so your primary model only pays for the hard work:
24
+
25
+ - **Image read fallback** — detects image type via magic bytes, calls a configured vision-capable model, and injects the analysis text, so your main model never touches image tokens
26
+ - **Compact model** — handles context compaction with a smaller model instead of burning main-model capacity
27
+
28
+ Configured via `/dp-model`.
29
+
30
+ **Cache‑friendly design** — stable system prompt prefix:
31
+
32
+ - tool definitions, guidelines, and skills are sorted alphabetically so the system prompt is identical across sessions
33
+ - volatile elements like `Current date: …` are stripped before prompt assembly
34
+ - MCP tool schemas are persisted to a local cache, so the tool list stays stable regardless of network conditions or server availability
35
+
36
+ ### 2. Smarter Tools
37
+
38
+ Drop‑in replacements for Pi's built‑in tools, with better UX and fewer wasted turns.
39
+
40
+ #### Patch Tool
18
41
 
19
42
  | Capability | Pi native `edit` | `patch` |
20
43
  | ------ | :---: | :---: |
21
- | Syntax‑highlighted overwrite | ✅ streaming | ✅ incremental |
22
44
  | **Anchor‑based search** | ❌ extending `oldText` for uniqueness | ✅ `anchor` bounds scope for precise matching |
23
45
  | **Fuzzy whitespace match** | ❌ only reports "not found" | ✅ auto‑corrects tab↔space / trailing whitespace mismatches |
24
46
  | **Edit fault diagnostics** | ❌ only reports "not found" | ✅ pinpoint faults for LLM comprehension |
25
47
  | **Stale‑read protection** | ❌ Blind to external changes | ✅ `read` captures mtime, `patch` rejects stale targets |
26
48
 
27
- ### 2. Smarter `@` File Search
28
-
29
- Replaces Pi's built-in `@` file completion with smarter matching and noise filtering:
49
+ #### Smarter `@` File Search
30
50
 
31
51
  | Aspect | Pi native `@` | `decorated-pi` |
32
52
  | ------ | :---: | :---: |
@@ -35,48 +55,15 @@ Replaces Pi's built-in `@` file completion with smarter matching and noise filte
35
55
  | **Default suggestions** | ❌ all files visible on empty query | ✅ only visible project files |
36
56
  | **Match precision** | ❌ case‑insensitive simple scoring | ✅ multi‑level case‑sensitive scoring |
37
57
 
38
- ### 3. Secret redaction
39
-
40
- Three-layer detection: high-confidence known-format patterns (AWS, GitHub, OpenAI, etc.), config-key regex matching, and adjusted Shannon entropy heuristics for unknown secret-like values.
41
-
42
- Example redaction on a `read` / `bash` output:
43
-
44
- ```json
45
- {
46
- "aws_access_key_id": "AKI**************PLE",
47
- "github_token": "ghp***************def",
48
- "database_password": "Sup#######t99",
49
- "api_key": "sk_**************f5a",
50
- "random_secret": "a1b??????5f5"
51
- }
52
- ```
53
-
54
- > `*` = known pattern, `#` = config key regex, `?` = entropy heuristic.
55
-
56
- ### 4. Auxiliary Models
57
-
58
- Offloads auxiliary ops to cheaper models, reducing cost on every session. Configured via `/dp-model`:
59
-
60
- - **Image read fallback** — when the model reads an image file, detects type via magic bytes, calls a configured vision-capable model, and replaces the read result with image analysis text (jpeg, png, gif, webp)
61
- - **Compact model** — uses a configured model for context compaction (instead of the main model).
62
-
63
- ### 5. Progressive Context from `AGENTS.md` / `CLAUDE.md`
64
-
65
- Extension capability: context is disclosed progressively as the agent works across different parts of the project.
58
+ #### LSP support
66
59
 
67
- - When reading or editing a file, discovers `AGENTS.md` / `CLAUDE.md` in that file's directory and ancestor directories
68
- - Newly discovered guidance is injected into tool results, scoped to the current context
69
-
70
- ### 6. LSP Tool Suite
71
-
72
- A cleaned-up, minimal LSP toolset. The extension keeps only the two LSP tools that cover the most practical coding workflows: checking diagnostics after edits and inspecting file structure before focused changes.
60
+ Covers what codegraph can't: real-time compiler and lint errors.
73
61
 
74
62
  - **`lsp_diagnostics`** — file diagnostics with severity filtering
75
- - **`lsp_document_symbols`** — file symbol outline
76
63
 
77
64
  Supported languages: c/cpp, go, java, lua, json, python, ruby, rust, svelte, typescript
78
65
 
79
- ### 7. Built-in MCP Client
66
+ ### 3. MCP Ecosystem
80
67
 
81
68
  Zero-config MCP client with built-in servers:
82
69
 
@@ -84,9 +71,9 @@ Zero-config MCP client with built-in servers:
84
71
  | --- | --- | --- |
85
72
  | Context7 | `context7_*` | `https://mcp.context7.com/mcp` |
86
73
  | Exa | `exa_*` | `https://mcp.exa.ai/mcp` |
74
+ | codegraph | `codegraph_*` | bundled binary |
87
75
 
88
- **Custom servers** in `.pi/agent/mcp.json` (project) or `~/.pi/agent/decorated-pi.json` (global). Project overrides global.
89
- Tool prompts and schemas are cached locally so MCP tools are available immediately on startup, even before servers connect.
76
+ **Custom servers** in `.pi/agent/mcp.json` (project) or `~/.pi/agent/decorated-pi.json` (global). Project overrides global. Tool prompts and schemas are cached locally so MCP tools are available immediately on startup — no manual `/reload` required even on first install.
90
77
 
91
78
  ```json
92
79
  {
@@ -108,34 +95,36 @@ Tool prompts and schemas are cached locally so MCP tools are available immediate
108
95
  }
109
96
  ```
110
97
 
111
- Use `/mcp` to view connection status and registered tools.
112
-
113
- ### 8. Extend Providers
98
+ Use `/mcp` to view connection status and toggle servers.
114
99
 
115
- Extend providers are registered via `/login` → "Use a subscription":
100
+ ### 4. Secret Redaction
116
101
 
117
- | Provider | Base URL |
118
- | ---------- | ----------- |
119
- | Ollama Cloud | `ollama.com/v1` |
120
- | Baidu Qianfan | `qianfan.baidubce.com/v2/coding` |
121
- | ARK Coding | `ark.cn-beijing.volces.com/api/coding/v3` |
102
+ Three-layer detection: high-confidence known-format patterns (AWS, GitHub, OpenAI, etc.), config-key regex matching, and adjusted Shannon entropy heuristics for unknown secret-like values.
122
103
 
123
- ### 9. Other
104
+ Example redaction on a `read` / `bash` output:
124
105
 
125
- - **RTK** — integrates [RTK](https://github.com/rtk-ai/rtk) for token-efficient command output.
126
- - **WakaTime** — tracks coding activity via [WakaTime](https://wakatime.com).
106
+ ```json
107
+ {
108
+ "aws_access_key_id": "AKI**************PLE",
109
+ "github_token": "ghp***************def",
110
+ "database_password": "Sup#######t99",
111
+ "api_key": "sk_**************f5a",
112
+ "random_secret": "a1b??????5f5"
113
+ }
114
+ ```
127
115
 
128
- ## Configuration
116
+ > `*` = known pattern, `#` = config key regex, `?` = entropy heuristic.
129
117
 
130
- Runtime settings are stored in:
118
+ ### 5. Other
131
119
 
132
- ```text
133
- ~/.pi/agent/decorated-pi.json
134
- ```
120
+ - `/usage` — token stats with cache‑hit rate, per‑model breakdown (Session / Today / This Week / This Month / All Time)
121
+ - Progressive context — supports subdirectory `AGENTS.md` / `CLAUDE.md` discovery and injection
122
+ - `/retry` — continue after interruption
123
+ - **WakaTime** — coding activity tracking via [WakaTime](https://wakatime.com)
135
124
 
136
- ### Module Loading
125
+ ## Configuration
137
126
 
138
- Modules can be toggled on/off by `/dp-settings`. Changes take effect after `/reload`.
127
+ Runtime settings in `~/.pi/agent/decorated-pi.json`. Modules can be toggled via `/dp-settings` (changes take effect after `/reload`).
139
128
 
140
129
  ```json
141
130
  {
@@ -0,0 +1,23 @@
1
+ /**
2
+ * /dp-model — pick image / compact model.
3
+ * UI lives in extensions/ui/model-picker.ts (shared with /mcp status etc).
4
+ */
5
+
6
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
7
+ import { ModelPickerComponent } from "../ui/model-picker.js";
8
+
9
+ export function registerDpModelCommand(pi: ExtensionAPI): void {
10
+ pi.registerCommand("dp-model", {
11
+ description: "Configure image and compact models",
12
+ handler: async (_args, ctx) => {
13
+ if (ctx.hasUI) {
14
+ await ctx.ui.custom<void>(
15
+ (tui, theme, _kb, done) =>
16
+ new ModelPickerComponent(tui, theme, ctx.modelRegistry, () => done(undefined))
17
+ );
18
+ return;
19
+ }
20
+ ctx.ui.notify("dp-model requires interactive mode.", "warning");
21
+ },
22
+ });
23
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * /dp-settings — toggle module on/off.
3
+ */
4
+
5
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+ import { ModuleSettingsComponent } from "../ui/module-settings.js";
7
+
8
+ export function registerDpSettingsCommand(pi: ExtensionAPI): void {
9
+ pi.registerCommand("dp-settings", {
10
+ description: "Toggle decorated-pi modules on/off",
11
+ handler: async (_args, ctx) => {
12
+ if (ctx.hasUI) {
13
+ await ctx.ui.custom<void>(
14
+ (tui, theme, _kb, done) =>
15
+ new ModuleSettingsComponent(tui, theme, () => done(undefined))
16
+ );
17
+ ctx.ui.notify("Module settings updated. /reload to apply.", "warning");
18
+ return;
19
+ }
20
+ ctx.ui.notify("dp-settings requires interactive mode.", "warning");
21
+ },
22
+ });
23
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * /mcp — show MCP servers, toggle, refresh.
3
+ *
4
+ * Wires the pure UI component (ui/mcp-status.ts) to the hook layer
5
+ * (hooks/mcp.ts) by injecting callbacks. The UI itself knows nothing
6
+ * about hooks/config persistence.
7
+ */
8
+
9
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
10
+ import { McpStatusComponent, type McpServerView } from "../ui/mcp-status.js";
11
+ import { getMcpStatus, refreshServerCache, updateConfigEnabled } from "../hooks/mcp.js";
12
+ import { toggleMcpServerEnabled } from "../tools/mcp/config.js";
13
+
14
+ export function registerMcpStatusCommand(pi: ExtensionAPI): void {
15
+ pi.registerCommand("mcp", {
16
+ description: "Show active MCP servers and their tools",
17
+ handler: async (_args, ctx) => {
18
+ if (ctx.hasUI) {
19
+ await ctx.ui.custom<void>(
20
+ (tui, theme, _kb, done) =>
21
+ new McpStatusComponent(tui, theme, {
22
+ read: () => getMcpStatus() as McpServerView[],
23
+ toggle: async (name, enabled) => {
24
+ const scope: "global" | "project" = "global";
25
+ const ok = toggleMcpServerEnabled(name, enabled, scope, ctx.cwd || undefined);
26
+ if (ok) {
27
+ // Await so the connection (if disabling) is fully torn down
28
+ // before /reload. Otherwise teardownMcp() may hang on a
29
+ // zombie conn and stall session_shutdown.
30
+ await updateConfigEnabled(name, enabled);
31
+ }
32
+ return ok;
33
+ },
34
+ refresh: (name) => refreshServerCache(name, ctx.modelRegistry),
35
+ }, done)
36
+ );
37
+ return;
38
+ }
39
+ const servers = getMcpStatus();
40
+ if (servers.length === 0) {
41
+ ctx.ui.notify("No MCP servers configured.", "info");
42
+ return;
43
+ }
44
+ const lines: string[] = [`MCP servers (${servers.length}):`, ""];
45
+ for (const s of servers) {
46
+ lines.push(`• ${s.name} (${s.source})`);
47
+ lines.push(` URL: ${s.url}`);
48
+ if (s.state === "connecting") lines.push(` Status: connecting...`);
49
+ else if (s.state === "failed") lines.push(` Status: failed — ${s.error ?? "unknown error"}`);
50
+ else {
51
+ lines.push(` Tools: ${s.toolCount}`);
52
+ for (const tool of s.tools) {
53
+ const desc = tool.description ? ` — ${tool.description.slice(0, 60)}` : "";
54
+ lines.push(` - ${tool.name}${desc}`);
55
+ }
56
+ }
57
+ lines.push("");
58
+ }
59
+ pi.sendMessage({ customType: "mcp-status", content: lines.join("\n"), display: true }, { triggerTurn: false });
60
+ },
61
+ });
62
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * /retry — continue after interruption.
3
+ */
4
+
5
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
+
7
+ export function registerRetryCommand(pi: ExtensionAPI): void {
8
+ let retryInProgress = false;
9
+ pi.registerCommand("retry", {
10
+ description: "Continue after interruption",
11
+ handler: async (_args, ctx) => {
12
+ if (retryInProgress) { ctx.ui.notify("Retry is already in progress", "warning"); return; }
13
+ if (!ctx.isIdle()) ctx.abort();
14
+ retryInProgress = true;
15
+ pi.sendMessage({ customType: "retry-trigger", content: "Continue.", display: false }, { triggerTurn: true });
16
+ },
17
+ });
18
+ pi.on("agent_start", () => { retryInProgress = false; });
19
+ }