@oh-my-pi/pi-coding-agent 16.3.12 → 16.3.13
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 +16 -0
- package/dist/cli.js +3244 -3246
- package/dist/types/config/keybindings.d.ts +9 -4
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +11 -2
- package/dist/types/mnemopi/state.d.ts +7 -3
- package/dist/types/modes/acp/acp-event-mapper.d.ts +1 -0
- package/dist/types/modes/components/read-tool-group.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -1
- package/dist/types/modes/rpc/rpc-client.d.ts +11 -5
- package/dist/types/modes/rpc/rpc-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +3 -4
- package/dist/types/tools/read.d.ts +1 -0
- package/dist/types/tools/renderers.d.ts +12 -5
- package/dist/types/tools/ssh.d.ts +4 -1
- package/dist/types/tools/write.d.ts +1 -0
- package/package.json +12 -12
- package/src/config/keybindings.ts +62 -10
- package/src/config/model-registry.ts +85 -20
- package/src/config/settings.ts +48 -21
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/extensions/types.ts +13 -2
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/mnemopi/state.ts +19 -5
- package/src/modes/acp/acp-agent.ts +69 -8
- package/src/modes/acp/acp-event-mapper.ts +1 -1
- package/src/modes/components/read-tool-group.ts +5 -1
- package/src/modes/components/tool-execution.ts +28 -24
- package/src/modes/controllers/extension-ui-controller.test.ts +16 -0
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +5 -55
- package/src/modes/interactive-mode.ts +43 -2
- package/src/modes/rpc/rpc-client.ts +42 -13
- package/src/modes/rpc/rpc-mode.ts +21 -19
- package/src/modes/types.ts +3 -0
- package/src/prompts/agents/plan.md +0 -1
- package/src/prompts/agents/reviewer.md +0 -1
- package/src/prompts/tools/grep.md +2 -1
- package/src/prompts/tools/memory-edit.md +2 -0
- package/src/session/agent-session.ts +8 -5
- package/src/tools/grep.ts +58 -13
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/memory-edit.ts +3 -1
- package/src/tools/read.ts +33 -13
- package/src/tools/renderers.ts +13 -5
- package/src/tools/ssh.ts +10 -3
- package/src/tools/tts.ts +1 -1
- package/src/tools/write.ts +26 -0
package/src/config/settings.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
getProjectDir,
|
|
23
23
|
isEnoent,
|
|
24
24
|
logger,
|
|
25
|
+
MAIN_CONFIG_FILENAMES,
|
|
25
26
|
procmgr,
|
|
26
27
|
setWorktreesDir,
|
|
27
28
|
} from "@oh-my-pi/pi-utils";
|
|
@@ -60,7 +61,7 @@ export interface RawSettings {
|
|
|
60
61
|
export interface SettingsOptions {
|
|
61
62
|
/** Current working directory for project settings discovery */
|
|
62
63
|
cwd?: string;
|
|
63
|
-
/** Agent directory for config.yml storage */
|
|
64
|
+
/** Agent directory for config.yml/config.yaml storage */
|
|
64
65
|
agentDir?: string;
|
|
65
66
|
/** Don't persist to disk (for tests) */
|
|
66
67
|
inMemory?: boolean;
|
|
@@ -234,7 +235,7 @@ export class Settings {
|
|
|
234
235
|
#storage: AgentStorage | null = null;
|
|
235
236
|
|
|
236
237
|
#configFiles: string[] = [];
|
|
237
|
-
/** Global settings from config.yml */
|
|
238
|
+
/** Global settings from config.yml/config.yaml */
|
|
238
239
|
#global: RawSettings = {};
|
|
239
240
|
/** Project settings from .claude/settings.yml etc */
|
|
240
241
|
#project: RawSettings = {};
|
|
@@ -264,7 +265,7 @@ export class Settings {
|
|
|
264
265
|
private constructor(options: SettingsOptions = {}) {
|
|
265
266
|
this.#cwd = path.normalize(options.cwd ?? getProjectDir());
|
|
266
267
|
this.#agentDir = path.normalize(options.agentDir ?? getAgentDir());
|
|
267
|
-
this.#configPath = options.inMemory ? null : path.join(this.#agentDir,
|
|
268
|
+
this.#configPath = options.inMemory ? null : path.join(this.#agentDir, MAIN_CONFIG_FILENAMES[0]);
|
|
268
269
|
this.#configFiles = options.configFiles?.map(file => path.resolve(this.#cwd, expandTilde(file))) ?? [];
|
|
269
270
|
this.#persist = !options.inMemory && options.readOnly !== true;
|
|
270
271
|
|
|
@@ -458,6 +459,7 @@ export class Settings {
|
|
|
458
459
|
inMemory: !this.#persist,
|
|
459
460
|
});
|
|
460
461
|
cloned.#storage = this.#storage;
|
|
462
|
+
cloned.#configPath = this.#configPath;
|
|
461
463
|
cloned.#global = structuredClone(this.#global);
|
|
462
464
|
cloned.#project = this.#persist ? await cloned.#loadProjectSettings() : structuredClone(this.#project);
|
|
463
465
|
cloned.#configFiles = [...this.#configFiles];
|
|
@@ -676,16 +678,22 @@ export class Settings {
|
|
|
676
678
|
|
|
677
679
|
async #load(): Promise<Settings> {
|
|
678
680
|
// Project settings load (loadCapability scans cwd) is independent of the
|
|
679
|
-
// persist chain (storage open → legacy migration → global config
|
|
680
|
-
//
|
|
681
|
-
// persist steps remain sequential:
|
|
682
|
-
//
|
|
681
|
+
// persist chain (storage open → legacy migration → global config read), so
|
|
682
|
+
// kick it off first and await after the persist chain completes. The
|
|
683
|
+
// persist steps remain sequential: existing config discovery decides
|
|
684
|
+
// whether migration may write config.yml before the global config is read;
|
|
685
|
+
// migration's db fallback needs #storage opened.
|
|
683
686
|
const projectPromise = this.#loadProjectSettings();
|
|
684
687
|
|
|
685
688
|
if (this.#persist) {
|
|
686
689
|
this.#storage = await AgentStorage.open(getAgentDbPath(this.#agentDir));
|
|
687
|
-
await this.#
|
|
688
|
-
|
|
690
|
+
const existingConfig = await this.#loadExistingMainYaml();
|
|
691
|
+
if (existingConfig) {
|
|
692
|
+
this.#global = existingConfig;
|
|
693
|
+
} else {
|
|
694
|
+
await this.#migrateFromLegacy();
|
|
695
|
+
this.#global = await this.#loadYaml(this.#configPath!);
|
|
696
|
+
}
|
|
689
697
|
await this.#seedLastChangelogVersionMarker();
|
|
690
698
|
}
|
|
691
699
|
|
|
@@ -701,8 +709,9 @@ export class Settings {
|
|
|
701
709
|
async #loadReadOnly(): Promise<Settings> {
|
|
702
710
|
const projectPromise = this.#loadProjectSettings();
|
|
703
711
|
|
|
704
|
-
|
|
705
|
-
|
|
712
|
+
const existingConfig = await this.#loadExistingMainYaml();
|
|
713
|
+
if (existingConfig) {
|
|
714
|
+
this.#global = existingConfig;
|
|
706
715
|
}
|
|
707
716
|
|
|
708
717
|
this.#project = await projectPromise;
|
|
@@ -712,20 +721,46 @@ export class Settings {
|
|
|
712
721
|
}
|
|
713
722
|
|
|
714
723
|
async #loadYaml(filePath: string): Promise<RawSettings> {
|
|
724
|
+
const loaded = await this.#loadYamlIfPresent(filePath);
|
|
725
|
+
return loaded ?? {};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
async #loadYamlIfPresent(filePath: string): Promise<RawSettings | null> {
|
|
729
|
+
let content: string;
|
|
730
|
+
try {
|
|
731
|
+
content = await Bun.file(filePath).text();
|
|
732
|
+
} catch (error) {
|
|
733
|
+
if (isEnoent(error)) return null;
|
|
734
|
+
logger.warn("Settings: failed to load", { path: filePath, error: String(error) });
|
|
735
|
+
return {};
|
|
736
|
+
}
|
|
737
|
+
|
|
715
738
|
try {
|
|
716
|
-
const content = await Bun.file(filePath).text();
|
|
717
739
|
const parsed = YAML.parse(content);
|
|
718
740
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
719
741
|
return {};
|
|
720
742
|
}
|
|
721
743
|
return this.#migrateRawSettings(parsed as RawSettings);
|
|
722
744
|
} catch (error) {
|
|
723
|
-
if (isEnoent(error)) return {};
|
|
724
745
|
logger.warn("Settings: failed to load", { path: filePath, error: String(error) });
|
|
725
746
|
return {};
|
|
726
747
|
}
|
|
727
748
|
}
|
|
728
749
|
|
|
750
|
+
async #loadExistingMainYaml(): Promise<RawSettings | null> {
|
|
751
|
+
if (!this.#configPath) return null;
|
|
752
|
+
for (const filename of MAIN_CONFIG_FILENAMES) {
|
|
753
|
+
const configPath = path.join(this.#agentDir, filename);
|
|
754
|
+
const loaded = await this.#loadYamlIfPresent(configPath);
|
|
755
|
+
if (loaded) {
|
|
756
|
+
this.#configPath = configPath;
|
|
757
|
+
return loaded;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
this.#configPath = path.join(this.#agentDir, MAIN_CONFIG_FILENAMES[0]);
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
763
|
+
|
|
729
764
|
async #loadProjectSettings(): Promise<RawSettings> {
|
|
730
765
|
try {
|
|
731
766
|
const result = await loadCapability(settingsCapability.id, { cwd: this.#cwd });
|
|
@@ -781,14 +816,6 @@ export class Settings {
|
|
|
781
816
|
async #migrateFromLegacy(): Promise<void> {
|
|
782
817
|
if (!this.#configPath) return;
|
|
783
818
|
|
|
784
|
-
// Check if config.yml already exists
|
|
785
|
-
try {
|
|
786
|
-
await Bun.file(this.#configPath).text();
|
|
787
|
-
return; // Already exists, no migration needed
|
|
788
|
-
} catch (err) {
|
|
789
|
-
if (!isEnoent(err)) return;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
819
|
let settings: RawSettings = {};
|
|
793
820
|
let migrated = false;
|
|
794
821
|
|
|
@@ -30,7 +30,7 @@ import type {
|
|
|
30
30
|
TSchema,
|
|
31
31
|
} from "@oh-my-pi/pi-ai";
|
|
32
32
|
import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/oauth/types";
|
|
33
|
-
import type { AutocompleteItem, Component, EditorTheme, KeyId, TUI } from "@oh-my-pi/pi-tui";
|
|
33
|
+
import type { AutocompleteItem, AutocompleteProvider, Component, EditorTheme, KeyId, TUI } from "@oh-my-pi/pi-tui";
|
|
34
34
|
import type { logger as PiLogger } from "@oh-my-pi/pi-utils";
|
|
35
35
|
import type { Type as arktype } from "arktype";
|
|
36
36
|
import type * as zod from "zod/v4";
|
|
@@ -163,6 +163,9 @@ export type ExtensionUiComponent = Component & { dispose?(): void };
|
|
|
163
163
|
export type ExtensionUiComponentFactory = (tui: TUI, theme: Theme) => ExtensionUiComponent;
|
|
164
164
|
export type ExtensionWidgetContent = string[] | ExtensionUiComponentFactory | undefined;
|
|
165
165
|
|
|
166
|
+
/** Wrap the current autocomplete provider with additional behavior (pi-compatible). */
|
|
167
|
+
export type AutocompleteProviderFactory = (current: AutocompleteProvider) => AutocompleteProvider;
|
|
168
|
+
|
|
166
169
|
/**
|
|
167
170
|
* UI context for extensions to request interactive UI.
|
|
168
171
|
* Each mode (interactive, RPC, print) provides its own implementation.
|
|
@@ -243,6 +246,14 @@ export interface ExtensionUIContext {
|
|
|
243
246
|
editorOptions?: { promptStyle?: boolean },
|
|
244
247
|
): Promise<string | undefined>;
|
|
245
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Stack additional autocomplete behavior on top of the built-in provider
|
|
251
|
+
* (pi-compatible). Interactive mode rebuilds the editor's provider through
|
|
252
|
+
* every registered factory, in registration order; headless modes (print,
|
|
253
|
+
* RPC, ACP, subagents) accept and ignore the factory.
|
|
254
|
+
*/
|
|
255
|
+
addAutocompleteProvider(factory: AutocompleteProviderFactory): void;
|
|
256
|
+
|
|
246
257
|
/**
|
|
247
258
|
* Set a custom editor component via factory function, or `undefined` to restore the default editor.
|
|
248
259
|
*
|
|
@@ -1107,7 +1118,7 @@ export interface ExtensionAPI {
|
|
|
1107
1118
|
options?: { triggerTurn?: boolean; deliverAs?: "steer" | "followUp" | "nextTurn" },
|
|
1108
1119
|
): void;
|
|
1109
1120
|
|
|
1110
|
-
/** Send a user
|
|
1121
|
+
/** Send a user prompt: idle starts a turn; streaming queues as steer unless deliverAs is set. */
|
|
1111
1122
|
sendUserMessage(
|
|
1112
1123
|
content: string | (TextContent | ImageContent)[],
|
|
1113
1124
|
options?: { deliverAs?: "steer" | "followUp" },
|