@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,154 @@
|
|
|
1
|
+
# SDK Examples
|
|
2
|
+
|
|
3
|
+
Programmatic usage of pi-coding-agent via `createAgentSession()`.
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
| File | Description |
|
|
8
|
+
| -------------------------- | ---------------------------------------------- |
|
|
9
|
+
| `01-minimal.ts` | Simplest usage with all defaults |
|
|
10
|
+
| `02-custom-model.ts` | Select model and thinking level |
|
|
11
|
+
| `03-custom-prompt.ts` | Replace or modify system prompt |
|
|
12
|
+
| `04-skills.ts` | Discover, filter, or replace skills |
|
|
13
|
+
| `05-tools.ts` | Built-in tools, custom tools |
|
|
14
|
+
| `06-hooks.ts` | Logging, blocking, result modification |
|
|
15
|
+
| `07-context-files.ts` | AGENTS.md context files |
|
|
16
|
+
| `08-slash-commands.ts` | File-based slash commands |
|
|
17
|
+
| `09-api-keys-and-oauth.ts` | API key resolution, OAuth config |
|
|
18
|
+
| `10-settings.ts` | Override compaction, retry, terminal settings |
|
|
19
|
+
| `11-sessions.ts` | In-memory, persistent, continue, list sessions |
|
|
20
|
+
| `12-full-control.ts` | Replace everything, no discovery |
|
|
21
|
+
|
|
22
|
+
## Running
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd packages/coding-agent
|
|
26
|
+
npx tsx examples/sdk/01-minimal.ts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Reference
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { getModel } from "@oh-my-pi/pi-ai";
|
|
33
|
+
import {
|
|
34
|
+
AuthStorage,
|
|
35
|
+
createAgentSession,
|
|
36
|
+
discoverAuthStorage,
|
|
37
|
+
discoverModels,
|
|
38
|
+
discoverSkills,
|
|
39
|
+
discoverHooks,
|
|
40
|
+
discoverCustomTools,
|
|
41
|
+
discoverContextFiles,
|
|
42
|
+
discoverSlashCommands,
|
|
43
|
+
loadSettings,
|
|
44
|
+
buildSystemPrompt,
|
|
45
|
+
ModelRegistry,
|
|
46
|
+
SessionManager,
|
|
47
|
+
codingTools,
|
|
48
|
+
readOnlyTools,
|
|
49
|
+
readTool,
|
|
50
|
+
bashTool,
|
|
51
|
+
editTool,
|
|
52
|
+
writeTool,
|
|
53
|
+
} from "@oh-my-pi/pi-coding-agent";
|
|
54
|
+
|
|
55
|
+
// Auth and models setup
|
|
56
|
+
const authStorage = discoverAuthStorage();
|
|
57
|
+
const modelRegistry = discoverModels(authStorage);
|
|
58
|
+
|
|
59
|
+
// Minimal
|
|
60
|
+
const { session } = await createAgentSession({ authStorage, modelRegistry });
|
|
61
|
+
|
|
62
|
+
// Custom model
|
|
63
|
+
const model = getModel("anthropic", "claude-opus-4-5");
|
|
64
|
+
const { session } = await createAgentSession({ model, thinkingLevel: "high", authStorage, modelRegistry });
|
|
65
|
+
|
|
66
|
+
// Modify prompt
|
|
67
|
+
const { session } = await createAgentSession({
|
|
68
|
+
systemPrompt: (defaultPrompt) => defaultPrompt + "\n\nBe concise.",
|
|
69
|
+
authStorage,
|
|
70
|
+
modelRegistry,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Read-only
|
|
74
|
+
const { session } = await createAgentSession({ tools: readOnlyTools, authStorage, modelRegistry });
|
|
75
|
+
|
|
76
|
+
// In-memory
|
|
77
|
+
const { session } = await createAgentSession({
|
|
78
|
+
sessionManager: SessionManager.inMemory(),
|
|
79
|
+
authStorage,
|
|
80
|
+
modelRegistry,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Full control
|
|
84
|
+
const customAuth = new AuthStorage("/my/app/auth.json");
|
|
85
|
+
customAuth.setRuntimeApiKey("anthropic", process.env.MY_KEY!);
|
|
86
|
+
const customRegistry = new ModelRegistry(customAuth);
|
|
87
|
+
|
|
88
|
+
const { session } = await createAgentSession({
|
|
89
|
+
model,
|
|
90
|
+
authStorage: customAuth,
|
|
91
|
+
modelRegistry: customRegistry,
|
|
92
|
+
systemPrompt: "You are helpful.",
|
|
93
|
+
tools: [readTool, bashTool],
|
|
94
|
+
customTools: [{ tool: myTool }],
|
|
95
|
+
hooks: [{ factory: myHook }],
|
|
96
|
+
skills: [],
|
|
97
|
+
contextFiles: [],
|
|
98
|
+
slashCommands: [],
|
|
99
|
+
sessionManager: SessionManager.inMemory(),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Run prompts
|
|
103
|
+
session.subscribe((event) => {
|
|
104
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
105
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
await session.prompt("Hello");
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Options
|
|
112
|
+
|
|
113
|
+
| Option | Default | Description |
|
|
114
|
+
| --------------------------- | ----------------------------- | --------------------------------- |
|
|
115
|
+
| `authStorage` | `discoverAuthStorage()` | Credential storage |
|
|
116
|
+
| `modelRegistry` | `discoverModels(authStorage)` | Model registry |
|
|
117
|
+
| `cwd` | `process.cwd()` | Working directory |
|
|
118
|
+
| `agentDir` | `~/.pi/agent` | Config directory |
|
|
119
|
+
| `model` | From settings/first available | Model to use |
|
|
120
|
+
| `thinkingLevel` | From settings/"off" | off, low, medium, high |
|
|
121
|
+
| `systemPrompt` | Discovered | String or `(default) => modified` |
|
|
122
|
+
| `tools` | `codingTools` | Built-in tools |
|
|
123
|
+
| `customTools` | Discovered | Replaces discovery |
|
|
124
|
+
| `additionalCustomToolPaths` | `[]` | Merge with discovery |
|
|
125
|
+
| `hooks` | Discovered | Replaces discovery |
|
|
126
|
+
| `additionalHookPaths` | `[]` | Merge with discovery |
|
|
127
|
+
| `skills` | Discovered | Skills for prompt |
|
|
128
|
+
| `contextFiles` | Discovered | AGENTS.md files |
|
|
129
|
+
| `slashCommands` | Discovered | File commands |
|
|
130
|
+
| `sessionManager` | `SessionManager.create(cwd)` | Persistence |
|
|
131
|
+
| `settingsManager` | From agentDir | Settings overrides |
|
|
132
|
+
|
|
133
|
+
## Events
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
session.subscribe((event) => {
|
|
137
|
+
switch (event.type) {
|
|
138
|
+
case "message_update":
|
|
139
|
+
if (event.assistantMessageEvent.type === "text_delta") {
|
|
140
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case "tool_execution_start":
|
|
144
|
+
console.log(`Tool: ${event.toolName}`);
|
|
145
|
+
break;
|
|
146
|
+
case "tool_execution_end":
|
|
147
|
+
console.log(`Result: ${event.result}`);
|
|
148
|
+
break;
|
|
149
|
+
case "agent_end":
|
|
150
|
+
console.log("Done");
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
+
"version": "1.337.0",
|
|
4
|
+
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"piConfig": {
|
|
7
|
+
"name": "pi",
|
|
8
|
+
"configDir": ".pi"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"pi": "src/cli.ts"
|
|
12
|
+
},
|
|
13
|
+
"main": "./src/index.ts",
|
|
14
|
+
"types": "./src/index.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./src/index.ts",
|
|
18
|
+
"import": "./src/index.ts"
|
|
19
|
+
},
|
|
20
|
+
"./hooks": {
|
|
21
|
+
"types": "./src/core/hooks/index.ts",
|
|
22
|
+
"import": "./src/core/hooks/index.ts"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"src",
|
|
27
|
+
"docs",
|
|
28
|
+
"examples",
|
|
29
|
+
"CHANGELOG.md"
|
|
30
|
+
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"check": "tsgo --noEmit",
|
|
33
|
+
"clean": "rm -rf dist",
|
|
34
|
+
"build": "tsgo -p tsconfig.build.json && chmod +x dist/cli.js && npm run copy-assets",
|
|
35
|
+
"build:binary": "npm run build && bun build --compile ./dist/cli.js --outfile dist/pi && npm run copy-binary-assets",
|
|
36
|
+
"copy-assets": "mkdir -p dist/modes/interactive/theme && cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && mkdir -p dist/core/export-html/vendor && cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
|
|
37
|
+
"copy-binary-assets": "cp package.json dist/ && cp README.md dist/ && cp CHANGELOG.md dist/ && mkdir -p dist/theme && cp src/modes/interactive/theme/*.json dist/theme/ && mkdir -p dist/export-html/vendor && cp src/core/export-html/template.html dist/export-html/ && cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && cp -r docs dist/ && cp -r examples dist/",
|
|
38
|
+
"test": "vitest --run",
|
|
39
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@oh-my-pi/pi-agent-core": "workspace:*",
|
|
43
|
+
"@oh-my-pi/pi-ai": "workspace:*",
|
|
44
|
+
"@oh-my-pi/pi-tui": "workspace:*",
|
|
45
|
+
"@sinclair/typebox": "^0.34.46",
|
|
46
|
+
"ajv": "^8.17.1",
|
|
47
|
+
"chalk": "^5.5.0",
|
|
48
|
+
"node-html-parser": "^6.1.13",
|
|
49
|
+
"cli-highlight": "^2.1.11",
|
|
50
|
+
"diff": "^8.0.2",
|
|
51
|
+
"file-type": "^21.1.1",
|
|
52
|
+
"glob": "^11.0.3",
|
|
53
|
+
"highlight.js": "^11.11.1",
|
|
54
|
+
"marked": "^15.0.12",
|
|
55
|
+
"minimatch": "^10.1.1",
|
|
56
|
+
"strip-ansi": "^7.1.2"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/diff": "^7.0.2",
|
|
60
|
+
"@types/node": "^24.3.0",
|
|
61
|
+
"vitest": "^3.2.4"
|
|
62
|
+
},
|
|
63
|
+
"keywords": [
|
|
64
|
+
"coding-agent",
|
|
65
|
+
"ai",
|
|
66
|
+
"llm",
|
|
67
|
+
"cli",
|
|
68
|
+
"tui",
|
|
69
|
+
"agent"
|
|
70
|
+
],
|
|
71
|
+
"author": "Mario Zechner",
|
|
72
|
+
"license": "MIT",
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "git+https://github.com/can1357/oh-my-pi.git",
|
|
76
|
+
"directory": "packages/coding-agent"
|
|
77
|
+
},
|
|
78
|
+
"engines": {
|
|
79
|
+
"bun": ">=1.0.0"
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/cli/args.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
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.js";
|
|
8
|
+
import { allTools, type ToolName } from "../core/tools/index.js";
|
|
9
|
+
|
|
10
|
+
export type Mode = "text" | "json" | "rpc";
|
|
11
|
+
|
|
12
|
+
export interface Args {
|
|
13
|
+
provider?: string;
|
|
14
|
+
model?: string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
systemPrompt?: string;
|
|
17
|
+
appendSystemPrompt?: string;
|
|
18
|
+
thinking?: ThinkingLevel;
|
|
19
|
+
continue?: boolean;
|
|
20
|
+
resume?: boolean;
|
|
21
|
+
help?: boolean;
|
|
22
|
+
version?: boolean;
|
|
23
|
+
mode?: Mode;
|
|
24
|
+
noSession?: boolean;
|
|
25
|
+
session?: string;
|
|
26
|
+
sessionDir?: string;
|
|
27
|
+
models?: string[];
|
|
28
|
+
tools?: ToolName[];
|
|
29
|
+
hooks?: string[];
|
|
30
|
+
customTools?: string[];
|
|
31
|
+
print?: boolean;
|
|
32
|
+
export?: string;
|
|
33
|
+
noSkills?: boolean;
|
|
34
|
+
skills?: string[];
|
|
35
|
+
listModels?: string | true;
|
|
36
|
+
messages: string[];
|
|
37
|
+
fileArgs: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
|
|
41
|
+
|
|
42
|
+
export function isValidThinkingLevel(level: string): level is ThinkingLevel {
|
|
43
|
+
return VALID_THINKING_LEVELS.includes(level as ThinkingLevel);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function parseArgs(args: string[]): Args {
|
|
47
|
+
const result: Args = {
|
|
48
|
+
messages: [],
|
|
49
|
+
fileArgs: [],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < args.length; i++) {
|
|
53
|
+
const arg = args[i];
|
|
54
|
+
|
|
55
|
+
if (arg === "--help" || arg === "-h") {
|
|
56
|
+
result.help = true;
|
|
57
|
+
} else if (arg === "--version" || arg === "-v") {
|
|
58
|
+
result.version = true;
|
|
59
|
+
} else if (arg === "--mode" && i + 1 < args.length) {
|
|
60
|
+
const mode = args[++i];
|
|
61
|
+
if (mode === "text" || mode === "json" || mode === "rpc") {
|
|
62
|
+
result.mode = mode;
|
|
63
|
+
}
|
|
64
|
+
} else if (arg === "--continue" || arg === "-c") {
|
|
65
|
+
result.continue = true;
|
|
66
|
+
} else if (arg === "--resume" || arg === "-r") {
|
|
67
|
+
result.resume = true;
|
|
68
|
+
} else if (arg === "--provider" && i + 1 < args.length) {
|
|
69
|
+
result.provider = args[++i];
|
|
70
|
+
} else if (arg === "--model" && i + 1 < args.length) {
|
|
71
|
+
result.model = args[++i];
|
|
72
|
+
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
73
|
+
result.apiKey = args[++i];
|
|
74
|
+
} else if (arg === "--system-prompt" && i + 1 < args.length) {
|
|
75
|
+
result.systemPrompt = args[++i];
|
|
76
|
+
} else if (arg === "--append-system-prompt" && i + 1 < args.length) {
|
|
77
|
+
result.appendSystemPrompt = args[++i];
|
|
78
|
+
} else if (arg === "--no-session") {
|
|
79
|
+
result.noSession = true;
|
|
80
|
+
} else if (arg === "--session" && i + 1 < args.length) {
|
|
81
|
+
result.session = args[++i];
|
|
82
|
+
} else if (arg === "--session-dir" && i + 1 < args.length) {
|
|
83
|
+
result.sessionDir = args[++i];
|
|
84
|
+
} else if (arg === "--models" && i + 1 < args.length) {
|
|
85
|
+
result.models = args[++i].split(",").map((s) => s.trim());
|
|
86
|
+
} else if (arg === "--tools" && i + 1 < args.length) {
|
|
87
|
+
const toolNames = args[++i].split(",").map((s) => s.trim());
|
|
88
|
+
const validTools: ToolName[] = [];
|
|
89
|
+
for (const name of toolNames) {
|
|
90
|
+
if (name in allTools) {
|
|
91
|
+
validTools.push(name as ToolName);
|
|
92
|
+
} else {
|
|
93
|
+
console.error(
|
|
94
|
+
chalk.yellow(`Warning: Unknown tool "${name}". Valid tools: ${Object.keys(allTools).join(", ")}`),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
result.tools = validTools;
|
|
99
|
+
} else if (arg === "--thinking" && i + 1 < args.length) {
|
|
100
|
+
const level = args[++i];
|
|
101
|
+
if (isValidThinkingLevel(level)) {
|
|
102
|
+
result.thinking = level;
|
|
103
|
+
} else {
|
|
104
|
+
console.error(
|
|
105
|
+
chalk.yellow(
|
|
106
|
+
`Warning: Invalid thinking level "${level}". Valid values: ${VALID_THINKING_LEVELS.join(", ")}`,
|
|
107
|
+
),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
} else if (arg === "--print" || arg === "-p") {
|
|
111
|
+
result.print = true;
|
|
112
|
+
} else if (arg === "--export" && i + 1 < args.length) {
|
|
113
|
+
result.export = args[++i];
|
|
114
|
+
} else if (arg === "--hook" && i + 1 < args.length) {
|
|
115
|
+
result.hooks = result.hooks ?? [];
|
|
116
|
+
result.hooks.push(args[++i]);
|
|
117
|
+
} else if (arg === "--tool" && i + 1 < args.length) {
|
|
118
|
+
result.customTools = result.customTools ?? [];
|
|
119
|
+
result.customTools.push(args[++i]);
|
|
120
|
+
} else if (arg === "--no-skills") {
|
|
121
|
+
result.noSkills = true;
|
|
122
|
+
} else if (arg === "--skills" && i + 1 < args.length) {
|
|
123
|
+
// Comma-separated glob patterns for skill filtering
|
|
124
|
+
result.skills = args[++i].split(",").map((s) => s.trim());
|
|
125
|
+
} else if (arg === "--list-models") {
|
|
126
|
+
// Check if next arg is a search pattern (not a flag or file arg)
|
|
127
|
+
if (i + 1 < args.length && !args[i + 1].startsWith("-") && !args[i + 1].startsWith("@")) {
|
|
128
|
+
result.listModels = args[++i];
|
|
129
|
+
} else {
|
|
130
|
+
result.listModels = true;
|
|
131
|
+
}
|
|
132
|
+
} else if (arg.startsWith("@")) {
|
|
133
|
+
result.fileArgs.push(arg.slice(1)); // Remove @ prefix
|
|
134
|
+
} else if (!arg.startsWith("-")) {
|
|
135
|
+
result.messages.push(arg);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function printHelp(): void {
|
|
143
|
+
console.log(`${chalk.bold(APP_NAME)} - AI coding assistant with read, bash, edit, write tools
|
|
144
|
+
|
|
145
|
+
${chalk.bold("Usage:")}
|
|
146
|
+
${APP_NAME} [options] [@files...] [messages...]
|
|
147
|
+
|
|
148
|
+
${chalk.bold("Options:")}
|
|
149
|
+
--provider <name> Provider name (default: google)
|
|
150
|
+
--model <id> Model ID (default: gemini-2.5-flash)
|
|
151
|
+
--api-key <key> API key (defaults to env vars)
|
|
152
|
+
--system-prompt <text> System prompt (default: coding assistant prompt)
|
|
153
|
+
--append-system-prompt <text> Append text or file contents to the system prompt
|
|
154
|
+
--mode <mode> Output mode: text (default), json, or rpc
|
|
155
|
+
--print, -p Non-interactive mode: process prompt and exit
|
|
156
|
+
--continue, -c Continue previous session
|
|
157
|
+
--resume, -r Select a session to resume
|
|
158
|
+
--session <path> Use specific session file
|
|
159
|
+
--session-dir <dir> Directory for session storage and lookup
|
|
160
|
+
--no-session Don't save session (ephemeral)
|
|
161
|
+
--models <patterns> Comma-separated model patterns for Ctrl+P cycling
|
|
162
|
+
Supports globs (anthropic/*, *sonnet*) and fuzzy matching
|
|
163
|
+
--tools <tools> Comma-separated list of tools to enable (default: read,bash,edit,write)
|
|
164
|
+
Available: read, bash, edit, write, grep, find, ls
|
|
165
|
+
--thinking <level> Set thinking level: off, minimal, low, medium, high, xhigh
|
|
166
|
+
--hook <path> Load a hook file (can be used multiple times)
|
|
167
|
+
--tool <path> Load a custom tool file (can be used multiple times)
|
|
168
|
+
--no-skills Disable skills discovery and loading
|
|
169
|
+
--skills <patterns> Comma-separated glob patterns to filter skills (e.g., git-*,docker)
|
|
170
|
+
--export <file> Export session file to HTML and exit
|
|
171
|
+
--list-models [search] List available models (with optional fuzzy search)
|
|
172
|
+
--help, -h Show this help
|
|
173
|
+
--version, -v Show version number
|
|
174
|
+
|
|
175
|
+
${chalk.bold("Examples:")}
|
|
176
|
+
# Interactive mode
|
|
177
|
+
${APP_NAME}
|
|
178
|
+
|
|
179
|
+
# Interactive mode with initial prompt
|
|
180
|
+
${APP_NAME} "List all .ts files in src/"
|
|
181
|
+
|
|
182
|
+
# Include files in initial message
|
|
183
|
+
${APP_NAME} @prompt.md @image.png "What color is the sky?"
|
|
184
|
+
|
|
185
|
+
# Non-interactive mode (process and exit)
|
|
186
|
+
${APP_NAME} -p "List all .ts files in src/"
|
|
187
|
+
|
|
188
|
+
# Multiple messages (interactive)
|
|
189
|
+
${APP_NAME} "Read package.json" "What dependencies do we have?"
|
|
190
|
+
|
|
191
|
+
# Continue previous session
|
|
192
|
+
${APP_NAME} --continue "What did we discuss?"
|
|
193
|
+
|
|
194
|
+
# Use different model
|
|
195
|
+
${APP_NAME} --provider openai --model gpt-4o-mini "Help me refactor this code"
|
|
196
|
+
|
|
197
|
+
# Limit model cycling to specific models
|
|
198
|
+
${APP_NAME} --models claude-sonnet,claude-haiku,gpt-4o
|
|
199
|
+
|
|
200
|
+
# Limit to a specific provider with glob pattern
|
|
201
|
+
${APP_NAME} --models "github-copilot/*"
|
|
202
|
+
|
|
203
|
+
# Cycle models with fixed thinking levels
|
|
204
|
+
${APP_NAME} --models sonnet:high,haiku:low
|
|
205
|
+
|
|
206
|
+
# Start with a specific thinking level
|
|
207
|
+
${APP_NAME} --thinking high "Solve this complex problem"
|
|
208
|
+
|
|
209
|
+
# Read-only mode (no file modifications possible)
|
|
210
|
+
${APP_NAME} --tools read,grep,find,ls -p "Review the code in src/"
|
|
211
|
+
|
|
212
|
+
# Export a session file to HTML
|
|
213
|
+
${APP_NAME} --export ~/${CONFIG_DIR_NAME}/agent/sessions/--path--/session.jsonl
|
|
214
|
+
${APP_NAME} --export session.jsonl output.html
|
|
215
|
+
|
|
216
|
+
${chalk.bold("Environment Variables:")}
|
|
217
|
+
${chalk.dim("# Model providers")}
|
|
218
|
+
ANTHROPIC_API_KEY - Anthropic Claude API key
|
|
219
|
+
ANTHROPIC_OAUTH_TOKEN - Anthropic OAuth token (alternative to API key)
|
|
220
|
+
OPENAI_API_KEY - OpenAI GPT API key
|
|
221
|
+
GEMINI_API_KEY - Google Gemini API key
|
|
222
|
+
GROQ_API_KEY - Groq API key
|
|
223
|
+
CEREBRAS_API_KEY - Cerebras API key
|
|
224
|
+
XAI_API_KEY - xAI Grok API key
|
|
225
|
+
OPENROUTER_API_KEY - OpenRouter API key
|
|
226
|
+
MISTRAL_API_KEY - Mistral API key
|
|
227
|
+
ZAI_API_KEY - ZAI API key
|
|
228
|
+
GITHUB_TOKEN - GitHub Copilot models (or GH_TOKEN, COPILOT_GITHUB_TOKEN)
|
|
229
|
+
|
|
230
|
+
${chalk.dim("# Web search providers")}
|
|
231
|
+
EXA_API_KEY - Exa search API key
|
|
232
|
+
PERPLEXITY_API_KEY - Perplexity search API key
|
|
233
|
+
|
|
234
|
+
${chalk.dim("# Configuration")}
|
|
235
|
+
${ENV_AGENT_DIR.padEnd(23)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
|
|
236
|
+
|
|
237
|
+
${chalk.bold("Available Tools (default: read, bash, edit, write):")}
|
|
238
|
+
read - Read file contents
|
|
239
|
+
bash - Execute bash commands
|
|
240
|
+
edit - Edit files with find/replace
|
|
241
|
+
write - Write files (creates/overwrites)
|
|
242
|
+
grep - Search file contents (read-only, off by default)
|
|
243
|
+
find - Find files by glob pattern (read-only, off by default)
|
|
244
|
+
ls - List directory contents (read-only, off by default)
|
|
245
|
+
`);
|
|
246
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
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 type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
import { resolveReadPath } from "../core/tools/path-utils.js";
|
|
10
|
+
import { detectSupportedImageMimeTypeFromFile } from "../utils/mime.js";
|
|
11
|
+
|
|
12
|
+
export interface ProcessedFiles {
|
|
13
|
+
text: string;
|
|
14
|
+
images: ImageContent[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Process @file arguments into text content and image attachments */
|
|
18
|
+
export async function processFileArguments(fileArgs: string[]): Promise<ProcessedFiles> {
|
|
19
|
+
let text = "";
|
|
20
|
+
const images: ImageContent[] = [];
|
|
21
|
+
|
|
22
|
+
for (const fileArg of fileArgs) {
|
|
23
|
+
// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)
|
|
24
|
+
const absolutePath = resolve(resolveReadPath(fileArg, process.cwd()));
|
|
25
|
+
|
|
26
|
+
// Check if file exists
|
|
27
|
+
try {
|
|
28
|
+
await access(absolutePath);
|
|
29
|
+
} catch {
|
|
30
|
+
console.error(chalk.red(`Error: File not found: ${absolutePath}`));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check if file is empty
|
|
35
|
+
const stats = await stat(absolutePath);
|
|
36
|
+
if (stats.size === 0) {
|
|
37
|
+
// Skip empty files
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const mimeType = await detectSupportedImageMimeTypeFromFile(absolutePath);
|
|
42
|
+
|
|
43
|
+
if (mimeType) {
|
|
44
|
+
// Handle image file
|
|
45
|
+
const content = await readFile(absolutePath);
|
|
46
|
+
const base64Content = content.toString("base64");
|
|
47
|
+
|
|
48
|
+
const attachment: ImageContent = {
|
|
49
|
+
type: "image",
|
|
50
|
+
mimeType,
|
|
51
|
+
data: base64Content,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
images.push(attachment);
|
|
55
|
+
|
|
56
|
+
// Add text reference to image
|
|
57
|
+
text += `<file name="${absolutePath}"></file>\n`;
|
|
58
|
+
} else {
|
|
59
|
+
// Handle text file
|
|
60
|
+
try {
|
|
61
|
+
const content = await readFile(absolutePath, "utf-8");
|
|
62
|
+
text += `<file name="${absolutePath}">\n${content}\n</file>\n`;
|
|
63
|
+
} catch (error: unknown) {
|
|
64
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
65
|
+
console.error(chalk.red(`Error: Could not read file ${absolutePath}: ${message}`));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { text, images };
|
|
72
|
+
}
|
|
@@ -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.js";
|
|
7
|
+
import { fuzzyFilter } from "../utils/fuzzy.js";
|
|
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
|
+
}
|