@oh-my-pi/pi-coding-agent 1.337.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1228 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +637 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/reviewer.md +35 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +81 -0
- package/src/cli/args.ts +246 -0
- package/src/cli/file-processor.ts +72 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +650 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli.ts +10 -0
- package/src/commands/init.md +20 -0
- package/src/config.ts +159 -0
- package/src/core/agent-session.ts +1900 -0
- package/src/core/auth-storage.ts +236 -0
- package/src/core/bash-executor.ts +196 -0
- package/src/core/compaction/branch-summarization.ts +343 -0
- package/src/core/compaction/compaction.ts +742 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +154 -0
- package/src/core/custom-tools/index.ts +21 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +169 -0
- package/src/core/custom-tools/wrapper.ts +28 -0
- package/src/core/exec.ts +129 -0
- package/src/core/export-html/index.ts +211 -0
- package/src/core/export-html/template.css +781 -0
- package/src/core/export-html/template.html +54 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/vendor/highlight.min.js +1213 -0
- package/src/core/export-html/vendor/marked.min.js +6 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +312 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +99 -0
- package/src/core/hooks/types.ts +773 -0
- package/src/core/index.ts +52 -0
- package/src/core/mcp/client.ts +158 -0
- package/src/core/mcp/config.ts +154 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +68 -0
- package/src/core/mcp/manager.ts +181 -0
- package/src/core/mcp/tool-bridge.ts +148 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +220 -0
- package/src/core/messages.ts +189 -0
- package/src/core/model-registry.ts +317 -0
- package/src/core/model-resolver.ts +393 -0
- package/src/core/plugins/doctor.ts +59 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +338 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +32 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +760 -0
- package/src/core/session-manager.ts +1128 -0
- package/src/core/settings-manager.ts +443 -0
- package/src/core/skills.ts +437 -0
- package/src/core/slash-commands.ts +248 -0
- package/src/core/system-prompt.ts +439 -0
- package/src/core/timings.ts +25 -0
- package/src/core/tools/ask.ts +211 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +250 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +475 -0
- package/src/core/tools/edit.ts +208 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +64 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/logger.ts +56 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +196 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +337 -0
- package/src/core/tools/exa/types.ts +168 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +261 -0
- package/src/core/tools/grep.ts +555 -0
- package/src/core/tools/index.ts +202 -0
- package/src/core/tools/ls.ts +140 -0
- package/src/core/tools/lsp/client.ts +605 -0
- package/src/core/tools/lsp/config.ts +147 -0
- package/src/core/tools/lsp/edits.ts +101 -0
- package/src/core/tools/lsp/index.ts +804 -0
- package/src/core/tools/lsp/render.ts +447 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +463 -0
- package/src/core/tools/lsp/utils.ts +486 -0
- package/src/core/tools/notebook.ts +229 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +240 -0
- package/src/core/tools/renderers.ts +540 -0
- package/src/core/tools/task/agents.ts +153 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/bundled-agents/browser.md +71 -0
- package/src/core/tools/task/bundled-agents/explore.md +82 -0
- package/src/core/tools/task/bundled-agents/plan.md +54 -0
- package/src/core/tools/task/bundled-agents/reviewer.md +59 -0
- package/src/core/tools/task/bundled-agents/task.md +53 -0
- package/src/core/tools/task/bundled-commands/architect-plan.md +10 -0
- package/src/core/tools/task/bundled-commands/implement-with-critic.md +11 -0
- package/src/core/tools/task/bundled-commands/implement.md +11 -0
- package/src/core/tools/task/commands.ts +213 -0
- package/src/core/tools/task/discovery.ts +208 -0
- package/src/core/tools/task/executor.ts +367 -0
- package/src/core/tools/task/index.ts +388 -0
- package/src/core/tools/task/model-resolver.ts +115 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +232 -0
- package/src/core/tools/task/types.ts +99 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2370 -0
- package/src/core/tools/web-search/auth.ts +193 -0
- package/src/core/tools/web-search/index.ts +537 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +302 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +182 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +99 -0
- package/src/index.ts +176 -0
- package/src/main.ts +464 -0
- package/src/migrations.ts +135 -0
- package/src/modes/index.ts +43 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +196 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/footer.ts +381 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +247 -0
- package/src/modes/interactive/components/oauth-selector.ts +120 -0
- package/src/modes/interactive/components/plugin-settings.ts +479 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +204 -0
- package/src/modes/interactive/components/settings-selector.ts +453 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +675 -0
- package/src/modes/interactive/components/tree-selector.ts +866 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +183 -0
- package/src/modes/interactive/interactive-mode.ts +2516 -0
- package/src/modes/interactive/theme/dark.json +101 -0
- package/src/modes/interactive/theme/light.json +98 -0
- package/src/modes/interactive/theme/theme-schema.json +308 -0
- package/src/modes/interactive/theme/theme.ts +998 -0
- package/src/modes/print-mode.ts +128 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +483 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell.ts +276 -0
- package/src/utils/tools-manager.ts +274 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin CLI command handlers.
|
|
3
|
+
*
|
|
4
|
+
* Handles `pi plugin <command>` subcommands for plugin lifecycle management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { PluginManager, parseSettingValue, validateSetting } from "../core/plugins/index.js";
|
|
9
|
+
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Types
|
|
12
|
+
// =============================================================================
|
|
13
|
+
|
|
14
|
+
export type PluginAction =
|
|
15
|
+
| "install"
|
|
16
|
+
| "uninstall"
|
|
17
|
+
| "list"
|
|
18
|
+
| "link"
|
|
19
|
+
| "doctor"
|
|
20
|
+
| "features"
|
|
21
|
+
| "config"
|
|
22
|
+
| "enable"
|
|
23
|
+
| "disable";
|
|
24
|
+
|
|
25
|
+
export interface PluginCommandArgs {
|
|
26
|
+
action: PluginAction;
|
|
27
|
+
args: string[];
|
|
28
|
+
flags: {
|
|
29
|
+
json?: boolean;
|
|
30
|
+
fix?: boolean;
|
|
31
|
+
force?: boolean;
|
|
32
|
+
dryRun?: boolean;
|
|
33
|
+
local?: boolean;
|
|
34
|
+
enable?: string;
|
|
35
|
+
disable?: string;
|
|
36
|
+
set?: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// =============================================================================
|
|
41
|
+
// Argument Parser
|
|
42
|
+
// =============================================================================
|
|
43
|
+
|
|
44
|
+
const VALID_ACTIONS: PluginAction[] = [
|
|
45
|
+
"install",
|
|
46
|
+
"uninstall",
|
|
47
|
+
"list",
|
|
48
|
+
"link",
|
|
49
|
+
"doctor",
|
|
50
|
+
"features",
|
|
51
|
+
"config",
|
|
52
|
+
"enable",
|
|
53
|
+
"disable",
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parse plugin subcommand arguments.
|
|
58
|
+
* Returns undefined if not a plugin command.
|
|
59
|
+
*/
|
|
60
|
+
export function parsePluginArgs(args: string[]): PluginCommandArgs | undefined {
|
|
61
|
+
if (args.length === 0 || args[0] !== "plugin") {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (args.length < 2) {
|
|
66
|
+
return { action: "list", args: [], flags: {} };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const action = args[1];
|
|
70
|
+
if (!VALID_ACTIONS.includes(action as PluginAction)) {
|
|
71
|
+
console.error(chalk.red(`Unknown plugin command: ${action}`));
|
|
72
|
+
console.error(`Valid commands: ${VALID_ACTIONS.join(", ")}`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const result: PluginCommandArgs = {
|
|
77
|
+
action: action as PluginAction,
|
|
78
|
+
args: [],
|
|
79
|
+
flags: {},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Parse remaining arguments
|
|
83
|
+
for (let i = 2; i < args.length; i++) {
|
|
84
|
+
const arg = args[i];
|
|
85
|
+
if (arg === "--json") {
|
|
86
|
+
result.flags.json = true;
|
|
87
|
+
} else if (arg === "--fix") {
|
|
88
|
+
result.flags.fix = true;
|
|
89
|
+
} else if (arg === "--force") {
|
|
90
|
+
result.flags.force = true;
|
|
91
|
+
} else if (arg === "--dry-run") {
|
|
92
|
+
result.flags.dryRun = true;
|
|
93
|
+
} else if (arg === "-l" || arg === "--local") {
|
|
94
|
+
result.flags.local = true;
|
|
95
|
+
} else if (arg === "--enable" && i + 1 < args.length) {
|
|
96
|
+
result.flags.enable = args[++i];
|
|
97
|
+
} else if (arg === "--disable" && i + 1 < args.length) {
|
|
98
|
+
result.flags.disable = args[++i];
|
|
99
|
+
} else if (arg === "--set" && i + 1 < args.length) {
|
|
100
|
+
result.flags.set = args[++i];
|
|
101
|
+
} else if (!arg.startsWith("-")) {
|
|
102
|
+
result.args.push(arg);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// =============================================================================
|
|
110
|
+
// Command Handlers
|
|
111
|
+
// =============================================================================
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Run a plugin command.
|
|
115
|
+
*/
|
|
116
|
+
export async function runPluginCommand(cmd: PluginCommandArgs): Promise<void> {
|
|
117
|
+
const manager = new PluginManager();
|
|
118
|
+
|
|
119
|
+
switch (cmd.action) {
|
|
120
|
+
case "install":
|
|
121
|
+
await handleInstall(manager, cmd.args, cmd.flags);
|
|
122
|
+
break;
|
|
123
|
+
case "uninstall":
|
|
124
|
+
await handleUninstall(manager, cmd.args, cmd.flags);
|
|
125
|
+
break;
|
|
126
|
+
case "list":
|
|
127
|
+
await handleList(manager, cmd.flags);
|
|
128
|
+
break;
|
|
129
|
+
case "link":
|
|
130
|
+
await handleLink(manager, cmd.args, cmd.flags);
|
|
131
|
+
break;
|
|
132
|
+
case "doctor":
|
|
133
|
+
await handleDoctor(manager, cmd.flags);
|
|
134
|
+
break;
|
|
135
|
+
case "features":
|
|
136
|
+
await handleFeatures(manager, cmd.args, cmd.flags);
|
|
137
|
+
break;
|
|
138
|
+
case "config":
|
|
139
|
+
await handleConfig(manager, cmd.args, cmd.flags);
|
|
140
|
+
break;
|
|
141
|
+
case "enable":
|
|
142
|
+
await handleEnable(manager, cmd.args, cmd.flags);
|
|
143
|
+
break;
|
|
144
|
+
case "disable":
|
|
145
|
+
await handleDisable(manager, cmd.args, cmd.flags);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function handleInstall(
|
|
151
|
+
manager: PluginManager,
|
|
152
|
+
packages: string[],
|
|
153
|
+
flags: { json?: boolean; force?: boolean; dryRun?: boolean },
|
|
154
|
+
): Promise<void> {
|
|
155
|
+
if (packages.length === 0) {
|
|
156
|
+
console.error(chalk.red("Usage: pi plugin install <package[@version]>[features] ..."));
|
|
157
|
+
console.error(chalk.dim("Examples:"));
|
|
158
|
+
console.error(chalk.dim(" pi plugin install @oh-my-pi/exa"));
|
|
159
|
+
console.error(chalk.dim(" pi plugin install @oh-my-pi/exa[search,websets]"));
|
|
160
|
+
console.error(chalk.dim(" pi plugin install @oh-my-pi/exa[*] # all features"));
|
|
161
|
+
console.error(chalk.dim(" pi plugin install @oh-my-pi/exa[] # no optional features"));
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const spec of packages) {
|
|
166
|
+
try {
|
|
167
|
+
const result = await manager.install(spec, { force: flags.force, dryRun: flags.dryRun });
|
|
168
|
+
|
|
169
|
+
if (flags.json) {
|
|
170
|
+
console.log(JSON.stringify(result, null, 2));
|
|
171
|
+
} else {
|
|
172
|
+
if (flags.dryRun) {
|
|
173
|
+
console.log(chalk.dim(`[dry-run] Would install ${spec}`));
|
|
174
|
+
} else {
|
|
175
|
+
console.log(chalk.green(`✓ Installed ${result.name}@${result.version}`));
|
|
176
|
+
if (result.enabledFeatures && result.enabledFeatures.length > 0) {
|
|
177
|
+
console.log(chalk.dim(` Features: ${result.enabledFeatures.join(", ")}`));
|
|
178
|
+
}
|
|
179
|
+
if (result.manifest.description) {
|
|
180
|
+
console.log(chalk.dim(` ${result.manifest.description}`));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch (err) {
|
|
185
|
+
console.error(chalk.red(`✗ Failed to install ${spec}: ${err}`));
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function handleUninstall(manager: PluginManager, packages: string[], flags: { json?: boolean }): Promise<void> {
|
|
192
|
+
if (packages.length === 0) {
|
|
193
|
+
console.error(chalk.red("Usage: pi plugin uninstall <package> ..."));
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for (const name of packages) {
|
|
198
|
+
try {
|
|
199
|
+
await manager.uninstall(name);
|
|
200
|
+
|
|
201
|
+
if (flags.json) {
|
|
202
|
+
console.log(JSON.stringify({ uninstalled: name }));
|
|
203
|
+
} else {
|
|
204
|
+
console.log(chalk.green(`✓ Uninstalled ${name}`));
|
|
205
|
+
}
|
|
206
|
+
} catch (err) {
|
|
207
|
+
console.error(chalk.red(`✗ Failed to uninstall ${name}: ${err}`));
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function handleList(manager: PluginManager, flags: { json?: boolean }): Promise<void> {
|
|
214
|
+
const plugins = await manager.list();
|
|
215
|
+
|
|
216
|
+
if (flags.json) {
|
|
217
|
+
console.log(JSON.stringify(plugins, null, 2));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (plugins.length === 0) {
|
|
222
|
+
console.log(chalk.dim("No plugins installed"));
|
|
223
|
+
console.log(chalk.dim("\nInstall plugins with: pi plugin install <package>"));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.log(chalk.bold("Installed Plugins:\n"));
|
|
228
|
+
for (const plugin of plugins) {
|
|
229
|
+
const status = plugin.enabled ? chalk.green("●") : chalk.dim("○");
|
|
230
|
+
const nameVersion = `${plugin.name}@${plugin.version}`;
|
|
231
|
+
console.log(`${status} ${nameVersion}`);
|
|
232
|
+
|
|
233
|
+
if (plugin.manifest.description) {
|
|
234
|
+
console.log(chalk.dim(` ${plugin.manifest.description}`));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (plugin.enabledFeatures && plugin.enabledFeatures.length > 0) {
|
|
238
|
+
console.log(chalk.dim(` Features: ${plugin.enabledFeatures.join(", ")}`));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Show available features if manifest has them
|
|
242
|
+
if (plugin.manifest.features) {
|
|
243
|
+
const availableFeatures = Object.keys(plugin.manifest.features);
|
|
244
|
+
if (availableFeatures.length > 0) {
|
|
245
|
+
const enabledSet = new Set(plugin.enabledFeatures ?? []);
|
|
246
|
+
const featureDisplay = availableFeatures
|
|
247
|
+
.map((f) => (enabledSet.has(f) ? chalk.green(f) : chalk.dim(f)))
|
|
248
|
+
.join(", ");
|
|
249
|
+
console.log(chalk.dim(` Available: [${featureDisplay}]`));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function handleLink(manager: PluginManager, paths: string[], flags: { json?: boolean }): Promise<void> {
|
|
256
|
+
if (paths.length === 0) {
|
|
257
|
+
console.error(chalk.red("Usage: pi plugin link <path>"));
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
const result = await manager.link(paths[0]);
|
|
263
|
+
|
|
264
|
+
if (flags.json) {
|
|
265
|
+
console.log(JSON.stringify(result, null, 2));
|
|
266
|
+
} else {
|
|
267
|
+
console.log(chalk.green(`✓ Linked ${result.name} from ${paths[0]}`));
|
|
268
|
+
}
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.error(chalk.red(`✗ Failed to link: ${err}`));
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function handleDoctor(manager: PluginManager, flags: { json?: boolean; fix?: boolean }): Promise<void> {
|
|
276
|
+
const checks = await manager.doctor({ fix: flags.fix });
|
|
277
|
+
|
|
278
|
+
if (flags.json) {
|
|
279
|
+
console.log(JSON.stringify(checks, null, 2));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
console.log(chalk.bold("Plugin Health Check\n"));
|
|
284
|
+
|
|
285
|
+
for (const check of checks) {
|
|
286
|
+
const icon =
|
|
287
|
+
check.status === "ok" ? chalk.green("✓") : check.status === "warning" ? chalk.yellow("!") : chalk.red("✗");
|
|
288
|
+
console.log(`${icon} ${check.name}: ${check.message}`);
|
|
289
|
+
if (check.fixed) {
|
|
290
|
+
console.log(chalk.dim(` → Fixed`));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const errors = checks.filter((c) => c.status === "error" && !c.fixed).length;
|
|
295
|
+
const warnings = checks.filter((c) => c.status === "warning" && !c.fixed).length;
|
|
296
|
+
const ok = checks.filter((c) => c.status === "ok").length;
|
|
297
|
+
const fixed = checks.filter((c) => c.fixed).length;
|
|
298
|
+
|
|
299
|
+
console.log("");
|
|
300
|
+
console.log(`Summary: ${ok} ok, ${warnings} warnings, ${errors} errors${fixed > 0 ? `, ${fixed} fixed` : ""}`);
|
|
301
|
+
|
|
302
|
+
if (errors > 0) {
|
|
303
|
+
if (!flags.fix) {
|
|
304
|
+
console.log(chalk.dim("\nRun with --fix to attempt automatic repair"));
|
|
305
|
+
}
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function handleFeatures(
|
|
311
|
+
manager: PluginManager,
|
|
312
|
+
args: string[],
|
|
313
|
+
flags: { json?: boolean; enable?: string; disable?: string; set?: string },
|
|
314
|
+
): Promise<void> {
|
|
315
|
+
if (args.length === 0) {
|
|
316
|
+
console.error(chalk.red("Usage: pi plugin features <plugin> [--enable f1,f2] [--disable f1] [--set f1,f2]"));
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const pluginName = args[0];
|
|
321
|
+
const plugins = await manager.list();
|
|
322
|
+
const plugin = plugins.find((p) => p.name === pluginName);
|
|
323
|
+
|
|
324
|
+
if (!plugin) {
|
|
325
|
+
console.error(chalk.red(`Plugin "${pluginName}" not found`));
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Handle modifications
|
|
330
|
+
if (flags.enable || flags.disable || flags.set) {
|
|
331
|
+
let currentFeatures = new Set(manager.getEnabledFeatures(pluginName) ?? []);
|
|
332
|
+
|
|
333
|
+
if (flags.set) {
|
|
334
|
+
// --set replaces all features
|
|
335
|
+
currentFeatures = new Set(
|
|
336
|
+
flags.set
|
|
337
|
+
.split(",")
|
|
338
|
+
.map((f) => f.trim())
|
|
339
|
+
.filter(Boolean),
|
|
340
|
+
);
|
|
341
|
+
} else {
|
|
342
|
+
if (flags.enable) {
|
|
343
|
+
for (const f of flags.enable
|
|
344
|
+
.split(",")
|
|
345
|
+
.map((f) => f.trim())
|
|
346
|
+
.filter(Boolean)) {
|
|
347
|
+
currentFeatures.add(f);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (flags.disable) {
|
|
351
|
+
for (const f of flags.disable
|
|
352
|
+
.split(",")
|
|
353
|
+
.map((f) => f.trim())
|
|
354
|
+
.filter(Boolean)) {
|
|
355
|
+
currentFeatures.delete(f);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
await manager.setEnabledFeatures(pluginName, [...currentFeatures]);
|
|
361
|
+
console.log(chalk.green(`✓ Updated features for ${pluginName}`));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Display current state
|
|
365
|
+
const updatedFeatures = manager.getEnabledFeatures(pluginName);
|
|
366
|
+
|
|
367
|
+
if (flags.json) {
|
|
368
|
+
console.log(
|
|
369
|
+
JSON.stringify(
|
|
370
|
+
{
|
|
371
|
+
plugin: pluginName,
|
|
372
|
+
enabledFeatures: updatedFeatures,
|
|
373
|
+
availableFeatures: plugin.manifest.features ? Object.keys(plugin.manifest.features) : [],
|
|
374
|
+
},
|
|
375
|
+
null,
|
|
376
|
+
2,
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
console.log(chalk.bold(`Features for ${pluginName}:\n`));
|
|
383
|
+
|
|
384
|
+
if (!plugin.manifest.features || Object.keys(plugin.manifest.features).length === 0) {
|
|
385
|
+
console.log(chalk.dim(" No optional features available"));
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const enabledSet = new Set(updatedFeatures ?? []);
|
|
390
|
+
for (const [name, feat] of Object.entries(plugin.manifest.features)) {
|
|
391
|
+
const enabled = enabledSet.has(name);
|
|
392
|
+
const icon = enabled ? chalk.green("●") : chalk.dim("○");
|
|
393
|
+
const defaultLabel = feat.default ? chalk.dim(" (default)") : "";
|
|
394
|
+
console.log(`${icon} ${name}${defaultLabel}`);
|
|
395
|
+
if (feat.description) {
|
|
396
|
+
console.log(chalk.dim(` ${feat.description}`));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function handleConfig(
|
|
402
|
+
manager: PluginManager,
|
|
403
|
+
args: string[],
|
|
404
|
+
flags: { json?: boolean; local?: boolean },
|
|
405
|
+
): Promise<void> {
|
|
406
|
+
if (args.length === 0) {
|
|
407
|
+
console.error(chalk.red("Usage: pi plugin config <list|get|set|delete|validate> <plugin> [key] [value]"));
|
|
408
|
+
process.exit(1);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const [subcommand, pluginName, key, ...valueArgs] = args;
|
|
412
|
+
|
|
413
|
+
// Special case: validate doesn't need a plugin name
|
|
414
|
+
if (subcommand === "validate") {
|
|
415
|
+
await handleConfigValidate(manager, flags);
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (!pluginName) {
|
|
420
|
+
console.error(chalk.red("Plugin name required"));
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const plugins = await manager.list();
|
|
425
|
+
const plugin = plugins.find((p) => p.name === pluginName);
|
|
426
|
+
|
|
427
|
+
if (!plugin) {
|
|
428
|
+
console.error(chalk.red(`Plugin "${pluginName}" not found`));
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
switch (subcommand) {
|
|
433
|
+
case "list": {
|
|
434
|
+
const settings = manager.getPluginSettings(pluginName);
|
|
435
|
+
const schema = plugin.manifest.settings || {};
|
|
436
|
+
|
|
437
|
+
if (flags.json) {
|
|
438
|
+
console.log(JSON.stringify({ settings, schema }, null, 2));
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
console.log(chalk.bold(`Settings for ${pluginName}:\n`));
|
|
443
|
+
|
|
444
|
+
if (Object.keys(schema).length === 0) {
|
|
445
|
+
console.log(chalk.dim(" No settings defined"));
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
for (const [k, s] of Object.entries(schema)) {
|
|
450
|
+
const value = settings[k] ?? s.default;
|
|
451
|
+
const displayValue = s.secret && value ? "********" : String(value ?? chalk.dim("(not set)"));
|
|
452
|
+
console.log(` ${k}: ${displayValue}`);
|
|
453
|
+
if (s.description) {
|
|
454
|
+
console.log(chalk.dim(` ${s.description}`));
|
|
455
|
+
}
|
|
456
|
+
if (s.env) {
|
|
457
|
+
console.log(chalk.dim(` env: ${s.env}`));
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
case "get": {
|
|
464
|
+
if (!key) {
|
|
465
|
+
console.error(chalk.red("Key required"));
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const settings = manager.getPluginSettings(pluginName);
|
|
470
|
+
const schema = plugin.manifest.settings?.[key];
|
|
471
|
+
const value = settings[key] ?? schema?.default;
|
|
472
|
+
|
|
473
|
+
if (flags.json) {
|
|
474
|
+
console.log(JSON.stringify({ [key]: value }));
|
|
475
|
+
} else {
|
|
476
|
+
const displayValue = schema?.secret && value ? "********" : String(value ?? "(not set)");
|
|
477
|
+
console.log(displayValue);
|
|
478
|
+
}
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
case "set": {
|
|
483
|
+
if (!key) {
|
|
484
|
+
console.error(chalk.red("Key required"));
|
|
485
|
+
process.exit(1);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const valueStr = valueArgs.join(" ");
|
|
489
|
+
const schema = plugin.manifest.settings?.[key];
|
|
490
|
+
|
|
491
|
+
// Parse value according to type
|
|
492
|
+
let value: unknown = valueStr;
|
|
493
|
+
if (schema) {
|
|
494
|
+
value = parseSettingValue(valueStr, schema);
|
|
495
|
+
|
|
496
|
+
// Validate
|
|
497
|
+
const validation = validateSetting(value, schema);
|
|
498
|
+
if (!validation.valid) {
|
|
499
|
+
console.error(chalk.red(validation.error!));
|
|
500
|
+
process.exit(1);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
manager.setPluginSetting(pluginName, key, value);
|
|
505
|
+
console.log(chalk.green(`✓ Set ${key}`));
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
case "delete": {
|
|
510
|
+
if (!key) {
|
|
511
|
+
console.error(chalk.red("Key required"));
|
|
512
|
+
process.exit(1);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
manager.deletePluginSetting(pluginName, key);
|
|
516
|
+
console.log(chalk.green(`✓ Deleted ${key}`));
|
|
517
|
+
break;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
default:
|
|
521
|
+
console.error(chalk.red(`Unknown config subcommand: ${subcommand}`));
|
|
522
|
+
console.error(chalk.dim("Valid subcommands: list, get, set, delete, validate"));
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
async function handleConfigValidate(manager: PluginManager, flags: { json?: boolean }): Promise<void> {
|
|
528
|
+
const plugins = await manager.list();
|
|
529
|
+
const results: Array<{ plugin: string; key: string; error: string }> = [];
|
|
530
|
+
|
|
531
|
+
for (const plugin of plugins) {
|
|
532
|
+
const settings = manager.getPluginSettings(plugin.name);
|
|
533
|
+
const schema = plugin.manifest.settings || {};
|
|
534
|
+
|
|
535
|
+
for (const [key, s] of Object.entries(schema)) {
|
|
536
|
+
const value = settings[key];
|
|
537
|
+
if (value !== undefined) {
|
|
538
|
+
const validation = validateSetting(value, s);
|
|
539
|
+
if (!validation.valid) {
|
|
540
|
+
results.push({ plugin: plugin.name, key, error: validation.error! });
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (flags.json) {
|
|
547
|
+
console.log(JSON.stringify({ valid: results.length === 0, errors: results }, null, 2));
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (results.length === 0) {
|
|
552
|
+
console.log(chalk.green("✓ All settings valid"));
|
|
553
|
+
} else {
|
|
554
|
+
for (const { plugin, key, error } of results) {
|
|
555
|
+
console.log(chalk.red(`✗ ${plugin}.${key}: ${error}`));
|
|
556
|
+
}
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function handleEnable(manager: PluginManager, plugins: string[], flags: { json?: boolean }): Promise<void> {
|
|
562
|
+
if (plugins.length === 0) {
|
|
563
|
+
console.error(chalk.red("Usage: pi plugin enable <plugin> ..."));
|
|
564
|
+
process.exit(1);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
for (const name of plugins) {
|
|
568
|
+
try {
|
|
569
|
+
await manager.setEnabled(name, true);
|
|
570
|
+
|
|
571
|
+
if (flags.json) {
|
|
572
|
+
console.log(JSON.stringify({ enabled: name }));
|
|
573
|
+
} else {
|
|
574
|
+
console.log(chalk.green(`✓ Enabled ${name}`));
|
|
575
|
+
}
|
|
576
|
+
} catch (err) {
|
|
577
|
+
console.error(chalk.red(`✗ Failed to enable ${name}: ${err}`));
|
|
578
|
+
process.exit(1);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
async function handleDisable(manager: PluginManager, plugins: string[], flags: { json?: boolean }): Promise<void> {
|
|
584
|
+
if (plugins.length === 0) {
|
|
585
|
+
console.error(chalk.red("Usage: pi plugin disable <plugin> ..."));
|
|
586
|
+
process.exit(1);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
for (const name of plugins) {
|
|
590
|
+
try {
|
|
591
|
+
await manager.setEnabled(name, false);
|
|
592
|
+
|
|
593
|
+
if (flags.json) {
|
|
594
|
+
console.log(JSON.stringify({ disabled: name }));
|
|
595
|
+
} else {
|
|
596
|
+
console.log(chalk.green(`✓ Disabled ${name}`));
|
|
597
|
+
}
|
|
598
|
+
} catch (err) {
|
|
599
|
+
console.error(chalk.red(`✗ Failed to disable ${name}: ${err}`));
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// =============================================================================
|
|
606
|
+
// Help
|
|
607
|
+
// =============================================================================
|
|
608
|
+
|
|
609
|
+
export function printPluginHelp(): void {
|
|
610
|
+
console.log(`${chalk.bold("pi plugin")} - Plugin lifecycle management
|
|
611
|
+
|
|
612
|
+
${chalk.bold("Commands:")}
|
|
613
|
+
install <pkg[@ver]>[features] Install plugins from npm
|
|
614
|
+
uninstall <pkg> Remove plugins
|
|
615
|
+
list Show installed plugins
|
|
616
|
+
link <path> Link local plugin for development
|
|
617
|
+
doctor Check plugin health
|
|
618
|
+
features <pkg> View/modify enabled features
|
|
619
|
+
config <cmd> <pkg> [key] [val] Manage plugin settings
|
|
620
|
+
enable <pkg> Enable a disabled plugin
|
|
621
|
+
disable <pkg> Disable plugin without uninstalling
|
|
622
|
+
|
|
623
|
+
${chalk.bold("Feature Syntax:")}
|
|
624
|
+
pkg Install with default features
|
|
625
|
+
pkg[feat1,feat2] Install with specific features
|
|
626
|
+
pkg[*] Install with all features
|
|
627
|
+
pkg[] Install with no optional features
|
|
628
|
+
|
|
629
|
+
${chalk.bold("Config Subcommands:")}
|
|
630
|
+
config list <pkg> List all settings
|
|
631
|
+
config get <pkg> <key> Get a setting value
|
|
632
|
+
config set <pkg> <key> <val> Set a setting value
|
|
633
|
+
config delete <pkg> <key> Delete a setting
|
|
634
|
+
config validate Validate all plugin settings
|
|
635
|
+
|
|
636
|
+
${chalk.bold("Options:")}
|
|
637
|
+
--json Output as JSON
|
|
638
|
+
--fix Attempt automatic fixes (doctor)
|
|
639
|
+
--force Overwrite without prompting (install)
|
|
640
|
+
--dry-run Preview changes without applying (install)
|
|
641
|
+
-l, --local Use project-local overrides
|
|
642
|
+
|
|
643
|
+
${chalk.bold("Examples:")}
|
|
644
|
+
pi plugin install @oh-my-pi/exa[search]
|
|
645
|
+
pi plugin list --json
|
|
646
|
+
pi plugin features my-plugin --enable search,web
|
|
647
|
+
pi plugin config set my-plugin apiKey sk-xxx
|
|
648
|
+
pi plugin doctor --fix
|
|
649
|
+
`);
|
|
650
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TUI session selector for --resume flag
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ProcessTerminal, TUI } from "@oh-my-pi/pi-tui";
|
|
6
|
+
import type { SessionInfo } from "../core/session-manager.js";
|
|
7
|
+
import { SessionSelectorComponent } from "../modes/interactive/components/session-selector.js";
|
|
8
|
+
|
|
9
|
+
/** Show TUI session selector and return selected session path or null if cancelled */
|
|
10
|
+
export async function selectSession(sessions: SessionInfo[]): Promise<string | null> {
|
|
11
|
+
return new Promise((resolve) => {
|
|
12
|
+
const ui = new TUI(new ProcessTerminal());
|
|
13
|
+
let resolved = false;
|
|
14
|
+
|
|
15
|
+
const selector = new SessionSelectorComponent(
|
|
16
|
+
sessions,
|
|
17
|
+
(path: string) => {
|
|
18
|
+
if (!resolved) {
|
|
19
|
+
resolved = true;
|
|
20
|
+
ui.stop();
|
|
21
|
+
resolve(path);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
() => {
|
|
25
|
+
if (!resolved) {
|
|
26
|
+
resolved = true;
|
|
27
|
+
ui.stop();
|
|
28
|
+
resolve(null);
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
() => {
|
|
32
|
+
ui.stop();
|
|
33
|
+
process.exit(0);
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
ui.addChild(selector);
|
|
38
|
+
ui.setFocus(selector.getSessionList());
|
|
39
|
+
ui.start();
|
|
40
|
+
});
|
|
41
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point for the refactored coding agent.
|
|
4
|
+
* Uses main.ts with AgentSession and new mode modules.
|
|
5
|
+
*
|
|
6
|
+
* Test with: npx tsx src/cli-new.ts [args...]
|
|
7
|
+
*/
|
|
8
|
+
import { main } from "./main.js";
|
|
9
|
+
|
|
10
|
+
main(process.argv.slice(2));
|