@cydm/pie 1.0.6 → 1.0.7
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/README.md +151 -9
- package/dist/builtin/extensions/ask-user/index.js +2 -3
- package/dist/builtin/extensions/kimi-attachments/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +85 -87
- package/dist/builtin/extensions/subagent/index.js +73 -8
- package/dist/builtin/extensions/todo/index.js +51 -22
- package/dist/builtin/skills/browser-tools/CHANGELOG.md +2 -44
- package/dist/builtin/skills/browser-tools/README.md +10 -99
- package/dist/builtin/skills/browser-tools/SKILL.md +21 -174
- package/dist/builtin/skills/browser-tools/package.json +6 -13
- package/dist/builtin/skills/browser-tools/playwright-cli.js +24 -0
- package/dist/builtin/skills/pie-unity-rpc/SKILL.md +121 -0
- package/dist/builtin/skills/pie-unity-rpc/pie-unity-rpc.js +417 -0
- package/dist/builtin/skills/skill-creator/SKILL.md +17 -17
- package/dist/builtin/skills/skill-creator/eval-viewer/generate_review.mjs +285 -0
- package/dist/builtin/skills/skill-creator/eval-viewer/viewer.html +1 -1
- package/dist/builtin/skills/skill-creator/scripts/aggregate_benchmark.mjs +271 -0
- package/dist/builtin/skills/skill-creator/scripts/claude_cli.mjs +115 -0
- package/dist/builtin/skills/skill-creator/scripts/generate_report.mjs +224 -0
- package/dist/builtin/skills/skill-creator/scripts/improve_description.mjs +198 -0
- package/dist/builtin/skills/skill-creator/scripts/package_skill.mjs +132 -0
- package/dist/builtin/skills/skill-creator/scripts/pie_runner.mjs +115 -0
- package/dist/builtin/skills/skill-creator/scripts/quick_validate.mjs +44 -0
- package/dist/builtin/skills/skill-creator/scripts/run_eval.mjs +169 -0
- package/dist/builtin/skills/skill-creator/scripts/run_loop.mjs +297 -0
- package/dist/builtin/skills/skill-creator/scripts/skill_metadata.mjs +134 -0
- package/dist/chunks/{chunk-MWFBYJOI.js → chunk-A5JSJAPK.js} +3973 -1313
- package/dist/chunks/chunk-BHNULR7U.js +7991 -0
- package/dist/chunks/chunk-GDTN4UPJ.js +701 -0
- package/dist/chunks/{src-EGWRDMLB.js → src-3X3HBT2G.js} +1 -2
- package/dist/chunks/typescript-GSKWJIO4.js +210747 -0
- package/dist/cli.js +14664 -11973
- package/models.schema.json +238 -0
- package/package.json +34 -8
- package/dist/builtin/skills/browser-tools/browser-content.js +0 -103
- package/dist/builtin/skills/browser-tools/browser-cookies.js +0 -35
- package/dist/builtin/skills/browser-tools/browser-eval.js +0 -49
- package/dist/builtin/skills/browser-tools/browser-hn-scraper.js +0 -108
- package/dist/builtin/skills/browser-tools/browser-nav.js +0 -44
- package/dist/builtin/skills/browser-tools/browser-pick.js +0 -162
- package/dist/builtin/skills/browser-tools/browser-screenshot.js +0 -34
- package/dist/builtin/skills/browser-tools/browser-start.js +0 -86
- package/dist/builtin/skills/skill-creator/eval-viewer/generate_review.py +0 -471
- package/dist/builtin/skills/skill-creator/scripts/__init__.py +0 -0
- package/dist/builtin/skills/skill-creator/scripts/aggregate_benchmark.py +0 -401
- package/dist/builtin/skills/skill-creator/scripts/generate_report.py +0 -326
- package/dist/builtin/skills/skill-creator/scripts/improve_description.py +0 -247
- package/dist/builtin/skills/skill-creator/scripts/package_skill.py +0 -136
- package/dist/builtin/skills/skill-creator/scripts/quick_validate.py +0 -103
- package/dist/builtin/skills/skill-creator/scripts/run_eval.py +0 -310
- package/dist/builtin/skills/skill-creator/scripts/run_loop.py +0 -328
- package/dist/builtin/skills/skill-creator/scripts/utils.py +0 -47
- package/dist/chunks/capabilities-FENCOHVA.js +0 -9
- package/dist/chunks/chunk-JYBXCEJJ.js +0 -315
- package/dist/chunks/chunk-RID3574D.js +0 -2718
- package/dist/chunks/chunk-TBJ25UWB.js +0 -3657
- package/dist/chunks/chunk-XZXLO7YB.js +0 -322
- package/dist/chunks/file-logger-AL5VVZHH.js +0 -22
- package/dist/chunks/src-WRUACRN2.js +0 -132
package/README.md
CHANGED
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
It gives you a terminal-first agent experience out of the box, while staying modular enough to embed into other runtimes and products. Pie is designed for teams that want a small surface area, strong defaults, persistent sessions, tool use, and a clean path from CLI usage to deeper integration.
|
|
6
6
|
|
|
7
|
-
Pie is heavily inspired by `pi`, while evolving in its own direction around modularity, embeddability, and a compact developer-facing workflow.
|
|
8
|
-
|
|
9
7
|
Pie is developed by [cydream](https://cydream.tech).
|
|
10
8
|
|
|
11
9
|
## Why Pie
|
|
@@ -22,6 +20,8 @@ Pie is developed by [cydream](https://cydream.tech).
|
|
|
22
20
|
npm install -g @cydm/pie
|
|
23
21
|
```
|
|
24
22
|
|
|
23
|
+
Pie supports Node.js 20 and newer. Release builds are validated with npm's lockfile workflow (`npm ci`) so dependency resolution is reproducible.
|
|
24
|
+
|
|
25
25
|
## Quick Start
|
|
26
26
|
|
|
27
27
|
Set an API key for the provider you want to use:
|
|
@@ -38,6 +38,71 @@ You can also configure defaults in:
|
|
|
38
38
|
- `~/.pie/models.json`
|
|
39
39
|
- `~/.pie/config.json` for legacy-compatible defaults
|
|
40
40
|
|
|
41
|
+
Minimal `~/.pie/models.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"profiles": {
|
|
46
|
+
"cy-gpt": {
|
|
47
|
+
"api": "openai-completions",
|
|
48
|
+
"baseUrl": "https://token.magicshell.ai/v1",
|
|
49
|
+
"apiKey": "YOUR_API_KEY",
|
|
50
|
+
"models": [
|
|
51
|
+
{
|
|
52
|
+
"id": "gpt-5.4",
|
|
53
|
+
"name": "GPT 5.4",
|
|
54
|
+
"reasoning": true,
|
|
55
|
+
"input": ["text", "image"],
|
|
56
|
+
"contextWindow": 128000,
|
|
57
|
+
"maxTokens": 16384,
|
|
58
|
+
"cost": {
|
|
59
|
+
"input": 0,
|
|
60
|
+
"output": 0,
|
|
61
|
+
"cacheRead": 0,
|
|
62
|
+
"cacheWrite": 0
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"defaults": {
|
|
69
|
+
"provider": "cy-gpt",
|
|
70
|
+
"modelId": "gpt-5.4"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Required profile fields:
|
|
76
|
+
|
|
77
|
+
- `api`
|
|
78
|
+
- `baseUrl`
|
|
79
|
+
- `apiKey` or `apiKeyEnv`
|
|
80
|
+
- `models`
|
|
81
|
+
|
|
82
|
+
Required model fields:
|
|
83
|
+
|
|
84
|
+
- `id`
|
|
85
|
+
- `input`
|
|
86
|
+
- `cost`
|
|
87
|
+
- `contextWindow`
|
|
88
|
+
- `maxTokens`
|
|
89
|
+
|
|
90
|
+
Validate the file explicitly:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pie models validate
|
|
94
|
+
pie models validate --path /abs/path/to/models.json
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Generate a starter template:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pie models init
|
|
101
|
+
pie models init --stdout
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For editor auto-complete and validation, you can also point the optional `"$schema"` field in your local `models.json` at your installed `@cydm/pie/models.schema.json` file.
|
|
105
|
+
|
|
41
106
|
Start Pie in interactive mode:
|
|
42
107
|
|
|
43
108
|
```bash
|
|
@@ -113,6 +178,10 @@ Pie uses a hierarchical slash-command menu. The current built-in commands includ
|
|
|
113
178
|
|
|
114
179
|
| Command | Description |
|
|
115
180
|
| --- | --- |
|
|
181
|
+
| `pie doctor [--json]` | Diagnose local config, model setup, writable paths, browser automation, and Unity bridge hints |
|
|
182
|
+
| `pie models init` | Create a starter `~/.pie/models.json` |
|
|
183
|
+
| `pie models validate` | Validate `~/.pie/models.json` |
|
|
184
|
+
| `pie permissions` | Explain tool and shell safety defaults |
|
|
116
185
|
| `/sessions/compact` | Summarize history and create a compact checkpoint |
|
|
117
186
|
| `/sessions/new` | Start a new session |
|
|
118
187
|
| `/sessions/resume` | Resume a different session |
|
|
@@ -130,9 +199,9 @@ Pie uses a hierarchical slash-command menu. The current built-in commands includ
|
|
|
130
199
|
| `/skills/use` | Use a skill |
|
|
131
200
|
| `/skills/reload` | Reload skills from disk |
|
|
132
201
|
| `/tools/subagent` | Spawn a subagent for a task |
|
|
133
|
-
| `/debugs/cache-test` |
|
|
134
|
-
| `/debugs/context` |
|
|
135
|
-
| `/debugs/package` |
|
|
202
|
+
| `/debugs/cache-test` | Run provider cache diagnostics with a dedicated 2-request probe |
|
|
203
|
+
| `/debugs/context` | Append a context summary to `~/.pie/logs/pie-cli.log` |
|
|
204
|
+
| `/debugs/package` | Append a package summary to `~/.pie/logs/pie-cli.log` |
|
|
136
205
|
|
|
137
206
|
Pie can also load extension-provided commands at runtime.
|
|
138
207
|
|
|
@@ -158,6 +227,55 @@ Pie supports Markdown-based skills and built-in extensions.
|
|
|
158
227
|
|
|
159
228
|
That is part of what makes Pie embeddable: the CLI is only one interface on top of a broader agent toolkit.
|
|
160
229
|
|
|
230
|
+
## Browser Tools
|
|
231
|
+
|
|
232
|
+
The built-in `browser-tools` skill uses Microsoft's Playwright CLI for visible browser automation. Its runtime dependencies ship with the CLI; do not run `npm install` inside the skill directory.
|
|
233
|
+
|
|
234
|
+
Resolve the skill resource and run Playwright commands:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
node <resolvedPath for browser-tools/playwright-cli.js> open https://example.com
|
|
238
|
+
node <resolvedPath for browser-tools/playwright-cli.js> snapshot
|
|
239
|
+
node <resolvedPath for browser-tools/playwright-cli.js> screenshot
|
|
240
|
+
node <resolvedPath for browser-tools/playwright-cli.js> console
|
|
241
|
+
node <resolvedPath for browser-tools/playwright-cli.js> network
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
If Playwright's Chromium browser is not installed, run:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
node <resolvedPath for browser-tools/playwright-cli.js> install-browser chromium
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Run `pie doctor` to check whether Playwright CLI and its browser runtime are discoverable.
|
|
251
|
+
|
|
252
|
+
## Web Search
|
|
253
|
+
|
|
254
|
+
Pie exposes a canonical `web_search` tool for public web research, current information, online documentation, and internet inspiration. Workspace search remains separate: use `grep_text`, `find_files`, and `list_dir` for local repository content.
|
|
255
|
+
|
|
256
|
+
`web_search` uses provider-native web search where supported. It does not scrape DuckDuckGo or fall back to `bash` with `curl` or Python HTTP scripts. If the current provider/model cannot perform native web search, the tool returns a clear unavailable result with provider/configuration details.
|
|
257
|
+
|
|
258
|
+
Use `web_fetch` after search when the agent needs to read a specific URL, documentation page, changelog, issue, or PDF. `web_fetch` follows redirects, extracts readable text/markdown, returns citation anchors and extraction diagnostics, and reports `requires_browser` for JavaScript-heavy pages instead of pretending the content was read.
|
|
259
|
+
|
|
260
|
+
## Permissions
|
|
261
|
+
|
|
262
|
+
Pie's default tools operate from the current workspace. File tools are workspace-scoped, `~/.pie` is allowlisted for Pie configuration/session support, and shell commands return structured timeout/truncation/error details. The canonical shell tool is still named `bash` for compatibility; on Windows `shell:auto` uses PowerShell, and callers can explicitly request `bash`, `powershell`, or `cmd`. High-risk shell commands require confirmation outside YOLO mode. `/settings/yolo` relaxes filesystem sandbox directory restrictions and should only be used in trusted workspaces. `pie permissions` prints the current policy-derived safety summary.
|
|
263
|
+
|
|
264
|
+
## Code Intelligence
|
|
265
|
+
|
|
266
|
+
`code_intel` provides read-only JS/TS diagnostics, definition, references, symbols, and hover. CLI uses the TypeScript language service by default and falls back to lightweight text analysis only when the TypeScript service cannot start. Unity uses a Node sidecar contract for TypeScript intelligence so the PuerTS bundle does not import Node-only parser dependencies.
|
|
267
|
+
|
|
268
|
+
## Diagnostics
|
|
269
|
+
|
|
270
|
+
Use:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
pie doctor
|
|
274
|
+
pie doctor --json
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Doctor checks Node/npm, model configuration, API key availability, writable Pie data paths, Playwright browser readiness, Unity bridge hints, and runtime log location. User-facing errors stay concise; detailed debugging goes to `~/.pie/logs/pie-cli.log` and session trace artifacts.
|
|
278
|
+
|
|
161
279
|
## Output Modes
|
|
162
280
|
|
|
163
281
|
For non-interactive usage, Pie supports structured output options:
|
|
@@ -179,12 +297,36 @@ For the best multiline editing experience, use a terminal with good modified-key
|
|
|
179
297
|
## Development
|
|
180
298
|
|
|
181
299
|
```bash
|
|
182
|
-
|
|
183
|
-
npm
|
|
184
|
-
npm run
|
|
185
|
-
npm run
|
|
300
|
+
npm ci
|
|
301
|
+
npm run verify:quality
|
|
302
|
+
npm run verify:release
|
|
303
|
+
npm run verify:browser
|
|
304
|
+
npm run verify:terminal:matrix
|
|
186
305
|
```
|
|
187
306
|
|
|
307
|
+
Use `npm run verify:agent` when model credentials are available and you want the real daily agent gate.
|
|
308
|
+
Use `npm run verify:agent:nightly` and `npm run verify:unity:reliability` before release candidates that claim real-provider or Unity reliability.
|
|
309
|
+
Set `PIE_REAL_BROWSER=1` when you want `verify:browser` to open a real browser session instead of only checking the Playwright CLI contract.
|
|
310
|
+
|
|
311
|
+
## Publishing
|
|
312
|
+
|
|
313
|
+
Publish `@cydm/pie` from the monorepo root.
|
|
314
|
+
|
|
315
|
+
Set the npm token first:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
npm config set //registry.npmjs.org/:_authToken <YOUR_TOKEN>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Then publish:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
npm run verify:release
|
|
325
|
+
npm publish --workspace products/cli --access public
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
If the package version already exists on npm, bump `products/cli/package.json` first.
|
|
329
|
+
|
|
188
330
|
## License
|
|
189
331
|
|
|
190
332
|
MIT
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createAskUserCapability
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
5
|
-
import "../../../chunks/chunk-
|
|
6
|
-
import "../../../chunks/chunk-RID3574D.js";
|
|
4
|
+
} from "../../../chunks/chunk-BHNULR7U.js";
|
|
5
|
+
import "../../../chunks/chunk-A5JSJAPK.js";
|
|
7
6
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
8
7
|
|
|
9
8
|
// builtin/extensions/ask-user/index.ts
|
|
@@ -2,8 +2,8 @@ import { createRequire as __createRequire } from "node:module"; const require =
|
|
|
2
2
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
3
3
|
|
|
4
4
|
// builtin/extensions/kimi-attachments/index.ts
|
|
5
|
-
function
|
|
6
|
-
return
|
|
5
|
+
function isKimiAttachmentPlatform(model) {
|
|
6
|
+
return model.attachmentPlatform === "kimi" || model.filePlatform === "kimi";
|
|
7
7
|
}
|
|
8
8
|
function getModality(mimeType) {
|
|
9
9
|
if (mimeType.startsWith("image/")) return "image";
|
|
@@ -16,7 +16,7 @@ function kimiAttachmentsExtension(ctx) {
|
|
|
16
16
|
name: "kimi-native-attachments",
|
|
17
17
|
priority: 100,
|
|
18
18
|
matches(model, attachment) {
|
|
19
|
-
return
|
|
19
|
+
return isKimiAttachmentPlatform(model) && Boolean(attachment.fileId) && getModality(attachment.mimeType) !== "file";
|
|
20
20
|
},
|
|
21
21
|
async prepare(_model, attachment, prepareCtx) {
|
|
22
22
|
const fileId = attachment.fileId;
|
|
@@ -1,73 +1,74 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
buildExecutionReminder,
|
|
4
|
+
createExecutionStateFromPlan,
|
|
4
5
|
createPlanCapability,
|
|
5
6
|
extractPlanTodoItems,
|
|
6
7
|
isPlanModeSafeCommand,
|
|
7
|
-
markCompletedPlanSteps
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import "../../../chunks/chunk-
|
|
8
|
+
markCompletedPlanSteps,
|
|
9
|
+
restoreExecutionState
|
|
10
|
+
} from "../../../chunks/chunk-BHNULR7U.js";
|
|
11
|
+
import "../../../chunks/chunk-A5JSJAPK.js";
|
|
11
12
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
12
13
|
|
|
13
14
|
// builtin/extensions/plan-mode/index.ts
|
|
14
15
|
var SESSION_METADATA_KEY = "planModeEnabled";
|
|
16
|
+
var EXECUTION_METADATA_KEY = "executionState";
|
|
15
17
|
var planModeEnabled = false;
|
|
16
18
|
var executionMode = false;
|
|
17
|
-
|
|
19
|
+
function toPlanTodoItems(state) {
|
|
20
|
+
return state.steps.map((step) => ({
|
|
21
|
+
step: step.id,
|
|
22
|
+
text: step.title,
|
|
23
|
+
completed: step.status === "completed"
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
18
26
|
function planModeExtension(ctx) {
|
|
19
|
-
ctx.log("Plan mode extension loaded");
|
|
20
27
|
const capability = createPlanCapability();
|
|
21
28
|
function readPersistedPlanMode() {
|
|
22
29
|
return ctx.getSessionMetadata()?.[SESSION_METADATA_KEY] === true;
|
|
23
30
|
}
|
|
31
|
+
function readExecutionState() {
|
|
32
|
+
return restoreExecutionState(ctx.getSessionMetadata()?.[EXECUTION_METADATA_KEY] ?? ctx.getExecutionState());
|
|
33
|
+
}
|
|
34
|
+
function writeExecutionState(state) {
|
|
35
|
+
const next = state ?? restoreExecutionState(null);
|
|
36
|
+
ctx.setSessionMetadata(EXECUTION_METADATA_KEY, next);
|
|
37
|
+
ctx.setExecutionState(next);
|
|
38
|
+
ctx.setExecutionReminder(buildExecutionReminder(next) ?? void 0);
|
|
39
|
+
return next;
|
|
40
|
+
}
|
|
24
41
|
function updateStatus() {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
42
|
+
const state = readExecutionState();
|
|
43
|
+
if (executionMode && state.steps.length > 0) {
|
|
44
|
+
const completed = state.steps.filter((step) => step.status === "completed").length;
|
|
45
|
+
ctx.ui.notify(`\u{1F4CB} Plan: ${completed}/${state.steps.length} completed`, "info");
|
|
28
46
|
} else if (planModeEnabled) {
|
|
29
47
|
ctx.ui.notify("\u23F8\uFE0F Plan mode active (read-only)", "info");
|
|
30
48
|
}
|
|
31
49
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
|
|
50
|
+
function applyPlanModeState(enabled, forcePromptInjection = false) {
|
|
51
|
+
planModeEnabled = enabled;
|
|
52
|
+
if (!enabled) {
|
|
53
|
+
executionMode = false;
|
|
36
54
|
}
|
|
37
|
-
|
|
55
|
+
ctx.setPlanMode(enabled, { forcePromptInjection });
|
|
38
56
|
}
|
|
39
57
|
function togglePlanMode() {
|
|
40
58
|
planModeEnabled = !planModeEnabled;
|
|
41
59
|
executionMode = false;
|
|
42
|
-
|
|
60
|
+
writeExecutionState(null);
|
|
43
61
|
ctx.setSessionMetadata(SESSION_METADATA_KEY, planModeEnabled);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
ctx.
|
|
47
|
-
|
|
48
|
-
} else {
|
|
49
|
-
ctx.setActiveTools(getNormalModeTools());
|
|
50
|
-
ctx.ui.notify("Plan mode disabled. Full access restored.");
|
|
51
|
-
}
|
|
52
|
-
updateStatus();
|
|
53
|
-
}
|
|
54
|
-
function applyPlanModeState(enabled, options = {}) {
|
|
55
|
-
planModeEnabled = enabled;
|
|
56
|
-
executionMode = false;
|
|
57
|
-
todoItems = [];
|
|
58
|
-
ctx.setPlanMode(enabled, { forcePromptInjection: options.forcePromptInjection });
|
|
59
|
-
ctx.setActiveTools(enabled ? [...PLAN_MODE_TOOL_NAMES] : getNormalModeTools());
|
|
60
|
-
if (options.notify) {
|
|
61
|
-
ctx.ui.notify(
|
|
62
|
-
enabled ? `Plan mode enabled. Available tools: ${PLAN_MODE_TOOL_NAMES.join(", ")}` : "Plan mode disabled. Full access restored."
|
|
63
|
-
);
|
|
64
|
-
}
|
|
62
|
+
applyPlanModeState(planModeEnabled, true);
|
|
63
|
+
ctx.ui.notify(
|
|
64
|
+
planModeEnabled ? `Plan mode enabled. Available tools: ${ctx.getActiveTools().join(", ")}` : "Plan mode disabled. Full access restored."
|
|
65
|
+
);
|
|
65
66
|
updateStatus();
|
|
66
67
|
}
|
|
67
68
|
ctx.registerCommand({
|
|
68
69
|
path: capability.commands?.[0]?.path ?? ["tools", "plan"],
|
|
69
70
|
description: capability.commands?.[0]?.description ?? "Toggle plan mode (read-only exploration)",
|
|
70
|
-
handler: async (
|
|
71
|
+
handler: async () => {
|
|
71
72
|
togglePlanMode();
|
|
72
73
|
}
|
|
73
74
|
});
|
|
@@ -91,78 +92,75 @@ Command: ${command}`,
|
|
|
91
92
|
});
|
|
92
93
|
ctx.on("agent:start", async () => {
|
|
93
94
|
const persisted = readPersistedPlanMode();
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
if (planModeEnabled) {
|
|
98
|
-
ctx.log("[plan-mode] Injecting plan mode context");
|
|
99
|
-
}
|
|
95
|
+
applyPlanModeState(persisted);
|
|
96
|
+
writeExecutionState(readExecutionState());
|
|
100
97
|
});
|
|
101
98
|
ctx.on("session:changed", async () => {
|
|
102
99
|
const enabled = readPersistedPlanMode();
|
|
103
|
-
applyPlanModeState(enabled,
|
|
100
|
+
applyPlanModeState(enabled, enabled);
|
|
101
|
+
writeExecutionState(readExecutionState());
|
|
104
102
|
});
|
|
105
103
|
ctx.on("turn:end", async (event) => {
|
|
106
|
-
|
|
104
|
+
const state = readExecutionState();
|
|
105
|
+
if (!executionMode || state.steps.length === 0) return;
|
|
107
106
|
const message = event.message;
|
|
108
|
-
if (message?.role
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
if (message?.role !== "assistant" || !Array.isArray(message.content)) return;
|
|
108
|
+
const text = message.content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
|
|
109
|
+
const todoItems = toPlanTodoItems(state);
|
|
110
|
+
if (markCompletedPlanSteps(text, todoItems) <= 0) return;
|
|
111
|
+
const nextState = createExecutionStateFromPlan(todoItems);
|
|
112
|
+
writeExecutionState(nextState);
|
|
113
|
+
updateStatus();
|
|
114
114
|
});
|
|
115
115
|
ctx.on("agent:end", async (event) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
const state = readExecutionState();
|
|
117
|
+
if (executionMode && state.steps.length > 0) {
|
|
118
|
+
if (state.steps.every((step) => step.status === "completed")) {
|
|
119
|
+
const completedList = state.steps.map((step) => `\u2713 ${step.title}`).join("\n");
|
|
119
120
|
ctx.ui.notify(`**Plan Complete!** \u2713
|
|
120
121
|
|
|
121
122
|
${completedList}`, "info");
|
|
122
123
|
executionMode = false;
|
|
123
|
-
|
|
124
|
-
ctx.setActiveTools(getNormalModeTools());
|
|
124
|
+
writeExecutionState(null);
|
|
125
125
|
}
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
if (!planModeEnabled || !ctx.hasUI) return;
|
|
129
129
|
const messages = event.messages || [];
|
|
130
130
|
const lastAssistant = [...messages].reverse().find(
|
|
131
|
-
(
|
|
131
|
+
(message) => message.role === "assistant" && Array.isArray(message.content)
|
|
132
132
|
);
|
|
133
|
-
if (lastAssistant)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
if (todoItems.length > 0) {
|
|
141
|
-
const todoListText = todoItems.map((t) => `${t.step}. \u2610 ${t.text}`).join("\n");
|
|
142
|
-
ctx.ui.notify(`**Plan Steps (${todoItems.length}):**
|
|
133
|
+
if (!lastAssistant) return;
|
|
134
|
+
const text = lastAssistant.content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
|
|
135
|
+
const extracted = extractPlanTodoItems(text);
|
|
136
|
+
if (extracted.length === 0) return;
|
|
137
|
+
const extractedState = writeExecutionState(createExecutionStateFromPlan(extracted));
|
|
138
|
+
const todoListText = extractedState.steps.map((step) => `${step.id}. \u2610 ${step.title}`).join("\n");
|
|
139
|
+
ctx.ui.notify(`**Plan Steps (${extractedState.steps.length}):**
|
|
143
140
|
${todoListText}`, "info");
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
141
|
+
const choice = await ctx.ui.select("Plan mode - what next?", [
|
|
142
|
+
"Execute the plan (track progress)",
|
|
143
|
+
"Stay in plan mode",
|
|
144
|
+
"Refine the plan"
|
|
145
|
+
]);
|
|
146
|
+
if (choice?.startsWith("Execute")) {
|
|
147
|
+
planModeEnabled = false;
|
|
148
|
+
executionMode = extractedState.steps.length > 0;
|
|
149
|
+
ctx.setSessionMetadata(SESSION_METADATA_KEY, false);
|
|
150
|
+
ctx.setPlanMode(false);
|
|
151
|
+
updateStatus();
|
|
152
|
+
const execMessage = extractedState.steps.length > 0 ? `Execute the plan. Start with: ${extractedState.steps[0].title}. Mark completed steps with [DONE:n] in your response.` : "Execute the plan you just created.";
|
|
153
|
+
ctx.sendUserMessage(execMessage);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (choice === "Refine the plan") {
|
|
157
|
+
const refinement = await ctx.ui.editor("Refine the plan:", "");
|
|
158
|
+
if (refinement?.trim()) {
|
|
159
|
+
ctx.sendUserMessage(refinement.trim());
|
|
163
160
|
}
|
|
164
161
|
}
|
|
165
162
|
});
|
|
163
|
+
ctx.log("Plan mode extension loaded");
|
|
166
164
|
}
|
|
167
165
|
export {
|
|
168
166
|
planModeExtension as default
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
3
|
createCliHostCapabilities
|
|
4
|
-
} from "../../../chunks/chunk-
|
|
4
|
+
} from "../../../chunks/chunk-GDTN4UPJ.js";
|
|
5
5
|
import {
|
|
6
|
+
createReadResourceCapability,
|
|
7
|
+
createReadSkillCapability,
|
|
8
|
+
createResolveResourceCapability,
|
|
6
9
|
createSharedFileSystemTools,
|
|
7
10
|
createSubagentCapability
|
|
8
|
-
} from "../../../chunks/chunk-
|
|
9
|
-
import "../../../chunks/chunk-
|
|
10
|
-
import "../../../chunks/chunk-RID3574D.js";
|
|
11
|
+
} from "../../../chunks/chunk-BHNULR7U.js";
|
|
12
|
+
import "../../../chunks/chunk-A5JSJAPK.js";
|
|
11
13
|
import "../../../chunks/chunk-TG2EQLX2.js";
|
|
12
14
|
|
|
13
15
|
// builtin/extensions/subagent/index.ts
|
|
@@ -34,7 +36,7 @@ function subagentExtension(ctx) {
|
|
|
34
36
|
let model = ctx.model;
|
|
35
37
|
if (!model) {
|
|
36
38
|
model = {
|
|
37
|
-
id: process.env.PIE_MODEL_ID || "kimi-
|
|
39
|
+
id: process.env.PIE_MODEL_ID || "kimi-api/kimi-k2.5",
|
|
38
40
|
name: "Default Model",
|
|
39
41
|
reasoning: false,
|
|
40
42
|
input: ["text"],
|
|
@@ -56,12 +58,75 @@ function subagentExtension(ctx) {
|
|
|
56
58
|
log: (message) => ctx.log(message),
|
|
57
59
|
notify: ctx.hasUI ? (message, type) => ctx.ui.notify(message, type) : void 0,
|
|
58
60
|
debugLog,
|
|
61
|
+
resolveModelClass: (modelClass) => ctx.resolveModelClass?.(modelClass),
|
|
59
62
|
createRuntimeTools: () => {
|
|
60
|
-
const
|
|
61
|
-
|
|
63
|
+
const parentRuntimeTools = ctx.getRuntimeTools?.();
|
|
64
|
+
if (Array.isArray(parentRuntimeTools) && parentRuntimeTools.length > 0) {
|
|
65
|
+
return parentRuntimeTools.filter((tool) => tool?.name !== "spawn_subagents_parallel");
|
|
66
|
+
}
|
|
67
|
+
const configDir = path.join(os.homedir(), ".pie");
|
|
68
|
+
const fsOptions = ctx.yoloMode ? { allowlistedDirs: ["/"] } : { allowlistedDirs: [configDir] };
|
|
62
69
|
const fsTools = createSharedFileSystemTools(ctx.cwd, fsOptions);
|
|
70
|
+
const readSkillTool = createReadSkillCapability({
|
|
71
|
+
resolveSkill(name) {
|
|
72
|
+
const skill = skills.find((entry) => entry.name === name);
|
|
73
|
+
if (!skill || !skill.filePath) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const content = skill.content ?? fs.readFileSync(skill.filePath, "utf8");
|
|
77
|
+
return {
|
|
78
|
+
name: skill.name,
|
|
79
|
+
content,
|
|
80
|
+
resources: skill.resourceRefs
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}).tool;
|
|
84
|
+
const readResourceTool = createReadResourceCapability({
|
|
85
|
+
resolveResource(owner, resourcePath) {
|
|
86
|
+
const skill = skills.find((entry) => entry.name === owner);
|
|
87
|
+
if (!skill) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const trimmed = String(resourcePath || "").trim().replace(/\\/g, "/");
|
|
91
|
+
if (!trimmed || trimmed.startsWith("/") || /^[A-Za-z]:\//.test(trimmed)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
95
|
+
if (segments.some((segment) => segment === "..")) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const absolutePath = path.resolve(skill.baseDir, ...segments);
|
|
99
|
+
const relativePath = path.relative(skill.baseDir, absolutePath).replace(/\\/g, "/");
|
|
100
|
+
if (relativePath.startsWith("../") || relativePath === ".." || !fs.existsSync(absolutePath)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
owner: skill.name,
|
|
105
|
+
path: trimmed.replace(/^\.\/+/, ""),
|
|
106
|
+
content: fs.readFileSync(absolutePath, "utf8")
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}).tool;
|
|
110
|
+
const resolveResourceTool = createResolveResourceCapability({
|
|
111
|
+
resolveResource(owner, resourcePath) {
|
|
112
|
+
const skill = skills.find((entry) => entry.name === owner);
|
|
113
|
+
if (!skill) return null;
|
|
114
|
+
const trimmed = String(resourcePath || "").trim().replace(/\\/g, "/");
|
|
115
|
+
if (!trimmed || trimmed.startsWith("/") || /^[A-Za-z]:\//.test(trimmed)) return null;
|
|
116
|
+
const segments = trimmed.split("/").filter(Boolean);
|
|
117
|
+
if (segments.some((segment) => segment === "..")) return null;
|
|
118
|
+
const absolutePath = path.resolve(skill.baseDir, ...segments);
|
|
119
|
+
const relativePath = path.relative(skill.baseDir, absolutePath).replace(/\\/g, "/");
|
|
120
|
+
if (relativePath.startsWith("../") || relativePath === ".." || !fs.existsSync(absolutePath)) return null;
|
|
121
|
+
return {
|
|
122
|
+
owner: skill.name,
|
|
123
|
+
path: trimmed.replace(/^\.\/+/, ""),
|
|
124
|
+
resolvedPath: absolutePath
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}).tool;
|
|
63
128
|
const cliHostCapabilities = createCliHostCapabilities(ctx.cwd);
|
|
64
|
-
return [...fsTools, ...cliHostCapabilities.tools];
|
|
129
|
+
return [...fsTools, readSkillTool, readResourceTool, resolveResourceTool, ...cliHostCapabilities.tools];
|
|
65
130
|
}
|
|
66
131
|
});
|
|
67
132
|
for (const tool of capability.tools) {
|