@oh-my-pi/pi-coding-agent 9.4.0 → 9.6.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 +84 -0
- package/package.json +9 -8
- package/src/capability/index.ts +7 -9
- package/src/cli/config-cli.ts +86 -73
- package/src/cli/update-cli.ts +45 -3
- package/src/commit/agentic/agent.ts +4 -4
- package/src/commit/agentic/index.ts +6 -5
- package/src/commit/agentic/tools/analyze-file.ts +5 -7
- package/src/commit/agentic/tools/index.ts +3 -3
- package/src/commit/model-selection.ts +13 -17
- package/src/commit/pipeline.ts +5 -5
- package/src/config/model-registry.ts +7 -0
- package/src/config/settings-schema.ts +836 -0
- package/src/config/settings.ts +702 -0
- package/src/discovery/helpers.ts +55 -11
- package/src/exa/index.ts +1 -1
- package/src/exec/bash-executor.ts +13 -13
- package/src/exec/shell-session.ts +15 -3
- package/src/export/ttsr.ts +1 -1
- package/src/extensibility/skills.ts +40 -9
- package/src/index.ts +2 -10
- package/src/ipy/gateway-coordinator.ts +5 -143
- package/src/ipy/kernel.ts +6 -171
- package/src/ipy/runtime.ts +198 -0
- package/src/lsp/client.ts +14 -1
- package/src/lsp/defaults.json +0 -6
- package/src/lsp/index.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/main.ts +26 -48
- package/src/modes/components/extensions/extension-dashboard.ts +22 -11
- package/src/modes/components/index.ts +1 -1
- package/src/modes/components/model-selector.ts +7 -7
- package/src/modes/components/settings-defs.ts +210 -915
- package/src/modes/components/settings-selector.ts +80 -106
- package/src/modes/components/status-line/types.ts +2 -8
- package/src/modes/components/status-line-segment-editor.ts +1 -1
- package/src/modes/components/status-line.ts +26 -3
- package/src/modes/controllers/event-controller.ts +9 -8
- package/src/modes/controllers/input-controller.ts +19 -15
- package/src/modes/controllers/selector-controller.ts +30 -14
- package/src/modes/interactive-mode.ts +10 -10
- package/src/modes/rpc/rpc-mode.ts +10 -0
- package/src/modes/rpc/rpc-types.ts +3 -0
- package/src/modes/types.ts +2 -2
- package/src/modes/utils/ui-helpers.ts +4 -3
- package/src/patch/index.ts +7 -7
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/bash.md +12 -2
- package/src/prompts/tools/task.md +180 -73
- package/src/sdk.ts +38 -61
- package/src/session/agent-session.ts +66 -55
- package/src/session/agent-storage.ts +1 -1
- package/src/session/session-manager.ts +10 -10
- package/src/system-prompt.ts +2 -2
- package/src/task/executor.ts +9 -9
- package/src/task/index.ts +2 -2
- package/src/tools/ask.ts +5 -6
- package/src/tools/bash-interceptor.ts +39 -1
- package/src/tools/bash-normalize.ts +126 -0
- package/src/tools/bash.ts +31 -5
- package/src/tools/find.ts +51 -33
- package/src/tools/index.ts +5 -23
- package/src/tools/plan-mode-guard.ts +1 -6
- package/src/tools/python.ts +2 -2
- package/src/tools/read.ts +2 -2
- package/src/tools/write.ts +2 -2
- package/src/utils/ignore-files.ts +119 -0
- package/src/web/search/providers/perplexity.ts +1 -1
- package/examples/sdk/10-settings.ts +0 -37
- package/src/config/settings-manager.ts +0 -2015
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,90 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
|
+
### Fixed
|
|
5
|
+
|
|
6
|
+
- Fixed bash command normalization to preserve newlines in heredocs and multiline commands
|
|
7
|
+
|
|
8
|
+
## [9.6.0] - 2026-02-01
|
|
9
|
+
### Breaking Changes
|
|
10
|
+
|
|
11
|
+
- Replaced `SettingsManager` class with new `Settings` singleton providing sync get/set API with background persistence
|
|
12
|
+
- Changed settings access from method calls (e.g., `getTheme()`) to path-based access (e.g., `settings.get("theme")`)
|
|
13
|
+
- Removed `settingsManager` parameter from `CreateAgentSessionOptions` in favor of `settingsInstance`
|
|
14
|
+
- Removed `loadSettings()` export from public API
|
|
15
|
+
- Removed example file `examples/sdk/10-settings.ts` demonstrating old SettingsManager API
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- New `Settings` singleton class with sync get/set operations and background persistence
|
|
20
|
+
- Added `Settings.isolated()` factory for creating isolated settings instances in tests
|
|
21
|
+
- Added `Settings.init()` for initializing global settings instance
|
|
22
|
+
- Added `settings` global export for convenient access to settings singleton
|
|
23
|
+
- New `settings-schema.ts` providing unified, type-safe settings definitions with UI metadata
|
|
24
|
+
- Added "none" option to `doubleEscapeAction` setting to disable double-escape behavior entirely ([#973](https://github.com/badlogic/pi-mono/issues/973) by [@juanibiapina](https://github.com/juanibiapina))
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Unified settings schema into single source of truth with `settings-schema.ts` replacing scattered definitions
|
|
29
|
+
- Refactored settings CLI to use new schema-based path resolution instead of SETTINGS_DEFS
|
|
30
|
+
- Updated config command examples to use new nested path syntax (e.g., `compaction.enabled` instead of `autoCompact`)
|
|
31
|
+
- Changed `InteractiveModeContext.settingsManager` to `InteractiveModeContext.settings`
|
|
32
|
+
- Updated all internal settings access throughout codebase to use new `settings.get()` and `settings.set()` API
|
|
33
|
+
- Moved `DEFAULT_BASH_INTERCEPTOR_RULES` from settings-manager to bash-interceptor module
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
|
|
37
|
+
- Deleted `settings-manager.ts` (2035 lines) - functionality replaced by new Settings singleton
|
|
38
|
+
- Removed `SettingsManager.create()`, `SettingsManager.acquire()`, and `SettingsManager.inMemory()` factory methods
|
|
39
|
+
- Removed individual getter/setter methods from settings API (e.g., `getTheme()`, `setTheme()`, `getCompactionSettings()`)
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Respect .gitignore, .ignore, and .fdignore files when scanning package resources for skills, prompts, themes, and extensions
|
|
44
|
+
|
|
45
|
+
## [9.5.1] - 2026-02-01
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- Changed persistent shell from opt-out to opt-in (default: off) for improved reliability; enable via Settings > Bash > Persistent shell or `OMP_SHELL_PERSIST=1`
|
|
50
|
+
- Added new "Bash" settings tab grouping shell-related settings (force basic shell, persistent shell, interceptor, intercept ls)
|
|
51
|
+
|
|
52
|
+
## [9.5.0] - 2026-02-01
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- Added `head` and `tail` parameters to bash tool to limit output lines without breaking streaming
|
|
56
|
+
- Added automatic normalization of bash commands to extract `| head -n N` and `| tail -n N` patterns into native parameters
|
|
57
|
+
- Added `maxResults` parameter to find tool to limit result set at the native layer
|
|
58
|
+
- Added context-structure template showing required sections (Goal, Constraints, Existing Code, API Contract) with examples of good vs bad context
|
|
59
|
+
- Added explicit dependency test: 'Can agent B write correct code without seeing agent A's output?' to determine sequencing
|
|
60
|
+
- Added detailed phased execution pattern with four phases (Foundation, Parallel Implementation, Integration, Dependent Layer) and WASM-to-N-API migration example
|
|
61
|
+
- Added table of dependency patterns that must be sequential (API creation before bindings, interface definition before implementation, etc.)
|
|
62
|
+
- Added phased execution guidance for migrations and refactors to prevent parallel work on dependent layers
|
|
63
|
+
- Added example demonstrating phased execution pattern for porting WASM to N-API with sequential foundation, parallel implementation, integration, and dependent layer phases
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- Improved find tool performance by delegating mtime-based sorting to native layer instead of post-processing results in JavaScript
|
|
68
|
+
- Simplified find tool result processing by removing redundant filesystem stat calls when native metadata is available
|
|
69
|
+
- Updated bash tool documentation to recommend using `head` and `tail` parameters instead of piping through head/tail commands
|
|
70
|
+
- Updated binary build process to exclude worker files from compilation, reducing binary size
|
|
71
|
+
- Modified update mechanism to download and install native addon alongside CLI binary for platform-specific functionality
|
|
72
|
+
- Updated find tool to emit streaming match updates via callback, allowing real-time progress feedback during file searches
|
|
73
|
+
- Modified find tool to use native match metadata (mtime, fileType) from WASM layer instead of redundant filesystem stats, improving performance
|
|
74
|
+
- Restructured Task tool documentation to emphasize context quality and explicit API contracts for subagent success
|
|
75
|
+
- Updated task execution guidance to require structured context with Goal, Constraints, Existing Code, and API Contract sections
|
|
76
|
+
- Reorganized parallelization rules with explicit dependency patterns and phased execution guidance for migrations
|
|
77
|
+
- Clarified that response format requirements must go in schema parameter, never in context descriptions
|
|
78
|
+
- Centralized Python runtime resolution into shared `ipy/runtime.ts` module, removing duplicate code from kernel and gateway coordinator
|
|
79
|
+
|
|
80
|
+
### Removed
|
|
81
|
+
|
|
82
|
+
- Removed Nushell language server configuration from LSP defaults
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
|
|
86
|
+
- Fixed race condition in shell session where command completion could occur before stream data was fully processed
|
|
87
|
+
- Fixed Python gateway spawning console window on Windows by using windowless Python interpreter (pythonw.exe)
|
|
4
88
|
|
|
5
89
|
## [9.4.0] - 2026-01-31
|
|
6
90
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.6.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"ompConfig": {
|
|
@@ -74,17 +74,17 @@
|
|
|
74
74
|
"scripts": {
|
|
75
75
|
"check": "tsgo -p tsconfig.json",
|
|
76
76
|
"format-prompts": "bun scripts/format-prompts.ts",
|
|
77
|
-
"build:binary": "cd ../.. && bun build --compile --define OMP_COMPILED=true --root . ./packages/coding-agent/src/cli.ts
|
|
77
|
+
"build:binary": "cd ../.. && bun build --compile --define OMP_COMPILED=true --root . ./packages/coding-agent/src/cli.ts --outfile packages/coding-agent/dist/omp",
|
|
78
78
|
"generate-template": "bun scripts/generate-template.ts",
|
|
79
79
|
"test": "bun test"
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
|
-
"@oh-my-pi/omp-stats": "9.
|
|
83
|
-
"@oh-my-pi/pi-agent-core": "9.
|
|
84
|
-
"@oh-my-pi/pi-ai": "9.
|
|
85
|
-
"@oh-my-pi/pi-natives": "9.
|
|
86
|
-
"@oh-my-pi/pi-tui": "9.
|
|
87
|
-
"@oh-my-pi/pi-utils": "9.
|
|
82
|
+
"@oh-my-pi/omp-stats": "9.6.0",
|
|
83
|
+
"@oh-my-pi/pi-agent-core": "9.6.0",
|
|
84
|
+
"@oh-my-pi/pi-ai": "9.6.0",
|
|
85
|
+
"@oh-my-pi/pi-natives": "9.6.0",
|
|
86
|
+
"@oh-my-pi/pi-tui": "9.6.0",
|
|
87
|
+
"@oh-my-pi/pi-utils": "9.6.0",
|
|
88
88
|
"@openai/agents": "^0.4.4",
|
|
89
89
|
"@sinclair/typebox": "^0.34.48",
|
|
90
90
|
"ajv": "^8.17.1",
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
"file-type": "^21.3.0",
|
|
94
94
|
"glob": "^13.0.0",
|
|
95
95
|
"handlebars": "^4.7.8",
|
|
96
|
+
"ignore": "^7.0.5",
|
|
96
97
|
"marked": "^17.0.1",
|
|
97
98
|
"nanoid": "^5.1.6",
|
|
98
99
|
"node-html-parser": "^7.0.2",
|
package/src/capability/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import * as os from "node:os";
|
|
10
10
|
import * as path from "node:path";
|
|
11
|
+
import type { Settings } from "../config/settings";
|
|
11
12
|
import { clearCache as clearFsCache, cacheStats as fsCacheStats, invalidate as invalidateFs } from "./fs";
|
|
12
13
|
import type {
|
|
13
14
|
Capability,
|
|
@@ -37,7 +38,7 @@ const providerMeta = new Map<string, { displayName: string; description: string
|
|
|
37
38
|
const disabledProviders = new Set<string>();
|
|
38
39
|
|
|
39
40
|
/** Settings manager for persistence (if set) */
|
|
40
|
-
let
|
|
41
|
+
let settings: Settings | null = null;
|
|
41
42
|
|
|
42
43
|
// =============================================================================
|
|
43
44
|
// Registration API
|
|
@@ -228,13 +229,10 @@ export async function loadCapability<T>(capabilityId: string, options: LoadOptio
|
|
|
228
229
|
* Initialize capability system with settings manager for persistence.
|
|
229
230
|
* Call this once on startup to enable persistent provider state.
|
|
230
231
|
*/
|
|
231
|
-
export function initializeWithSettings(
|
|
232
|
-
|
|
233
|
-
setDisabledProviders(ids: string[]): void;
|
|
234
|
-
}): void {
|
|
235
|
-
settingsManager = manager;
|
|
232
|
+
export function initializeWithSettings(settingsInstance: Settings): void {
|
|
233
|
+
settings = settingsInstance;
|
|
236
234
|
// Load disabled providers from settings
|
|
237
|
-
const disabled =
|
|
235
|
+
const disabled = settings.get("disabledProviders");
|
|
238
236
|
disabledProviders.clear();
|
|
239
237
|
for (const id of disabled) {
|
|
240
238
|
disabledProviders.add(id);
|
|
@@ -245,8 +243,8 @@ export function initializeWithSettings(manager: {
|
|
|
245
243
|
* Persist current disabled providers to settings.
|
|
246
244
|
*/
|
|
247
245
|
function persistDisabledProviders(): void {
|
|
248
|
-
if (
|
|
249
|
-
|
|
246
|
+
if (settings) {
|
|
247
|
+
settings.set("disabledProviders", Array.from(disabledProviders));
|
|
250
248
|
}
|
|
251
249
|
}
|
|
252
250
|
|
package/src/cli/config-cli.ts
CHANGED
|
@@ -2,12 +2,20 @@
|
|
|
2
2
|
* Config CLI command handlers.
|
|
3
3
|
*
|
|
4
4
|
* Handles `omp config <command>` subcommands for managing settings.
|
|
5
|
-
* Uses
|
|
5
|
+
* Uses settings-defs as the source of truth for available settings.
|
|
6
6
|
*/
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import { APP_NAME, getAgentDir } from "../config";
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
getDefault,
|
|
11
|
+
getEnumValues,
|
|
12
|
+
getType,
|
|
13
|
+
type SettingPath,
|
|
14
|
+
Settings,
|
|
15
|
+
type SettingValue,
|
|
16
|
+
settings,
|
|
17
|
+
} from "../config/settings";
|
|
18
|
+
import { getAllSettingDefs, type SettingDef } from "../modes/components/settings-defs";
|
|
11
19
|
import { theme } from "../modes/theme/theme";
|
|
12
20
|
|
|
13
21
|
// =============================================================================
|
|
@@ -29,18 +37,18 @@ export interface ConfigCommandArgs {
|
|
|
29
37
|
// Setting Filtering
|
|
30
38
|
// =============================================================================
|
|
31
39
|
|
|
32
|
-
/** Find setting definition by
|
|
33
|
-
function findSettingDef(
|
|
34
|
-
return
|
|
40
|
+
/** Find setting definition by path */
|
|
41
|
+
function findSettingDef(path: string): SettingDef | undefined {
|
|
42
|
+
return getAllSettingDefs().find(def => def.path === path);
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
/** Get available values for a setting */
|
|
38
|
-
function getSettingValues(def: SettingDef
|
|
46
|
+
function getSettingValues(def: SettingDef): readonly string[] | undefined {
|
|
39
47
|
if (def.type === "enum") {
|
|
40
48
|
return def.values;
|
|
41
49
|
}
|
|
42
50
|
if (def.type === "submenu") {
|
|
43
|
-
const options = def.getOptions(
|
|
51
|
+
const options = def.getOptions();
|
|
44
52
|
if (options.length > 0) {
|
|
45
53
|
return options.map(o => o.value);
|
|
46
54
|
}
|
|
@@ -100,29 +108,9 @@ export function parseConfigArgs(args: string[]): ConfigCommandArgs | undefined {
|
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
// =============================================================================
|
|
103
|
-
// Value
|
|
111
|
+
// Value Formatting
|
|
104
112
|
// =============================================================================
|
|
105
113
|
|
|
106
|
-
function parseValue(value: string, def: SettingDef, sm: SettingsManager): unknown {
|
|
107
|
-
if (def.type === "boolean") {
|
|
108
|
-
const lower = value.toLowerCase();
|
|
109
|
-
if (lower === "true" || lower === "1" || lower === "yes" || lower === "on") {
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
if (lower === "false" || lower === "0" || lower === "no" || lower === "off") {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
throw new Error(`Invalid boolean value: ${value}. Use true/false, yes/no, on/off, or 1/0`);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const validValues = getSettingValues(def, sm);
|
|
119
|
-
if (validValues && validValues.length > 0 && !validValues.includes(value)) {
|
|
120
|
-
throw new Error(`Invalid value: ${value}. Valid values: ${validValues.join(", ")}`);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return value;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
114
|
function formatValue(value: unknown): string {
|
|
127
115
|
if (value === undefined || value === null) {
|
|
128
116
|
return chalk.dim("(not set)");
|
|
@@ -136,36 +124,72 @@ function formatValue(value: unknown): string {
|
|
|
136
124
|
return chalk.yellow(String(value));
|
|
137
125
|
}
|
|
138
126
|
|
|
139
|
-
function getTypeDisplay(def: SettingDef
|
|
127
|
+
function getTypeDisplay(def: SettingDef): string {
|
|
140
128
|
if (def.type === "boolean") {
|
|
141
129
|
return "(boolean)";
|
|
142
130
|
}
|
|
143
|
-
const values = getSettingValues(def
|
|
131
|
+
const values = getSettingValues(def);
|
|
144
132
|
if (values && values.length > 0) {
|
|
145
133
|
return `(${values.join("|")})`;
|
|
146
134
|
}
|
|
147
135
|
return "(string)";
|
|
148
136
|
}
|
|
149
137
|
|
|
138
|
+
// =============================================================================
|
|
139
|
+
// Schema-Driven Value Parsing
|
|
140
|
+
// =============================================================================
|
|
141
|
+
|
|
142
|
+
function parseAndSetValue(path: SettingPath, rawValue: string): void {
|
|
143
|
+
const schemaType = getType(path);
|
|
144
|
+
let parsedValue: unknown;
|
|
145
|
+
|
|
146
|
+
const trimmed = rawValue.trim();
|
|
147
|
+
switch (schemaType) {
|
|
148
|
+
case "boolean": {
|
|
149
|
+
const lower = trimmed.toLowerCase();
|
|
150
|
+
if (["true", "1", "yes", "on"].includes(lower)) parsedValue = true;
|
|
151
|
+
else if (["false", "0", "no", "off"].includes(lower)) parsedValue = false;
|
|
152
|
+
else throw new Error(`Invalid boolean value: ${rawValue}. Use true/false, yes/no, on/off, or 1/0`);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "number":
|
|
156
|
+
parsedValue = Number(trimmed);
|
|
157
|
+
if (!Number.isFinite(parsedValue)) throw new Error(`Invalid number: ${rawValue}`);
|
|
158
|
+
break;
|
|
159
|
+
case "enum": {
|
|
160
|
+
const valid = getEnumValues(path);
|
|
161
|
+
if (valid && !valid.includes(trimmed)) {
|
|
162
|
+
throw new Error(`Invalid value: ${rawValue}. Valid values: ${valid.join(", ")}`);
|
|
163
|
+
}
|
|
164
|
+
parsedValue = trimmed;
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
default:
|
|
168
|
+
parsedValue = trimmed;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
settings.set(path, parsedValue as SettingValue<typeof path>);
|
|
172
|
+
}
|
|
173
|
+
|
|
150
174
|
// =============================================================================
|
|
151
175
|
// Command Handlers
|
|
152
176
|
// =============================================================================
|
|
153
177
|
|
|
154
178
|
export async function runConfigCommand(cmd: ConfigCommandArgs): Promise<void> {
|
|
155
|
-
|
|
179
|
+
await Settings.init();
|
|
156
180
|
|
|
157
181
|
switch (cmd.action) {
|
|
158
182
|
case "list":
|
|
159
|
-
handleList(
|
|
183
|
+
handleList(cmd.flags);
|
|
160
184
|
break;
|
|
161
185
|
case "get":
|
|
162
|
-
handleGet(
|
|
186
|
+
handleGet(cmd.key, cmd.flags);
|
|
163
187
|
break;
|
|
164
188
|
case "set":
|
|
165
|
-
await handleSet(
|
|
189
|
+
await handleSet(cmd.key, cmd.value, cmd.flags);
|
|
166
190
|
break;
|
|
167
191
|
case "reset":
|
|
168
|
-
await handleReset(
|
|
192
|
+
await handleReset(cmd.key, cmd.flags);
|
|
169
193
|
break;
|
|
170
194
|
case "path":
|
|
171
195
|
handlePath();
|
|
@@ -173,12 +197,14 @@ export async function runConfigCommand(cmd: ConfigCommandArgs): Promise<void> {
|
|
|
173
197
|
}
|
|
174
198
|
}
|
|
175
199
|
|
|
176
|
-
function handleList(
|
|
200
|
+
function handleList(flags: { json?: boolean }): void {
|
|
201
|
+
const defs = getAllSettingDefs();
|
|
202
|
+
|
|
177
203
|
if (flags.json) {
|
|
178
204
|
const result: Record<string, { value: unknown; type: string; description: string }> = {};
|
|
179
|
-
for (const def of
|
|
180
|
-
result[def.
|
|
181
|
-
value:
|
|
205
|
+
for (const def of defs) {
|
|
206
|
+
result[def.path] = {
|
|
207
|
+
value: settings.get(def.path as SettingPath),
|
|
182
208
|
type: def.type,
|
|
183
209
|
description: def.description,
|
|
184
210
|
};
|
|
@@ -189,9 +215,8 @@ function handleList(settingsManager: SettingsManager, flags: { json?: boolean })
|
|
|
189
215
|
|
|
190
216
|
console.log(chalk.bold("Settings:\n"));
|
|
191
217
|
|
|
192
|
-
// Group by tab
|
|
193
218
|
const groups: Record<string, SettingDef[]> = {};
|
|
194
|
-
for (const def of
|
|
219
|
+
for (const def of defs) {
|
|
195
220
|
if (!groups[def.tab]) {
|
|
196
221
|
groups[def.tab] = [];
|
|
197
222
|
}
|
|
@@ -207,16 +232,16 @@ function handleList(settingsManager: SettingsManager, flags: { json?: boolean })
|
|
|
207
232
|
for (const group of sortedGroups) {
|
|
208
233
|
console.log(chalk.bold.blue(`[${group}]`));
|
|
209
234
|
for (const def of groups[group]) {
|
|
210
|
-
const value =
|
|
235
|
+
const value = settings.get(def.path as SettingPath);
|
|
211
236
|
const valueStr = formatValue(value);
|
|
212
|
-
const typeStr = getTypeDisplay(def
|
|
213
|
-
console.log(` ${chalk.white(def.
|
|
237
|
+
const typeStr = getTypeDisplay(def);
|
|
238
|
+
console.log(` ${chalk.white(def.path)} = ${valueStr} ${chalk.dim(typeStr)}`);
|
|
214
239
|
}
|
|
215
240
|
console.log("");
|
|
216
241
|
}
|
|
217
242
|
}
|
|
218
243
|
|
|
219
|
-
function handleGet(
|
|
244
|
+
function handleGet(key: string | undefined, flags: { json?: boolean }): void {
|
|
220
245
|
if (!key) {
|
|
221
246
|
console.error(chalk.red(`Usage: ${APP_NAME} config get <key>`));
|
|
222
247
|
console.error(chalk.dim(`\nRun '${APP_NAME} config list' to see available keys`));
|
|
@@ -230,22 +255,17 @@ function handleGet(settingsManager: SettingsManager, key: string | undefined, fl
|
|
|
230
255
|
process.exit(1);
|
|
231
256
|
}
|
|
232
257
|
|
|
233
|
-
const value =
|
|
258
|
+
const value = settings.get(def.path as SettingPath);
|
|
234
259
|
|
|
235
260
|
if (flags.json) {
|
|
236
|
-
console.log(JSON.stringify({ key: def.
|
|
261
|
+
console.log(JSON.stringify({ key: def.path, value, type: def.type, description: def.description }, null, 2));
|
|
237
262
|
return;
|
|
238
263
|
}
|
|
239
264
|
|
|
240
265
|
console.log(formatValue(value));
|
|
241
266
|
}
|
|
242
267
|
|
|
243
|
-
async function handleSet(
|
|
244
|
-
settingsManager: SettingsManager,
|
|
245
|
-
key: string | undefined,
|
|
246
|
-
value: string | undefined,
|
|
247
|
-
flags: { json?: boolean },
|
|
248
|
-
): Promise<void> {
|
|
268
|
+
async function handleSet(key: string | undefined, value: string | undefined, flags: { json?: boolean }): Promise<void> {
|
|
249
269
|
if (!key || value === undefined) {
|
|
250
270
|
console.error(chalk.red(`Usage: ${APP_NAME} config set <key> <value>`));
|
|
251
271
|
console.error(chalk.dim(`\nRun '${APP_NAME} config list' to see available keys`));
|
|
@@ -259,28 +279,23 @@ async function handleSet(
|
|
|
259
279
|
process.exit(1);
|
|
260
280
|
}
|
|
261
281
|
|
|
262
|
-
let parsedValue: unknown;
|
|
263
282
|
try {
|
|
264
|
-
|
|
283
|
+
parseAndSetValue(def.path as SettingPath, value);
|
|
265
284
|
} catch (err) {
|
|
266
285
|
console.error(chalk.red(String(err)));
|
|
267
286
|
process.exit(1);
|
|
268
287
|
}
|
|
269
288
|
|
|
270
|
-
def.
|
|
289
|
+
const newValue = settings.get(def.path as SettingPath);
|
|
271
290
|
|
|
272
291
|
if (flags.json) {
|
|
273
|
-
console.log(JSON.stringify({ key: def.
|
|
292
|
+
console.log(JSON.stringify({ key: def.path, value: newValue }));
|
|
274
293
|
} else {
|
|
275
|
-
console.log(chalk.green(`${theme.status.success} Set ${def.
|
|
294
|
+
console.log(chalk.green(`${theme.status.success} Set ${def.path} = ${formatValue(newValue)}`));
|
|
276
295
|
}
|
|
277
296
|
}
|
|
278
297
|
|
|
279
|
-
async function handleReset(
|
|
280
|
-
settingsManager: SettingsManager,
|
|
281
|
-
key: string | undefined,
|
|
282
|
-
flags: { json?: boolean },
|
|
283
|
-
): Promise<void> {
|
|
298
|
+
async function handleReset(key: string | undefined, flags: { json?: boolean }): Promise<void> {
|
|
284
299
|
if (!key) {
|
|
285
300
|
console.error(chalk.red(`Usage: ${APP_NAME} config reset <key>`));
|
|
286
301
|
console.error(chalk.dim(`\nRun '${APP_NAME} config list' to see available keys`));
|
|
@@ -294,16 +309,14 @@ async function handleReset(
|
|
|
294
309
|
process.exit(1);
|
|
295
310
|
}
|
|
296
311
|
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
def.set(settingsManager, defaultValue as never);
|
|
312
|
+
const path = def.path as SettingPath;
|
|
313
|
+
const defaultValue = getDefault(path);
|
|
314
|
+
settings.set(path, defaultValue as SettingValue<typeof path>);
|
|
302
315
|
|
|
303
316
|
if (flags.json) {
|
|
304
|
-
console.log(JSON.stringify({ key: def.
|
|
317
|
+
console.log(JSON.stringify({ key: def.path, value: defaultValue }));
|
|
305
318
|
} else {
|
|
306
|
-
console.log(chalk.green(`${theme.status.success} Reset ${def.
|
|
319
|
+
console.log(chalk.green(`${theme.status.success} Reset ${def.path} to ${formatValue(defaultValue)}`));
|
|
307
320
|
}
|
|
308
321
|
}
|
|
309
322
|
|
|
@@ -332,8 +345,8 @@ ${chalk.bold("Examples:")}
|
|
|
332
345
|
${APP_NAME} config list
|
|
333
346
|
${APP_NAME} config get theme
|
|
334
347
|
${APP_NAME} config set theme catppuccin-mocha
|
|
335
|
-
${APP_NAME} config set
|
|
336
|
-
${APP_NAME} config set
|
|
348
|
+
${APP_NAME} config set compaction.enabled false
|
|
349
|
+
${APP_NAME} config set defaultThinkingLevel medium
|
|
337
350
|
${APP_NAME} config reset steeringMode
|
|
338
351
|
${APP_NAME} config list --json
|
|
339
352
|
|
package/src/cli/update-cli.ts
CHANGED
|
@@ -135,6 +135,24 @@ function getBinaryName(): string {
|
|
|
135
135
|
return `${APP_NAME}-${os}-${archName}`;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Get the appropriate native addon name for this platform.
|
|
140
|
+
* Uses process.platform directly (linux, darwin, win32).
|
|
141
|
+
*/
|
|
142
|
+
function getNativeAddonName(): string {
|
|
143
|
+
const platform = process.platform;
|
|
144
|
+
const arch = process.arch;
|
|
145
|
+
|
|
146
|
+
if (!["linux", "darwin", "win32"].includes(platform)) {
|
|
147
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
148
|
+
}
|
|
149
|
+
if (!["x64", "arm64"].includes(arch)) {
|
|
150
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return `pi_natives.${platform}-${arch}.node`;
|
|
154
|
+
}
|
|
155
|
+
|
|
138
156
|
/**
|
|
139
157
|
* Update via bun package manager.
|
|
140
158
|
*/
|
|
@@ -154,16 +172,24 @@ async function updateViaBun(): Promise<void> {
|
|
|
154
172
|
*/
|
|
155
173
|
async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
156
174
|
const binaryName = getBinaryName();
|
|
175
|
+
const nativeAddonName = getNativeAddonName();
|
|
176
|
+
|
|
157
177
|
const asset = release.assets.find(a => a.name === binaryName);
|
|
178
|
+
const nativeAsset = release.assets.find(a => a.name === nativeAddonName);
|
|
158
179
|
|
|
159
180
|
if (!asset) {
|
|
160
181
|
throw new Error(`No binary found for ${binaryName}`);
|
|
161
182
|
}
|
|
183
|
+
if (!nativeAsset) {
|
|
184
|
+
throw new Error(`No native addon found for ${nativeAddonName}`);
|
|
185
|
+
}
|
|
162
186
|
|
|
163
187
|
const execPath = process.execPath;
|
|
164
|
-
const
|
|
188
|
+
const execDir = path.dirname(execPath);
|
|
165
189
|
const tempPath = `${execPath}.new`;
|
|
166
190
|
const backupPath = `${execPath}.bak`;
|
|
191
|
+
const nativePath = path.join(execDir, nativeAddonName);
|
|
192
|
+
const nativeTempPath = `${nativePath}.new`;
|
|
167
193
|
|
|
168
194
|
console.log(chalk.dim(`Downloading ${binaryName}...`));
|
|
169
195
|
|
|
@@ -177,6 +203,18 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
|
177
203
|
const nodeStream = Readable.fromWeb(response.body as import("stream/web").ReadableStream);
|
|
178
204
|
await pipeline(nodeStream, fileStream);
|
|
179
205
|
|
|
206
|
+
// Download native addon
|
|
207
|
+
console.log(chalk.dim(`Downloading ${nativeAddonName}...`));
|
|
208
|
+
|
|
209
|
+
const nativeResponse = await fetch(nativeAsset.url, { redirect: "follow" });
|
|
210
|
+
if (!nativeResponse.ok || !nativeResponse.body) {
|
|
211
|
+
throw new Error(`Native addon download failed: ${nativeResponse.statusText}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const nativeFileStream = fs.createWriteStream(nativeTempPath, { mode: 0o755 });
|
|
215
|
+
const nativeNodeStream = Readable.fromWeb(nativeResponse.body as import("stream/web").ReadableStream);
|
|
216
|
+
await pipeline(nativeNodeStream, nativeFileStream);
|
|
217
|
+
|
|
180
218
|
// Replace current binary
|
|
181
219
|
console.log(chalk.dim("Installing update..."));
|
|
182
220
|
|
|
@@ -187,11 +225,12 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
|
187
225
|
if (!isEnoent(err)) throw err;
|
|
188
226
|
}
|
|
189
227
|
await fs.promises.rename(execPath, backupPath);
|
|
190
|
-
|
|
191
228
|
await fs.promises.rename(tempPath, execPath);
|
|
192
|
-
|
|
193
229
|
await fs.promises.unlink(backupPath);
|
|
194
230
|
|
|
231
|
+
// Replace native addon (no backup needed, just overwrite)
|
|
232
|
+
await fs.promises.rename(nativeTempPath, nativePath);
|
|
233
|
+
|
|
195
234
|
console.log(chalk.green(`\n${theme.status.success} Updated to ${release.version}`));
|
|
196
235
|
console.log(chalk.dim(`Restart ${APP_NAME} to use the new version`));
|
|
197
236
|
} catch (err) {
|
|
@@ -201,6 +240,9 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
|
201
240
|
if (fs.existsSync(tempPath)) {
|
|
202
241
|
await fs.promises.unlink(tempPath);
|
|
203
242
|
}
|
|
243
|
+
if (fs.existsSync(nativeTempPath)) {
|
|
244
|
+
await fs.promises.unlink(nativeTempPath);
|
|
245
|
+
}
|
|
204
246
|
throw err;
|
|
205
247
|
}
|
|
206
248
|
}
|
|
@@ -5,7 +5,7 @@ import type { ControlledGit } from "../../commit/git";
|
|
|
5
5
|
import typesDescriptionPrompt from "../../commit/prompts/types-description.md" with { type: "text" };
|
|
6
6
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
7
7
|
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
8
|
-
import type {
|
|
8
|
+
import type { Settings } from "../../config/settings";
|
|
9
9
|
import { getMarkdownTheme } from "../../modes/theme/theme";
|
|
10
10
|
import { createAgentSession } from "../../sdk";
|
|
11
11
|
import type { AgentSessionEvent } from "../../session/agent-session";
|
|
@@ -19,7 +19,7 @@ export interface CommitAgentInput {
|
|
|
19
19
|
cwd: string;
|
|
20
20
|
git: ControlledGit;
|
|
21
21
|
model: Model<Api>;
|
|
22
|
-
|
|
22
|
+
settings: Settings;
|
|
23
23
|
modelRegistry: ModelRegistry;
|
|
24
24
|
authStorage: AuthStorage;
|
|
25
25
|
userContext?: string;
|
|
@@ -47,7 +47,7 @@ export async function runCommitAgentSession(input: CommitAgentInput): Promise<Co
|
|
|
47
47
|
git: input.git,
|
|
48
48
|
authStorage: input.authStorage,
|
|
49
49
|
modelRegistry: input.modelRegistry,
|
|
50
|
-
|
|
50
|
+
settings: input.settings,
|
|
51
51
|
spawns,
|
|
52
52
|
state,
|
|
53
53
|
changelogTargets: input.changelogTargets,
|
|
@@ -58,7 +58,7 @@ export async function runCommitAgentSession(input: CommitAgentInput): Promise<Co
|
|
|
58
58
|
cwd: input.cwd,
|
|
59
59
|
authStorage: input.authStorage,
|
|
60
60
|
modelRegistry: input.modelRegistry,
|
|
61
|
-
|
|
61
|
+
settingsInstance: input.settings,
|
|
62
62
|
model: input.model,
|
|
63
63
|
systemPrompt,
|
|
64
64
|
customTools: tools,
|
|
@@ -8,7 +8,7 @@ import { formatCommitMessage } from "../../commit/message";
|
|
|
8
8
|
import { resolvePrimaryModel, resolveSmolModel } from "../../commit/model-selection";
|
|
9
9
|
import type { CommitCommandArgs, ConventionalAnalysis } from "../../commit/types";
|
|
10
10
|
import { renderPromptTemplate } from "../../config/prompt-templates";
|
|
11
|
-
import {
|
|
11
|
+
import { Settings } from "../../config/settings";
|
|
12
12
|
import { discoverAuthStorage, discoverContextFiles, discoverModels } from "../../sdk";
|
|
13
13
|
import { type ExistingChangelogEntries, runCommitAgentSession } from "./agent";
|
|
14
14
|
import { generateFallbackProposal } from "./fallback";
|
|
@@ -26,7 +26,8 @@ interface CommitExecutionContext {
|
|
|
26
26
|
export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
27
27
|
const cwd = process.cwd();
|
|
28
28
|
const git = new ControlledGit(cwd);
|
|
29
|
-
const [
|
|
29
|
+
const [settingsInstance, authStorage] = await Promise.all([Settings.init({ cwd }), discoverAuthStorage()]);
|
|
30
|
+
const settings = settingsInstance;
|
|
30
31
|
|
|
31
32
|
writeStdout("● Resolving model...");
|
|
32
33
|
const modelRegistry = discoverModels(authStorage);
|
|
@@ -40,12 +41,12 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
40
41
|
return stagedFiles;
|
|
41
42
|
})();
|
|
42
43
|
|
|
43
|
-
const primaryModelPromise = resolvePrimaryModel(args.model,
|
|
44
|
+
const primaryModelPromise = resolvePrimaryModel(args.model, settings, modelRegistry);
|
|
44
45
|
const [primaryModelResult, stagedFiles] = await Promise.all([primaryModelPromise, stagedFilesPromise]);
|
|
45
46
|
const { model: primaryModel, apiKey: primaryApiKey } = primaryModelResult;
|
|
46
47
|
writeStdout(` └─ ${primaryModel.name}`);
|
|
47
48
|
|
|
48
|
-
const { model: agentModel } = await resolveSmolModel(
|
|
49
|
+
const { model: agentModel } = await resolveSmolModel(settings, modelRegistry, primaryModel, primaryApiKey);
|
|
49
50
|
|
|
50
51
|
if (stagedFiles.length === 0) {
|
|
51
52
|
writeStderr("No changes to commit.");
|
|
@@ -123,7 +124,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
|
123
124
|
cwd,
|
|
124
125
|
git,
|
|
125
126
|
model: agentModel,
|
|
126
|
-
|
|
127
|
+
settings,
|
|
127
128
|
modelRegistry,
|
|
128
129
|
authStorage,
|
|
129
130
|
userContext: args.context,
|