@oh-my-pi/pi-coding-agent 12.0.0 → 12.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 +17 -0
- package/README.md +4 -29
- package/docs/fs-scan-cache-architecture.md +50 -0
- package/docs/models.md +234 -0
- package/package.json +7 -11
- package/src/cli/args.ts +1 -1
- package/src/cli/config-cli.ts +2 -1
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/jupyter-cli.ts +2 -1
- package/src/cli/plugin-cli.ts +2 -1
- package/src/cli/setup-cli.ts +117 -22
- package/src/cli/shell-cli.ts +1 -1
- package/src/cli/stats-cli.ts +2 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +2 -1
- package/src/cli.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commit/agentic/index.ts +1 -0
- package/src/commit/pipeline.ts +1 -0
- package/src/config/keybindings.ts +1 -1
- package/src/config/model-registry.ts +210 -11
- package/src/config/model-resolver.ts +3 -3
- package/src/config/prompt-templates.ts +4 -4
- package/src/config/settings.ts +1 -1
- package/src/config.ts +3 -62
- package/src/debug/index.ts +1 -1
- package/src/debug/report-bundle.ts +1 -12
- package/src/debug/system-info.ts +1 -1
- package/src/discovery/claude-plugins.ts +205 -0
- package/src/discovery/helpers.ts +139 -3
- package/src/discovery/index.ts +1 -0
- package/src/export/custom-share.ts +1 -1
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/custom-commands/loader.ts +2 -1
- package/src/extensibility/plugins/index.ts +0 -7
- package/src/extensibility/plugins/installer.ts +1 -1
- package/src/extensibility/plugins/loader.ts +3 -7
- package/src/extensibility/plugins/manager.ts +4 -4
- package/src/index.ts +1 -1
- package/src/ipy/executor.ts +1 -1
- package/src/ipy/gateway-coordinator.ts +1 -1
- package/src/ipy/modules.ts +5 -6
- package/src/ipy/runtime.ts +27 -0
- package/src/main.ts +3 -1
- package/src/mcp/config-writer.ts +1 -13
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/mcp-command-controller.ts +2 -7
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +2 -2
- package/src/modes/theme/theme.ts +1 -1
- package/src/patch/hashline.ts +0 -12
- package/src/patch/index.ts +14 -0
- package/src/sdk.ts +2 -1
- package/src/session/agent-session.ts +1 -1
- package/src/session/agent-storage.ts +1 -1
- package/src/session/auth-storage.ts +2 -2
- package/src/session/history-storage.ts +1 -1
- package/src/session/session-manager.ts +1 -1
- package/src/ssh/connection-manager.ts +3 -4
- package/src/ssh/sshfs-mount.ts +2 -3
- package/src/system-prompt.ts +2 -2
- package/src/task/discovery.ts +14 -1
- package/src/task/executor.ts +1 -0
- package/src/task/worktree.ts +2 -1
- package/src/tools/bash-interactive.ts +33 -1
- package/src/tools/fs-cache-invalidation.ts +28 -0
- package/src/tools/grep.ts +1 -0
- package/src/tools/read.ts +2 -3
- package/src/tools/write.ts +2 -0
- package/src/utils/file-mentions.ts +128 -7
- package/src/utils/tools-manager.ts +1 -1
- package/src/web/search/auth.ts +1 -1
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/gemini.ts +1 -1
- package/src/web/search/providers/perplexity.ts +1 -1
- package/src/extensibility/plugins/paths.ts +0 -37
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [12.1.0] - 2026-02-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Filesystem scan cache invalidation helpers (`invalidateFsScanAfterWrite`, `invalidateFsScanAfterDelete`, `invalidateFsScanAfterRename`) to properly invalidate shared caches after file mutations
|
|
10
|
+
- Named discovery profile for file mention candidates to standardize cache visibility and ignore semantics across callers
|
|
11
|
+
- Comprehensive `models.yml` provider integration guide documenting custom model registration, provider overrides, API adapters, merge behavior, and practical integration patterns for Ollama, vLLM, LM Studio, and proxy endpoints
|
|
12
|
+
- Claude Code marketplace plugin discovery: automatically loads skills, commands, hooks, tools, and agents from `~/.claude/plugins/cache/` based on `installed_plugins.json` registry ([#48](https://github.com/can1357/oh-my-pi/issues/48))
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Moved directory path utilities from `src/config.ts` to `@oh-my-pi/pi-utils/dirs` for shared use across packages
|
|
17
|
+
- Updated imports throughout codebase to use centralized directory path functions from `@oh-my-pi/pi-utils/dirs`
|
|
18
|
+
- Updated interactive bash terminal UI label from 'InteractiveTerm' to 'Console' for clarity
|
|
19
|
+
- Enhanced bash execution environment with comprehensive non-interactive defaults for pagers, editors, and package managers to prevent command blocking and interactive prompts
|
|
20
|
+
- Updated custom models configuration to use `~/.omp/agent/models.yml` (YAML format) while maintaining backward compatibility with legacy `models.json`
|
|
21
|
+
|
|
5
22
|
## [12.0.0] - 2026-02-12
|
|
6
23
|
|
|
7
24
|
### Added
|
package/README.md
CHANGED
|
@@ -447,7 +447,9 @@ The `--system-prompt` CLI flag overrides both files. Use `--append-system-prompt
|
|
|
447
447
|
|
|
448
448
|
### Custom Models and Providers
|
|
449
449
|
|
|
450
|
-
Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.omp/agent/models.json
|
|
450
|
+
Add custom models (Ollama, vLLM, LM Studio, etc.) via `~/.omp/agent/models.yml` (`models.json` is still supported for legacy configs):
|
|
451
|
+
|
|
452
|
+
> See [models.yml provider integration guide](docs/models.md) for full schema, merge behavior, and provider integration patterns.
|
|
451
453
|
|
|
452
454
|
```json
|
|
453
455
|
{
|
|
@@ -750,7 +752,7 @@ export default function (omp: HookAPI) {
|
|
|
750
752
|
content,
|
|
751
753
|
display: true,
|
|
752
754
|
},
|
|
753
|
-
true
|
|
755
|
+
true,
|
|
754
756
|
); // triggerTurn: start agent loop
|
|
755
757
|
}
|
|
756
758
|
});
|
|
@@ -1035,33 +1037,6 @@ Omp is a fork of [Pi](https://github.com/badlogic/pi) by [Mario Zechner](https:/
|
|
|
1035
1037
|
|
|
1036
1038
|
## Development
|
|
1037
1039
|
|
|
1038
|
-
### Forking / Rebranding
|
|
1039
|
-
|
|
1040
|
-
Configure via `package.json`:
|
|
1041
|
-
|
|
1042
|
-
```json
|
|
1043
|
-
{
|
|
1044
|
-
"ompConfig": {
|
|
1045
|
-
"name": "omp",
|
|
1046
|
-
"configDir": ".omp"
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
```
|
|
1050
|
-
|
|
1051
|
-
Change `name`, `configDir`, and `bin` field for your fork. Affects CLI banner, config paths, and environment variable names.
|
|
1052
|
-
|
|
1053
|
-
### Path Resolution
|
|
1054
|
-
|
|
1055
|
-
Three execution modes: npm install, standalone binary, tsx from source.
|
|
1056
|
-
|
|
1057
|
-
**Always use `src/config.ts`** for package assets:
|
|
1058
|
-
|
|
1059
|
-
```typescript
|
|
1060
|
-
import { getPackageDir } from "./config.js";
|
|
1061
|
-
```
|
|
1062
|
-
|
|
1063
|
-
Never use `__dirname` directly for package assets.
|
|
1064
|
-
|
|
1065
1040
|
### Debug Command
|
|
1066
1041
|
|
|
1067
1042
|
`/debug` (hidden) writes rendered lines with ANSI codes to `~/.omp/agent/omp-debug.log` for TUI debugging, as well as the last set of messages that were sent to the LLM.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# FS scan cache architecture
|
|
2
|
+
|
|
3
|
+
This document defines the shared filesystem-scan cache contract used by `pi-natives` discovery/search callers.
|
|
4
|
+
|
|
5
|
+
## Cache key contract
|
|
6
|
+
|
|
7
|
+
Cache entries are keyed by:
|
|
8
|
+
|
|
9
|
+
- `root` (absolute search root path)
|
|
10
|
+
- `include_hidden` (hidden-file visibility)
|
|
11
|
+
- `use_gitignore` (ignore-rule behavior)
|
|
12
|
+
|
|
13
|
+
Callers with different visibility/ignore semantics must use different profiles so they do not share incompatible cache entries.
|
|
14
|
+
|
|
15
|
+
## Freshness and recheck contract
|
|
16
|
+
|
|
17
|
+
`crates/pi-natives/src/fs_cache.rs` owns global policy:
|
|
18
|
+
|
|
19
|
+
- `FS_SCAN_CACHE_TTL_MS` (default `1000`)
|
|
20
|
+
- `FS_SCAN_EMPTY_RECHECK_MS` (default `200`)
|
|
21
|
+
- `FS_SCAN_CACHE_MAX_ENTRIES` (default `16`)
|
|
22
|
+
|
|
23
|
+
`get_or_scan()` returns `cache_age_ms` so callers can decide whether an empty filtered result should trigger `force_rescan()`.
|
|
24
|
+
|
|
25
|
+
Current callers using this contract:
|
|
26
|
+
|
|
27
|
+
- `fd` (`fuzzyFind`) uses empty-result fast recheck.
|
|
28
|
+
- `grep` consumes shared scan entries and applies grep-specific glob/type filtering on top.
|
|
29
|
+
|
|
30
|
+
## Invalidation contract
|
|
31
|
+
|
|
32
|
+
Mutation-triggered invalidation is explicit and path-based via `invalidateFsScanCache`.
|
|
33
|
+
|
|
34
|
+
Coding-agent routes invalidation through `packages/coding-agent/src/tools/fs-cache-invalidation.ts`:
|
|
35
|
+
|
|
36
|
+
- `invalidateFsScanAfterWrite(path)`
|
|
37
|
+
- `invalidateFsScanAfterDelete(path)`
|
|
38
|
+
- `invalidateFsScanAfterRename(oldPath, newPath)` (invalidates both paths)
|
|
39
|
+
|
|
40
|
+
Write/edit flows call these helpers after successful filesystem mutation.
|
|
41
|
+
|
|
42
|
+
## Caller discovery profiles
|
|
43
|
+
|
|
44
|
+
Callers should not build ad-hoc discovery flags inline. Use named profile/policy helpers at callsites.
|
|
45
|
+
|
|
46
|
+
Current profile boundaries:
|
|
47
|
+
|
|
48
|
+
- File mention candidate discovery (`file-mentions.ts`): hidden on, gitignore on, node_modules included.
|
|
49
|
+
- TUI fuzzy `@` discovery (`autocomplete.ts`): hidden on, gitignore on, bounded result count.
|
|
50
|
+
- TUI local path prefix completion keeps a separate per-directory `readdir` cache as an intentional latency fast-path; global fuzzy discovery remains on natives shared scan cache.
|
package/docs/models.md
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# `models.yml` provider integration guide
|
|
2
|
+
|
|
3
|
+
`models.yml` lets you register custom model providers (local or hosted), override built-in providers, and tune model metadata.
|
|
4
|
+
|
|
5
|
+
Default location:
|
|
6
|
+
|
|
7
|
+
- `~/.omp/agent/models.yml`
|
|
8
|
+
|
|
9
|
+
Legacy support:
|
|
10
|
+
|
|
11
|
+
- `models.json` is still read and auto-migrated to `models.yml` when possible.
|
|
12
|
+
|
|
13
|
+
## Top-level shape
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
providers:
|
|
17
|
+
<provider-name>:
|
|
18
|
+
# Provider config
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`<provider-name>` is the provider ID used everywhere else (selection, auth lookup, etc.).
|
|
22
|
+
|
|
23
|
+
## Provider fields
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
providers:
|
|
27
|
+
my-provider:
|
|
28
|
+
baseUrl: https://api.example.com/v1
|
|
29
|
+
apiKey: MY_PROVIDER_API_KEY
|
|
30
|
+
api: openai-responses
|
|
31
|
+
headers:
|
|
32
|
+
X-Custom-Header: value
|
|
33
|
+
authHeader: true
|
|
34
|
+
auth: apiKey
|
|
35
|
+
discovery:
|
|
36
|
+
type: ollama
|
|
37
|
+
modelOverrides:
|
|
38
|
+
<model-id-within-provider>:
|
|
39
|
+
name: Friendly Name
|
|
40
|
+
models:
|
|
41
|
+
- id: model-id
|
|
42
|
+
name: My Model
|
|
43
|
+
api: openai-responses
|
|
44
|
+
reasoning: false
|
|
45
|
+
input: [text]
|
|
46
|
+
cost:
|
|
47
|
+
input: 0
|
|
48
|
+
output: 0
|
|
49
|
+
cacheRead: 0
|
|
50
|
+
cacheWrite: 0
|
|
51
|
+
contextWindow: 128000
|
|
52
|
+
maxTokens: 16384
|
|
53
|
+
headers:
|
|
54
|
+
X-Model-Header: value
|
|
55
|
+
compat:
|
|
56
|
+
supportsStore: true
|
|
57
|
+
supportsDeveloperRole: true
|
|
58
|
+
supportsReasoningEffort: true
|
|
59
|
+
maxTokensField: max_completion_tokens
|
|
60
|
+
openRouterRouting:
|
|
61
|
+
only: [anthropic]
|
|
62
|
+
vercelGatewayRouting:
|
|
63
|
+
order: [openai, anthropic]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `api` values
|
|
67
|
+
|
|
68
|
+
Supported API adapters:
|
|
69
|
+
|
|
70
|
+
- `openai-completions`
|
|
71
|
+
- `openai-responses`
|
|
72
|
+
- `openai-codex-responses`
|
|
73
|
+
- `azure-openai-responses`
|
|
74
|
+
- `anthropic-messages`
|
|
75
|
+
- `google-generative-ai`
|
|
76
|
+
- `google-vertex`
|
|
77
|
+
|
|
78
|
+
`auth` values:
|
|
79
|
+
|
|
80
|
+
- `apiKey` (default)
|
|
81
|
+
- `none`
|
|
82
|
+
|
|
83
|
+
`discovery.type` values:
|
|
84
|
+
|
|
85
|
+
- `ollama`
|
|
86
|
+
|
|
87
|
+
If `discovery` is set, provider-level `api` is required.
|
|
88
|
+
|
|
89
|
+
## Required vs optional
|
|
90
|
+
|
|
91
|
+
### Full custom provider (defines `models`)
|
|
92
|
+
|
|
93
|
+
If `models` is non-empty, you must set:
|
|
94
|
+
|
|
95
|
+
- `baseUrl`
|
|
96
|
+
- `apiKey` (unless `auth: none`)
|
|
97
|
+
- `api` at provider level or per model
|
|
98
|
+
|
|
99
|
+
If `auth: none` is set, `apiKey` is optional even when `models` are defined.
|
|
100
|
+
|
|
101
|
+
### Override-only provider (no `models`)
|
|
102
|
+
|
|
103
|
+
If `models` is empty/missing, set at least one of:
|
|
104
|
+
|
|
105
|
+
- `baseUrl`
|
|
106
|
+
- `modelOverrides`
|
|
107
|
+
- `discovery`
|
|
108
|
+
|
|
109
|
+
Use this to modify built-in providers without redefining all models.
|
|
110
|
+
|
|
111
|
+
Default values when omitted in a model definition:
|
|
112
|
+
|
|
113
|
+
- `reasoning: false`
|
|
114
|
+
- `input: [text]`
|
|
115
|
+
- `cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }`
|
|
116
|
+
- `contextWindow: 128000`
|
|
117
|
+
- `maxTokens: 16384`
|
|
118
|
+
|
|
119
|
+
Header merge behavior:
|
|
120
|
+
|
|
121
|
+
- Provider `headers` are applied first
|
|
122
|
+
- Model-level `headers` override provider headers on key conflicts
|
|
123
|
+
## Merge behavior
|
|
124
|
+
|
|
125
|
+
`models.yml` does not replace the built-in registry.
|
|
126
|
+
|
|
127
|
+
1. Built-in models load first.
|
|
128
|
+
2. Provider-level overrides (`baseUrl`, `headers`) are applied.
|
|
129
|
+
3. `modelOverrides` are applied by model ID within each provider.
|
|
130
|
+
4. Custom `models` are merged in.
|
|
131
|
+
5. If a custom model has the same `provider + id` as an existing model, it replaces that model.
|
|
132
|
+
|
|
133
|
+
## API key behavior
|
|
134
|
+
|
|
135
|
+
`apiKey` resolution is:
|
|
136
|
+
|
|
137
|
+
1. Treat value as env var name (preferred)
|
|
138
|
+
2. If env var not found, treat value as literal key
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
apiKey: OPENROUTER_API_KEY
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If `OPENROUTER_API_KEY` exists, that value is used. Otherwise, the literal string `OPENROUTER_API_KEY` is used as the token.
|
|
147
|
+
|
|
148
|
+
Use `authHeader: true` when your endpoint expects:
|
|
149
|
+
|
|
150
|
+
```http
|
|
151
|
+
Authorization: Bearer <apiKey>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Set `auth: none` for keyless providers (local gateways, unauthenticated dev endpoints).
|
|
155
|
+
|
|
156
|
+
## Practical integration patterns
|
|
157
|
+
|
|
158
|
+
### 1) OpenAI-compatible endpoint (vLLM / LM Studio / gateway)
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
providers:
|
|
162
|
+
local-openai:
|
|
163
|
+
baseUrl: http://127.0.0.1:8000/v1
|
|
164
|
+
auth: none
|
|
165
|
+
api: openai-completions
|
|
166
|
+
models:
|
|
167
|
+
- id: Qwen/Qwen2.5-Coder-32B-Instruct
|
|
168
|
+
name: Qwen 2.5 Coder 32B (local)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 2) Anthropic-compatible proxy
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
providers:
|
|
175
|
+
anthropic-proxy:
|
|
176
|
+
baseUrl: https://proxy.example.com/anthropic
|
|
177
|
+
apiKey: ANTHROPIC_PROXY_KEY
|
|
178
|
+
api: anthropic-messages
|
|
179
|
+
authHeader: true
|
|
180
|
+
models:
|
|
181
|
+
- id: claude-sonnet-4-20250514
|
|
182
|
+
name: Claude Sonnet 4 (Proxy)
|
|
183
|
+
reasoning: true
|
|
184
|
+
input: [text, image]
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 3) Override built-in provider without redefining models
|
|
188
|
+
|
|
189
|
+
```yaml
|
|
190
|
+
providers:
|
|
191
|
+
openrouter:
|
|
192
|
+
baseUrl: https://my-corp-proxy.example.com/v1
|
|
193
|
+
headers:
|
|
194
|
+
X-Team: platform
|
|
195
|
+
modelOverrides:
|
|
196
|
+
anthropic/claude-sonnet-4:
|
|
197
|
+
name: Sonnet 4 (Corp Route)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 4) Runtime discovery for Ollama
|
|
201
|
+
|
|
202
|
+
```yaml
|
|
203
|
+
providers:
|
|
204
|
+
ollama:
|
|
205
|
+
baseUrl: http://127.0.0.1:11434
|
|
206
|
+
api: openai-completions
|
|
207
|
+
auth: none
|
|
208
|
+
discovery:
|
|
209
|
+
type: ollama
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The agent will query `GET /api/tags` and register discovered models dynamically.
|
|
213
|
+
|
|
214
|
+
## Validation failures to watch for
|
|
215
|
+
|
|
216
|
+
Common schema/validation errors:
|
|
217
|
+
|
|
218
|
+
- Provider with `models` but missing `baseUrl`
|
|
219
|
+
- Provider with `models` and `auth != none` but missing `apiKey`
|
|
220
|
+
- Model missing `api` when neither provider-level nor model-level `api` is set
|
|
221
|
+
- Non-positive `contextWindow` or `maxTokens`
|
|
222
|
+
- `discovery` configured without provider-level `api`
|
|
223
|
+
|
|
224
|
+
When `models.yml` has errors, the agent falls back to built-in models and reports a load error.
|
|
225
|
+
|
|
226
|
+
## Quick start
|
|
227
|
+
|
|
228
|
+
1. Create `~/.omp/agent/models.yml`
|
|
229
|
+
2. Add one provider with one model
|
|
230
|
+
3. Start the agent and open `/model`
|
|
231
|
+
4. Confirm your provider/model appears
|
|
232
|
+
5. If auth fails, check env vars and `authHeader`
|
|
233
|
+
|
|
234
|
+
For SDK usage, `ModelRegistry` also accepts a custom path so you can load non-default `models.yml` files programmatically.
|
package/package.json
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.1.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"ompConfig": {
|
|
7
|
-
"name": "omp",
|
|
8
|
-
"configDir": ".omp"
|
|
9
|
-
},
|
|
10
6
|
"bin": {
|
|
11
7
|
"omp": "src/cli.ts"
|
|
12
8
|
},
|
|
@@ -88,12 +84,12 @@
|
|
|
88
84
|
},
|
|
89
85
|
"dependencies": {
|
|
90
86
|
"@mozilla/readability": "0.6.0",
|
|
91
|
-
"@oh-my-pi/omp-stats": "12.
|
|
92
|
-
"@oh-my-pi/pi-agent-core": "12.
|
|
93
|
-
"@oh-my-pi/pi-ai": "12.
|
|
94
|
-
"@oh-my-pi/pi-natives": "12.
|
|
95
|
-
"@oh-my-pi/pi-tui": "12.
|
|
96
|
-
"@oh-my-pi/pi-utils": "12.
|
|
87
|
+
"@oh-my-pi/omp-stats": "12.1.0",
|
|
88
|
+
"@oh-my-pi/pi-agent-core": "12.1.0",
|
|
89
|
+
"@oh-my-pi/pi-ai": "12.1.0",
|
|
90
|
+
"@oh-my-pi/pi-natives": "12.1.0",
|
|
91
|
+
"@oh-my-pi/pi-tui": "12.1.0",
|
|
92
|
+
"@oh-my-pi/pi-utils": "12.1.0",
|
|
97
93
|
"@sinclair/typebox": "^0.34.48",
|
|
98
94
|
"@xterm/headless": "^6.0.0",
|
|
99
95
|
"ajv": "^8.17.1",
|
package/src/cli/args.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
5
5
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
6
|
+
import { APP_NAME, CONFIG_DIR_NAME } from "@oh-my-pi/pi-utils/dirs";
|
|
6
7
|
import chalk from "chalk";
|
|
7
|
-
import { APP_NAME, CONFIG_DIR_NAME } from "../config";
|
|
8
8
|
import { BUILTIN_TOOLS } from "../tools";
|
|
9
9
|
|
|
10
10
|
export type Mode = "text" | "json" | "rpc";
|
package/src/cli/config-cli.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* Handles `omp config <command>` subcommands for managing settings.
|
|
5
5
|
* Uses settings-defs as the source of truth for available settings.
|
|
6
6
|
*/
|
|
7
|
+
|
|
8
|
+
import { APP_NAME, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
|
7
9
|
import chalk from "chalk";
|
|
8
|
-
import { APP_NAME, getAgentDir } from "../config";
|
|
9
10
|
import {
|
|
10
11
|
getDefault,
|
|
11
12
|
getEnumValues,
|
package/src/cli/grep-cli.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import { grep } from "@oh-my-pi/pi-natives";
|
|
8
|
+
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
|
8
9
|
import chalk from "chalk";
|
|
9
|
-
import { APP_NAME } from "../config";
|
|
10
10
|
|
|
11
11
|
export interface GrepCommandArgs {
|
|
12
12
|
pattern: string;
|
package/src/cli/jupyter-cli.ts
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles `omp jupyter` subcommand for managing the shared Python gateway.
|
|
5
5
|
*/
|
|
6
|
+
|
|
7
|
+
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
|
6
8
|
import chalk from "chalk";
|
|
7
|
-
import { APP_NAME } from "../config";
|
|
8
9
|
import { getGatewayStatus, shutdownSharedGateway } from "../ipy/gateway-coordinator";
|
|
9
10
|
|
|
10
11
|
export type JupyterAction = "kill" | "status";
|
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles `omp plugin <command>` subcommands for plugin lifecycle management.
|
|
5
5
|
*/
|
|
6
|
+
|
|
7
|
+
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
|
6
8
|
import chalk from "chalk";
|
|
7
|
-
import { APP_NAME } from "../config";
|
|
8
9
|
import { PluginManager, parseSettingValue, validateSetting } from "../extensibility/plugins";
|
|
9
10
|
import { theme } from "../modes/theme/theme";
|
|
10
11
|
|
package/src/cli/setup-cli.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles `omp setup <component>` to install dependencies for optional features.
|
|
5
5
|
*/
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
import { APP_NAME, getPythonEnvDir } from "@oh-my-pi/pi-utils/dirs";
|
|
6
8
|
import { $ } from "bun";
|
|
7
9
|
import chalk from "chalk";
|
|
8
|
-
import { APP_NAME } from "../config";
|
|
9
10
|
import { theme } from "../modes/theme/theme";
|
|
10
11
|
|
|
11
12
|
export type SetupComponent = "python";
|
|
@@ -21,6 +22,7 @@ export interface SetupCommandArgs {
|
|
|
21
22
|
const VALID_COMPONENTS: SetupComponent[] = ["python"];
|
|
22
23
|
|
|
23
24
|
const PYTHON_PACKAGES = ["jupyter_kernel_gateway", "ipykernel"];
|
|
25
|
+
const MANAGED_PYTHON_ENV = getPythonEnvDir();
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* Parse setup subcommand arguments.
|
|
@@ -67,6 +69,14 @@ interface PythonCheckResult {
|
|
|
67
69
|
pipPath?: string;
|
|
68
70
|
missingPackages: string[];
|
|
69
71
|
installedPackages: string[];
|
|
72
|
+
usingManagedEnv?: boolean;
|
|
73
|
+
managedEnvPath?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function managedPythonPath(): string {
|
|
77
|
+
return process.platform === "win32"
|
|
78
|
+
? path.join(MANAGED_PYTHON_ENV, "Scripts", "python.exe")
|
|
79
|
+
: path.join(MANAGED_PYTHON_ENV, "bin", "python");
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
/**
|
|
@@ -77,48 +87,115 @@ async function checkPythonSetup(): Promise<PythonCheckResult> {
|
|
|
77
87
|
available: false,
|
|
78
88
|
missingPackages: [],
|
|
79
89
|
installedPackages: [],
|
|
90
|
+
managedEnvPath: MANAGED_PYTHON_ENV,
|
|
80
91
|
};
|
|
81
92
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
result.pythonPath = pythonPath;
|
|
93
|
+
const systemPythonPath = Bun.which("python") ?? Bun.which("python3");
|
|
94
|
+
const managedPath = managedPythonPath();
|
|
95
|
+
const hasManagedEnv = await Bun.file(managedPath).exists();
|
|
96
|
+
|
|
87
97
|
result.uvPath = Bun.which("uv") ?? undefined;
|
|
88
98
|
result.pipPath = Bun.which("pip3") ?? Bun.which("pip") ?? undefined;
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
const candidates = [systemPythonPath, hasManagedEnv ? managedPath : undefined].filter(
|
|
101
|
+
(candidate): candidate is string => !!candidate,
|
|
102
|
+
);
|
|
103
|
+
if (candidates.length === 0) {
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
result.pythonPath = systemPythonPath ?? managedPath;
|
|
108
|
+
let bestMatch = {
|
|
109
|
+
pythonPath: candidates[0],
|
|
110
|
+
missingPackages: [...PYTHON_PACKAGES],
|
|
111
|
+
installedPackages: [] as string[],
|
|
112
|
+
usingManagedEnv: candidates[0] === managedPath,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
for (const pythonPath of candidates) {
|
|
116
|
+
const installedPackages: string[] = [];
|
|
117
|
+
const missingPackages: string[] = [];
|
|
118
|
+
for (const pkg of PYTHON_PACKAGES) {
|
|
119
|
+
const moduleName = pkg === "jupyter_kernel_gateway" ? "kernel_gateway" : pkg;
|
|
120
|
+
const script = `import importlib.util; raise SystemExit(0 if importlib.util.find_spec('${moduleName}') else 1)`;
|
|
121
|
+
const check = await $`${pythonPath} -c ${script}`.quiet().nothrow();
|
|
122
|
+
if (check.exitCode === 0) {
|
|
123
|
+
installedPackages.push(pkg);
|
|
124
|
+
} else {
|
|
125
|
+
missingPackages.push(pkg);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (missingPackages.length < bestMatch.missingPackages.length) {
|
|
130
|
+
bestMatch = {
|
|
131
|
+
pythonPath,
|
|
132
|
+
missingPackages,
|
|
133
|
+
installedPackages,
|
|
134
|
+
usingManagedEnv: pythonPath === managedPath,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (missingPackages.length === 0) {
|
|
139
|
+
result.available = true;
|
|
140
|
+
result.pythonPath = pythonPath;
|
|
141
|
+
result.missingPackages = missingPackages;
|
|
142
|
+
result.installedPackages = installedPackages;
|
|
143
|
+
result.usingManagedEnv = pythonPath === managedPath;
|
|
144
|
+
return result;
|
|
98
145
|
}
|
|
99
146
|
}
|
|
100
147
|
|
|
101
|
-
result.
|
|
148
|
+
result.pythonPath = bestMatch.pythonPath;
|
|
149
|
+
result.missingPackages = bestMatch.missingPackages;
|
|
150
|
+
result.installedPackages = bestMatch.installedPackages;
|
|
151
|
+
result.usingManagedEnv = bestMatch.usingManagedEnv;
|
|
102
152
|
return result;
|
|
103
153
|
}
|
|
104
154
|
|
|
105
155
|
/**
|
|
106
156
|
* Install Python packages using uv (preferred) or pip.
|
|
107
157
|
*/
|
|
108
|
-
async function installPythonPackages(
|
|
158
|
+
async function installPythonPackages(
|
|
159
|
+
packages: string[],
|
|
160
|
+
pythonPath: string,
|
|
161
|
+
uvPath?: string,
|
|
162
|
+
pipPath?: string,
|
|
163
|
+
): Promise<{ success: boolean; usedManagedEnv: boolean }> {
|
|
109
164
|
if (uvPath) {
|
|
110
165
|
console.log(chalk.dim(`Installing via uv: ${packages.join(" ")}`));
|
|
111
166
|
const result = await $`${uvPath} pip install ${packages}`.nothrow();
|
|
112
|
-
|
|
167
|
+
if (result.exitCode === 0) {
|
|
168
|
+
return { success: true, usedManagedEnv: false };
|
|
169
|
+
}
|
|
113
170
|
}
|
|
114
171
|
|
|
115
172
|
if (pipPath) {
|
|
116
173
|
console.log(chalk.dim(`Installing via pip: ${packages.join(" ")}`));
|
|
117
174
|
const result = await $`${pipPath} install ${packages}`.nothrow();
|
|
118
|
-
|
|
175
|
+
if (result.exitCode === 0) {
|
|
176
|
+
return { success: true, usedManagedEnv: false };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log(chalk.dim(`Falling back to managed virtual environment: ${MANAGED_PYTHON_ENV}`));
|
|
181
|
+
|
|
182
|
+
if (uvPath) {
|
|
183
|
+
const createEnv = await $`${uvPath} venv ${MANAGED_PYTHON_ENV}`.quiet().nothrow();
|
|
184
|
+
if (createEnv.exitCode !== 0) {
|
|
185
|
+
return { success: false, usedManagedEnv: true };
|
|
186
|
+
}
|
|
187
|
+
const installInManagedEnv = await $`${uvPath} pip install --python ${MANAGED_PYTHON_ENV} ${packages}`.nothrow();
|
|
188
|
+
return { success: installInManagedEnv.exitCode === 0, usedManagedEnv: true };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const createEnv = await $`${pythonPath} -m venv ${MANAGED_PYTHON_ENV}`.quiet().nothrow();
|
|
192
|
+
if (createEnv.exitCode !== 0) {
|
|
193
|
+
return { success: false, usedManagedEnv: true };
|
|
119
194
|
}
|
|
120
195
|
|
|
121
|
-
|
|
196
|
+
const managedPython = managedPythonPath();
|
|
197
|
+
const installInManagedEnv = await $`${managedPython} -m pip install ${packages}`.nothrow();
|
|
198
|
+
return { success: installInManagedEnv.exitCode === 0, usedManagedEnv: true };
|
|
122
199
|
}
|
|
123
200
|
|
|
124
201
|
/**
|
|
@@ -148,6 +225,9 @@ async function handlePythonSetup(flags: { json?: boolean; check?: boolean }): Pr
|
|
|
148
225
|
}
|
|
149
226
|
|
|
150
227
|
console.log(chalk.dim(`Python: ${check.pythonPath}`));
|
|
228
|
+
if (check.usingManagedEnv) {
|
|
229
|
+
console.log(chalk.dim(`Using managed environment: ${check.managedEnvPath}`));
|
|
230
|
+
}
|
|
151
231
|
|
|
152
232
|
if (check.uvPath) {
|
|
153
233
|
console.log(chalk.dim(`uv: ${check.uvPath}`));
|
|
@@ -178,18 +258,33 @@ async function handlePythonSetup(flags: { json?: boolean; check?: boolean }): Pr
|
|
|
178
258
|
}
|
|
179
259
|
|
|
180
260
|
console.log("");
|
|
181
|
-
const
|
|
261
|
+
const install = await installPythonPackages(check.missingPackages, check.pythonPath, check.uvPath, check.pipPath);
|
|
182
262
|
|
|
183
|
-
if (!success) {
|
|
263
|
+
if (!install.success) {
|
|
184
264
|
console.error(chalk.red(`\n${theme.status.error} Installation failed`));
|
|
185
265
|
console.error(chalk.dim("Try installing manually:"));
|
|
186
|
-
|
|
266
|
+
if (install.usedManagedEnv) {
|
|
267
|
+
if (check.uvPath) {
|
|
268
|
+
console.error(chalk.dim(` uv venv ${MANAGED_PYTHON_ENV}`));
|
|
269
|
+
console.error(
|
|
270
|
+
chalk.dim(` uv pip install --python ${MANAGED_PYTHON_ENV} ${check.missingPackages.join(" ")}`),
|
|
271
|
+
);
|
|
272
|
+
} else {
|
|
273
|
+
console.error(chalk.dim(` ${check.pythonPath} -m venv ${MANAGED_PYTHON_ENV}`));
|
|
274
|
+
console.error(chalk.dim(` ${managedPythonPath()} -m pip install ${check.missingPackages.join(" ")}`));
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
console.error(chalk.dim(` ${check.uvPath ? "uv pip" : "pip"} install ${check.missingPackages.join(" ")}`));
|
|
278
|
+
}
|
|
187
279
|
process.exit(1);
|
|
188
280
|
}
|
|
189
281
|
|
|
190
282
|
const recheck = await checkPythonSetup();
|
|
191
283
|
if (recheck.available) {
|
|
192
284
|
console.log(chalk.green(`\n${theme.status.success} Python execution is ready`));
|
|
285
|
+
if (recheck.usingManagedEnv) {
|
|
286
|
+
console.log(chalk.dim(`Managed Python environment: ${recheck.managedEnvPath}`));
|
|
287
|
+
}
|
|
193
288
|
} else {
|
|
194
289
|
console.error(chalk.red(`\n${theme.status.error} Setup incomplete`));
|
|
195
290
|
console.error(chalk.dim(`Still missing: ${recheck.missingPackages.join(", ")}`));
|
package/src/cli/shell-cli.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import * as path from "node:path";
|
|
7
7
|
import { createInterface } from "node:readline/promises";
|
|
8
8
|
import { Shell } from "@oh-my-pi/pi-natives";
|
|
9
|
+
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
|
9
10
|
import chalk from "chalk";
|
|
10
|
-
import { APP_NAME } from "../config";
|
|
11
11
|
import { Settings } from "../config/settings";
|
|
12
12
|
import { getOrCreateSnapshot } from "../utils/shell-snapshot";
|
|
13
13
|
|
package/src/cli/stats-cli.ts
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles `omp stats` subcommand for viewing AI usage statistics.
|
|
5
5
|
*/
|
|
6
|
+
|
|
7
|
+
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
|
6
8
|
import chalk from "chalk";
|
|
7
|
-
import { APP_NAME } from "../config";
|
|
8
9
|
import { openPath } from "../utils/open";
|
|
9
10
|
|
|
10
11
|
// =============================================================================
|