@oh-my-pi/pi-coding-agent 11.0.3 → 11.2.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 +199 -49
- package/README.md +1 -1
- package/docs/config-usage.md +3 -4
- package/docs/sdk.md +6 -5
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/examples/sdk/README.md +1 -1
- package/package.json +19 -11
- package/src/cli/args.ts +11 -94
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/oclif-help.ts +26 -0
- package/src/cli/web-search-cli.ts +148 -0
- package/src/cli.ts +8 -2
- package/src/commands/commit.ts +36 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/grep.ts +41 -0
- package/src/commands/index/index.ts +136 -0
- package/src/commands/jupyter.ts +32 -0
- package/src/commands/plugin.ts +70 -0
- package/src/commands/setup.ts +39 -0
- package/src/commands/shell.ts +29 -0
- package/src/commands/stats.ts +29 -0
- package/src/commands/update.ts +21 -0
- package/src/commands/web-search.ts +50 -0
- package/src/commit/agentic/index.ts +3 -2
- package/src/commit/agentic/tools/analyze-file.ts +1 -3
- package/src/commit/git/errors.ts +4 -6
- package/src/commit/pipeline.ts +3 -2
- package/src/config/keybindings.ts +1 -3
- package/src/config/model-registry.ts +89 -162
- package/src/config/settings-schema.ts +10 -0
- package/src/config.ts +202 -132
- package/src/exa/mcp-client.ts +8 -41
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/extensions/loader.ts +7 -10
- package/src/extensibility/extensions/runner.ts +5 -15
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/runner.ts +6 -9
- package/src/index.ts +0 -1
- package/src/ipy/kernel.ts +10 -22
- package/src/lsp/clients/biome-client.ts +4 -7
- package/src/lsp/clients/lsp-linter-client.ts +4 -6
- package/src/lsp/index.ts +5 -4
- package/src/lsp/utils.ts +18 -0
- package/src/main.ts +86 -181
- package/src/mcp/json-rpc.ts +2 -2
- package/src/mcp/transports/http.ts +12 -49
- package/src/modes/components/armin.ts +1 -3
- package/src/modes/components/assistant-message.ts +4 -4
- package/src/modes/components/bash-execution.ts +5 -3
- package/src/modes/components/branch-summary-message.ts +1 -3
- package/src/modes/components/compaction-summary-message.ts +1 -3
- package/src/modes/components/custom-message.ts +4 -5
- package/src/modes/components/extensions/extension-dashboard.ts +10 -16
- package/src/modes/components/extensions/extension-list.ts +5 -5
- package/src/modes/components/footer.ts +1 -4
- package/src/modes/components/hook-editor.ts +7 -32
- package/src/modes/components/hook-message.ts +4 -5
- package/src/modes/components/model-selector.ts +2 -2
- package/src/modes/components/plugin-settings.ts +16 -20
- package/src/modes/components/python-execution.ts +5 -5
- package/src/modes/components/session-selector.ts +6 -7
- package/src/modes/components/settings-defs.ts +49 -40
- package/src/modes/components/settings-selector.ts +8 -17
- package/src/modes/components/skill-message.ts +1 -3
- package/src/modes/components/status-line-segment-editor.ts +1 -3
- package/src/modes/components/status-line.ts +1 -3
- package/src/modes/components/todo-reminder.ts +5 -7
- package/src/modes/components/tree-selector.ts +10 -12
- package/src/modes/components/ttsr-notification.ts +1 -3
- package/src/modes/components/user-message-selector.ts +2 -4
- package/src/modes/components/welcome.ts +6 -18
- package/src/modes/controllers/event-controller.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +7 -34
- package/src/modes/controllers/selector-controller.ts +3 -3
- package/src/modes/interactive-mode.ts +27 -1
- package/src/modes/rpc/rpc-client.ts +2 -5
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/theme/theme.ts +2 -6
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +6 -1
- package/src/patch/index.ts +1 -4
- package/src/prompts/agents/explore.md +1 -0
- package/src/prompts/agents/frontmatter.md +2 -1
- package/src/prompts/agents/init.md +1 -0
- package/src/prompts/agents/plan.md +1 -0
- package/src/prompts/agents/reviewer.md +1 -0
- package/src/prompts/system/subagent-submit-reminder.md +2 -0
- package/src/prompts/system/subagent-system-prompt.md +2 -0
- package/src/prompts/system/subagent-user-prompt.md +8 -0
- package/src/prompts/system/system-prompt.md +5 -3
- package/src/prompts/system/web-search.md +6 -4
- package/src/prompts/tools/task.md +216 -163
- package/src/sdk.ts +11 -110
- package/src/session/agent-session.ts +117 -83
- package/src/session/auth-storage.ts +10 -51
- package/src/session/messages.ts +17 -3
- package/src/session/session-manager.ts +30 -30
- package/src/session/streaming-output.ts +1 -1
- package/src/ssh/ssh-executor.ts +6 -3
- package/src/task/agents.ts +2 -0
- package/src/task/discovery.ts +1 -1
- package/src/task/executor.ts +5 -10
- package/src/task/index.ts +43 -23
- package/src/task/render.ts +67 -64
- package/src/task/template.ts +17 -34
- package/src/task/types.ts +49 -22
- package/src/tools/ask.ts +1 -3
- package/src/tools/bash.ts +1 -4
- package/src/tools/browser.ts +5 -7
- package/src/tools/exit-plan-mode.ts +1 -4
- package/src/tools/fetch.ts +1 -3
- package/src/tools/find.ts +4 -3
- package/src/tools/gemini-image.ts +24 -55
- package/src/tools/grep.ts +4 -4
- package/src/tools/index.ts +12 -14
- package/src/tools/notebook.ts +1 -5
- package/src/tools/python.ts +4 -3
- package/src/tools/read.ts +2 -4
- package/src/tools/render-utils.ts +23 -0
- package/src/tools/ssh.ts +8 -12
- package/src/tools/todo-write.ts +1 -4
- package/src/tools/tool-errors.ts +1 -4
- package/src/tools/write.ts +1 -3
- package/src/utils/external-editor.ts +59 -0
- package/src/utils/file-mentions.ts +39 -1
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +4 -4
- package/src/web/search/auth.ts +3 -33
- package/src/web/search/index.ts +73 -139
- package/src/web/search/provider.ts +58 -0
- package/src/web/search/providers/anthropic.ts +53 -14
- package/src/web/search/providers/base.ts +22 -0
- package/src/web/search/providers/codex.ts +38 -16
- package/src/web/search/providers/exa.ts +30 -6
- package/src/web/search/providers/gemini.ts +56 -20
- package/src/web/search/providers/jina.ts +28 -5
- package/src/web/search/providers/perplexity.ts +103 -36
- package/src/web/search/render.ts +84 -74
- package/src/web/search/types.ts +285 -59
- package/src/migrations.ts +0 -175
- package/src/session/storage-migration.ts +0 -173
package/docs/sdk.md
CHANGED
|
@@ -272,7 +272,7 @@ const { session } = await createAgentSession({
|
|
|
272
272
|
`agentDir` is used for:
|
|
273
273
|
|
|
274
274
|
- Global settings (`config.yml` + `agent.db`)
|
|
275
|
-
- Primary auth/models locations (`
|
|
275
|
+
- Primary auth/models locations (`agent.db`, `models.yml`, `models.json`)
|
|
276
276
|
- Prompt templates (`prompts/`)
|
|
277
277
|
- Custom TS commands (`commands/`)
|
|
278
278
|
|
|
@@ -328,12 +328,13 @@ API key resolution priority (handled by AuthStorage):
|
|
|
328
328
|
3. Environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, etc.)
|
|
329
329
|
4. Fallback resolver (for custom provider keys from `models.yml`)
|
|
330
330
|
|
|
331
|
-
|
|
331
|
+
|
|
332
|
+
`discoverAuthStorage` opens the `agent.db` SQLite database in the agent directory.
|
|
332
333
|
|
|
333
334
|
```typescript
|
|
334
335
|
import { AuthStorage, ModelRegistry, discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent";
|
|
335
336
|
|
|
336
|
-
// Default: uses agentDir/
|
|
337
|
+
// Default: uses agentDir/agent.db and agentDir/models.yml
|
|
337
338
|
const authStorage = await discoverAuthStorage();
|
|
338
339
|
const modelRegistry = discoverModels(authStorage);
|
|
339
340
|
|
|
@@ -347,7 +348,7 @@ const { session } = await createAgentSession({
|
|
|
347
348
|
authStorage.setRuntimeApiKey("anthropic", "sk-my-temp-key");
|
|
348
349
|
|
|
349
350
|
// Custom auth storage location (use create(), constructor is private)
|
|
350
|
-
const customAuth = await AuthStorage.create("/my/app/
|
|
351
|
+
const customAuth = await AuthStorage.create("/my/app/agent.db");
|
|
351
352
|
const customRegistry = new ModelRegistry(customAuth, "/my/app/models.yml");
|
|
352
353
|
|
|
353
354
|
const { session } = await createAgentSession({
|
|
@@ -788,7 +789,7 @@ import {
|
|
|
788
789
|
} from "@oh-my-pi/pi-coding-agent";
|
|
789
790
|
|
|
790
791
|
// Auth and Models
|
|
791
|
-
const authStorage = await discoverAuthStorage(); // <agentDir>/
|
|
792
|
+
const authStorage = await discoverAuthStorage(); // <agentDir>/agent.db
|
|
792
793
|
const modelRegistry = discoverModels(authStorage); // + <agentDir>/models.yml (or models.json)
|
|
793
794
|
const allModels = modelRegistry.getAll(); // All models (built-in + custom)
|
|
794
795
|
const available = modelRegistry.getAvailable(); // Only models with API keys
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
SessionManager,
|
|
13
13
|
} from "@oh-my-pi/pi-coding-agent";
|
|
14
14
|
|
|
15
|
-
// Default: discoverAuthStorage() uses ~/.omp/agent/
|
|
15
|
+
// Default: discoverAuthStorage() uses ~/.omp/agent/agent.db
|
|
16
16
|
// discoverModels() loads built-in + custom models from ~/.omp/agent/models.json
|
|
17
17
|
const authStorage = await discoverAuthStorage();
|
|
18
18
|
const modelRegistry = await discoverModels(authStorage);
|
|
@@ -25,7 +25,7 @@ await createAgentSession({
|
|
|
25
25
|
console.log("Session with default auth storage and model registry");
|
|
26
26
|
|
|
27
27
|
// Custom auth storage location
|
|
28
|
-
const customAuthStorage =
|
|
28
|
+
const customAuthStorage = await AuthStorage.create("/tmp/my-app/agent.db");
|
|
29
29
|
const customModelRegistry = new ModelRegistry(customAuthStorage, "/tmp/my-app/models.json");
|
|
30
30
|
|
|
31
31
|
await createAgentSession({
|
package/examples/sdk/README.md
CHANGED
|
@@ -77,7 +77,7 @@ const { session } = await createAgentSession({
|
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
// Full control
|
|
80
|
-
const customAuth =
|
|
80
|
+
const customAuth = await AuthStorage.create("/my/app/agent.db");
|
|
81
81
|
customAuth.setRuntimeApiKey("anthropic", Bun.env.MY_KEY!);
|
|
82
82
|
const customRegistry = new ModelRegistry(customAuth);
|
|
83
83
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "11.0
|
|
3
|
+
"version": "11.2.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"ompConfig": {
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
"bin": {
|
|
11
11
|
"omp": "src/cli.ts"
|
|
12
12
|
},
|
|
13
|
+
"oclif": {
|
|
14
|
+
"bin": "omp",
|
|
15
|
+
"commands": "./src/commands",
|
|
16
|
+
"helpClass": "./src/cli/oclif-help.ts",
|
|
17
|
+
"plugins": [
|
|
18
|
+
"@oclif/plugin-autocomplete"
|
|
19
|
+
]
|
|
20
|
+
},
|
|
13
21
|
"main": "./src/index.ts",
|
|
14
22
|
"types": "./src/index.ts",
|
|
15
23
|
"exports": {
|
|
@@ -79,14 +87,15 @@
|
|
|
79
87
|
"test": "bun test"
|
|
80
88
|
},
|
|
81
89
|
"dependencies": {
|
|
90
|
+
"@oclif/core": "^4.5.6",
|
|
91
|
+
"@oclif/plugin-autocomplete": "^3.2.23",
|
|
82
92
|
"@mozilla/readability": "0.6.0",
|
|
83
|
-
"@oh-my-pi/omp-stats": "11.0
|
|
84
|
-
"@oh-my-pi/pi-agent-core": "11.0
|
|
85
|
-
"@oh-my-pi/pi-ai": "11.0
|
|
86
|
-
"@oh-my-pi/pi-natives": "11.0
|
|
87
|
-
"@oh-my-pi/pi-tui": "11.0
|
|
88
|
-
"@oh-my-pi/pi-utils": "11.0
|
|
89
|
-
"@openai/agents": "^0.4.5",
|
|
93
|
+
"@oh-my-pi/omp-stats": "11.2.0",
|
|
94
|
+
"@oh-my-pi/pi-agent-core": "11.2.0",
|
|
95
|
+
"@oh-my-pi/pi-ai": "11.2.0",
|
|
96
|
+
"@oh-my-pi/pi-natives": "11.2.0",
|
|
97
|
+
"@oh-my-pi/pi-tui": "11.2.0",
|
|
98
|
+
"@oh-my-pi/pi-utils": "11.2.0",
|
|
90
99
|
"@sinclair/typebox": "^0.34.48",
|
|
91
100
|
"ajv": "^8.17.1",
|
|
92
101
|
"chalk": "^5.6.2",
|
|
@@ -104,11 +113,10 @@
|
|
|
104
113
|
},
|
|
105
114
|
"devDependencies": {
|
|
106
115
|
"@types/diff": "^7.0.2",
|
|
107
|
-
"@types/jsdom": "27.0.0",
|
|
108
|
-
"bun-types": "^1.3.8",
|
|
109
116
|
"@types/ms": "^2.1.0",
|
|
110
117
|
"@types/bun": "^1.3.8",
|
|
111
|
-
"ms": "^2.1.3"
|
|
118
|
+
"ms": "^2.1.3",
|
|
119
|
+
"@types/jsdom": "27.0.0"
|
|
112
120
|
},
|
|
113
121
|
"keywords": [
|
|
114
122
|
"coding-agent",
|
package/src/cli/args.ts
CHANGED
|
@@ -180,99 +180,8 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
|
|
|
180
180
|
return result;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
export function
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
${chalk.bold("Usage:")}
|
|
187
|
-
${APP_NAME} [options] [@files...] [messages...]
|
|
188
|
-
|
|
189
|
-
${chalk.bold("Subcommands:")}
|
|
190
|
-
plugin Manage plugins (install, uninstall, list, etc.)
|
|
191
|
-
update Check for and install updates
|
|
192
|
-
config Manage configuration settings
|
|
193
|
-
setup Install dependencies for optional features
|
|
194
|
-
commit AI-assisted git commits
|
|
195
|
-
stats AI usage statistics dashboard
|
|
196
|
-
jupyter Manage the shared Jupyter gateway
|
|
197
|
-
shell Interactive shell console (brush-core test)
|
|
198
|
-
grep Test grep tool
|
|
199
|
-
|
|
200
|
-
${chalk.bold("Options:")}
|
|
201
|
-
--model <pattern> Model to use (fuzzy match: "opus", "gpt-5.2", or "p-openai/gpt-5.2")
|
|
202
|
-
--smol <id> Smol/fast model for lightweight tasks (or PI_SMOL_MODEL env)
|
|
203
|
-
--slow <id> Slow/reasoning model for thorough analysis (or PI_SLOW_MODEL env)
|
|
204
|
-
--plan <id> Plan model for architectural planning (or PI_PLAN_MODEL env)
|
|
205
|
-
--api-key <key> API key (defaults to env vars)
|
|
206
|
-
--system-prompt <text> System prompt (default: coding assistant prompt)
|
|
207
|
-
--append-system-prompt <text> Append text or file contents to the system prompt
|
|
208
|
-
--allow-home Allow starting in ~ without auto-switching to a temp dir
|
|
209
|
-
--mode <mode> Output mode: text (default), json, or rpc
|
|
210
|
-
--print, -p Non-interactive mode: process prompt and exit
|
|
211
|
-
--continue, -c Continue previous session
|
|
212
|
-
--resume, -r Select a session to resume
|
|
213
|
-
--session <path> Use specific session file
|
|
214
|
-
--session-dir <dir> Directory for session storage and lookup
|
|
215
|
-
--no-session Don't save session (ephemeral)
|
|
216
|
-
--models <patterns> Comma-separated model patterns for Ctrl+P cycling
|
|
217
|
-
Supports globs (anthropic/*, *sonnet*) and fuzzy matching
|
|
218
|
-
--no-tools Disable all built-in tools
|
|
219
|
-
--no-lsp Disable LSP tools, formatting, and diagnostics
|
|
220
|
-
--tools <tools> Comma-separated list of tools to enable (default: all)
|
|
221
|
-
Available: read, bash, edit, write, grep, find, lsp,
|
|
222
|
-
python, notebook, task, fetch, web_search, browser, ask
|
|
223
|
-
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
|
224
|
-
--hook <path> Load a hook/extension file (can be used multiple times)
|
|
225
|
-
--extension, -e <path> Load an extension file (can be used multiple times)
|
|
226
|
-
--no-extensions Disable extension discovery (explicit -e paths still work)
|
|
227
|
-
--no-skills Disable skills discovery and loading
|
|
228
|
-
--skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)
|
|
229
|
-
--export <file> Export session file to HTML and exit
|
|
230
|
-
--list-models [search] List available models (with optional fuzzy search)
|
|
231
|
-
--help, -h Show this help
|
|
232
|
-
--version, -v Show version number
|
|
233
|
-
|
|
234
|
-
${chalk.bold("Examples:")}
|
|
235
|
-
# Interactive mode
|
|
236
|
-
${APP_NAME}
|
|
237
|
-
|
|
238
|
-
# Interactive mode with initial prompt
|
|
239
|
-
${APP_NAME} "List all .ts files in src/"
|
|
240
|
-
|
|
241
|
-
# Include files in initial message
|
|
242
|
-
${APP_NAME} @prompt.md @image.png "What color is the sky?"
|
|
243
|
-
|
|
244
|
-
# Non-interactive mode (process and exit)
|
|
245
|
-
${APP_NAME} -p "List all .ts files in src/"
|
|
246
|
-
|
|
247
|
-
# Multiple messages (interactive)
|
|
248
|
-
${APP_NAME} "Read package.json" "What dependencies do we have?"
|
|
249
|
-
|
|
250
|
-
# Continue previous session
|
|
251
|
-
${APP_NAME} --continue "What did we discuss?"
|
|
252
|
-
|
|
253
|
-
# Use different model (fuzzy matching)
|
|
254
|
-
${APP_NAME} --model opus "Help me refactor this code"
|
|
255
|
-
|
|
256
|
-
# Limit model cycling to specific models
|
|
257
|
-
${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o
|
|
258
|
-
|
|
259
|
-
# Limit to a specific provider with glob pattern
|
|
260
|
-
${APP_NAME} --models "github-copilot/*"
|
|
261
|
-
|
|
262
|
-
# Cycle models with fixed thinking levels
|
|
263
|
-
${APP_NAME} --models sonnet:high,haiku:low
|
|
264
|
-
|
|
265
|
-
# Start with a specific thinking level
|
|
266
|
-
${APP_NAME} --thinking high "Solve this complex problem"
|
|
267
|
-
|
|
268
|
-
# Read-only mode (no file modifications possible)
|
|
269
|
-
${APP_NAME} --tools read,grep,find -p "Review the code in src/"
|
|
270
|
-
|
|
271
|
-
# Export a session file to HTML
|
|
272
|
-
${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl
|
|
273
|
-
${APP_NAME} --export session.jsonl output.html
|
|
274
|
-
|
|
275
|
-
${chalk.bold("Environment Variables:")}
|
|
183
|
+
export function getExtraHelpText(): string {
|
|
184
|
+
return `${chalk.bold("Environment Variables:")}
|
|
276
185
|
${chalk.dim("# Core Providers")}
|
|
277
186
|
ANTHROPIC_API_KEY - Anthropic Claude models
|
|
278
187
|
ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth (takes precedence over API key)
|
|
@@ -327,5 +236,13 @@ ${chalk.bold("Available Tools (all enabled by default):")}
|
|
|
327
236
|
fetch - Fetch and process URLs
|
|
328
237
|
web_search - Search the web
|
|
329
238
|
ask - Ask user questions (interactive mode only)
|
|
330
|
-
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function printHelp(): void {
|
|
243
|
+
process.stdout.write(
|
|
244
|
+
`${chalk.bold(APP_NAME)} - AI coding assistant\n\n` +
|
|
245
|
+
`Run ${APP_NAME} --help for full command and option details.\n\n` +
|
|
246
|
+
`${getExtraHelpText()}\n`,
|
|
247
|
+
);
|
|
331
248
|
}
|
package/src/cli/config-cli.ts
CHANGED
|
@@ -48,7 +48,7 @@ function getSettingValues(def: SettingDef): readonly string[] | undefined {
|
|
|
48
48
|
return def.values;
|
|
49
49
|
}
|
|
50
50
|
if (def.type === "submenu") {
|
|
51
|
-
const options = def.
|
|
51
|
+
const options = def.options;
|
|
52
52
|
if (options.length > 0) {
|
|
53
53
|
return options.map(o => o.value);
|
|
54
54
|
}
|
|
@@ -22,7 +22,7 @@ export interface ProcessFileOptions {
|
|
|
22
22
|
|
|
23
23
|
/** Process @file arguments into text content and image attachments */
|
|
24
24
|
export async function processFileArguments(fileArgs: string[], options?: ProcessFileOptions): Promise<ProcessedFiles> {
|
|
25
|
-
const
|
|
25
|
+
const autoResizeImages = options?.autoResizeImages ?? true;
|
|
26
26
|
let text = "";
|
|
27
27
|
const images: ImageContent[] = [];
|
|
28
28
|
|
|
@@ -49,11 +49,11 @@ export async function processFileArguments(fileArgs: string[], options?: Process
|
|
|
49
49
|
|
|
50
50
|
if (mimeType) {
|
|
51
51
|
// Handle image file
|
|
52
|
-
const base64Content = buffer.
|
|
52
|
+
const base64Content = buffer.toBase64();
|
|
53
53
|
let attachment: ImageContent;
|
|
54
54
|
let dimensionNote: string | undefined;
|
|
55
55
|
|
|
56
|
-
if (
|
|
56
|
+
if (autoResizeImages) {
|
|
57
57
|
try {
|
|
58
58
|
const resized = await resizeImage({ type: "image", data: base64Content, mimeType });
|
|
59
59
|
dimensionNote = formatDimensionNote(resized);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom help renderer for the coding agent CLI.
|
|
3
|
+
*/
|
|
4
|
+
import { CommandHelp, Help } from "@oclif/core";
|
|
5
|
+
import { getExtraHelpText } from "./args";
|
|
6
|
+
|
|
7
|
+
export default class OclifHelp extends Help {
|
|
8
|
+
protected async showRootHelp(): Promise<void> {
|
|
9
|
+
await super.showRootHelp();
|
|
10
|
+
const rootCommand = this.config.findCommand("index");
|
|
11
|
+
if (rootCommand) {
|
|
12
|
+
const rootHelp = new CommandHelp(rootCommand, this.config, {
|
|
13
|
+
...this.opts,
|
|
14
|
+
sections: ["arguments", "flags", "examples"],
|
|
15
|
+
});
|
|
16
|
+
const output = rootHelp.generate();
|
|
17
|
+
if (output.trim().length > 0) {
|
|
18
|
+
process.stdout.write(`\n${output}\n`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const extra = getExtraHelpText();
|
|
22
|
+
if (extra.trim().length > 0) {
|
|
23
|
+
process.stdout.write(`\n${extra}\n`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web search CLI command handlers.
|
|
3
|
+
*
|
|
4
|
+
* Handles `omp q`/`omp web-search` subcommands for testing web search providers.
|
|
5
|
+
*/
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { APP_NAME } from "../config";
|
|
8
|
+
import { initTheme, theme } from "../modes/theme/theme";
|
|
9
|
+
import { runSearchQuery, type SearchParams } from "../web/search/index";
|
|
10
|
+
import { renderSearchResult } from "../web/search/render";
|
|
11
|
+
import type { SearchProviderId } from "../web/search/types";
|
|
12
|
+
|
|
13
|
+
export interface SearchCommandArgs {
|
|
14
|
+
query: string;
|
|
15
|
+
provider?: SearchProviderId | "auto";
|
|
16
|
+
recency?: "day" | "week" | "month" | "year";
|
|
17
|
+
limit?: number;
|
|
18
|
+
expanded: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const PROVIDERS: Array<SearchProviderId | "auto"> = [
|
|
22
|
+
"auto",
|
|
23
|
+
"anthropic",
|
|
24
|
+
"perplexity",
|
|
25
|
+
"exa",
|
|
26
|
+
"jina",
|
|
27
|
+
"gemini",
|
|
28
|
+
"codex",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const RECENCY_OPTIONS: SearchCommandArgs["recency"][] = ["day", "week", "month", "year"];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parse web search subcommand arguments.
|
|
35
|
+
* Returns undefined if not a web search command.
|
|
36
|
+
*/
|
|
37
|
+
export function parseSearchArgs(args: string[]): SearchCommandArgs | undefined {
|
|
38
|
+
if (args.length === 0 || (args[0] !== "q" && args[0] !== "web-search")) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result: SearchCommandArgs = {
|
|
43
|
+
query: "",
|
|
44
|
+
expanded: true,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const positional: string[] = [];
|
|
48
|
+
|
|
49
|
+
for (let i = 1; i < args.length; i++) {
|
|
50
|
+
const arg = args[i];
|
|
51
|
+
if (arg === "--provider") {
|
|
52
|
+
result.provider = args[++i] as SearchCommandArgs["provider"];
|
|
53
|
+
} else if (arg === "--recency") {
|
|
54
|
+
result.recency = args[++i] as SearchCommandArgs["recency"];
|
|
55
|
+
} else if (arg === "--limit" || arg === "-l") {
|
|
56
|
+
result.limit = Number.parseInt(args[++i], 10);
|
|
57
|
+
} else if (arg === "--compact") {
|
|
58
|
+
result.expanded = false;
|
|
59
|
+
} else if (!arg.startsWith("-")) {
|
|
60
|
+
positional.push(arg);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (positional.length > 0) {
|
|
65
|
+
result.query = positional.join(" ");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
|
|
72
|
+
if (!cmd.query) {
|
|
73
|
+
writeStderr(chalk.red("Error: Query is required"));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (cmd.provider && !PROVIDERS.includes(cmd.provider)) {
|
|
78
|
+
writeStderr(chalk.red(`Error: Unknown provider "${cmd.provider}"`));
|
|
79
|
+
writeStderr(chalk.dim(`Valid providers: ${PROVIDERS.join(", ")}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (cmd.recency && !RECENCY_OPTIONS.includes(cmd.recency)) {
|
|
84
|
+
writeStderr(chalk.red(`Error: Invalid recency "${cmd.recency}"`));
|
|
85
|
+
writeStderr(chalk.dim(`Valid recency values: ${RECENCY_OPTIONS.join(", ")}`));
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (cmd.limit !== undefined && Number.isNaN(cmd.limit)) {
|
|
90
|
+
writeStderr(chalk.red("Error: --limit must be a number"));
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await initTheme();
|
|
95
|
+
|
|
96
|
+
const params: SearchParams = {
|
|
97
|
+
query: cmd.query,
|
|
98
|
+
provider: cmd.provider,
|
|
99
|
+
recency: cmd.recency,
|
|
100
|
+
limit: cmd.limit,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const result = await runSearchQuery(params);
|
|
104
|
+
const component = renderSearchResult(result, { expanded: cmd.expanded, isPartial: false }, theme, {
|
|
105
|
+
query: cmd.query,
|
|
106
|
+
provider: cmd.provider,
|
|
107
|
+
allowLongAnswer: true,
|
|
108
|
+
maxAnswerLines: cmd.expanded ? undefined : 6,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const width = Math.max(60, process.stdout.columns ?? 100);
|
|
112
|
+
writeStdout(component.render(width).join("\n"));
|
|
113
|
+
|
|
114
|
+
if (result.details?.error) {
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function printSearchHelp(): void {
|
|
120
|
+
writeStdout(`${chalk.bold(`${APP_NAME} q`)} - Test web search providers
|
|
121
|
+
|
|
122
|
+
${chalk.bold("Usage:")}
|
|
123
|
+
${APP_NAME} q [options] <query>
|
|
124
|
+
${APP_NAME} web-search [options] <query>
|
|
125
|
+
|
|
126
|
+
${chalk.bold("Arguments:")}
|
|
127
|
+
query Search query text
|
|
128
|
+
|
|
129
|
+
${chalk.bold("Options:")}
|
|
130
|
+
--provider <name> Provider: ${PROVIDERS.join(", ")}
|
|
131
|
+
--recency <value> Recency filter (Perplexity only): ${RECENCY_OPTIONS.join(", ")}
|
|
132
|
+
-l, --limit <n> Max results to return
|
|
133
|
+
--compact Render condensed output
|
|
134
|
+
-h, --help Show this help
|
|
135
|
+
|
|
136
|
+
${chalk.bold("Examples:")}
|
|
137
|
+
${APP_NAME} q --provider=exa "what's the color of the sky"
|
|
138
|
+
${APP_NAME} q --provider=perplexity --recency=week "latest TypeScript 5.7 changes"
|
|
139
|
+
`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function writeStdout(message: string): void {
|
|
143
|
+
process.stdout.write(`${message}\n`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function writeStderr(message: string): void {
|
|
147
|
+
process.stderr.write(`${message}\n`);
|
|
148
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Test with: npx tsx src/cli-new.ts [args...]
|
|
7
7
|
*/
|
|
8
|
+
import { run } from "@oclif/core";
|
|
8
9
|
import { APP_NAME } from "./config";
|
|
9
|
-
import { main } from "./main";
|
|
10
10
|
|
|
11
11
|
process.title = APP_NAME;
|
|
12
|
-
|
|
12
|
+
const argv = process.argv.slice(2);
|
|
13
|
+
const runArgv = argv.length === 0 ? ["index"] : argv;
|
|
14
|
+
run(runArgv, import.meta.url).catch((error: unknown) => {
|
|
15
|
+
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
|
16
|
+
process.stderr.write(`${message}\n`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate and optionally push a commit with changelog updates.
|
|
3
|
+
*/
|
|
4
|
+
import { Command, Flags } from "@oclif/core";
|
|
5
|
+
import { runCommitCommand } from "../commit";
|
|
6
|
+
import type { CommitCommandArgs } from "../commit/types";
|
|
7
|
+
import { initTheme } from "../modes/theme/theme";
|
|
8
|
+
|
|
9
|
+
export default class Commit extends Command {
|
|
10
|
+
static description = "Generate a commit message and update changelogs";
|
|
11
|
+
|
|
12
|
+
static flags = {
|
|
13
|
+
push: Flags.boolean({ description: "Push after committing" }),
|
|
14
|
+
"dry-run": Flags.boolean({ description: "Preview without committing" }),
|
|
15
|
+
"no-changelog": Flags.boolean({ description: "Skip changelog updates" }),
|
|
16
|
+
legacy: Flags.boolean({ description: "Use legacy deterministic pipeline" }),
|
|
17
|
+
context: Flags.string({ char: "c", description: "Additional context for the model" }),
|
|
18
|
+
model: Flags.string({ char: "m", description: "Override model selection" }),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async run(): Promise<void> {
|
|
22
|
+
const { flags } = await this.parse(Commit);
|
|
23
|
+
|
|
24
|
+
const cmd: CommitCommandArgs = {
|
|
25
|
+
push: flags.push ?? false,
|
|
26
|
+
dryRun: flags["dry-run"] ?? false,
|
|
27
|
+
noChangelog: flags["no-changelog"] ?? false,
|
|
28
|
+
legacy: flags.legacy,
|
|
29
|
+
context: flags.context,
|
|
30
|
+
model: flags.model,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
await initTheme();
|
|
34
|
+
await runCommitCommand(cmd);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manage configuration settings.
|
|
3
|
+
*/
|
|
4
|
+
import { Args, Command, Flags } from "@oclif/core";
|
|
5
|
+
import { type ConfigAction, type ConfigCommandArgs, runConfigCommand } from "../cli/config-cli";
|
|
6
|
+
import { initTheme } from "../modes/theme/theme";
|
|
7
|
+
|
|
8
|
+
const ACTIONS: ConfigAction[] = ["list", "get", "set", "reset", "path"];
|
|
9
|
+
|
|
10
|
+
export default class Config extends Command {
|
|
11
|
+
static description = "Manage configuration settings";
|
|
12
|
+
|
|
13
|
+
static args = {
|
|
14
|
+
action: Args.string({
|
|
15
|
+
description: "Config action",
|
|
16
|
+
required: false,
|
|
17
|
+
options: ACTIONS,
|
|
18
|
+
}),
|
|
19
|
+
key: Args.string({
|
|
20
|
+
description: "Setting key",
|
|
21
|
+
required: false,
|
|
22
|
+
}),
|
|
23
|
+
value: Args.string({
|
|
24
|
+
description: "Value (for set/reset)",
|
|
25
|
+
required: false,
|
|
26
|
+
multiple: true,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
static flags = {
|
|
31
|
+
json: Flags.boolean({ description: "Output JSON" }),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
async run(): Promise<void> {
|
|
35
|
+
const { args, flags } = await this.parse(Config);
|
|
36
|
+
const action = (args.action ?? "list") as ConfigAction;
|
|
37
|
+
const value = Array.isArray(args.value) ? args.value.join(" ") : args.value;
|
|
38
|
+
|
|
39
|
+
const cmd: ConfigCommandArgs = {
|
|
40
|
+
action,
|
|
41
|
+
key: args.key,
|
|
42
|
+
value,
|
|
43
|
+
flags: {
|
|
44
|
+
json: flags.json,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
await initTheme();
|
|
49
|
+
await runConfigCommand(cmd);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test grep tool.
|
|
3
|
+
*/
|
|
4
|
+
import { Args, Command, Flags } from "@oclif/core";
|
|
5
|
+
import { type GrepCommandArgs, runGrepCommand } from "../cli/grep-cli";
|
|
6
|
+
import { initTheme } from "../modes/theme/theme";
|
|
7
|
+
|
|
8
|
+
export default class Grep extends Command {
|
|
9
|
+
static description = "Test grep tool";
|
|
10
|
+
|
|
11
|
+
static args = {
|
|
12
|
+
pattern: Args.string({ description: "Regex pattern to search for", required: false }),
|
|
13
|
+
path: Args.string({ description: "Directory or file to search", required: false }),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
static flags = {
|
|
17
|
+
glob: Flags.string({ char: "g", description: "Filter files by glob pattern" }),
|
|
18
|
+
limit: Flags.integer({ char: "l", description: "Max matches", default: 20 }),
|
|
19
|
+
context: Flags.integer({ char: "C", description: "Context lines", default: 2 }),
|
|
20
|
+
files: Flags.boolean({ char: "f", description: "Output file names only" }),
|
|
21
|
+
count: Flags.boolean({ char: "c", description: "Output match counts per file" }),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async run(): Promise<void> {
|
|
25
|
+
const { args, flags } = await this.parse(Grep);
|
|
26
|
+
|
|
27
|
+
const mode: GrepCommandArgs["mode"] = flags.count ? "count" : flags.files ? "filesWithMatches" : "content";
|
|
28
|
+
|
|
29
|
+
const cmd: GrepCommandArgs = {
|
|
30
|
+
pattern: args.pattern ?? "",
|
|
31
|
+
path: args.path ?? ".",
|
|
32
|
+
glob: flags.glob,
|
|
33
|
+
limit: flags.limit,
|
|
34
|
+
context: flags.context,
|
|
35
|
+
mode,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
await initTheme();
|
|
39
|
+
await runGrepCommand(cmd);
|
|
40
|
+
}
|
|
41
|
+
}
|