decorated-pi 0.7.3 → 0.8.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/README.md +3 -3
- package/commands/usage.ts +5 -1
- package/hooks/compaction.ts +11 -146
- package/hooks/image-vision.ts +127 -5
- package/hooks/mcp.ts +10 -2
- package/hooks/pi-tool-filter.ts +2 -6
- package/hooks/skeleton.ts +4 -1
- package/hooks/smart-at.ts +18 -23
- package/hooks/thinking-label-strip.ts +79 -0
- package/index.ts +207 -176
- package/package.json +3 -1
- package/skills/pi-docs/SKILL.md +3 -4
- package/tools/ask/index.ts +1 -1
- package/tools/patch/core.ts +154 -81
- package/tools/patch/encoding.ts +172 -0
- package/tools/patch/index.ts +11 -7
- package/ui/ask.ts +71 -31
package/index.ts
CHANGED
|
@@ -23,8 +23,12 @@ import { createSkeleton } from "./hooks/skeleton.js";
|
|
|
23
23
|
import { setupRedact, REDACT_GUIDANCE } from "./hooks/redact.js";
|
|
24
24
|
import { externalizeModule } from "./hooks/externalize.js";
|
|
25
25
|
import { normalizeCodeblocksModule } from "./hooks/normalize-codeblocks.js";
|
|
26
|
+
import { thinkingLabelStripModule } from "./hooks/thinking-label-strip.js";
|
|
26
27
|
import { trackMtimeModule } from "./hooks/track-mtime.js";
|
|
27
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
injectAgentsMdModule,
|
|
30
|
+
INJECT_AGENTS_MD_GUIDANCE,
|
|
31
|
+
} from "./hooks/inject-agents-md.js";
|
|
28
32
|
import { imageVisionModule } from "./hooks/image-vision.js";
|
|
29
33
|
import { smartAtModule } from "./hooks/smart-at.js";
|
|
30
34
|
import { sessionTitleModule } from "./hooks/session-title.js";
|
|
@@ -39,7 +43,11 @@ import { registerLspTools } from "./tools/lsp/tools.js";
|
|
|
39
43
|
import { LspServerManager } from "./tools/lsp/manager.js";
|
|
40
44
|
import { collectLspDependencyStatuses } from "./tools/lsp/servers.js";
|
|
41
45
|
import { registerAskTool } from "./tools/ask/index.js";
|
|
42
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
resolveMcpConfigs,
|
|
48
|
+
migrateLegacyGlobalMcpConfig,
|
|
49
|
+
collectMcpDependencyStatuses,
|
|
50
|
+
} from "./tools/mcp/config.js";
|
|
43
51
|
import { ensureMcpServerReady } from "./hooks/mcp.js";
|
|
44
52
|
|
|
45
53
|
import { registerDpModelCommand } from "./commands/dp-model.js";
|
|
@@ -56,80 +64,98 @@ import { captureModuleSnapshot, isModuleEnabled } from "./settings.js";
|
|
|
56
64
|
// its constant and pushing it here. No priority / sort logic — just push.
|
|
57
65
|
|
|
58
66
|
const BASE_GUIDANCE = [
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
"## Decorated Pi Guidance",
|
|
68
|
+
"",
|
|
69
|
+
"### Workflow, how to approach tasks",
|
|
70
|
+
"- Before acting on a prompt, do sufficient research on the existing state — read files, search, investigate — and only proceed once you have a clear picture.",
|
|
71
|
+
"- Exercise caution when performing any **write** operations, especially when you are in a research or exploration phase.",
|
|
72
|
+
"- Before modifying code, match the user's existing code style (naming, formatting, patterns). Do not re-modify lines the user has manually edited since your last change.",
|
|
73
|
+
"",
|
|
74
|
+
"### Filesystem Safety, where NOT to write",
|
|
75
|
+
"- CAUTION: Do not perform write operations in the following directories unless explicitly instructed: `node_modules`, `venv`, `env`, `__pycache__`, `.git` or any other hidden directories.",
|
|
76
|
+
].join("\n");
|
|
77
|
+
|
|
78
|
+
// Adapted from hexiecs/talk-normal (MIT), prompt-chatgpt.md v0.6.2.
|
|
79
|
+
const TALK_NORMAL_GUIDANCE = [
|
|
80
|
+
"## Your talking style",
|
|
81
|
+
"",
|
|
82
|
+
"- Be direct and informative. No filler, no fluff, but give enough to be useful.",
|
|
83
|
+
"- Lead with the answer; add context only if it helps.",
|
|
84
|
+
"- Prefer direct positive claims. Avoid negation-based contrastive phrasing like `不是X,而是Y` / `it's not X, it's Y`; state the positive claim directly.",
|
|
85
|
+
"- Kill filler: `I'd be happy to`, `Great question`, `It's worth noting`, `Certainly`, `Of course`, `首先`, `值得注意的是`, `综上所述`.",
|
|
86
|
+
"- Never restate the question.",
|
|
87
|
+
"- Yes/no questions: answer first, then give one sentence of reasoning.",
|
|
88
|
+
"- Comparisons: give a recommendation with brief reasoning, not a balanced essay.",
|
|
89
|
+
"- Code: give the code plus a usage example when non-trivial. Skip preambles like `Certainly! Here is...`.",
|
|
90
|
+
"- Use bullets or numbered lists only when the content has real parallel or sequential structure.",
|
|
91
|
+
"- Do not end with conditional follow-up offers like `If you want, I can...` / `如果你愿意,我还可以...`; take the real next step or name it directly.",
|
|
92
|
+
"- Do not use summary-stamp closings like `In summary`, `Hope this helps`, `一句话总结`, `总结一下`, `简而言之`; state the final claim directly.",
|
|
68
93
|
].join("\n");
|
|
69
94
|
|
|
70
95
|
/** Remove the injected Pi documentation block from the base system prompt.
|
|
71
96
|
* Matches a line containing "Pi documentation" and deletes it plus all
|
|
72
97
|
* following non-empty lines, stopping at the first blank line. */
|
|
73
98
|
export function stripPiDocsBlock(prompt: string): string {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
99
|
+
const lines = prompt.split("\n");
|
|
100
|
+
const out: string[] = [];
|
|
101
|
+
let i = 0;
|
|
102
|
+
while (i < lines.length) {
|
|
103
|
+
const line = lines[i];
|
|
104
|
+
if (line.includes("Pi documentation")) {
|
|
105
|
+
i++;
|
|
106
|
+
while (i < lines.length && lines[i].trim() !== "") i++;
|
|
107
|
+
// Drop the terminating blank line as well so we don't leave orphan whitespace.
|
|
108
|
+
if (i < lines.length && lines[i].trim() === "") i++;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
out.push(line);
|
|
112
|
+
i++;
|
|
85
113
|
}
|
|
86
|
-
out.
|
|
87
|
-
i++;
|
|
88
|
-
}
|
|
89
|
-
return out.join("\n");
|
|
114
|
+
return out.join("\n");
|
|
90
115
|
}
|
|
91
116
|
|
|
92
117
|
/** Sort the <available_skills> block in the system prompt by skill name.
|
|
93
118
|
* Pi core appends extension-provided skills after user/project skills and does
|
|
94
119
|
* not sort the XML; this makes the final prompt stable and cache-friendly. */
|
|
95
120
|
export function sortSkillsInSystemPrompt(prompt: string): string {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
121
|
+
const startMarker = "\n<available_skills>";
|
|
122
|
+
const endMarker = "</available_skills>";
|
|
123
|
+
const startIdx = prompt.indexOf(startMarker);
|
|
124
|
+
if (startIdx === -1) return prompt;
|
|
125
|
+
const endIdx = prompt.indexOf(endMarker, startIdx);
|
|
126
|
+
if (endIdx === -1) return prompt;
|
|
127
|
+
|
|
128
|
+
const before = prompt.slice(0, startIdx + startMarker.length);
|
|
129
|
+
const after = prompt.slice(endIdx);
|
|
130
|
+
const inner = prompt.slice(startIdx + startMarker.length, endIdx);
|
|
131
|
+
|
|
132
|
+
const chunks: string[][] = [];
|
|
133
|
+
let current: string[] = [];
|
|
134
|
+
for (const line of inner.split("\n")) {
|
|
135
|
+
const trimmed = line.trim();
|
|
136
|
+
if (trimmed === "<skill>") {
|
|
137
|
+
current = [line];
|
|
138
|
+
} else if (trimmed === "</skill>") {
|
|
139
|
+
current.push(line);
|
|
140
|
+
chunks.push(current);
|
|
141
|
+
current = [];
|
|
142
|
+
} else if (current.length > 0) {
|
|
143
|
+
current.push(line);
|
|
144
|
+
}
|
|
119
145
|
}
|
|
120
|
-
}
|
|
121
146
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
const nameOf = (chunk: string[]) => {
|
|
148
|
+
const line = chunk.find((l) => l.trim().startsWith("<name>"));
|
|
149
|
+
if (!line) return "";
|
|
150
|
+
const t = line.trim();
|
|
151
|
+
return t.slice(6, t.indexOf("</name>"));
|
|
152
|
+
};
|
|
128
153
|
|
|
129
|
-
|
|
154
|
+
chunks.sort((a, b) => nameOf(a).localeCompare(nameOf(b)));
|
|
130
155
|
|
|
131
|
-
|
|
132
|
-
|
|
156
|
+
const sortedInner =
|
|
157
|
+
"\n" + chunks.map((chunk) => chunk.join("\n")).join("\n") + "\n";
|
|
158
|
+
return before + sortedInner + after;
|
|
133
159
|
}
|
|
134
160
|
|
|
135
161
|
/** Build the list of guideline strings to inject, in prompt order.
|
|
@@ -139,146 +165,151 @@ export function sortSkillsInSystemPrompt(prompt: string): string {
|
|
|
139
165
|
* injected). The mcp module check lives in `resolveMcpConfigs`, so this
|
|
140
166
|
* code only needs to look at the server's own `enabled` flag. */
|
|
141
167
|
function buildGuidelines(): string[] {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
168
|
+
const out: string[] = [
|
|
169
|
+
BASE_GUIDANCE,
|
|
170
|
+
TALK_NORMAL_GUIDANCE,
|
|
171
|
+
INJECT_AGENTS_MD_GUIDANCE, // from hooks/inject-agents-md.ts — always on
|
|
172
|
+
];
|
|
173
|
+
if (isModuleEnabled("secretRedaction")) out.push(REDACT_GUIDANCE);
|
|
174
|
+
return out;
|
|
148
175
|
}
|
|
149
176
|
|
|
150
|
-
function canRegisterMcpServer(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
177
|
+
function canRegisterMcpServer(
|
|
178
|
+
config: { name: string; command?: string },
|
|
179
|
+
deps: Array<{ module: string; state: string }>,
|
|
180
|
+
): boolean {
|
|
181
|
+
if (!config.command) return true;
|
|
182
|
+
const dep = deps.find((d) => d.module === `mcp:${config.name}`);
|
|
183
|
+
return dep ? dep.state === "ok" : true;
|
|
154
184
|
}
|
|
155
185
|
|
|
156
186
|
/** Absolute path to the plugin's builtin skills directory.
|
|
157
187
|
* Used by `resources_discover` so the skill travels with the plugin
|
|
158
188
|
* regardless of which project pi is running in. */
|
|
159
189
|
export function getBuiltinSkillPaths(): string[] {
|
|
160
|
-
|
|
190
|
+
return [fileURLToPath(new URL("./skills", import.meta.url))];
|
|
161
191
|
}
|
|
162
192
|
|
|
163
193
|
/** Register the plugin's builtin skill paths with Pi core. */
|
|
164
194
|
function installBuiltinSkills(pi: ExtensionAPI): void {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
195
|
+
pi.on("resources_discover", async (_event: any) => ({
|
|
196
|
+
skillPaths: getBuiltinSkillPaths(),
|
|
197
|
+
}));
|
|
168
198
|
}
|
|
169
199
|
|
|
170
200
|
/** Install a single before_agent_start handler that appends every
|
|
171
|
-
* guideline in order
|
|
172
|
-
* for cache stability. Idempotent — re-injection is a no-op via marker. */
|
|
201
|
+
* guideline in order. Idempotent — re-injection is a no-op via marker. */
|
|
173
202
|
function installGuidelines(pi: ExtensionAPI): void {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
203
|
+
const blocks = buildGuidelines();
|
|
204
|
+
const joined = blocks.join("\n\n");
|
|
205
|
+
const marker = "## Decorated Pi Guidance";
|
|
206
|
+
|
|
207
|
+
pi.on("before_agent_start", async (event: any) => {
|
|
208
|
+
if (!event.systemPrompt) return undefined;
|
|
209
|
+
let prompt: string = stripPiDocsBlock(event.systemPrompt);
|
|
210
|
+
prompt = sortSkillsInSystemPrompt(prompt);
|
|
211
|
+
if (prompt.includes(marker)) return undefined; // already injected this turn
|
|
212
|
+
return { systemPrompt: `${prompt}\n\n${joined}` };
|
|
213
|
+
});
|
|
186
214
|
}
|
|
187
215
|
|
|
188
216
|
export default async function (pi: ExtensionAPI) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if (isModuleEnabled("ask")) registerAskTool(pi);
|
|
238
|
-
|
|
239
|
-
// MCP: hook, tools, and /mcp command are gated together. Disabling the
|
|
240
|
-
// module means no session_start handler runs, no tools register, no
|
|
241
|
-
// /mcp command is available, and no background connections are attempted.
|
|
242
|
-
if (isModuleEnabled("mcp")) {
|
|
243
|
-
// One-time migration: legacy global MCP configs in
|
|
244
|
-
// ~/.pi/agent/decorated-pi.json move to ~/.pi/agent/mcp.json. Run
|
|
245
|
-
// explicitly here so `loadGlobalMcpConfigs` stays pure.
|
|
246
|
-
migrateLegacyGlobalMcpConfig();
|
|
247
|
-
sk.register(mcpModule);
|
|
248
|
-
const mcpDeps = collectMcpDependencyStatuses(process.cwd());
|
|
249
|
-
for (const dep of mcpDeps) {
|
|
250
|
-
if (dep.state !== "ok") {
|
|
251
|
-
sk.declareMissing({
|
|
252
|
-
name: dep.label, // binary name (e.g. "codegraph")
|
|
253
|
-
module: "mcp",
|
|
254
|
-
hint: dep.detail,
|
|
255
|
-
});
|
|
256
|
-
}
|
|
217
|
+
// Snapshot the module settings that pi is about to load. /dp-settings
|
|
218
|
+
// compares against this to avoid prompting for reload when the user
|
|
219
|
+
// has only returned the settings to the currently-loaded state.
|
|
220
|
+
captureModuleSnapshot();
|
|
221
|
+
|
|
222
|
+
// ── Skeleton (hooks) ───────────────────────────────────────────────────
|
|
223
|
+
const sk = createSkeleton();
|
|
224
|
+
|
|
225
|
+
// Order matters for tool_result compose chain:
|
|
226
|
+
// 1. redact → normalize-codeblocks → externalize → track-mtime → inject-agents-md → image-vision → wakatime
|
|
227
|
+
// The first module registered for a given event runs first (compose chain).
|
|
228
|
+
if (isModuleEnabled("secretRedaction")) setupRedact(sk);
|
|
229
|
+
sk.register(normalizeCodeblocksModule);
|
|
230
|
+
sk.register(thinkingLabelStripModule);
|
|
231
|
+
sk.register(externalizeModule);
|
|
232
|
+
sk.register(trackMtimeModule);
|
|
233
|
+
sk.register(injectAgentsMdModule);
|
|
234
|
+
sk.register(imageVisionModule);
|
|
235
|
+
|
|
236
|
+
// session_start handlers (parallel)
|
|
237
|
+
// pi-tool-filter must register first so native tools are dropped before
|
|
238
|
+
// anything else inspects the tool list.
|
|
239
|
+
sk.register(piToolFilterModule);
|
|
240
|
+
sk.register(sessionTitleModule);
|
|
241
|
+
if (isModuleEnabled("atOverride")) sk.register(smartAtModule);
|
|
242
|
+
if (isModuleEnabled("wakatime")) sk.register(wakatimeModule);
|
|
243
|
+
|
|
244
|
+
// Compaction + RTK (these also install their own pi.on via setup<>()).
|
|
245
|
+
setupCompaction(sk);
|
|
246
|
+
if (isModuleEnabled("rtk")) setupRtk(sk);
|
|
247
|
+
if (isModuleEnabled("wakatime")) setupWakatime(sk, pi);
|
|
248
|
+
|
|
249
|
+
// ── Tools (conditional on module switches) ────────────────────────────
|
|
250
|
+
if (isModuleEnabled("patchOverrideEdit")) registerPatchTool(pi);
|
|
251
|
+
if (isModuleEnabled("lsp")) {
|
|
252
|
+
const lspDeps = collectLspDependencyStatuses(process.cwd());
|
|
253
|
+
if (lspDeps.some((d) => d.state === "ok")) {
|
|
254
|
+
registerLspTools(pi, new LspServerManager());
|
|
255
|
+
}
|
|
256
|
+
for (const dep of lspDeps) {
|
|
257
|
+
if (dep.state !== "ok") {
|
|
258
|
+
sk.declareMissing({
|
|
259
|
+
name: dep.label,
|
|
260
|
+
module: "lsp",
|
|
261
|
+
hint: dep.detail,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
257
265
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
+
if (isModuleEnabled("ask")) registerAskTool(pi);
|
|
267
|
+
|
|
268
|
+
// MCP: hook, tools, and /mcp command are gated together. Disabling the
|
|
269
|
+
// module means no session_start handler runs, no tools register, no
|
|
270
|
+
// /mcp command is available, and no background connections are attempted.
|
|
271
|
+
if (isModuleEnabled("mcp")) {
|
|
272
|
+
// One-time migration: legacy global MCP configs in
|
|
273
|
+
// ~/.pi/agent/decorated-pi.json move to ~/.pi/agent/mcp.json. Run
|
|
274
|
+
// explicitly here so `loadGlobalMcpConfigs` stays pure.
|
|
275
|
+
migrateLegacyGlobalMcpConfig();
|
|
276
|
+
sk.register(mcpModule);
|
|
277
|
+
const mcpDeps = collectMcpDependencyStatuses(process.cwd());
|
|
278
|
+
for (const dep of mcpDeps) {
|
|
279
|
+
if (dep.state !== "ok") {
|
|
280
|
+
sk.declareMissing({
|
|
281
|
+
name: dep.label, // binary name (e.g. "codegraph")
|
|
282
|
+
module: "mcp",
|
|
283
|
+
hint: dep.detail,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const configs = resolveMcpConfigs(process.cwd()).filter(
|
|
288
|
+
(s) => s.enabled,
|
|
289
|
+
);
|
|
290
|
+
// Per-server readiness: cache hit → register from cache (fast).
|
|
291
|
+
// Cache miss → connect synchronously, write cache, then register
|
|
292
|
+
// live tools. This blocks startup only for cache-miss servers.
|
|
293
|
+
// Skip servers whose binary is missing (dependency not met).
|
|
294
|
+
for (const config of configs) {
|
|
295
|
+
if (!canRegisterMcpServer(config, mcpDeps)) continue;
|
|
296
|
+
await ensureMcpServerReady(pi, config, process.cwd());
|
|
297
|
+
}
|
|
298
|
+
registerMcpStatusCommand(pi);
|
|
266
299
|
}
|
|
267
|
-
registerMcpStatusCommand(pi);
|
|
268
|
-
}
|
|
269
300
|
|
|
270
|
-
|
|
271
|
-
|
|
301
|
+
// ── Builtin skills (travel with the plugin in every project) ─────────────
|
|
302
|
+
installBuiltinSkills(pi);
|
|
272
303
|
|
|
273
|
-
|
|
274
|
-
|
|
304
|
+
// ── System-prompt guidelines (single handler, array order = prompt order) ──
|
|
305
|
+
installGuidelines(pi);
|
|
275
306
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
307
|
+
// ── Commands ──────────────────────────────────────────────────────────
|
|
308
|
+
registerDpModelCommand(pi);
|
|
309
|
+
registerDpSettingsCommand(pi);
|
|
310
|
+
if (isModuleEnabled("retry")) registerRetryCommand(pi);
|
|
311
|
+
if (isModuleEnabled("usage")) registerUsageCommand(pi);
|
|
281
312
|
|
|
282
|
-
|
|
283
|
-
|
|
313
|
+
// ── Install skeleton (last) ────────────────────────────────────────────
|
|
314
|
+
sk.install(pi);
|
|
284
315
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "decorated-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "decorated-pi is a practical enhancement pack for pi coding agent — token-efficient workflow, cache-friendly design, and smarter tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@ff-labs/fff-node": "^0.9.4",
|
|
28
28
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
29
|
+
"chardet": "^2.2.0",
|
|
29
30
|
"file-type": "^21.3.4",
|
|
31
|
+
"iconv-lite": "^0.7.2",
|
|
30
32
|
"openai": "^6.37.0"
|
|
31
33
|
},
|
|
32
34
|
"peerDependencies": {
|
package/skills/pi-docs/SKILL.md
CHANGED
|
@@ -4,10 +4,9 @@ description: pi docs resources
|
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Pi documentation (read only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):
|
|
7
|
-
- Main documentation:
|
|
8
|
-
- Additional docs:
|
|
9
|
-
- Examples:
|
|
10
|
-
- When reading pi docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory
|
|
7
|
+
- Main documentation: `$(npm root -g)/@earendil-works/pi-coding-agent/README.md`
|
|
8
|
+
- Additional docs: `$(npm root -g)/@earendil-works/pi-coding-agent/docs`
|
|
9
|
+
- Examples: `$(npm root -g)/@earendil-works/pi-coding-agent/examples` (extensions, custom tools, SDK)
|
|
11
10
|
- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), pi packages (docs/packages.md)
|
|
12
11
|
- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing
|
|
13
12
|
- Always read pi .md files completely and follow links to related docs (e.g., tui.md for TUI API details)
|
package/tools/ask/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ const askQuestionSchema = Type.Object({
|
|
|
17
17
|
{ description: "text = free input, single = one option, multi = many options" },
|
|
18
18
|
),
|
|
19
19
|
question: Type.String({ description: "Question text shown to the user." }),
|
|
20
|
-
options: Type.Optional(Type.Array(Type.String(), { description: "Options for single or multi choice." })),
|
|
20
|
+
options: Type.Optional(Type.Array(Type.String(), { description: "Options for single or multi choice. Each option MUST be a plain string (not an object). Example: [\"选项A\", \"选项B\", \"选项C\"]. The user picks by index; do NOT pass {id,text} objects." })),
|
|
21
21
|
default: Type.Optional(Type.String({ description: "Default answer. For multi, comma-separated values." })),
|
|
22
22
|
});
|
|
23
23
|
|