pi-agent-toolkit 0.1.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/dist/dotfiles/AGENTS.md +197 -0
- package/dist/dotfiles/APPEND_SYSTEM.md +78 -0
- package/dist/dotfiles/agent-modes.json +12 -0
- package/dist/dotfiles/agent-skills/exa-search/.env.example +4 -0
- package/dist/dotfiles/agent-skills/exa-search/SKILL.md +234 -0
- package/dist/dotfiles/agent-skills/exa-search/scripts/exa-api.cjs +197 -0
- package/dist/dotfiles/auth.json.template +5 -0
- package/dist/dotfiles/damage-control-rules.yaml +318 -0
- package/dist/dotfiles/extensions/btw.ts +1031 -0
- package/dist/dotfiles/extensions/commit-approval.ts +590 -0
- package/dist/dotfiles/extensions/context.ts +578 -0
- package/dist/dotfiles/extensions/control.ts +1748 -0
- package/dist/dotfiles/extensions/damage-control/index.ts +543 -0
- package/dist/dotfiles/extensions/damage-control/node_modules/.package-lock.json +22 -0
- package/dist/dotfiles/extensions/damage-control/package-lock.json +28 -0
- package/dist/dotfiles/extensions/damage-control/package.json +7 -0
- package/dist/dotfiles/extensions/dirty-repo-guard.ts +56 -0
- package/dist/dotfiles/extensions/exa-enforce.ts +51 -0
- package/dist/dotfiles/extensions/exa-search-tool.ts +384 -0
- package/dist/dotfiles/extensions/execute-command/index.ts +82 -0
- package/dist/dotfiles/extensions/files.ts +1112 -0
- package/dist/dotfiles/extensions/loop.ts +446 -0
- package/dist/dotfiles/extensions/pr-approval.ts +730 -0
- package/dist/dotfiles/extensions/qna-interactive.ts +532 -0
- package/dist/dotfiles/extensions/question-mode.ts +242 -0
- package/dist/dotfiles/extensions/require-session-name-on-exit.ts +141 -0
- package/dist/dotfiles/extensions/review.ts +2091 -0
- package/dist/dotfiles/extensions/session-breakdown.ts +1629 -0
- package/dist/dotfiles/extensions/term-notify.ts +150 -0
- package/dist/dotfiles/extensions/tilldone.ts +527 -0
- package/dist/dotfiles/extensions/todos.ts +2082 -0
- package/dist/dotfiles/extensions/tools.ts +146 -0
- package/dist/dotfiles/extensions/uv.ts +123 -0
- package/dist/dotfiles/global-skills/brainstorm/SKILL.md +10 -0
- package/dist/dotfiles/global-skills/cli-detector/SKILL.md +192 -0
- package/dist/dotfiles/global-skills/gh-issue-creator/SKILL.md +173 -0
- package/dist/dotfiles/global-skills/google-chat-cards-v2/SKILL.md +237 -0
- package/dist/dotfiles/global-skills/google-chat-cards-v2/references/bridge_tap_implementation.md +466 -0
- package/dist/dotfiles/global-skills/technical-docs/SKILL.md +204 -0
- package/dist/dotfiles/global-skills/technical-docs/references/diagrams.md +168 -0
- package/dist/dotfiles/global-skills/technical-docs/references/examples.md +449 -0
- package/dist/dotfiles/global-skills/technical-docs/scripts/validate_docs.py +352 -0
- package/dist/dotfiles/global-skills/whats-new/SKILL.md +159 -0
- package/dist/dotfiles/intercepted-commands/pip +7 -0
- package/dist/dotfiles/intercepted-commands/pip3 +7 -0
- package/dist/dotfiles/intercepted-commands/poetry +10 -0
- package/dist/dotfiles/intercepted-commands/python +104 -0
- package/dist/dotfiles/intercepted-commands/python3 +104 -0
- package/dist/dotfiles/mcp.json.template +32 -0
- package/dist/dotfiles/models.json +27 -0
- package/dist/dotfiles/settings.json +25 -0
- package/dist/index.js +1344 -0
- package/package.json +34 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1344 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { defineCommand, runMain } from "citty";
|
|
5
|
+
|
|
6
|
+
// src/commands/install.ts
|
|
7
|
+
import * as p3 from "@clack/prompts";
|
|
8
|
+
import pc from "picocolors";
|
|
9
|
+
|
|
10
|
+
// src/lib/registry.ts
|
|
11
|
+
var extensions = [
|
|
12
|
+
// Safety
|
|
13
|
+
{
|
|
14
|
+
name: "damage-control",
|
|
15
|
+
category: "extensions",
|
|
16
|
+
group: "safety",
|
|
17
|
+
description: "Safety guardrail engine: blocks destructive commands, enforces path access rules",
|
|
18
|
+
method: "copy",
|
|
19
|
+
source: "extensions/damage-control",
|
|
20
|
+
isDirectory: true,
|
|
21
|
+
recommends: ["damage-control-rules.yaml"]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "commit-approval",
|
|
25
|
+
category: "extensions",
|
|
26
|
+
group: "safety",
|
|
27
|
+
description: "Intercepts git commits for interactive review before execution",
|
|
28
|
+
method: "copy",
|
|
29
|
+
source: "extensions/commit-approval.ts"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "pr-approval",
|
|
33
|
+
category: "extensions",
|
|
34
|
+
group: "safety",
|
|
35
|
+
description: "Intercepts PR creation for interactive review",
|
|
36
|
+
method: "copy",
|
|
37
|
+
source: "extensions/pr-approval.ts"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "dirty-repo-guard",
|
|
41
|
+
category: "extensions",
|
|
42
|
+
group: "safety",
|
|
43
|
+
description: "Warns when working in a repo with uncommitted changes",
|
|
44
|
+
method: "copy",
|
|
45
|
+
source: "extensions/dirty-repo-guard.ts"
|
|
46
|
+
},
|
|
47
|
+
// Search
|
|
48
|
+
{
|
|
49
|
+
name: "exa-search-tool",
|
|
50
|
+
category: "extensions",
|
|
51
|
+
group: "search",
|
|
52
|
+
description: "Registers Exa as a semantic web search tool",
|
|
53
|
+
method: "copy",
|
|
54
|
+
source: "extensions/exa-search-tool.ts"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "exa-enforce",
|
|
58
|
+
category: "extensions",
|
|
59
|
+
group: "search",
|
|
60
|
+
description: "Enforces Exa over ad-hoc web search methods",
|
|
61
|
+
method: "copy",
|
|
62
|
+
source: "extensions/exa-enforce.ts",
|
|
63
|
+
recommends: ["exa-search-tool"]
|
|
64
|
+
},
|
|
65
|
+
// Tasks
|
|
66
|
+
{
|
|
67
|
+
name: "tilldone",
|
|
68
|
+
category: "extensions",
|
|
69
|
+
group: "tasks",
|
|
70
|
+
description: "Task list management with progress tracking",
|
|
71
|
+
method: "copy",
|
|
72
|
+
source: "extensions/tilldone.ts"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "todos",
|
|
76
|
+
category: "extensions",
|
|
77
|
+
group: "tasks",
|
|
78
|
+
description: "File-based todo management",
|
|
79
|
+
method: "copy",
|
|
80
|
+
source: "extensions/todos.ts"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "loop",
|
|
84
|
+
category: "extensions",
|
|
85
|
+
group: "tasks",
|
|
86
|
+
description: "Loop execution with breakout conditions",
|
|
87
|
+
method: "copy",
|
|
88
|
+
source: "extensions/loop.ts"
|
|
89
|
+
},
|
|
90
|
+
// UI
|
|
91
|
+
{
|
|
92
|
+
name: "btw",
|
|
93
|
+
category: "extensions",
|
|
94
|
+
group: "ui",
|
|
95
|
+
description: "Overlay chat panel with scroll support",
|
|
96
|
+
method: "copy",
|
|
97
|
+
source: "extensions/btw.ts"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "control",
|
|
101
|
+
category: "extensions",
|
|
102
|
+
group: "ui",
|
|
103
|
+
description: "Session control and summarization",
|
|
104
|
+
method: "copy",
|
|
105
|
+
source: "extensions/control.ts"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "context",
|
|
109
|
+
category: "extensions",
|
|
110
|
+
group: "ui",
|
|
111
|
+
description: "TUI showing loaded extensions, skills, and token usage",
|
|
112
|
+
method: "copy",
|
|
113
|
+
source: "extensions/context.ts"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "files",
|
|
117
|
+
category: "extensions",
|
|
118
|
+
group: "ui",
|
|
119
|
+
description: "File picker with quick actions (reveal, open, edit, diff)",
|
|
120
|
+
method: "copy",
|
|
121
|
+
source: "extensions/files.ts"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "session-breakdown",
|
|
125
|
+
category: "extensions",
|
|
126
|
+
group: "ui",
|
|
127
|
+
description: "Session cost and usage analytics with calendar heatmap",
|
|
128
|
+
method: "copy",
|
|
129
|
+
source: "extensions/session-breakdown.ts"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "term-notify",
|
|
133
|
+
category: "extensions",
|
|
134
|
+
group: "ui",
|
|
135
|
+
description: "Desktop notifications on agent completion (cmux + OSC 777)",
|
|
136
|
+
method: "copy",
|
|
137
|
+
source: "extensions/term-notify.ts"
|
|
138
|
+
},
|
|
139
|
+
// Review
|
|
140
|
+
{
|
|
141
|
+
name: "review",
|
|
142
|
+
category: "extensions",
|
|
143
|
+
group: "review",
|
|
144
|
+
description: "Code review: PR review, branch diffs, uncommitted changes",
|
|
145
|
+
method: "copy",
|
|
146
|
+
source: "extensions/review.ts"
|
|
147
|
+
},
|
|
148
|
+
// Workflow
|
|
149
|
+
{
|
|
150
|
+
name: "question-mode",
|
|
151
|
+
category: "extensions",
|
|
152
|
+
group: "workflow",
|
|
153
|
+
description: "Read-only question mode (no file changes)",
|
|
154
|
+
method: "copy",
|
|
155
|
+
source: "extensions/question-mode.ts"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "qna-interactive",
|
|
159
|
+
category: "extensions",
|
|
160
|
+
group: "workflow",
|
|
161
|
+
description: "Structured Q&A mode",
|
|
162
|
+
method: "copy",
|
|
163
|
+
source: "extensions/qna-interactive.ts"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "require-session-name-on-exit",
|
|
167
|
+
category: "extensions",
|
|
168
|
+
group: "workflow",
|
|
169
|
+
description: "Prompts for session name before exit",
|
|
170
|
+
method: "copy",
|
|
171
|
+
source: "extensions/require-session-name-on-exit.ts"
|
|
172
|
+
},
|
|
173
|
+
// Tools
|
|
174
|
+
{
|
|
175
|
+
name: "tools",
|
|
176
|
+
category: "extensions",
|
|
177
|
+
group: "tools",
|
|
178
|
+
description: "Custom tool registrations",
|
|
179
|
+
method: "copy",
|
|
180
|
+
source: "extensions/tools.ts"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "uv",
|
|
184
|
+
category: "extensions",
|
|
185
|
+
group: "tools",
|
|
186
|
+
description: "Intercepts pip/python calls and redirects to uv",
|
|
187
|
+
method: "copy",
|
|
188
|
+
source: "extensions/uv.ts",
|
|
189
|
+
recommends: ["intercepted-commands"]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "execute-command",
|
|
193
|
+
category: "extensions",
|
|
194
|
+
group: "tools",
|
|
195
|
+
description: "Slash command execution",
|
|
196
|
+
method: "copy",
|
|
197
|
+
source: "extensions/execute-command",
|
|
198
|
+
isDirectory: true
|
|
199
|
+
}
|
|
200
|
+
];
|
|
201
|
+
var bundledSkills = [
|
|
202
|
+
{
|
|
203
|
+
name: "brainstorm",
|
|
204
|
+
category: "skills-bundled",
|
|
205
|
+
description: "Interview-driven plan stress-testing",
|
|
206
|
+
method: "copy",
|
|
207
|
+
source: "global-skills/brainstorm",
|
|
208
|
+
target: "global-skills",
|
|
209
|
+
isDirectory: true
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: "cli-detector",
|
|
213
|
+
category: "skills-bundled",
|
|
214
|
+
description: "Scan repos for service integrations and their CLIs",
|
|
215
|
+
method: "copy",
|
|
216
|
+
source: "global-skills/cli-detector",
|
|
217
|
+
target: "global-skills",
|
|
218
|
+
isDirectory: true
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "exa-search",
|
|
222
|
+
category: "skills-bundled",
|
|
223
|
+
description: "Semantic web search via Exa API",
|
|
224
|
+
method: "copy",
|
|
225
|
+
source: "agent-skills/exa-search",
|
|
226
|
+
target: "agent-skills",
|
|
227
|
+
isDirectory: true
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
name: "gh-issue-creator",
|
|
231
|
+
category: "skills-bundled",
|
|
232
|
+
description: "Create GitHub issues via gh CLI with consistent formatting",
|
|
233
|
+
method: "copy",
|
|
234
|
+
source: "global-skills/gh-issue-creator",
|
|
235
|
+
target: "global-skills",
|
|
236
|
+
isDirectory: true
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: "google-chat-cards-v2",
|
|
240
|
+
category: "skills-bundled",
|
|
241
|
+
description: "Google Chat Cards v2 format for rich alert notifications",
|
|
242
|
+
method: "copy",
|
|
243
|
+
source: "global-skills/google-chat-cards-v2",
|
|
244
|
+
target: "global-skills",
|
|
245
|
+
isDirectory: true
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: "technical-docs",
|
|
249
|
+
category: "skills-bundled",
|
|
250
|
+
description: "Technical documentation standards and best practices",
|
|
251
|
+
method: "copy",
|
|
252
|
+
source: "global-skills/technical-docs",
|
|
253
|
+
target: "global-skills",
|
|
254
|
+
isDirectory: true
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "whats-new",
|
|
258
|
+
category: "skills-bundled",
|
|
259
|
+
description: "Git changelog generation between branches",
|
|
260
|
+
method: "copy",
|
|
261
|
+
source: "global-skills/whats-new",
|
|
262
|
+
target: "global-skills",
|
|
263
|
+
isDirectory: true
|
|
264
|
+
}
|
|
265
|
+
];
|
|
266
|
+
var externalSkills = [
|
|
267
|
+
{
|
|
268
|
+
name: "docx",
|
|
269
|
+
category: "skills-external",
|
|
270
|
+
description: "Create, read, edit, and manipulate Word documents",
|
|
271
|
+
method: "skills-cli",
|
|
272
|
+
remote: "anthropics/skills",
|
|
273
|
+
remoteSkills: ["docx"]
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "pdf",
|
|
277
|
+
category: "skills-external",
|
|
278
|
+
description: "Read, merge, split, create, and manipulate PDF files",
|
|
279
|
+
method: "skills-cli",
|
|
280
|
+
remote: "anthropics/skills",
|
|
281
|
+
remoteSkills: ["pdf"]
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: "pptx",
|
|
285
|
+
category: "skills-external",
|
|
286
|
+
description: "Create and edit PowerPoint presentations",
|
|
287
|
+
method: "skills-cli",
|
|
288
|
+
remote: "anthropics/skills",
|
|
289
|
+
remoteSkills: ["pptx"]
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "xlsx",
|
|
293
|
+
category: "skills-external",
|
|
294
|
+
description: "Create, read, and edit spreadsheet files",
|
|
295
|
+
method: "skills-cli",
|
|
296
|
+
remote: "anthropics/skills",
|
|
297
|
+
remoteSkills: ["xlsx"]
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
name: "frontend-design",
|
|
301
|
+
category: "skills-external",
|
|
302
|
+
description: "Production-grade frontend interfaces with high design quality",
|
|
303
|
+
method: "skills-cli",
|
|
304
|
+
remote: "anthropics/skills",
|
|
305
|
+
remoteSkills: ["frontend-design"]
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: "skill-creator",
|
|
309
|
+
category: "skills-external",
|
|
310
|
+
description: "Create, modify, and measure skill performance",
|
|
311
|
+
method: "skills-cli",
|
|
312
|
+
remote: "anthropics/skills",
|
|
313
|
+
remoteSkills: ["skill-creator"]
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "agent-browser",
|
|
317
|
+
category: "skills-external",
|
|
318
|
+
description: "Browser automation for agents",
|
|
319
|
+
method: "skills-cli",
|
|
320
|
+
remote: "anthropics/skills",
|
|
321
|
+
remoteSkills: ["agent-browser"]
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "vercel-react-best-practices",
|
|
325
|
+
category: "skills-external",
|
|
326
|
+
description: "React best practices from Vercel",
|
|
327
|
+
method: "skills-cli",
|
|
328
|
+
remote: "vercel-labs/skills",
|
|
329
|
+
remoteSkills: ["vercel-react-best-practices"]
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "web-design-guidelines",
|
|
333
|
+
category: "skills-external",
|
|
334
|
+
description: "Web design guidelines and patterns",
|
|
335
|
+
method: "skills-cli",
|
|
336
|
+
remote: "vercel-labs/skills",
|
|
337
|
+
remoteSkills: ["web-design-guidelines"]
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: "find-skills",
|
|
341
|
+
category: "skills-external",
|
|
342
|
+
description: "Discover and install agent skills",
|
|
343
|
+
method: "skills-cli",
|
|
344
|
+
remote: "vercel-labs/skills",
|
|
345
|
+
remoteSkills: ["find-skills"]
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: "learn-codebase",
|
|
349
|
+
category: "skills-external",
|
|
350
|
+
description: "Discover project conventions and surface security concerns",
|
|
351
|
+
method: "skills-cli",
|
|
352
|
+
remote: "HazAT/pi-config",
|
|
353
|
+
remoteSkills: ["learn-codebase"]
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: "self-improve",
|
|
357
|
+
category: "skills-external",
|
|
358
|
+
description: "Self-improvement and reflection for agents",
|
|
359
|
+
method: "skills-cli",
|
|
360
|
+
remote: "HazAT/pi-config",
|
|
361
|
+
remoteSkills: ["self-improve"]
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "cmux",
|
|
365
|
+
category: "skills-external",
|
|
366
|
+
description: "Control cmux topology: windows, workspaces, panes, focus",
|
|
367
|
+
method: "skills-cli",
|
|
368
|
+
remote: "manaflow-ai/cmux",
|
|
369
|
+
remoteSkills: ["cmux"]
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
name: "cmux-and-worktrees",
|
|
373
|
+
category: "skills-external",
|
|
374
|
+
description: "Parallel development with cmux-style git worktrees",
|
|
375
|
+
method: "skills-cli",
|
|
376
|
+
remote: "manaflow-ai/cmux",
|
|
377
|
+
remoteSkills: ["cmux-and-worktrees"]
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: "cmux-browser",
|
|
381
|
+
category: "skills-external",
|
|
382
|
+
description: "Browser automation with cmux surfaces",
|
|
383
|
+
method: "skills-cli",
|
|
384
|
+
remote: "manaflow-ai/cmux",
|
|
385
|
+
remoteSkills: ["cmux-browser"]
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
name: "vue-best-practices",
|
|
389
|
+
category: "skills-external",
|
|
390
|
+
description: "Vue 3 Composition API with TypeScript best practices",
|
|
391
|
+
method: "skills-cli",
|
|
392
|
+
remote: "hyf0/vue-skills",
|
|
393
|
+
remoteSkills: ["vue-best-practices"]
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: "systematic-debugging",
|
|
397
|
+
category: "skills-external",
|
|
398
|
+
description: "Systematic approach to debugging and test failures",
|
|
399
|
+
method: "skills-cli",
|
|
400
|
+
remote: "obra/superpowers",
|
|
401
|
+
remoteSkills: ["systematic-debugging"]
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
name: "writing-skills",
|
|
405
|
+
category: "skills-external",
|
|
406
|
+
description: "Create and verify skills before deployment",
|
|
407
|
+
method: "skills-cli",
|
|
408
|
+
remote: "obra/superpowers",
|
|
409
|
+
remoteSkills: ["writing-skills"]
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "code-simplifier",
|
|
413
|
+
category: "skills-external",
|
|
414
|
+
description: "Simplify and refine code for clarity and maintainability",
|
|
415
|
+
method: "skills-cli",
|
|
416
|
+
remote: "getsentry/skills",
|
|
417
|
+
remoteSkills: ["code-simplifier"]
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: "iterate-pr",
|
|
421
|
+
category: "skills-external",
|
|
422
|
+
description: "Iterate on a PR until CI passes",
|
|
423
|
+
method: "skills-cli",
|
|
424
|
+
remote: "getsentry/skills",
|
|
425
|
+
remoteSkills: ["iterate-pr"]
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
name: "playwright-cli",
|
|
429
|
+
category: "skills-external",
|
|
430
|
+
description: "Automate browser interactions and Playwright tests",
|
|
431
|
+
method: "skills-cli",
|
|
432
|
+
remote: "microsoft/playwright-cli"
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
name: "firecrawl",
|
|
436
|
+
category: "skills-external",
|
|
437
|
+
description: "Web scraping, search, crawling, and page interaction",
|
|
438
|
+
method: "skills-cli",
|
|
439
|
+
remote: "firecrawl/cli",
|
|
440
|
+
remoteSkills: ["firecrawl"]
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
name: "excalidraw-diagram",
|
|
444
|
+
category: "skills-external",
|
|
445
|
+
description: "Create Excalidraw diagram JSON files for visual workflows",
|
|
446
|
+
method: "skills-cli",
|
|
447
|
+
remote: "coleam00/excalidraw-diagram-skill"
|
|
448
|
+
}
|
|
449
|
+
];
|
|
450
|
+
var packages = [
|
|
451
|
+
{
|
|
452
|
+
name: "agent-modes",
|
|
453
|
+
category: "packages",
|
|
454
|
+
description: "Switch between code, architect, debug, ask, and review modes",
|
|
455
|
+
method: "pi-install",
|
|
456
|
+
remote: "npm:@danchamorro/pi-agent-modes"
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
name: "prompt-enhancer",
|
|
460
|
+
category: "packages",
|
|
461
|
+
description: "Rewrite prompts to be clearer and more actionable before sending",
|
|
462
|
+
method: "pi-install",
|
|
463
|
+
remote: "npm:@danchamorro/pi-prompt-enhancer"
|
|
464
|
+
}
|
|
465
|
+
];
|
|
466
|
+
var configs = [
|
|
467
|
+
{
|
|
468
|
+
name: "AGENTS.md",
|
|
469
|
+
category: "configs",
|
|
470
|
+
description: "Global agent rules: git safety, commit style, code style",
|
|
471
|
+
method: "copy",
|
|
472
|
+
source: "AGENTS.md",
|
|
473
|
+
isTemplate: true
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: "APPEND_SYSTEM.md",
|
|
477
|
+
category: "configs",
|
|
478
|
+
description: "System prompt: reasoning quality, jCodeMunch policy, writing style",
|
|
479
|
+
method: "copy",
|
|
480
|
+
source: "APPEND_SYSTEM.md",
|
|
481
|
+
isTemplate: true
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "settings.json",
|
|
485
|
+
category: "configs",
|
|
486
|
+
description: "Pi settings: default provider, model, compaction",
|
|
487
|
+
method: "copy",
|
|
488
|
+
source: "settings.json",
|
|
489
|
+
isTemplate: true
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "models.json",
|
|
493
|
+
category: "configs",
|
|
494
|
+
description: "Custom provider definitions (e.g., local MLX models)",
|
|
495
|
+
method: "copy",
|
|
496
|
+
source: "models.json",
|
|
497
|
+
isTemplate: true
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
name: "agent-modes.json",
|
|
501
|
+
category: "configs",
|
|
502
|
+
description: "Per-mode model and thinking overrides",
|
|
503
|
+
method: "copy",
|
|
504
|
+
source: "agent-modes.json",
|
|
505
|
+
isTemplate: true
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
name: "damage-control-rules.yaml",
|
|
509
|
+
category: "configs",
|
|
510
|
+
description: "Safety rules: bash patterns, path access, delete protection",
|
|
511
|
+
method: "copy",
|
|
512
|
+
source: "damage-control-rules.yaml",
|
|
513
|
+
isTemplate: true,
|
|
514
|
+
recommends: ["damage-control"]
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
name: "auth.json",
|
|
518
|
+
category: "configs",
|
|
519
|
+
description: "API key configuration (created from template)",
|
|
520
|
+
method: "copy",
|
|
521
|
+
source: "auth.json.template",
|
|
522
|
+
isTemplate: true
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "mcp.json",
|
|
526
|
+
category: "configs",
|
|
527
|
+
description: "MCP server configuration (created from template)",
|
|
528
|
+
method: "copy",
|
|
529
|
+
source: "mcp.json.template",
|
|
530
|
+
isTemplate: true
|
|
531
|
+
}
|
|
532
|
+
];
|
|
533
|
+
var registry = [
|
|
534
|
+
...extensions,
|
|
535
|
+
...bundledSkills,
|
|
536
|
+
...externalSkills,
|
|
537
|
+
...packages,
|
|
538
|
+
...configs
|
|
539
|
+
];
|
|
540
|
+
function getByCategory(category) {
|
|
541
|
+
return registry.filter((c) => c.category === category);
|
|
542
|
+
}
|
|
543
|
+
function getExtensionGroups() {
|
|
544
|
+
const groups = {};
|
|
545
|
+
for (const ext of getByCategory("extensions")) {
|
|
546
|
+
const group = ext.group ?? "tools";
|
|
547
|
+
if (!groups[group]) groups[group] = [];
|
|
548
|
+
groups[group].push(ext);
|
|
549
|
+
}
|
|
550
|
+
return groups;
|
|
551
|
+
}
|
|
552
|
+
var GROUP_LABELS = {
|
|
553
|
+
safety: "Safety",
|
|
554
|
+
search: "Search",
|
|
555
|
+
tasks: "Tasks",
|
|
556
|
+
ui: "UI & Session",
|
|
557
|
+
review: "Review",
|
|
558
|
+
workflow: "Workflow",
|
|
559
|
+
tools: "Tools"
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// src/lib/warnings.ts
|
|
563
|
+
import * as p from "@clack/prompts";
|
|
564
|
+
async function checkRecommendations(selected) {
|
|
565
|
+
const selectedNames = new Set(selected.map((c) => c.name));
|
|
566
|
+
const warnings = [];
|
|
567
|
+
for (const component of selected) {
|
|
568
|
+
if (!component.recommends) continue;
|
|
569
|
+
for (const rec of component.recommends) {
|
|
570
|
+
if (!selectedNames.has(rec)) {
|
|
571
|
+
warnings.push(
|
|
572
|
+
`${component.name} works best with ${rec}, which you didn't select.`
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (warnings.length === 0) return true;
|
|
578
|
+
p.log.warn("Some selected components have recommendations:");
|
|
579
|
+
for (const warning of warnings) {
|
|
580
|
+
p.log.message(` - ${warning}`);
|
|
581
|
+
}
|
|
582
|
+
const proceed = await p.confirm({
|
|
583
|
+
message: "Continue anyway?",
|
|
584
|
+
initialValue: true
|
|
585
|
+
});
|
|
586
|
+
if (p.isCancel(proceed)) {
|
|
587
|
+
p.cancel("Installation cancelled.");
|
|
588
|
+
process.exit(0);
|
|
589
|
+
}
|
|
590
|
+
return proceed;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// src/lib/installer.ts
|
|
594
|
+
import {
|
|
595
|
+
cpSync,
|
|
596
|
+
existsSync as existsSync2,
|
|
597
|
+
mkdirSync as mkdirSync2,
|
|
598
|
+
symlinkSync,
|
|
599
|
+
lstatSync,
|
|
600
|
+
readlinkSync,
|
|
601
|
+
readdirSync,
|
|
602
|
+
statSync,
|
|
603
|
+
unlinkSync
|
|
604
|
+
} from "fs";
|
|
605
|
+
import { resolve as resolve2, basename } from "path";
|
|
606
|
+
import { execSync } from "child_process";
|
|
607
|
+
import * as p2 from "@clack/prompts";
|
|
608
|
+
|
|
609
|
+
// src/lib/paths.ts
|
|
610
|
+
import { resolve, dirname } from "path";
|
|
611
|
+
import { fileURLToPath } from "url";
|
|
612
|
+
import { homedir } from "os";
|
|
613
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
614
|
+
var __dirname = dirname(__filename);
|
|
615
|
+
var BUNDLED_DOTFILES = resolve(__dirname, "dotfiles");
|
|
616
|
+
var PI_AGENT_DIR = resolve(homedir(), ".pi", "agent");
|
|
617
|
+
var AGENTS_SKILLS_DIR = resolve(homedir(), ".agents", "skills");
|
|
618
|
+
var MANIFEST_PATH = resolve(PI_AGENT_DIR, ".pi-toolkit.json");
|
|
619
|
+
var PI_EXTENSIONS_DIR = resolve(PI_AGENT_DIR, "extensions");
|
|
620
|
+
var PI_SKILLS_DIR = resolve(PI_AGENT_DIR, "skills");
|
|
621
|
+
|
|
622
|
+
// src/lib/manifest.ts
|
|
623
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
624
|
+
import { dirname as dirname2 } from "path";
|
|
625
|
+
function emptyManifest() {
|
|
626
|
+
return {
|
|
627
|
+
version: "",
|
|
628
|
+
installed: {
|
|
629
|
+
extensions: [],
|
|
630
|
+
skills: { bundled: [], external: [] },
|
|
631
|
+
packages: [],
|
|
632
|
+
configs: []
|
|
633
|
+
},
|
|
634
|
+
installedAt: "",
|
|
635
|
+
updatedAt: ""
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function readManifest() {
|
|
639
|
+
if (!existsSync(MANIFEST_PATH)) return emptyManifest();
|
|
640
|
+
try {
|
|
641
|
+
const raw = readFileSync(MANIFEST_PATH, "utf-8");
|
|
642
|
+
return JSON.parse(raw);
|
|
643
|
+
} catch {
|
|
644
|
+
return emptyManifest();
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function writeManifest(manifest) {
|
|
648
|
+
const dir = dirname2(MANIFEST_PATH);
|
|
649
|
+
mkdirSync(dir, { recursive: true });
|
|
650
|
+
writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + "\n");
|
|
651
|
+
}
|
|
652
|
+
function recordInstall(names, category, cliVersion) {
|
|
653
|
+
const manifest = readManifest();
|
|
654
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
655
|
+
if (!manifest.installedAt) manifest.installedAt = now;
|
|
656
|
+
manifest.updatedAt = now;
|
|
657
|
+
manifest.version = cliVersion;
|
|
658
|
+
for (const name of names) {
|
|
659
|
+
switch (category) {
|
|
660
|
+
case "extensions":
|
|
661
|
+
if (!manifest.installed.extensions.includes(name))
|
|
662
|
+
manifest.installed.extensions.push(name);
|
|
663
|
+
break;
|
|
664
|
+
case "skills-bundled":
|
|
665
|
+
if (!manifest.installed.skills.bundled.includes(name))
|
|
666
|
+
manifest.installed.skills.bundled.push(name);
|
|
667
|
+
break;
|
|
668
|
+
case "skills-external":
|
|
669
|
+
if (!manifest.installed.skills.external.includes(name))
|
|
670
|
+
manifest.installed.skills.external.push(name);
|
|
671
|
+
break;
|
|
672
|
+
case "packages":
|
|
673
|
+
if (!manifest.installed.packages.includes(name))
|
|
674
|
+
manifest.installed.packages.push(name);
|
|
675
|
+
break;
|
|
676
|
+
case "configs":
|
|
677
|
+
if (!manifest.installed.configs.includes(name))
|
|
678
|
+
manifest.installed.configs.push(name);
|
|
679
|
+
break;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
writeManifest(manifest);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/lib/installer.ts
|
|
686
|
+
function resolveSource(component, options) {
|
|
687
|
+
if (!component.source) {
|
|
688
|
+
throw new Error(`Component ${component.name} has no source path`);
|
|
689
|
+
}
|
|
690
|
+
if (options.link && options.repoPath) {
|
|
691
|
+
return resolve2(options.repoPath, "dotfiles", component.source);
|
|
692
|
+
}
|
|
693
|
+
return resolve2(BUNDLED_DOTFILES, component.source);
|
|
694
|
+
}
|
|
695
|
+
function resolveTarget(component) {
|
|
696
|
+
switch (component.category) {
|
|
697
|
+
case "extensions": {
|
|
698
|
+
const name = component.isDirectory ? component.name : basename(component.source ?? component.name);
|
|
699
|
+
return resolve2(PI_EXTENSIONS_DIR, name);
|
|
700
|
+
}
|
|
701
|
+
case "skills-bundled": {
|
|
702
|
+
if (component.target === "global-skills") {
|
|
703
|
+
return resolve2(AGENTS_SKILLS_DIR, component.name);
|
|
704
|
+
}
|
|
705
|
+
return resolve2(PI_SKILLS_DIR, component.name);
|
|
706
|
+
}
|
|
707
|
+
case "configs": {
|
|
708
|
+
const name = (component.source ?? component.name).replace(".template", "");
|
|
709
|
+
return resolve2(PI_AGENT_DIR, name);
|
|
710
|
+
}
|
|
711
|
+
default:
|
|
712
|
+
return resolve2(PI_AGENT_DIR, component.name);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
function copyComponent(source, target, isDirectory) {
|
|
716
|
+
const targetDir = isDirectory ? resolve2(target, "..") : resolve2(target, "..");
|
|
717
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
718
|
+
if (isDirectory) {
|
|
719
|
+
cpSync(source, target, { recursive: true });
|
|
720
|
+
} else {
|
|
721
|
+
cpSync(source, target);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function linkComponent(source, target) {
|
|
725
|
+
const targetDir = resolve2(target, "..");
|
|
726
|
+
mkdirSync2(targetDir, { recursive: true });
|
|
727
|
+
if (existsSync2(target) || isSymlink(target)) {
|
|
728
|
+
unlinkSync(target);
|
|
729
|
+
}
|
|
730
|
+
symlinkSync(source, target);
|
|
731
|
+
}
|
|
732
|
+
function isSymlink(path) {
|
|
733
|
+
try {
|
|
734
|
+
lstatSync(path);
|
|
735
|
+
return lstatSync(path).isSymbolicLink();
|
|
736
|
+
} catch {
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
function targetExists(target) {
|
|
741
|
+
if (isSymlink(target)) {
|
|
742
|
+
try {
|
|
743
|
+
readlinkSync(target);
|
|
744
|
+
return true;
|
|
745
|
+
} catch {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return existsSync2(target);
|
|
750
|
+
}
|
|
751
|
+
function installLocal(component, options) {
|
|
752
|
+
const source = resolveSource(component, options);
|
|
753
|
+
const target = resolveTarget(component);
|
|
754
|
+
if (!existsSync2(source)) {
|
|
755
|
+
return { success: false, message: `Source not found: ${source}` };
|
|
756
|
+
}
|
|
757
|
+
if (component.isTemplate && targetExists(target) && !options.overrideConfigs) {
|
|
758
|
+
return { success: true, message: "already exists (skipped)" };
|
|
759
|
+
}
|
|
760
|
+
try {
|
|
761
|
+
if (options.link) {
|
|
762
|
+
linkComponent(source, target);
|
|
763
|
+
return { success: true, message: "linked" };
|
|
764
|
+
} else {
|
|
765
|
+
copyComponent(source, target, component.isDirectory ?? false);
|
|
766
|
+
return { success: true, message: "copied" };
|
|
767
|
+
}
|
|
768
|
+
} catch (err) {
|
|
769
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
770
|
+
return { success: false, message: msg };
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
function installViaSkillsCli(component) {
|
|
774
|
+
if (!component.remote) {
|
|
775
|
+
return { success: false, message: "No remote source defined" };
|
|
776
|
+
}
|
|
777
|
+
try {
|
|
778
|
+
let cmd = `npx skills add ${component.remote}`;
|
|
779
|
+
if (component.remoteSkills?.length) {
|
|
780
|
+
for (const skill of component.remoteSkills) {
|
|
781
|
+
cmd += ` -s ${skill}`;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
cmd += " -g -y";
|
|
785
|
+
execSync(cmd, { stdio: "pipe", timeout: 6e4 });
|
|
786
|
+
return { success: true, message: "installed via skills CLI" };
|
|
787
|
+
} catch (err) {
|
|
788
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
789
|
+
return { success: false, message: msg };
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function installViaPi(component) {
|
|
793
|
+
if (!component.remote) {
|
|
794
|
+
return { success: false, message: "No remote source defined" };
|
|
795
|
+
}
|
|
796
|
+
try {
|
|
797
|
+
execSync(`pi install ${component.remote}`, {
|
|
798
|
+
stdio: "pipe",
|
|
799
|
+
timeout: 6e4
|
|
800
|
+
});
|
|
801
|
+
return { success: true, message: "installed via pi" };
|
|
802
|
+
} catch (err) {
|
|
803
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
804
|
+
return { success: false, message: msg };
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async function installComponents(components, options) {
|
|
808
|
+
if (components.length === 0) return;
|
|
809
|
+
mkdirSync2(PI_AGENT_DIR, { recursive: true });
|
|
810
|
+
mkdirSync2(PI_EXTENSIONS_DIR, { recursive: true });
|
|
811
|
+
mkdirSync2(PI_SKILLS_DIR, { recursive: true });
|
|
812
|
+
mkdirSync2(AGENTS_SKILLS_DIR, { recursive: true });
|
|
813
|
+
const spinner2 = p2.spinner();
|
|
814
|
+
const results = [];
|
|
815
|
+
for (const component of components) {
|
|
816
|
+
spinner2.start(`Installing ${component.name}...`);
|
|
817
|
+
let result;
|
|
818
|
+
switch (component.method) {
|
|
819
|
+
case "copy":
|
|
820
|
+
case "symlink":
|
|
821
|
+
result = installLocal(component, options);
|
|
822
|
+
break;
|
|
823
|
+
case "skills-cli":
|
|
824
|
+
result = installViaSkillsCli(component);
|
|
825
|
+
break;
|
|
826
|
+
case "pi-install":
|
|
827
|
+
result = installViaPi(component);
|
|
828
|
+
break;
|
|
829
|
+
default:
|
|
830
|
+
result = { success: false, message: `Unknown method: ${component.method}` };
|
|
831
|
+
}
|
|
832
|
+
results.push({ name: component.name, ...result });
|
|
833
|
+
if (result.success) {
|
|
834
|
+
spinner2.stop(`${component.name}: ${result.message}`);
|
|
835
|
+
} else {
|
|
836
|
+
spinner2.stop(`${component.name}: FAILED - ${result.message}`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
const successByCategory = /* @__PURE__ */ new Map();
|
|
840
|
+
for (const r of results) {
|
|
841
|
+
if (!r.success) continue;
|
|
842
|
+
const component = components.find((c) => c.name === r.name);
|
|
843
|
+
if (!component) continue;
|
|
844
|
+
const cat = component.category;
|
|
845
|
+
if (!successByCategory.has(cat)) successByCategory.set(cat, []);
|
|
846
|
+
successByCategory.get(cat).push(r.name);
|
|
847
|
+
}
|
|
848
|
+
for (const [category, names] of successByCategory) {
|
|
849
|
+
recordInstall(names, category, options.cliVersion);
|
|
850
|
+
}
|
|
851
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
852
|
+
const failed = results.filter((r) => !r.success).length;
|
|
853
|
+
if (failed > 0) {
|
|
854
|
+
p2.log.warn(
|
|
855
|
+
`Installed ${succeeded}/${results.length} components. ${failed} failed:`
|
|
856
|
+
);
|
|
857
|
+
for (const r of results.filter((r2) => !r2.success)) {
|
|
858
|
+
p2.log.error(` ${r.name}: ${r.message}`);
|
|
859
|
+
}
|
|
860
|
+
} else {
|
|
861
|
+
p2.log.success(`All ${succeeded} components installed successfully.`);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
function installExtensionDeps() {
|
|
865
|
+
const extDir = PI_EXTENSIONS_DIR;
|
|
866
|
+
if (!existsSync2(extDir)) return;
|
|
867
|
+
const entries = readdirSync(extDir);
|
|
868
|
+
for (const entry of entries) {
|
|
869
|
+
const fullPath = resolve2(extDir, entry);
|
|
870
|
+
try {
|
|
871
|
+
if (statSync(fullPath).isDirectory() && existsSync2(resolve2(fullPath, "package.json"))) {
|
|
872
|
+
p2.log.info(`Installing dependencies for ${entry}...`);
|
|
873
|
+
execSync("npm install --silent", { cwd: fullPath, stdio: "pipe" });
|
|
874
|
+
}
|
|
875
|
+
} catch {
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/commands/install.ts
|
|
881
|
+
function componentOptions(components) {
|
|
882
|
+
return components.map((c) => ({
|
|
883
|
+
value: c.name,
|
|
884
|
+
label: c.name,
|
|
885
|
+
hint: c.description
|
|
886
|
+
}));
|
|
887
|
+
}
|
|
888
|
+
async function interactivePicker() {
|
|
889
|
+
const selected = [];
|
|
890
|
+
const groups = getExtensionGroups();
|
|
891
|
+
const extensionOptions = [];
|
|
892
|
+
for (const group of Object.keys(GROUP_LABELS)) {
|
|
893
|
+
const components = groups[group];
|
|
894
|
+
if (!components?.length) continue;
|
|
895
|
+
extensionOptions.push({
|
|
896
|
+
value: `__separator_${group}`,
|
|
897
|
+
label: pc.dim(`--- ${GROUP_LABELS[group]} ---`),
|
|
898
|
+
hint: ""
|
|
899
|
+
});
|
|
900
|
+
extensionOptions.push(...componentOptions(components));
|
|
901
|
+
}
|
|
902
|
+
const extResult = await p3.multiselect({
|
|
903
|
+
message: "Select extensions to install:",
|
|
904
|
+
options: extensionOptions.filter((o) => !o.value.startsWith("__separator")),
|
|
905
|
+
required: false
|
|
906
|
+
});
|
|
907
|
+
if (p3.isCancel(extResult)) {
|
|
908
|
+
p3.cancel("Installation cancelled.");
|
|
909
|
+
process.exit(0);
|
|
910
|
+
}
|
|
911
|
+
selected.push(...extResult);
|
|
912
|
+
const bundledSkills2 = getByCategory("skills-bundled");
|
|
913
|
+
if (bundledSkills2.length > 0) {
|
|
914
|
+
const skillResult = await p3.multiselect({
|
|
915
|
+
message: "Select bundled skills to install:",
|
|
916
|
+
options: componentOptions(bundledSkills2),
|
|
917
|
+
required: false
|
|
918
|
+
});
|
|
919
|
+
if (p3.isCancel(skillResult)) {
|
|
920
|
+
p3.cancel("Installation cancelled.");
|
|
921
|
+
process.exit(0);
|
|
922
|
+
}
|
|
923
|
+
selected.push(...skillResult);
|
|
924
|
+
}
|
|
925
|
+
const externalSkills2 = getByCategory("skills-external");
|
|
926
|
+
if (externalSkills2.length > 0) {
|
|
927
|
+
const extSkillResult = await p3.multiselect({
|
|
928
|
+
message: "Select external skills to install (fetched from source repos):",
|
|
929
|
+
options: componentOptions(externalSkills2),
|
|
930
|
+
required: false
|
|
931
|
+
});
|
|
932
|
+
if (p3.isCancel(extSkillResult)) {
|
|
933
|
+
p3.cancel("Installation cancelled.");
|
|
934
|
+
process.exit(0);
|
|
935
|
+
}
|
|
936
|
+
selected.push(...extSkillResult);
|
|
937
|
+
}
|
|
938
|
+
const pkgs = getByCategory("packages");
|
|
939
|
+
if (pkgs.length > 0) {
|
|
940
|
+
const pkgResult = await p3.multiselect({
|
|
941
|
+
message: "Select pi packages to install:",
|
|
942
|
+
options: componentOptions(pkgs),
|
|
943
|
+
required: false
|
|
944
|
+
});
|
|
945
|
+
if (p3.isCancel(pkgResult)) {
|
|
946
|
+
p3.cancel("Installation cancelled.");
|
|
947
|
+
process.exit(0);
|
|
948
|
+
}
|
|
949
|
+
selected.push(...pkgResult);
|
|
950
|
+
}
|
|
951
|
+
const configs2 = getByCategory("configs");
|
|
952
|
+
if (configs2.length > 0) {
|
|
953
|
+
const configResult = await p3.multiselect({
|
|
954
|
+
message: "Select starter configs to install (copied as templates, won't overwrite existing):",
|
|
955
|
+
options: componentOptions(configs2),
|
|
956
|
+
required: false
|
|
957
|
+
});
|
|
958
|
+
if (p3.isCancel(configResult)) {
|
|
959
|
+
p3.cancel("Installation cancelled.");
|
|
960
|
+
process.exit(0);
|
|
961
|
+
}
|
|
962
|
+
selected.push(...configResult);
|
|
963
|
+
}
|
|
964
|
+
return registry.filter((c) => selected.includes(c.name));
|
|
965
|
+
}
|
|
966
|
+
function resolveFromFlags(args) {
|
|
967
|
+
const names = /* @__PURE__ */ new Set();
|
|
968
|
+
if (args.extensions) {
|
|
969
|
+
for (const name of args.extensions) names.add(name);
|
|
970
|
+
}
|
|
971
|
+
if (args.skills) {
|
|
972
|
+
for (const name of args.skills) names.add(name);
|
|
973
|
+
}
|
|
974
|
+
if (args.packages) {
|
|
975
|
+
for (const name of args.packages) names.add(name);
|
|
976
|
+
}
|
|
977
|
+
const resolved = [];
|
|
978
|
+
const notFound = [];
|
|
979
|
+
for (const name of names) {
|
|
980
|
+
const component = registry.find((c) => c.name === name);
|
|
981
|
+
if (component) {
|
|
982
|
+
resolved.push(component);
|
|
983
|
+
} else {
|
|
984
|
+
notFound.push(name);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (notFound.length > 0) {
|
|
988
|
+
p3.log.warn(`Unknown components: ${notFound.join(", ")}`);
|
|
989
|
+
p3.log.info('Run "pi-toolkit list" to see available components.');
|
|
990
|
+
}
|
|
991
|
+
return resolved;
|
|
992
|
+
}
|
|
993
|
+
async function runInstall(args) {
|
|
994
|
+
p3.intro(pc.bold("pi-toolkit install"));
|
|
995
|
+
if (args.link && !args.repoPath) {
|
|
996
|
+
p3.log.error("--link requires --repo-path to be set.");
|
|
997
|
+
p3.log.info("Example: pi-toolkit install --link --repo-path ~/Code/pi-toolkit");
|
|
998
|
+
process.exit(1);
|
|
999
|
+
}
|
|
1000
|
+
let components;
|
|
1001
|
+
if (args.all) {
|
|
1002
|
+
components = [...registry];
|
|
1003
|
+
p3.log.info(`Installing all ${components.length} components...`);
|
|
1004
|
+
} else if (args.extensions || args.skills || args.packages) {
|
|
1005
|
+
components = resolveFromFlags(args);
|
|
1006
|
+
} else {
|
|
1007
|
+
components = await interactivePicker();
|
|
1008
|
+
}
|
|
1009
|
+
if (components.length === 0) {
|
|
1010
|
+
p3.log.warn("Nothing selected to install.");
|
|
1011
|
+
p3.outro("Done.");
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const extCount = components.filter((c) => c.category === "extensions").length;
|
|
1015
|
+
const skillCount = components.filter(
|
|
1016
|
+
(c) => c.category === "skills-bundled" || c.category === "skills-external"
|
|
1017
|
+
).length;
|
|
1018
|
+
const pkgCount = components.filter((c) => c.category === "packages").length;
|
|
1019
|
+
const cfgCount = components.filter((c) => c.category === "configs").length;
|
|
1020
|
+
const parts = [];
|
|
1021
|
+
if (extCount) parts.push(`${extCount} extension${extCount > 1 ? "s" : ""}`);
|
|
1022
|
+
if (skillCount) parts.push(`${skillCount} skill${skillCount > 1 ? "s" : ""}`);
|
|
1023
|
+
if (pkgCount) parts.push(`${pkgCount} package${pkgCount > 1 ? "s" : ""}`);
|
|
1024
|
+
if (cfgCount) parts.push(`${cfgCount} config${cfgCount > 1 ? "s" : ""}`);
|
|
1025
|
+
p3.log.info(`Will install: ${parts.join(", ")}`);
|
|
1026
|
+
if (args.link) {
|
|
1027
|
+
p3.log.info(`Mode: symlink (repo: ${args.repoPath})`);
|
|
1028
|
+
}
|
|
1029
|
+
if (args.overrideConfigs) {
|
|
1030
|
+
p3.log.warn("Config override enabled: existing configs will be overwritten.");
|
|
1031
|
+
}
|
|
1032
|
+
const proceed = await checkRecommendations(components);
|
|
1033
|
+
if (!proceed) {
|
|
1034
|
+
p3.log.info("Go back and adjust your selections, then re-run.");
|
|
1035
|
+
p3.outro("Cancelled.");
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
await installComponents(components, {
|
|
1039
|
+
link: args.link ?? false,
|
|
1040
|
+
repoPath: args.repoPath,
|
|
1041
|
+
overrideConfigs: args.overrideConfigs ?? false,
|
|
1042
|
+
cliVersion: args.version
|
|
1043
|
+
});
|
|
1044
|
+
const hasDirectoryExtensions = components.some(
|
|
1045
|
+
(c) => c.category === "extensions" && c.isDirectory
|
|
1046
|
+
);
|
|
1047
|
+
if (hasDirectoryExtensions) {
|
|
1048
|
+
installExtensionDeps();
|
|
1049
|
+
}
|
|
1050
|
+
p3.outro(pc.green("Installation complete!"));
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// src/commands/list.ts
|
|
1054
|
+
import pc2 from "picocolors";
|
|
1055
|
+
function runList() {
|
|
1056
|
+
console.log();
|
|
1057
|
+
console.log(pc2.bold("pi-toolkit: available components"));
|
|
1058
|
+
console.log();
|
|
1059
|
+
console.log(pc2.bold(pc2.cyan("Extensions")));
|
|
1060
|
+
const groups = getExtensionGroups();
|
|
1061
|
+
for (const group of Object.keys(GROUP_LABELS)) {
|
|
1062
|
+
const components = groups[group];
|
|
1063
|
+
if (!components?.length) continue;
|
|
1064
|
+
console.log(` ${pc2.dim(GROUP_LABELS[group])}`);
|
|
1065
|
+
for (const c of components) {
|
|
1066
|
+
console.log(` ${pc2.green(c.name.padEnd(36))} ${pc2.dim(c.description)}`);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
console.log();
|
|
1070
|
+
const bundled = getByCategory("skills-bundled");
|
|
1071
|
+
if (bundled.length > 0) {
|
|
1072
|
+
console.log(pc2.bold(pc2.cyan("Bundled Skills")));
|
|
1073
|
+
for (const c of bundled) {
|
|
1074
|
+
console.log(` ${pc2.green(c.name.padEnd(38))} ${pc2.dim(c.description)}`);
|
|
1075
|
+
}
|
|
1076
|
+
console.log();
|
|
1077
|
+
}
|
|
1078
|
+
const external = getByCategory("skills-external");
|
|
1079
|
+
if (external.length > 0) {
|
|
1080
|
+
console.log(pc2.bold(pc2.cyan("External Skills")) + pc2.dim(" (installed from source repos)"));
|
|
1081
|
+
for (const c of external) {
|
|
1082
|
+
const source = c.remote ? pc2.dim(` [${c.remote}]`) : "";
|
|
1083
|
+
console.log(` ${pc2.green(c.name.padEnd(38))} ${pc2.dim(c.description)}${source}`);
|
|
1084
|
+
}
|
|
1085
|
+
console.log();
|
|
1086
|
+
}
|
|
1087
|
+
const pkgs = getByCategory("packages");
|
|
1088
|
+
if (pkgs.length > 0) {
|
|
1089
|
+
console.log(pc2.bold(pc2.cyan("Packages")) + pc2.dim(" (installed via pi install)"));
|
|
1090
|
+
for (const c of pkgs) {
|
|
1091
|
+
console.log(` ${pc2.green(c.name.padEnd(38))} ${pc2.dim(c.description)}`);
|
|
1092
|
+
}
|
|
1093
|
+
console.log();
|
|
1094
|
+
}
|
|
1095
|
+
const configs2 = getByCategory("configs");
|
|
1096
|
+
if (configs2.length > 0) {
|
|
1097
|
+
console.log(pc2.bold(pc2.cyan("Starter Configs")) + pc2.dim(" (copied as templates)"));
|
|
1098
|
+
for (const c of configs2) {
|
|
1099
|
+
console.log(` ${pc2.green(c.name.padEnd(38))} ${pc2.dim(c.description)}`);
|
|
1100
|
+
}
|
|
1101
|
+
console.log();
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// src/commands/status.ts
|
|
1106
|
+
import { existsSync as existsSync3, lstatSync as lstatSync2, readlinkSync as readlinkSync2 } from "fs";
|
|
1107
|
+
import { resolve as resolve3, basename as basename2 } from "path";
|
|
1108
|
+
import pc3 from "picocolors";
|
|
1109
|
+
function expectedPath(component) {
|
|
1110
|
+
switch (component.category) {
|
|
1111
|
+
case "extensions": {
|
|
1112
|
+
const name = component.isDirectory ? component.name : basename2(component.source ?? component.name);
|
|
1113
|
+
return resolve3(PI_EXTENSIONS_DIR, name);
|
|
1114
|
+
}
|
|
1115
|
+
case "skills-bundled": {
|
|
1116
|
+
if (component.target === "global-skills") {
|
|
1117
|
+
return resolve3(AGENTS_SKILLS_DIR, component.name);
|
|
1118
|
+
}
|
|
1119
|
+
return resolve3(PI_SKILLS_DIR, component.name);
|
|
1120
|
+
}
|
|
1121
|
+
case "skills-external": {
|
|
1122
|
+
const piPath = resolve3(PI_SKILLS_DIR, component.name);
|
|
1123
|
+
const agentsPath = resolve3(AGENTS_SKILLS_DIR, component.name);
|
|
1124
|
+
if (existsSync3(piPath) || isSymlink2(piPath)) return piPath;
|
|
1125
|
+
if (existsSync3(agentsPath) || isSymlink2(agentsPath)) return agentsPath;
|
|
1126
|
+
return agentsPath;
|
|
1127
|
+
}
|
|
1128
|
+
case "packages":
|
|
1129
|
+
return null;
|
|
1130
|
+
// Can't easily check pi packages on disk
|
|
1131
|
+
case "configs": {
|
|
1132
|
+
const name = (component.source ?? component.name).replace(".template", "");
|
|
1133
|
+
return resolve3(PI_AGENT_DIR, name);
|
|
1134
|
+
}
|
|
1135
|
+
default:
|
|
1136
|
+
return null;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function isSymlink2(path) {
|
|
1140
|
+
try {
|
|
1141
|
+
return lstatSync2(path).isSymbolicLink();
|
|
1142
|
+
} catch {
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
function checkFile(path) {
|
|
1147
|
+
if (isSymlink2(path)) {
|
|
1148
|
+
const target = readlinkSync2(path);
|
|
1149
|
+
const dangling = !existsSync3(path);
|
|
1150
|
+
return {
|
|
1151
|
+
exists: !dangling,
|
|
1152
|
+
detail: dangling ? `dangling symlink -> ${target}` : `symlink -> ${target}`
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
return { exists: existsSync3(path) };
|
|
1156
|
+
}
|
|
1157
|
+
function runStatus() {
|
|
1158
|
+
const manifest = readManifest();
|
|
1159
|
+
console.log();
|
|
1160
|
+
console.log(pc3.bold("pi-toolkit status"));
|
|
1161
|
+
console.log();
|
|
1162
|
+
if (!manifest.installedAt) {
|
|
1163
|
+
console.log(pc3.dim("No pi-toolkit manifest found. Nothing has been installed yet."));
|
|
1164
|
+
console.log(pc3.dim('Run "pi-toolkit install" to get started.'));
|
|
1165
|
+
console.log();
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
console.log(
|
|
1169
|
+
`${pc3.dim("CLI version:")} ${manifest.version || "unknown"}`
|
|
1170
|
+
);
|
|
1171
|
+
console.log(
|
|
1172
|
+
`${pc3.dim("Installed at:")} ${manifest.installedAt}`
|
|
1173
|
+
);
|
|
1174
|
+
console.log(
|
|
1175
|
+
`${pc3.dim("Updated at:")} ${manifest.updatedAt}`
|
|
1176
|
+
);
|
|
1177
|
+
console.log();
|
|
1178
|
+
const installedNames = /* @__PURE__ */ new Set([
|
|
1179
|
+
...manifest.installed.extensions,
|
|
1180
|
+
...manifest.installed.skills.bundled,
|
|
1181
|
+
...manifest.installed.skills.external,
|
|
1182
|
+
...manifest.installed.packages,
|
|
1183
|
+
...manifest.installed.configs
|
|
1184
|
+
]);
|
|
1185
|
+
const entries = [];
|
|
1186
|
+
for (const component of registry) {
|
|
1187
|
+
const isInstalled = installedNames.has(component.name);
|
|
1188
|
+
if (!isInstalled) {
|
|
1189
|
+
entries.push({
|
|
1190
|
+
name: component.name,
|
|
1191
|
+
category: component.category,
|
|
1192
|
+
status: "not-installed"
|
|
1193
|
+
});
|
|
1194
|
+
continue;
|
|
1195
|
+
}
|
|
1196
|
+
const path = expectedPath(component);
|
|
1197
|
+
if (!path) {
|
|
1198
|
+
entries.push({
|
|
1199
|
+
name: component.name,
|
|
1200
|
+
category: component.category,
|
|
1201
|
+
status: "ok",
|
|
1202
|
+
detail: "installed (cannot verify on disk)"
|
|
1203
|
+
});
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
const check = checkFile(path);
|
|
1207
|
+
entries.push({
|
|
1208
|
+
name: component.name,
|
|
1209
|
+
category: component.category,
|
|
1210
|
+
status: check.exists ? "ok" : "missing",
|
|
1211
|
+
detail: check.detail
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
const categories = [
|
|
1215
|
+
{ key: "extensions", label: "Extensions" },
|
|
1216
|
+
{ key: "skills-bundled", label: "Bundled Skills" },
|
|
1217
|
+
{ key: "skills-external", label: "External Skills" },
|
|
1218
|
+
{ key: "packages", label: "Packages" },
|
|
1219
|
+
{ key: "configs", label: "Configs" }
|
|
1220
|
+
];
|
|
1221
|
+
for (const cat of categories) {
|
|
1222
|
+
const catEntries = entries.filter((e) => e.category === cat.key);
|
|
1223
|
+
if (catEntries.length === 0) continue;
|
|
1224
|
+
const installed = catEntries.filter((e) => e.status !== "not-installed");
|
|
1225
|
+
const available = catEntries.filter((e) => e.status === "not-installed");
|
|
1226
|
+
console.log(
|
|
1227
|
+
pc3.bold(pc3.cyan(cat.label)) + pc3.dim(` (${installed.length}/${catEntries.length} installed)`)
|
|
1228
|
+
);
|
|
1229
|
+
for (const entry of installed) {
|
|
1230
|
+
const icon = entry.status === "ok" ? pc3.green("*") : pc3.red("!");
|
|
1231
|
+
const detail = entry.detail ? pc3.dim(` (${entry.detail})`) : "";
|
|
1232
|
+
const statusLabel = entry.status === "missing" ? pc3.red(" MISSING") : "";
|
|
1233
|
+
console.log(` ${icon} ${entry.name}${statusLabel}${detail}`);
|
|
1234
|
+
}
|
|
1235
|
+
if (available.length > 0) {
|
|
1236
|
+
console.log(
|
|
1237
|
+
pc3.dim(` ${available.length} more available: ${available.map((e) => e.name).join(", ")}`)
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
console.log();
|
|
1241
|
+
}
|
|
1242
|
+
const missing = entries.filter((e) => e.status === "missing");
|
|
1243
|
+
if (missing.length > 0) {
|
|
1244
|
+
console.log(
|
|
1245
|
+
pc3.yellow(
|
|
1246
|
+
`${missing.length} component(s) in manifest but missing from disk:`
|
|
1247
|
+
)
|
|
1248
|
+
);
|
|
1249
|
+
for (const m of missing) {
|
|
1250
|
+
console.log(pc3.yellow(` - ${m.name}`));
|
|
1251
|
+
}
|
|
1252
|
+
console.log(pc3.dim('Re-run "pi-toolkit install" to restore them.'));
|
|
1253
|
+
console.log();
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
// src/index.ts
|
|
1258
|
+
var CLI_VERSION = "0.1.0";
|
|
1259
|
+
var install = defineCommand({
|
|
1260
|
+
meta: {
|
|
1261
|
+
name: "install",
|
|
1262
|
+
description: "Install extensions, skills, packages, and configs"
|
|
1263
|
+
},
|
|
1264
|
+
args: {
|
|
1265
|
+
all: {
|
|
1266
|
+
type: "boolean",
|
|
1267
|
+
description: "Install all available components",
|
|
1268
|
+
default: false
|
|
1269
|
+
},
|
|
1270
|
+
"override-configs": {
|
|
1271
|
+
type: "boolean",
|
|
1272
|
+
description: "Overwrite existing config files",
|
|
1273
|
+
default: false
|
|
1274
|
+
},
|
|
1275
|
+
link: {
|
|
1276
|
+
type: "boolean",
|
|
1277
|
+
description: "Symlink to local repo clone instead of copying",
|
|
1278
|
+
default: false
|
|
1279
|
+
},
|
|
1280
|
+
"repo-path": {
|
|
1281
|
+
type: "string",
|
|
1282
|
+
description: "Path to local pi-toolkit repo clone (required with --link)"
|
|
1283
|
+
},
|
|
1284
|
+
extensions: {
|
|
1285
|
+
type: "string",
|
|
1286
|
+
description: "Extensions to install (space-separated names)"
|
|
1287
|
+
},
|
|
1288
|
+
skills: {
|
|
1289
|
+
type: "string",
|
|
1290
|
+
description: "Skills to install (space-separated names)"
|
|
1291
|
+
},
|
|
1292
|
+
packages: {
|
|
1293
|
+
type: "string",
|
|
1294
|
+
description: "Packages to install (space-separated names)"
|
|
1295
|
+
}
|
|
1296
|
+
},
|
|
1297
|
+
run({ args }) {
|
|
1298
|
+
const splitArg = (val) => {
|
|
1299
|
+
if (!val) return void 0;
|
|
1300
|
+
return val.split(/[\s,]+/).map((s) => s.trim()).filter(Boolean);
|
|
1301
|
+
};
|
|
1302
|
+
return runInstall({
|
|
1303
|
+
all: args.all,
|
|
1304
|
+
overrideConfigs: args["override-configs"],
|
|
1305
|
+
link: args.link,
|
|
1306
|
+
repoPath: args["repo-path"],
|
|
1307
|
+
extensions: splitArg(args.extensions),
|
|
1308
|
+
skills: splitArg(args.skills),
|
|
1309
|
+
packages: splitArg(args.packages),
|
|
1310
|
+
version: CLI_VERSION
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
var list = defineCommand({
|
|
1315
|
+
meta: {
|
|
1316
|
+
name: "list",
|
|
1317
|
+
description: "Browse all available components"
|
|
1318
|
+
},
|
|
1319
|
+
run() {
|
|
1320
|
+
runList();
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
var status = defineCommand({
|
|
1324
|
+
meta: {
|
|
1325
|
+
name: "status",
|
|
1326
|
+
description: "Show installed components and detect drift"
|
|
1327
|
+
},
|
|
1328
|
+
run() {
|
|
1329
|
+
runStatus();
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
var main = defineCommand({
|
|
1333
|
+
meta: {
|
|
1334
|
+
name: "pi-toolkit",
|
|
1335
|
+
version: CLI_VERSION,
|
|
1336
|
+
description: "Selectively install curated extensions, skills, and configs for the pi coding agent"
|
|
1337
|
+
},
|
|
1338
|
+
subCommands: {
|
|
1339
|
+
install,
|
|
1340
|
+
list,
|
|
1341
|
+
status
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
runMain(main);
|