@oh-my-pi/pi-coding-agent 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/CHANGELOG.md +1629 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/config-usage.md +113 -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 +670 -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/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 +89 -0
- package/src/bun-imports.d.ts +16 -0
- package/src/capability/context-file.ts +40 -0
- package/src/capability/extension.ts +48 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +616 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +52 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +56 -0
- package/src/capability/settings.ts +35 -0
- package/src/capability/skill.ts +49 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/system-prompt.ts +35 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +166 -0
- package/src/cli/args.ts +259 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +661 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli/update-cli.ts +274 -0
- package/src/cli.ts +10 -0
- package/src/config.ts +391 -0
- package/src/core/agent-session.ts +2178 -0
- package/src/core/auth-storage.ts +258 -0
- package/src/core/bash-executor.ts +197 -0
- package/src/core/compaction/branch-summarization.ts +315 -0
- package/src/core/compaction/compaction.ts +664 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +153 -0
- package/src/core/custom-commands/bundled/review/index.ts +156 -0
- package/src/core/custom-commands/index.ts +15 -0
- package/src/core/custom-commands/loader.ts +226 -0
- package/src/core/custom-commands/types.ts +112 -0
- package/src/core/custom-tools/index.ts +22 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +185 -0
- package/src/core/custom-tools/wrapper.ts +29 -0
- package/src/core/exec.ts +139 -0
- package/src/core/export-html/index.ts +159 -0
- package/src/core/export-html/template.css +774 -0
- package/src/core/export-html/template.generated.ts +2 -0
- package/src/core/export-html/template.html +45 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/template.macro.ts +24 -0
- package/src/core/file-mentions.ts +54 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +288 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +98 -0
- package/src/core/hooks/types.ts +770 -0
- package/src/core/index.ts +53 -0
- package/src/core/logger.ts +112 -0
- package/src/core/mcp/client.ts +185 -0
- package/src/core/mcp/config.ts +248 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +99 -0
- package/src/core/mcp/manager.ts +235 -0
- package/src/core/mcp/tool-bridge.ts +156 -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 +228 -0
- package/src/core/messages.ts +211 -0
- package/src/core/model-registry.ts +334 -0
- package/src/core/model-resolver.ts +494 -0
- package/src/core/plugins/doctor.ts +67 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +339 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +37 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +900 -0
- package/src/core/session-manager.ts +1837 -0
- package/src/core/settings-manager.ts +860 -0
- package/src/core/skills.ts +352 -0
- package/src/core/slash-commands.ts +132 -0
- package/src/core/system-prompt.ts +442 -0
- package/src/core/timings.ts +25 -0
- package/src/core/title-generator.ts +110 -0
- package/src/core/tools/ask.ts +193 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +91 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +487 -0
- package/src/core/tools/edit.ts +140 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +63 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +200 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +338 -0
- package/src/core/tools/exa/types.ts +167 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +244 -0
- package/src/core/tools/grep.ts +584 -0
- package/src/core/tools/index.ts +283 -0
- package/src/core/tools/ls.ts +142 -0
- package/src/core/tools/lsp/client.ts +767 -0
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +845 -0
- package/src/core/tools/lsp/edits.ts +110 -0
- package/src/core/tools/lsp/index.ts +1364 -0
- package/src/core/tools/lsp/render.ts +560 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +495 -0
- package/src/core/tools/lsp/utils.ts +526 -0
- package/src/core/tools/notebook.ts +182 -0
- package/src/core/tools/output.ts +198 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +507 -0
- package/src/core/tools/renderers.ts +820 -0
- package/src/core/tools/review.ts +275 -0
- package/src/core/tools/rulebook.ts +124 -0
- package/src/core/tools/task/agents.ts +158 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/commands.ts +157 -0
- package/src/core/tools/task/discovery.ts +217 -0
- package/src/core/tools/task/executor.ts +531 -0
- package/src/core/tools/task/index.ts +548 -0
- package/src/core/tools/task/model-resolver.ts +176 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +502 -0
- package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
- package/src/core/tools/task/types.ts +142 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2511 -0
- package/src/core/tools/web-search/auth.ts +199 -0
- package/src/core/tools/web-search/index.ts +583 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +196 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +372 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +63 -0
- package/src/core/ttsr.ts +211 -0
- package/src/core/utils.ts +187 -0
- package/src/discovery/agents-md.ts +75 -0
- package/src/discovery/builtin.ts +647 -0
- package/src/discovery/claude.ts +623 -0
- package/src/discovery/cline.ts +104 -0
- package/src/discovery/codex.ts +571 -0
- package/src/discovery/cursor.ts +266 -0
- package/src/discovery/gemini.ts +368 -0
- package/src/discovery/github.ts +120 -0
- package/src/discovery/helpers.test.ts +127 -0
- package/src/discovery/helpers.ts +249 -0
- package/src/discovery/index.ts +84 -0
- package/src/discovery/mcp-json.ts +127 -0
- package/src/discovery/vscode.ts +99 -0
- package/src/discovery/windsurf.ts +219 -0
- package/src/index.ts +192 -0
- package/src/main.ts +507 -0
- package/src/migrations.ts +156 -0
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +48 -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 +199 -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/extensions/extension-dashboard.ts +296 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
- package/src/modes/interactive/components/extensions/index.ts +9 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
- package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
- package/src/modes/interactive/components/extensions/types.ts +191 -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 +560 -0
- package/src/modes/interactive/components/oauth-selector.ts +136 -0
- package/src/modes/interactive/components/plugin-settings.ts +481 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +220 -0
- package/src/modes/interactive/components/settings-defs.ts +597 -0
- package/src/modes/interactive/components/settings-selector.ts +545 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +384 -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 +946 -0
- package/src/modes/interactive/components/tree-selector.ts +877 -0
- package/src/modes/interactive/components/ttsr-notification.ts +82 -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 +228 -0
- package/src/modes/interactive/interactive-mode.ts +2669 -0
- package/src/modes/interactive/theme/dark.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +99 -0
- package/src/modes/interactive/theme/theme-schema.json +424 -0
- package/src/modes/interactive/theme/theme.ts +2211 -0
- package/src/modes/print-mode.ts +163 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +494 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/prompts/architect-plan.md +10 -0
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/browser.md +71 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/explore.md +82 -0
- package/src/prompts/implement-with-critic.md +11 -0
- package/src/prompts/implement.md +11 -0
- package/src/prompts/init.md +30 -0
- package/src/prompts/plan.md +54 -0
- package/src/prompts/reviewer.md +81 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/prompts/task.md +56 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -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-snapshot.ts +218 -0
- package/src/utils/shell.ts +364 -0
- package/src/utils/tools-manager.ts +265 -0
package/src/cli/args.ts
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI argument parsing and help display
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR } from "../config";
|
|
8
|
+
import { allTools, type ToolName } from "../core/tools/index";
|
|
9
|
+
|
|
10
|
+
export type Mode = "text" | "json" | "rpc";
|
|
11
|
+
|
|
12
|
+
export interface Args {
|
|
13
|
+
provider?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
smol?: string;
|
|
16
|
+
slow?: string;
|
|
17
|
+
apiKey?: string;
|
|
18
|
+
systemPrompt?: string;
|
|
19
|
+
appendSystemPrompt?: string;
|
|
20
|
+
thinking?: ThinkingLevel;
|
|
21
|
+
continue?: boolean;
|
|
22
|
+
resume?: boolean;
|
|
23
|
+
help?: boolean;
|
|
24
|
+
version?: boolean;
|
|
25
|
+
mode?: Mode;
|
|
26
|
+
noSession?: boolean;
|
|
27
|
+
session?: string;
|
|
28
|
+
sessionDir?: string;
|
|
29
|
+
models?: string[];
|
|
30
|
+
tools?: ToolName[];
|
|
31
|
+
hooks?: string[];
|
|
32
|
+
customTools?: string[];
|
|
33
|
+
print?: boolean;
|
|
34
|
+
export?: string;
|
|
35
|
+
noSkills?: boolean;
|
|
36
|
+
skills?: string[];
|
|
37
|
+
listModels?: string | true;
|
|
38
|
+
messages: string[];
|
|
39
|
+
fileArgs: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
43
|
+
|
|
44
|
+
export function isValidThinkingLevel(level: string): level is ThinkingLevel {
|
|
45
|
+
return VALID_THINKING_LEVELS.includes(level as ThinkingLevel);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function parseArgs(args: string[]): Args {
|
|
49
|
+
const result: Args = {
|
|
50
|
+
messages: [],
|
|
51
|
+
fileArgs: [],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
for (let i = 0; i < args.length; i++) {
|
|
55
|
+
const arg = args[i];
|
|
56
|
+
|
|
57
|
+
if (arg === "--help" || arg === "-h") {
|
|
58
|
+
result.help = true;
|
|
59
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
60
|
+
result.version = true;
|
|
61
|
+
} else if (arg === "--mode" && i + 1 < args.length) {
|
|
62
|
+
const mode = args[++i];
|
|
63
|
+
if (mode === "text" || mode === "json" || mode === "rpc") {
|
|
64
|
+
result.mode = mode;
|
|
65
|
+
}
|
|
66
|
+
} else if (arg === "--continue" || arg === "-c") {
|
|
67
|
+
result.continue = true;
|
|
68
|
+
} else if (arg === "--resume" || arg === "-r") {
|
|
69
|
+
result.resume = true;
|
|
70
|
+
} else if (arg === "--provider" && i + 1 < args.length) {
|
|
71
|
+
result.provider = args[++i];
|
|
72
|
+
} else if (arg === "--model" && i + 1 < args.length) {
|
|
73
|
+
result.model = args[++i];
|
|
74
|
+
} else if (arg === "--smol" && i + 1 < args.length) {
|
|
75
|
+
result.smol = args[++i];
|
|
76
|
+
} else if (arg === "--slow" && i + 1 < args.length) {
|
|
77
|
+
result.slow = args[++i];
|
|
78
|
+
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
79
|
+
result.apiKey = args[++i];
|
|
80
|
+
} else if (arg === "--system-prompt" && i + 1 < args.length) {
|
|
81
|
+
result.systemPrompt = args[++i];
|
|
82
|
+
} else if (arg === "--append-system-prompt" && i + 1 < args.length) {
|
|
83
|
+
result.appendSystemPrompt = args[++i];
|
|
84
|
+
} else if (arg === "--no-session") {
|
|
85
|
+
result.noSession = true;
|
|
86
|
+
} else if (arg === "--session" && i + 1 < args.length) {
|
|
87
|
+
result.session = args[++i];
|
|
88
|
+
} else if (arg === "--session-dir" && i + 1 < args.length) {
|
|
89
|
+
result.sessionDir = args[++i];
|
|
90
|
+
} else if (arg === "--models" && i + 1 < args.length) {
|
|
91
|
+
result.models = args[++i].split(",").map((s) => s.trim());
|
|
92
|
+
} else if (arg === "--tools" && i + 1 < args.length) {
|
|
93
|
+
const toolNames = args[++i].split(",").map((s) => s.trim());
|
|
94
|
+
const validTools: ToolName[] = [];
|
|
95
|
+
for (const name of toolNames) {
|
|
96
|
+
if (name in allTools) {
|
|
97
|
+
validTools.push(name as ToolName);
|
|
98
|
+
} else {
|
|
99
|
+
console.error(
|
|
100
|
+
chalk.yellow(`Warning: Unknown tool "${name}". Valid tools: ${Object.keys(allTools).join(", ")}`),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
result.tools = validTools;
|
|
105
|
+
} else if (arg === "--thinking" && i + 1 < args.length) {
|
|
106
|
+
const level = args[++i];
|
|
107
|
+
if (isValidThinkingLevel(level)) {
|
|
108
|
+
result.thinking = level;
|
|
109
|
+
} else {
|
|
110
|
+
console.error(
|
|
111
|
+
chalk.yellow(
|
|
112
|
+
`Warning: Invalid thinking level "${level}". Valid values: ${VALID_THINKING_LEVELS.join(", ")}`,
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
} else if (arg === "--print" || arg === "-p") {
|
|
117
|
+
result.print = true;
|
|
118
|
+
} else if (arg === "--export" && i + 1 < args.length) {
|
|
119
|
+
result.export = args[++i];
|
|
120
|
+
} else if (arg === "--hook" && i + 1 < args.length) {
|
|
121
|
+
result.hooks = result.hooks ?? [];
|
|
122
|
+
result.hooks.push(args[++i]);
|
|
123
|
+
} else if (arg === "--tool" && i + 1 < args.length) {
|
|
124
|
+
result.customTools = result.customTools ?? [];
|
|
125
|
+
result.customTools.push(args[++i]);
|
|
126
|
+
} else if (arg === "--no-skills") {
|
|
127
|
+
result.noSkills = true;
|
|
128
|
+
} else if (arg === "--skills" && i + 1 < args.length) {
|
|
129
|
+
// Comma-separated glob patterns for skill filtering
|
|
130
|
+
result.skills = args[++i].split(",").map((s) => s.trim());
|
|
131
|
+
} else if (arg === "--list-models") {
|
|
132
|
+
// Check if next arg is a search pattern (not a flag or file arg)
|
|
133
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("-") && !args[i + 1].startsWith("@")) {
|
|
134
|
+
result.listModels = args[++i];
|
|
135
|
+
} else {
|
|
136
|
+
result.listModels = true;
|
|
137
|
+
}
|
|
138
|
+
} else if (arg.startsWith("@")) {
|
|
139
|
+
result.fileArgs.push(arg.slice(1)); // Remove @ prefix
|
|
140
|
+
} else if (!arg.startsWith("-")) {
|
|
141
|
+
result.messages.push(arg);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function printHelp(): void {
|
|
149
|
+
console.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools
|
|
150
|
+
|
|
151
|
+
${chalk.bold("Usage:")}
|
|
152
|
+
${APP_NAME} [options] [@files...] [messages...]
|
|
153
|
+
|
|
154
|
+
${chalk.bold("Options:")}
|
|
155
|
+
--model <pattern> Model to use (fuzzy match: "opus", "gpt-5.2", or "p-openai/gpt-5.2")
|
|
156
|
+
--smol <id> Smol/fast model for lightweight tasks (or OMP_SMOL_MODEL env)
|
|
157
|
+
--slow <id> Slow/reasoning model for thorough analysis (or OMP_SLOW_MODEL env)
|
|
158
|
+
--api-key <key> API key (defaults to env vars)
|
|
159
|
+
--system-prompt <text> System prompt (default: coding assistant prompt)
|
|
160
|
+
--append-system-prompt <text> Append text or file contents to the system prompt
|
|
161
|
+
--mode <mode> Output mode: text (default), json, or rpc
|
|
162
|
+
--print, -p Non-interactive mode: process prompt and exit
|
|
163
|
+
--continue, -c Continue previous session
|
|
164
|
+
--resume, -r Select a session to resume
|
|
165
|
+
--session <path> Use specific session file
|
|
166
|
+
--session-dir <dir> Directory for session storage and lookup
|
|
167
|
+
--no-session Don't save session (ephemeral)
|
|
168
|
+
--models <patterns> Comma-separated model patterns for Ctrl+P cycling
|
|
169
|
+
Supports globs (anthropic/*, *sonnet*) and fuzzy matching
|
|
170
|
+
--tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)
|
|
171
|
+
Available: read, bash, edit, write, grep, find, ls
|
|
172
|
+
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
|
173
|
+
--hook <path> Load a hook file (can be used multiple times)
|
|
174
|
+
--tool <path> Load a custom tool file (can be used multiple times)
|
|
175
|
+
--no-skills Disable skills discovery and loading
|
|
176
|
+
--skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)
|
|
177
|
+
--export <file> Export session file to HTML and exit
|
|
178
|
+
--list-models [search] List available models (with optional fuzzy search)
|
|
179
|
+
--help, -h Show this help
|
|
180
|
+
--version, -v Show version number
|
|
181
|
+
|
|
182
|
+
${chalk.bold("Examples:")}
|
|
183
|
+
# Interactive mode
|
|
184
|
+
${APP_NAME}
|
|
185
|
+
|
|
186
|
+
# Interactive mode with initial prompt
|
|
187
|
+
${APP_NAME} "List all .ts files in src/"
|
|
188
|
+
|
|
189
|
+
# Include files in initial message
|
|
190
|
+
${APP_NAME} @prompt.md @image.png "What color is the sky?"
|
|
191
|
+
|
|
192
|
+
# Non-interactive mode (process and exit)
|
|
193
|
+
${APP_NAME} -p "List all .ts files in src/"
|
|
194
|
+
|
|
195
|
+
# Multiple messages (interactive)
|
|
196
|
+
${APP_NAME} "Read package.json" "What dependencies do we have?"
|
|
197
|
+
|
|
198
|
+
# Continue previous session
|
|
199
|
+
${APP_NAME} --continue "What did we discuss?"
|
|
200
|
+
|
|
201
|
+
# Use different model (fuzzy matching)
|
|
202
|
+
${APP_NAME} --model opus "Help me refactor this code"
|
|
203
|
+
|
|
204
|
+
# Limit model cycling to specific models
|
|
205
|
+
${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o
|
|
206
|
+
|
|
207
|
+
# Limit to a specific provider with glob pattern
|
|
208
|
+
${APP_NAME} --models "github-copilot/*"
|
|
209
|
+
|
|
210
|
+
# Cycle models with fixed thinking levels
|
|
211
|
+
${APP_NAME} --models sonnet:high,haiku:low
|
|
212
|
+
|
|
213
|
+
# Start with a specific thinking level
|
|
214
|
+
${APP_NAME} --thinking high "Solve this complex problem"
|
|
215
|
+
|
|
216
|
+
# Read-only mode (no file modifications possible)
|
|
217
|
+
${APP_NAME} --tools read,grep,find,ls -p "Review the code in src/"
|
|
218
|
+
|
|
219
|
+
# Export a session file to HTML
|
|
220
|
+
${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl
|
|
221
|
+
${APP_NAME} --export session.jsonl output.html
|
|
222
|
+
|
|
223
|
+
${chalk.bold("Environment Variables:")}
|
|
224
|
+
${chalk.dim("# Model providers")}
|
|
225
|
+
ANTHROPIC_API_KEY - Anthropic Claude API key
|
|
226
|
+
ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)
|
|
227
|
+
OPENAI_API_KEY - OpenAI GPT API key
|
|
228
|
+
GEMINI_API_KEY - Google Gemini API key
|
|
229
|
+
GROQ_API_KEY - Groq API key
|
|
230
|
+
CEREBRAS_API_KEY - Cerebras API key
|
|
231
|
+
XAI_API_KEY - xAI Grok API key
|
|
232
|
+
OPENROUTER_API_KEY - OpenRouter API key
|
|
233
|
+
MISTRAL_API_KEY - Mistral API key
|
|
234
|
+
ZAI_API_KEY - ZAI API key
|
|
235
|
+
GITHUB_TOKEN - GitHub Copilot models (or GH_TOKEN, COPILOT_GITHUB_TOKEN)
|
|
236
|
+
|
|
237
|
+
${chalk.dim("# Web search providers")}
|
|
238
|
+
EXA_API_KEY - Exa search API key
|
|
239
|
+
PERPLEXITY_API_KEY - Perplexity search API key
|
|
240
|
+
|
|
241
|
+
${chalk.dim("# Configuration")}
|
|
242
|
+
${ENV_AGENT_DIR.padEnd(23)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
|
|
243
|
+
|
|
244
|
+
${chalk.bold("Available Tools (all enabled by default):")}
|
|
245
|
+
read - Read file contents
|
|
246
|
+
bash - Execute bash commands
|
|
247
|
+
edit - Edit files with find/replace
|
|
248
|
+
write - Write files (creates/overwrites)
|
|
249
|
+
grep - Search file contents
|
|
250
|
+
find - Find files by glob pattern
|
|
251
|
+
ls - List directory contents
|
|
252
|
+
lsp - Language server protocol (code intelligence)
|
|
253
|
+
notebook - Edit Jupyter notebooks
|
|
254
|
+
task - Launch sub-agents for parallel tasks
|
|
255
|
+
web_fetch - Fetch and process web pages
|
|
256
|
+
web_search - Search the web
|
|
257
|
+
ask - Ask user questions (interactive mode only)
|
|
258
|
+
`);
|
|
259
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process @file CLI arguments into text content and image attachments
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { access, readFile, stat } from "node:fs/promises";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import sharp from "sharp";
|
|
10
|
+
import { resolveReadPath } from "../core/tools/path-utils";
|
|
11
|
+
import { detectSupportedImageMimeTypeFromFile } from "../utils/mime";
|
|
12
|
+
|
|
13
|
+
export interface ProcessedFiles {
|
|
14
|
+
text: string;
|
|
15
|
+
images: ImageContent[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const RESIZE_TRIGGER_MAX_DIMENSION = 2048;
|
|
19
|
+
const MAX_RESIZE_WIDTH = 1920;
|
|
20
|
+
const MAX_RESIZE_HEIGHT = 1080;
|
|
21
|
+
const JPEG_CONVERT_THRESHOLD_BYTES = 2 * 1024 * 1024;
|
|
22
|
+
const JPEG_QUALITY = 85;
|
|
23
|
+
|
|
24
|
+
async function processImageAttachment(buffer: Buffer, mimeType: string): Promise<{ buffer: Buffer; mimeType: string }> {
|
|
25
|
+
const metadata = await sharp(buffer, { failOnError: false }).metadata();
|
|
26
|
+
const width = metadata.width ?? 0;
|
|
27
|
+
const height = metadata.height ?? 0;
|
|
28
|
+
const maxDim = Math.max(width, height);
|
|
29
|
+
const shouldResize = width > 0 && height > 0 && maxDim > RESIZE_TRIGGER_MAX_DIMENSION;
|
|
30
|
+
const shouldConvertToJpeg = buffer.length > JPEG_CONVERT_THRESHOLD_BYTES;
|
|
31
|
+
|
|
32
|
+
if (!shouldResize && !shouldConvertToJpeg) {
|
|
33
|
+
return { buffer, mimeType };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let pipeline = sharp(buffer, { failOnError: false });
|
|
37
|
+
if (shouldResize) {
|
|
38
|
+
pipeline = pipeline.resize({
|
|
39
|
+
width: MAX_RESIZE_WIDTH,
|
|
40
|
+
height: MAX_RESIZE_HEIGHT,
|
|
41
|
+
fit: "inside",
|
|
42
|
+
withoutEnlargement: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (shouldConvertToJpeg) {
|
|
47
|
+
pipeline = pipeline.jpeg({ quality: JPEG_QUALITY });
|
|
48
|
+
return { buffer: await pipeline.toBuffer(), mimeType: "image/jpeg" };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (mimeType === "image/png") {
|
|
52
|
+
pipeline = pipeline.png();
|
|
53
|
+
} else if (mimeType === "image/webp") {
|
|
54
|
+
pipeline = pipeline.webp();
|
|
55
|
+
} else if (mimeType === "image/gif") {
|
|
56
|
+
pipeline = pipeline.gif();
|
|
57
|
+
} else {
|
|
58
|
+
pipeline = pipeline.jpeg({ quality: JPEG_QUALITY });
|
|
59
|
+
return { buffer: await pipeline.toBuffer(), mimeType: "image/jpeg" };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { buffer: await pipeline.toBuffer(), mimeType };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Process @file arguments into text content and image attachments */
|
|
66
|
+
export async function processFileArguments(fileArgs: string[]): Promise<ProcessedFiles> {
|
|
67
|
+
let text = "";
|
|
68
|
+
const images: ImageContent[] = [];
|
|
69
|
+
|
|
70
|
+
for (const fileArg of fileArgs) {
|
|
71
|
+
// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)
|
|
72
|
+
const absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));
|
|
73
|
+
|
|
74
|
+
// Check if file exists
|
|
75
|
+
try {
|
|
76
|
+
await access(absolutePath);
|
|
77
|
+
} catch {
|
|
78
|
+
console.error(chalk.red(`Error: File not found: ${absolutePath}`));
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if file is empty
|
|
83
|
+
const stats = await stat(absolutePath);
|
|
84
|
+
if (stats.size === 0) {
|
|
85
|
+
// Skip empty files
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);
|
|
90
|
+
|
|
91
|
+
if (mimeType) {
|
|
92
|
+
// Handle image file
|
|
93
|
+
const content = await readFile(absolutePath);
|
|
94
|
+
const processed = await processImageAttachment(content, mimeType);
|
|
95
|
+
const base64Content = processed.buffer.toString("base64");
|
|
96
|
+
|
|
97
|
+
const attachment: ImageContent = {
|
|
98
|
+
type: "image",
|
|
99
|
+
mimeType: processed.mimeType,
|
|
100
|
+
data: base64Content,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
images.push(attachment);
|
|
104
|
+
|
|
105
|
+
// Add text reference to image
|
|
106
|
+
text += `<file name="${absolutePath}"></file>\n`;
|
|
107
|
+
} else {
|
|
108
|
+
// Handle text file
|
|
109
|
+
try {
|
|
110
|
+
const content = await readFile(absolutePath, "utf-8");
|
|
111
|
+
text += `<file name="${absolutePath}">\n${content}\n</file>\n`;
|
|
112
|
+
} catch (error: unknown) {
|
|
113
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
114
|
+
console.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { text, images };
|
|
121
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List available models with optional fuzzy search
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
6
|
+
import type { ModelRegistry } from "../core/model-registry";
|
|
7
|
+
import { fuzzyFilter } from "../utils/fuzzy";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Format a number as human-readable (e.g., 200000 -> "200K", 1000000 -> "1M")
|
|
11
|
+
*/
|
|
12
|
+
function formatTokenCount(count: number): string {
|
|
13
|
+
if (count >= 1_000_000) {
|
|
14
|
+
const millions = count / 1_000_000;
|
|
15
|
+
return millions % 1 === 0 ? `${millions}M` : `${millions.toFixed(1)}M`;
|
|
16
|
+
}
|
|
17
|
+
if (count >= 1_000) {
|
|
18
|
+
const thousands = count / 1_000;
|
|
19
|
+
return thousands % 1 === 0 ? `${thousands}K` : `${thousands.toFixed(1)}K`;
|
|
20
|
+
}
|
|
21
|
+
return count.toString();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* List available models, optionally filtered by search pattern
|
|
26
|
+
*/
|
|
27
|
+
export async function listModels(modelRegistry: ModelRegistry, searchPattern?: string): Promise<void> {
|
|
28
|
+
const models = await modelRegistry.getAvailable();
|
|
29
|
+
|
|
30
|
+
if (models.length === 0) {
|
|
31
|
+
console.log("No models available. Set API keys in environment variables.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Apply fuzzy filter if search pattern provided
|
|
36
|
+
let filteredModels: Model<Api>[] = models;
|
|
37
|
+
if (searchPattern) {
|
|
38
|
+
filteredModels = fuzzyFilter(models, searchPattern, (m) => `${m.provider} ${m.id}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (filteredModels.length === 0) {
|
|
42
|
+
console.log(`No models matching "${searchPattern}"`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Sort by provider, then by model id
|
|
47
|
+
filteredModels.sort((a, b) => {
|
|
48
|
+
const providerCmp = a.provider.localeCompare(b.provider);
|
|
49
|
+
if (providerCmp !== 0) return providerCmp;
|
|
50
|
+
return a.id.localeCompare(b.id);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Calculate column widths
|
|
54
|
+
const rows = filteredModels.map((m) => ({
|
|
55
|
+
provider: m.provider,
|
|
56
|
+
model: m.id,
|
|
57
|
+
context: formatTokenCount(m.contextWindow),
|
|
58
|
+
maxOut: formatTokenCount(m.maxTokens),
|
|
59
|
+
thinking: m.reasoning ? "yes" : "no",
|
|
60
|
+
images: m.input.includes("image") ? "yes" : "no",
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
const headers = {
|
|
64
|
+
provider: "provider",
|
|
65
|
+
model: "model",
|
|
66
|
+
context: "context",
|
|
67
|
+
maxOut: "max-out",
|
|
68
|
+
thinking: "thinking",
|
|
69
|
+
images: "images",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const widths = {
|
|
73
|
+
provider: Math.max(headers.provider.length, ...rows.map((r) => r.provider.length)),
|
|
74
|
+
model: Math.max(headers.model.length, ...rows.map((r) => r.model.length)),
|
|
75
|
+
context: Math.max(headers.context.length, ...rows.map((r) => r.context.length)),
|
|
76
|
+
maxOut: Math.max(headers.maxOut.length, ...rows.map((r) => r.maxOut.length)),
|
|
77
|
+
thinking: Math.max(headers.thinking.length, ...rows.map((r) => r.thinking.length)),
|
|
78
|
+
images: Math.max(headers.images.length, ...rows.map((r) => r.images.length)),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Print header
|
|
82
|
+
const headerLine = [
|
|
83
|
+
headers.provider.padEnd(widths.provider),
|
|
84
|
+
headers.model.padEnd(widths.model),
|
|
85
|
+
headers.context.padEnd(widths.context),
|
|
86
|
+
headers.maxOut.padEnd(widths.maxOut),
|
|
87
|
+
headers.thinking.padEnd(widths.thinking),
|
|
88
|
+
headers.images.padEnd(widths.images),
|
|
89
|
+
].join(" ");
|
|
90
|
+
console.log(headerLine);
|
|
91
|
+
|
|
92
|
+
// Print rows
|
|
93
|
+
for (const row of rows) {
|
|
94
|
+
const line = [
|
|
95
|
+
row.provider.padEnd(widths.provider),
|
|
96
|
+
row.model.padEnd(widths.model),
|
|
97
|
+
row.context.padEnd(widths.context),
|
|
98
|
+
row.maxOut.padEnd(widths.maxOut),
|
|
99
|
+
row.thinking.padEnd(widths.thinking),
|
|
100
|
+
row.images.padEnd(widths.images),
|
|
101
|
+
].join(" ");
|
|
102
|
+
console.log(line);
|
|
103
|
+
}
|
|
104
|
+
}
|