@oh-my-pi/pi-coding-agent 12.1.0 → 12.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/examples/sdk/11-sessions.ts +1 -1
- package/package.json +16 -11
- package/src/capability/index.ts +2 -1
- package/src/capability/types.ts +1 -1
- package/src/cli/file-processor.ts +2 -1
- package/src/cli/shell-cli.ts +2 -2
- package/src/commit/agentic/index.ts +2 -1
- package/src/commit/pipeline.ts +2 -1
- package/src/config/prompt-templates.ts +3 -3
- package/src/config/settings-schema.ts +11 -0
- package/src/config/settings.ts +2 -2
- package/src/config.ts +6 -6
- package/src/debug/system-info.ts +2 -2
- package/src/extensibility/custom-commands/loader.ts +5 -5
- package/src/extensibility/plugins/installer.ts +2 -2
- package/src/extensibility/plugins/manager.ts +2 -1
- package/src/extensibility/skills.ts +3 -2
- package/src/extensibility/slash-commands.ts +1 -1
- package/src/ipy/executor.ts +2 -2
- package/src/ipy/modules.ts +3 -3
- package/src/main.ts +9 -7
- package/src/mcp/transports/stdio.ts +2 -1
- package/src/modes/components/footer.ts +3 -2
- package/src/modes/components/oauth-selector.ts +96 -21
- package/src/modes/components/status-line/segments.ts +2 -1
- package/src/modes/components/status-line.ts +2 -1
- package/src/modes/components/tool-execution.ts +2 -1
- package/src/modes/controllers/command-controller.ts +60 -2
- package/src/modes/controllers/mcp-command-controller.ts +8 -8
- package/src/modes/controllers/selector-controller.ts +35 -11
- package/src/modes/interactive-mode.ts +2 -2
- package/src/sdk.ts +37 -11
- package/src/session/agent-session.ts +12 -0
- package/src/session/session-manager.ts +7 -4
- package/src/system-prompt.ts +41 -28
- package/src/tools/bash-normalize.ts +2 -2
- package/src/tools/bash.ts +2 -1
- package/src/tools/fetch.ts +3 -3
- package/src/tools/python.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [12.2.0] - 2026-02-13
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `providerSessionState` property to AgentSession for managing provider-scoped transport and session caches
|
|
10
|
+
- Added automatic cleanup of provider session state resources on session disposal
|
|
11
|
+
- Added `providers.openaiWebsockets` setting to prefer websocket transport for OpenAI Codex models
|
|
12
|
+
- Added provider details display in session info showing authentication mode, transport, and connection settings
|
|
13
|
+
- Added automatic prewarm of OpenAI Codex websocket connections on session creation for improved performance
|
|
14
|
+
- Added real-time authentication validation in OAuth provider selector with visual status indicators (checking, valid, invalid)
|
|
15
|
+
- Added `validateAuth` and `requestRender` options to OAuthSelectorComponent for custom authentication validation and UI refresh callbacks
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Changed `providers.openaiWebsockets` setting from boolean to enum with values "auto", "off", "on" for more granular websocket policy control (auto uses model defaults, on forces websocket, off disables it)
|
|
20
|
+
- Enhanced provider details display to include live provider session state information
|
|
21
|
+
- Enhanced session info output to display active provider configuration and authentication details
|
|
22
|
+
- Replaced `process.cwd()` with `getProjectDir()` throughout codebase for improved project directory detection and handling
|
|
23
|
+
- Made `SessionManager.list()` async to support asynchronous session discovery operations
|
|
24
|
+
- Preserved internal whitespace and indentation in bash command normalization to support heredocs and indentation-sensitive scripts
|
|
25
|
+
- Improved git context loading performance with configurable timeouts and parallel status/commit queries
|
|
26
|
+
- Enhanced git context reliability with better error handling for timeout and command failures
|
|
27
|
+
- Changed OAuth provider selector to display live authentication status instead of static login state
|
|
28
|
+
- Changed logout flow to refresh OAuth provider authentication state before showing selector
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- Improved error reporting in fetch tool to include HTTP status codes when URL fetching fails
|
|
33
|
+
- Fixed fetch tool to preserve actual response metadata (finalUrl, contentType) instead of defaults when requests fail
|
|
34
|
+
|
|
5
35
|
## [12.1.0] - 2026-02-13
|
|
6
36
|
|
|
7
37
|
### Added
|
|
@@ -25,7 +25,7 @@ if (modelFallbackMessage) console.log("Note:", modelFallbackMessage);
|
|
|
25
25
|
console.log("Continued session:", continued.sessionFile);
|
|
26
26
|
|
|
27
27
|
// List and open specific session
|
|
28
|
-
const sessions = SessionManager.list(process.cwd());
|
|
28
|
+
const sessions = await SessionManager.list(process.cwd());
|
|
29
29
|
console.log(`\nFound ${sessions.length} sessions:`);
|
|
30
30
|
for (const info of sessions.slice(0, 3)) {
|
|
31
31
|
console.log(` ${info.id.slice(0, 8)}… - "${info.firstMessage.slice(0, 30)}…"`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.2.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -84,12 +84,12 @@
|
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
86
|
"@mozilla/readability": "0.6.0",
|
|
87
|
-
"@oh-my-pi/omp-stats": "12.
|
|
88
|
-
"@oh-my-pi/pi-agent-core": "12.
|
|
89
|
-
"@oh-my-pi/pi-ai": "12.
|
|
90
|
-
"@oh-my-pi/pi-natives": "12.
|
|
91
|
-
"@oh-my-pi/pi-tui": "12.
|
|
92
|
-
"@oh-my-pi/pi-utils": "12.
|
|
87
|
+
"@oh-my-pi/omp-stats": "12.2.0",
|
|
88
|
+
"@oh-my-pi/pi-agent-core": "12.2.0",
|
|
89
|
+
"@oh-my-pi/pi-ai": "12.2.0",
|
|
90
|
+
"@oh-my-pi/pi-natives": "12.2.0",
|
|
91
|
+
"@oh-my-pi/pi-tui": "12.2.0",
|
|
92
|
+
"@oh-my-pi/pi-utils": "12.2.0",
|
|
93
93
|
"@sinclair/typebox": "^0.34.48",
|
|
94
94
|
"@xterm/headless": "^6.0.0",
|
|
95
95
|
"ajv": "^8.17.1",
|
|
@@ -119,14 +119,19 @@
|
|
|
119
119
|
"tui",
|
|
120
120
|
"agent"
|
|
121
121
|
],
|
|
122
|
-
"author": "
|
|
122
|
+
"author": "Can Bölük",
|
|
123
|
+
"contributors": ["Mario Zechner"],
|
|
123
124
|
"license": "MIT",
|
|
124
125
|
"repository": {
|
|
125
126
|
"type": "git",
|
|
126
127
|
"url": "git+https://github.com/can1357/oh-my-pi.git",
|
|
127
128
|
"directory": "packages/coding-agent"
|
|
128
129
|
},
|
|
129
|
-
"
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
131
|
+
"bugs": {
|
|
132
|
+
"url": "https://github.com/can1357/oh-my-pi/issues"
|
|
133
|
+
},
|
|
134
|
+
"engines": {
|
|
135
|
+
"bun": ">=1.3.7"
|
|
136
|
+
}
|
|
132
137
|
}
|
package/src/capability/index.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import * as os from "node:os";
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
12
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
12
13
|
|
|
13
14
|
/** Conditional startup debug prints (stderr) when PI_DEBUG_STARTUP is set */
|
|
14
15
|
const debugStartup = $env.PI_DEBUG_STARTUP ? (stage: string) => process.stderr.write(`[startup] ${stage}\n`) : () => {};
|
|
@@ -221,7 +222,7 @@ export async function loadCapability<T>(capabilityId: string, options: LoadOptio
|
|
|
221
222
|
throw new Error(`Unknown capability: "${capabilityId}"`);
|
|
222
223
|
}
|
|
223
224
|
|
|
224
|
-
const cwd = options.cwd ??
|
|
225
|
+
const cwd = options.cwd ?? getProjectDir();
|
|
225
226
|
const home = os.homedir();
|
|
226
227
|
const ctx: LoadContext = { cwd, home };
|
|
227
228
|
const providers = filterProviders(capability, options);
|
package/src/capability/types.ts
CHANGED
|
@@ -62,7 +62,7 @@ export interface LoadOptions {
|
|
|
62
62
|
providers?: string[];
|
|
63
63
|
/** Exclude these providers (by ID). Default: none */
|
|
64
64
|
excludeProviders?: string[];
|
|
65
|
-
/** Custom cwd. Default:
|
|
65
|
+
/** Custom cwd. Default: getProjectDir() */
|
|
66
66
|
cwd?: string;
|
|
67
67
|
/** Include items even if they fail validation. Default: false */
|
|
68
68
|
includeInvalid?: boolean;
|
|
@@ -5,6 +5,7 @@ import * as fs from "node:fs";
|
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
7
7
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
8
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
8
9
|
import chalk from "chalk";
|
|
9
10
|
import { resolveReadPath } from "../tools/path-utils";
|
|
10
11
|
import { formatSize } from "../tools/truncate";
|
|
@@ -34,7 +35,7 @@ export async function processFileArguments(fileArgs: string[], options?: Process
|
|
|
34
35
|
|
|
35
36
|
for (const fileArg of fileArgs) {
|
|
36
37
|
// Expand and resolve path (handles ~ expansion and macOS screenshot Unicode spaces)
|
|
37
|
-
const absolutePath = path.resolve(resolveReadPath(fileArg,
|
|
38
|
+
const absolutePath = path.resolve(resolveReadPath(fileArg, getProjectDir()));
|
|
38
39
|
|
|
39
40
|
const stat = fs.statSync(absolutePath, { throwIfNoEntry: false });
|
|
40
41
|
if (!stat) {
|
package/src/cli/shell-cli.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
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
|
+
import { APP_NAME, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
10
10
|
import chalk from "chalk";
|
|
11
11
|
import { Settings } from "../config/settings";
|
|
12
12
|
import { getOrCreateSnapshot } from "../utils/shell-snapshot";
|
|
@@ -47,7 +47,7 @@ export async function runShellCommand(cmd: ShellCommandArgs): Promise<void> {
|
|
|
47
47
|
process.exit(1);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
const cwd = cmd.cwd ? path.resolve(cmd.cwd) :
|
|
50
|
+
const cwd = cmd.cwd ? path.resolve(cmd.cwd) : getProjectDir();
|
|
51
51
|
const settings = await Settings.init({ cwd });
|
|
52
52
|
const { shell, env: shellEnv } = settings.getShellConfig();
|
|
53
53
|
const snapshotPath = cmd.noSnapshot || !shell.includes("bash") ? null : await getOrCreateSnapshot(shell, shellEnv);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { createInterface } from "node:readline/promises";
|
|
3
3
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
4
5
|
import { applyChangelogProposals } from "../../commit/changelog";
|
|
5
6
|
import { detectChangelogBoundaries } from "../../commit/changelog/detect";
|
|
6
7
|
import { parseUnreleasedSection } from "../../commit/changelog/parse";
|
|
@@ -26,7 +27,7 @@ interface CommitExecutionContext {
|
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export async function runAgenticCommit(args: CommitCommandArgs): Promise<void> {
|
|
29
|
-
const cwd =
|
|
30
|
+
const cwd = getProjectDir();
|
|
30
31
|
const git = new ControlledGit(cwd);
|
|
31
32
|
const [settings, authStorage] = await Promise.all([Settings.init({ cwd }), discoverAuthStorage()]);
|
|
32
33
|
|
package/src/commit/pipeline.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
4
5
|
import { ModelRegistry } from "../config/model-registry";
|
|
5
6
|
import { renderPromptTemplate } from "../config/prompt-templates";
|
|
6
7
|
import { Settings } from "../config/settings";
|
|
@@ -38,7 +39,7 @@ export async function runCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
async function runLegacyCommitCommand(args: CommitCommandArgs): Promise<void> {
|
|
41
|
-
const cwd =
|
|
42
|
+
const cwd = getProjectDir();
|
|
42
43
|
const settings = await Settings.init();
|
|
43
44
|
const commitSettings = settings.getGroup("commit");
|
|
44
45
|
const authStorage = await discoverAuthStorage();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import { getProjectPromptsDir, getPromptsDir } from "@oh-my-pi/pi-utils/dirs";
|
|
4
|
+
import { getProjectDir, getProjectPromptsDir, getPromptsDir } from "@oh-my-pi/pi-utils/dirs";
|
|
5
5
|
import Handlebars from "handlebars";
|
|
6
6
|
import { computeLineHash } from "../patch/hashline";
|
|
7
7
|
import { jtdToTypeScript } from "../tools/jtd-to-typescript";
|
|
@@ -459,7 +459,7 @@ async function loadTemplatesFromDir(
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
export interface LoadPromptTemplatesOptions {
|
|
462
|
-
/** Working directory for project-local templates. Default:
|
|
462
|
+
/** Working directory for project-local templates. Default: getProjectDir() */
|
|
463
463
|
cwd?: string;
|
|
464
464
|
/** Agent config directory for global templates. Default: from getPromptsDir() */
|
|
465
465
|
agentDir?: string;
|
|
@@ -471,7 +471,7 @@ export interface LoadPromptTemplatesOptions {
|
|
|
471
471
|
* 2. Project: cwd/.omp/prompts/
|
|
472
472
|
*/
|
|
473
473
|
export async function loadPromptTemplates(options: LoadPromptTemplatesOptions = {}): Promise<PromptTemplate[]> {
|
|
474
|
-
const resolvedCwd = options.cwd ??
|
|
474
|
+
const resolvedCwd = options.cwd ?? getProjectDir();
|
|
475
475
|
const resolvedAgentDir = options.agentDir ?? getPromptsDir();
|
|
476
476
|
|
|
477
477
|
const templates: PromptTemplate[] = [];
|
|
@@ -592,6 +592,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
592
592
|
submenu: true,
|
|
593
593
|
},
|
|
594
594
|
},
|
|
595
|
+
"providers.openaiWebsockets": {
|
|
596
|
+
type: "enum",
|
|
597
|
+
values: ["auto", "off", "on"] as const,
|
|
598
|
+
default: "auto",
|
|
599
|
+
ui: {
|
|
600
|
+
tab: "services",
|
|
601
|
+
label: "OpenAI websockets",
|
|
602
|
+
description: "Websocket policy for OpenAI Codex models (auto uses model defaults, on forces, off disables)",
|
|
603
|
+
submenu: true,
|
|
604
|
+
},
|
|
605
|
+
},
|
|
595
606
|
|
|
596
607
|
// ─────────────────────────────────────────────────────────────────────────
|
|
597
608
|
// Exa settings
|
package/src/config/settings.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as path from "node:path";
|
|
16
16
|
import { isEnoent, logger, procmgr } from "@oh-my-pi/pi-utils";
|
|
17
|
-
import { getAgentDbPath, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
|
17
|
+
import { getAgentDbPath, getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
18
18
|
import { YAML } from "bun";
|
|
19
19
|
import { type Settings as SettingsCapabilityItem, settingsCapability } from "../capability/settings";
|
|
20
20
|
import type { ModelRole } from "../config/model-registry";
|
|
@@ -146,7 +146,7 @@ export class Settings {
|
|
|
146
146
|
#persist: boolean;
|
|
147
147
|
|
|
148
148
|
private constructor(options: SettingsOptions = {}) {
|
|
149
|
-
this.#cwd = path.normalize(options.cwd ??
|
|
149
|
+
this.#cwd = path.normalize(options.cwd ?? getProjectDir());
|
|
150
150
|
this.#agentDir = path.normalize(options.agentDir ?? getAgentDir());
|
|
151
151
|
this.#configPath = options.inMemory ? null : path.join(this.#agentDir, "config.yml");
|
|
152
152
|
this.#persist = !options.inMemory;
|
package/src/config.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import { CONFIG_DIR_NAME, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
|
5
|
+
import { CONFIG_DIR_NAME, getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
6
6
|
import type { TSchema } from "@sinclair/typebox";
|
|
7
7
|
import { Value } from "@sinclair/typebox/value";
|
|
8
8
|
import { Ajv, type ErrorObject, type ValidateFunction } from "ajv";
|
|
@@ -39,8 +39,8 @@ export function getPackageDir(): string {
|
|
|
39
39
|
}
|
|
40
40
|
dir = path.dirname(dir);
|
|
41
41
|
}
|
|
42
|
-
// Fallback to
|
|
43
|
-
return
|
|
42
|
+
// Fallback to project dir (docs/examples won't be found, but that's fine)
|
|
43
|
+
return getProjectDir();
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/** Get path to CHANGELOG.md (optional, may not exist in binary) */
|
|
@@ -273,7 +273,7 @@ export interface GetConfigDirsOptions {
|
|
|
273
273
|
user?: boolean;
|
|
274
274
|
/** Include project-level directories (.omp/...). Default: true */
|
|
275
275
|
project?: boolean;
|
|
276
|
-
/** Current working directory for project paths. Default:
|
|
276
|
+
/** Current working directory for project paths. Default: getProjectDir() */
|
|
277
277
|
cwd?: string;
|
|
278
278
|
/** Only return directories that exist. Default: false */
|
|
279
279
|
existingOnly?: boolean;
|
|
@@ -296,7 +296,7 @@ export interface GetConfigDirsOptions {
|
|
|
296
296
|
* getConfigDirs("skills", { user: false, existingOnly: true })
|
|
297
297
|
*/
|
|
298
298
|
export function getConfigDirs(subpath: string, options: GetConfigDirsOptions = {}): ConfigDirEntry[] {
|
|
299
|
-
const { user = true, project = true, cwd =
|
|
299
|
+
const { user = true, project = true, cwd = getProjectDir(), existingOnly = false } = options;
|
|
300
300
|
const results: ConfigDirEntry[] = [];
|
|
301
301
|
|
|
302
302
|
// User-level directories (highest priority)
|
|
@@ -382,7 +382,7 @@ export function findConfigFileWithMeta(
|
|
|
382
382
|
* Returns one entry per config base (.omp, .claude) - the nearest one found.
|
|
383
383
|
* Results are in priority order (highest first).
|
|
384
384
|
*/
|
|
385
|
-
export function findAllNearestProjectConfigDirs(subpath: string, cwd: string =
|
|
385
|
+
export function findAllNearestProjectConfigDirs(subpath: string, cwd: string = getProjectDir()): ConfigDirEntry[] {
|
|
386
386
|
const results: ConfigDirEntry[] = [];
|
|
387
387
|
const foundBases = new Set<string>();
|
|
388
388
|
|
package/src/debug/system-info.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* System information collection for debug reports.
|
|
3
3
|
*/
|
|
4
4
|
import * as os from "node:os";
|
|
5
|
-
import { VERSION } from "@oh-my-pi/pi-utils/dirs";
|
|
5
|
+
import { getProjectDir, VERSION } from "@oh-my-pi/pi-utils/dirs";
|
|
6
6
|
|
|
7
7
|
export interface SystemInfo {
|
|
8
8
|
os: string;
|
|
@@ -65,7 +65,7 @@ export async function collectSystemInfo(): Promise<SystemInfo> {
|
|
|
65
65
|
bun: Bun.version,
|
|
66
66
|
node: process.version,
|
|
67
67
|
},
|
|
68
|
-
cwd:
|
|
68
|
+
cwd: getProjectDir(),
|
|
69
69
|
shell,
|
|
70
70
|
terminal,
|
|
71
71
|
};
|
|
@@ -8,7 +8,7 @@ import * as fs from "node:fs";
|
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import * as piCodingAgent from "@oh-my-pi/pi-coding-agent";
|
|
10
10
|
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
11
|
-
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
|
11
|
+
import { getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
12
12
|
import * as typebox from "@sinclair/typebox";
|
|
13
13
|
import { getConfigDirs } from "../../config";
|
|
14
14
|
import { execCommand } from "../../exec/exec";
|
|
@@ -62,7 +62,7 @@ async function loadCommandModule(
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export interface DiscoverCustomCommandsOptions {
|
|
65
|
-
/** Current working directory. Default:
|
|
65
|
+
/** Current working directory. Default: getProjectDir() */
|
|
66
66
|
cwd?: string;
|
|
67
67
|
/** Agent config directory. Default: from getAgentDir() */
|
|
68
68
|
agentDir?: string;
|
|
@@ -80,7 +80,7 @@ export interface DiscoverCustomCommandsResult {
|
|
|
80
80
|
export async function discoverCustomCommands(
|
|
81
81
|
options: DiscoverCustomCommandsOptions = {},
|
|
82
82
|
): Promise<DiscoverCustomCommandsResult> {
|
|
83
|
-
const cwd = options.cwd ??
|
|
83
|
+
const cwd = options.cwd ?? getProjectDir();
|
|
84
84
|
const agentDir = options.agentDir ?? getAgentDir();
|
|
85
85
|
const paths: Array<{ path: string; source: CustomCommandSource }> = [];
|
|
86
86
|
const seen = new Set<string>();
|
|
@@ -136,7 +136,7 @@ export async function discoverCustomCommands(
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
export interface LoadCustomCommandsOptions {
|
|
139
|
-
/** Current working directory. Default:
|
|
139
|
+
/** Current working directory. Default: getProjectDir() */
|
|
140
140
|
cwd?: string;
|
|
141
141
|
/** Agent config directory. Default: from getAgentDir() */
|
|
142
142
|
agentDir?: string;
|
|
@@ -163,7 +163,7 @@ function loadBundledCommands(sharedApi: CustomCommandAPI): LoadedCustomCommand[]
|
|
|
163
163
|
* Discover and load custom commands from standard locations.
|
|
164
164
|
*/
|
|
165
165
|
export async function loadCustomCommands(options: LoadCustomCommandsOptions = {}): Promise<CustomCommandsLoadResult> {
|
|
166
|
-
const cwd = options.cwd ??
|
|
166
|
+
const cwd = options.cwd ?? getProjectDir();
|
|
167
167
|
const agentDir = options.agentDir ?? getAgentDir();
|
|
168
168
|
|
|
169
169
|
const { paths } = await discoverCustomCommands({ cwd, agentDir });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
|
4
|
+
import { getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
5
5
|
import type { InstalledPlugin } from "./types";
|
|
6
6
|
|
|
7
7
|
const PLUGINS_DIR = path.join(getAgentDir(), "plugins");
|
|
@@ -131,7 +131,7 @@ export async function listPlugins(): Promise<InstalledPlugin[]> {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export async function linkPlugin(localPath: string): Promise<void> {
|
|
134
|
-
const cwd =
|
|
134
|
+
const cwd = getProjectDir();
|
|
135
135
|
const absolutePath = path.resolve(cwd, localPath);
|
|
136
136
|
|
|
137
137
|
// Validate that resolved path is within cwd to prevent path traversal
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getPluginsLockfile,
|
|
7
7
|
getPluginsNodeModules,
|
|
8
8
|
getPluginsPackageJson,
|
|
9
|
+
getProjectDir,
|
|
9
10
|
getProjectPluginOverridesPath,
|
|
10
11
|
} from "@oh-my-pi/pi-utils/dirs";
|
|
11
12
|
import { extractPackageName, parsePluginSpec } from "./parser";
|
|
@@ -50,7 +51,7 @@ export class PluginManager {
|
|
|
50
51
|
#runtimeConfig: PluginRuntimeConfig | null = null;
|
|
51
52
|
#cwd: string;
|
|
52
53
|
|
|
53
|
-
constructor(cwd: string =
|
|
54
|
+
constructor(cwd: string = getProjectDir()) {
|
|
54
55
|
this.#cwd = cwd;
|
|
55
56
|
}
|
|
56
57
|
|
|
@@ -2,6 +2,7 @@ import * as fs from "node:fs/promises";
|
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
5
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
5
6
|
import { skillCapability } from "../capability/skill";
|
|
6
7
|
import type { SourceMeta } from "../capability/types";
|
|
7
8
|
import type { SkillsSettings } from "../config/settings";
|
|
@@ -207,7 +208,7 @@ async function scanDirectoryForSkills(dir: string): Promise<LoadSkillsResult> {
|
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
export interface LoadSkillsOptions extends SkillsSettings {
|
|
210
|
-
/** Working directory for project-local skills. Default:
|
|
211
|
+
/** Working directory for project-local skills. Default: getProjectDir() */
|
|
211
212
|
cwd?: string;
|
|
212
213
|
}
|
|
213
214
|
|
|
@@ -217,7 +218,7 @@ export interface LoadSkillsOptions extends SkillsSettings {
|
|
|
217
218
|
*/
|
|
218
219
|
export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadSkillsResult> {
|
|
219
220
|
const {
|
|
220
|
-
cwd =
|
|
221
|
+
cwd = getProjectDir(),
|
|
221
222
|
enabled = true,
|
|
222
223
|
enableCodexUser = true,
|
|
223
224
|
enableClaudeUser = true,
|
|
@@ -281,7 +281,7 @@ export function substituteArgs(content: string, args: string[]): string {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
export interface LoadSlashCommandsOptions {
|
|
284
|
-
/** Working directory for project-local commands. Default:
|
|
284
|
+
/** Working directory for project-local commands. Default: getProjectDir() */
|
|
285
285
|
cwd?: string;
|
|
286
286
|
}
|
|
287
287
|
|
package/src/ipy/executor.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { $env, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
3
|
-
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
|
3
|
+
import { getAgentDir, getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
4
4
|
import { OutputSink } from "../session/streaming-output";
|
|
5
5
|
import { time } from "../utils/timings";
|
|
6
6
|
import { shutdownSharedGateway } from "./gateway-coordinator";
|
|
@@ -523,7 +523,7 @@ export async function executePythonWithKernel(
|
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
export async function executePython(code: string, options?: PythonExecutorOptions): Promise<PythonResult> {
|
|
526
|
-
const cwd = options?.cwd ??
|
|
526
|
+
const cwd = options?.cwd ?? getProjectDir();
|
|
527
527
|
await ensureKernelAvailable(cwd);
|
|
528
528
|
|
|
529
529
|
const kernelMode = options?.kernelMode ?? "session";
|
package/src/ipy/modules.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { getAgentModulesDir, getProjectModulesDir } from "@oh-my-pi/pi-utils/dirs";
|
|
3
|
+
import { getAgentModulesDir, getProjectDir, getProjectModulesDir } from "@oh-my-pi/pi-utils/dirs";
|
|
4
4
|
|
|
5
5
|
export type PythonModuleSource = "user" | "project";
|
|
6
6
|
|
|
@@ -24,7 +24,7 @@ export interface PythonModuleExecutor {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface DiscoverPythonModulesOptions {
|
|
27
|
-
/** Working directory for project-level modules. Default:
|
|
27
|
+
/** Working directory for project-level modules. Default: getProjectDir() */
|
|
28
28
|
cwd?: string;
|
|
29
29
|
/** Agent directory for user-level modules. Default: from getAgentDir() */
|
|
30
30
|
agentDir?: string;
|
|
@@ -65,7 +65,7 @@ async function readModuleContent(candidate: ModuleCandidate): Promise<PythonModu
|
|
|
65
65
|
* Discover Python prelude extension modules from user and project directories.
|
|
66
66
|
*/
|
|
67
67
|
export async function discoverPythonModules(options: DiscoverPythonModulesOptions = {}): Promise<PythonModuleEntry[]> {
|
|
68
|
-
const cwd = options.cwd ??
|
|
68
|
+
const cwd = options.cwd ?? getProjectDir();
|
|
69
69
|
|
|
70
70
|
const userDir = getAgentModulesDir(options.agentDir);
|
|
71
71
|
const projectDir = getProjectModulesDir(cwd);
|
package/src/main.ts
CHANGED
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
* This file handles CLI argument parsing and translates them into
|
|
5
5
|
* createAgentSession() options. The SDK does the heavy lifting.
|
|
6
6
|
*/
|
|
7
|
+
|
|
8
|
+
import { realpathSync } from "node:fs";
|
|
7
9
|
import * as fs from "node:fs/promises";
|
|
8
10
|
import * as os from "node:os";
|
|
9
11
|
import * as path from "node:path";
|
|
10
12
|
import { createInterface } from "node:readline/promises";
|
|
11
13
|
import { type ImageContent, supportsXhigh } from "@oh-my-pi/pi-ai";
|
|
12
14
|
import { $env, postmortem } from "@oh-my-pi/pi-utils";
|
|
13
|
-
import { VERSION } from "@oh-my-pi/pi-utils/dirs";
|
|
15
|
+
import { getProjectDir, setProjectDir, VERSION } from "@oh-my-pi/pi-utils/dirs";
|
|
14
16
|
import chalk from "chalk";
|
|
15
17
|
import type { Args } from "./cli/args";
|
|
16
18
|
import { processFileArguments } from "./cli/file-processor";
|
|
@@ -278,11 +280,11 @@ async function maybeAutoChdir(parsed: Args): Promise<void> {
|
|
|
278
280
|
}
|
|
279
281
|
|
|
280
282
|
const normalizePath = (value: string) => {
|
|
281
|
-
const resolved = path.resolve(value);
|
|
283
|
+
const resolved = realpathSync(path.resolve(value));
|
|
282
284
|
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
283
285
|
};
|
|
284
286
|
|
|
285
|
-
const cwd = normalizePath(
|
|
287
|
+
const cwd = normalizePath(getProjectDir());
|
|
286
288
|
const normalizedHome = normalizePath(home);
|
|
287
289
|
if (cwd !== normalizedHome) {
|
|
288
290
|
return;
|
|
@@ -303,7 +305,7 @@ async function maybeAutoChdir(parsed: Args): Promise<void> {
|
|
|
303
305
|
if (!(await isDirectory(candidate))) {
|
|
304
306
|
continue;
|
|
305
307
|
}
|
|
306
|
-
|
|
308
|
+
setProjectDir(candidate);
|
|
307
309
|
return;
|
|
308
310
|
} catch {
|
|
309
311
|
// Try next candidate.
|
|
@@ -313,7 +315,7 @@ async function maybeAutoChdir(parsed: Args): Promise<void> {
|
|
|
313
315
|
try {
|
|
314
316
|
const fallback = os.tmpdir();
|
|
315
317
|
if (fallback && normalizePath(fallback) !== cwd && (await isDirectory(fallback))) {
|
|
316
|
-
|
|
318
|
+
setProjectDir(fallback);
|
|
317
319
|
}
|
|
318
320
|
} catch {
|
|
319
321
|
// Ignore fallback errors.
|
|
@@ -355,7 +357,7 @@ async function buildSessionOptions(
|
|
|
355
357
|
modelRegistry: ModelRegistry,
|
|
356
358
|
): Promise<CreateAgentSessionOptions> {
|
|
357
359
|
const options: CreateAgentSessionOptions = {
|
|
358
|
-
cwd: parsed.cwd ??
|
|
360
|
+
cwd: parsed.cwd ?? getProjectDir(),
|
|
359
361
|
};
|
|
360
362
|
|
|
361
363
|
// Auto-discover SYSTEM.md if no CLI system prompt provided
|
|
@@ -524,7 +526,7 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
|
|
|
524
526
|
process.exit(1);
|
|
525
527
|
}
|
|
526
528
|
|
|
527
|
-
const cwd =
|
|
529
|
+
const cwd = getProjectDir();
|
|
528
530
|
await Settings.init({ cwd });
|
|
529
531
|
debugStartup("main:Settings.init");
|
|
530
532
|
time("Settings.init");
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { readJsonl } from "@oh-my-pi/pi-utils";
|
|
9
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
9
10
|
import { type Subprocess, spawn } from "bun";
|
|
10
11
|
import type { JsonRpcResponse, MCPRequestOptions, MCPStdioServerConfig, MCPTransport } from "../../mcp/types";
|
|
11
12
|
|
|
@@ -54,7 +55,7 @@ export class StdioTransport implements MCPTransport {
|
|
|
54
55
|
|
|
55
56
|
this.#process = spawn({
|
|
56
57
|
cmd: [this.config.command, ...args],
|
|
57
|
-
cwd: this.config.cwd ??
|
|
58
|
+
cwd: this.config.cwd ?? getProjectDir(),
|
|
58
59
|
env,
|
|
59
60
|
stdin: "pipe",
|
|
60
61
|
stdout: "pipe",
|
|
@@ -3,6 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { type Component, padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
5
5
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
6
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils/dirs";
|
|
6
7
|
import { theme } from "../../modes/theme/theme";
|
|
7
8
|
import type { AgentSession } from "../../session/agent-session";
|
|
8
9
|
import { shortenPath } from "../../tools/render-utils";
|
|
@@ -21,7 +22,7 @@ function sanitizeStatusText(text: string): string {
|
|
|
21
22
|
|
|
22
23
|
/** Find the git root by walking up from cwd. Returns path and content of .git/HEAD if found. */
|
|
23
24
|
async function findGitHeadPath(): Promise<{ path: string; content: string } | null> {
|
|
24
|
-
let dir =
|
|
25
|
+
let dir = getProjectDir();
|
|
25
26
|
while (true) {
|
|
26
27
|
const gitHeadPath = path.join(dir, ".git", "HEAD");
|
|
27
28
|
try {
|
|
@@ -201,7 +202,7 @@ export class FooterComponent implements Component {
|
|
|
201
202
|
};
|
|
202
203
|
|
|
203
204
|
// Replace home directory with ~
|
|
204
|
-
let pwd = shortenPath(
|
|
205
|
+
let pwd = shortenPath(getProjectDir());
|
|
205
206
|
|
|
206
207
|
// Add git branch if available
|
|
207
208
|
const branch = this.#getCurrentBranch();
|