refactor(dirs): centralized directory path utilities into @oh-my-pi/pi-utils/dirs module
- Extracted directory path utilities from multiple packages into a centralized '@oh-my-pi/pi-utils/dirs' module. - Moved 30+ path helper functions (getAgentDir, getConfigRootDir, getPluginsDir, getMCPConfigPath, etc.) from scattered locations into a single shared utility module. - Consolidated APP_NAME, CONFIG_DIR_NAME, and VERSION constants into the centralized dirs module for reuse across packages. - Updated 70+ import statements across packages/ai, packages/coding-agent, packages/stats, and packages/tui to use the new centralized module. - Removed local path construction logic and replaced with utility function calls for improved maintainability and consistency. - Deleted packages/coding-agent/src/extensibility/plugins/paths.ts as its functions were moved to the centralized dirs module.
This commit is contained in:
@@ -5,9 +5,8 @@
|
||||
|
||||
import { Database, type Statement } from "bun:sqlite";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { $env } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { OAuthCredentials } from "./utils/oauth/types";
|
||||
|
||||
type AuthCredential = { type: "api_key"; key: string } | ({ type: "oauth" } & OAuthCredentials);
|
||||
@@ -21,14 +20,6 @@ type AuthRow = {
|
||||
updated_at: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the agent config directory (e.g., ~/.omp/agent/)
|
||||
*/
|
||||
function getAgentDir(): string {
|
||||
const configDir = $env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".omp", "agent");
|
||||
return configDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path to agent.db
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { $env, abortableSleep, isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import packageJson from "../../../package.json" with { type: "json" };
|
||||
import type { OAuthController, OAuthCredentials } from "./types";
|
||||
|
||||
@@ -36,11 +37,6 @@ interface TokenResponse {
|
||||
interval?: number;
|
||||
}
|
||||
|
||||
function getAgentDir(): string {
|
||||
const configDir = $env.PI_CODING_AGENT_DIR || path.join(os.homedir(), ".omp", "agent");
|
||||
return configDir;
|
||||
}
|
||||
|
||||
function resolveOAuthHost(): string {
|
||||
return $env.KIMI_CODE_OAUTH_HOST || $env.KIMI_OAUTH_HOST || DEFAULT_OAUTH_HOST;
|
||||
}
|
||||
|
||||
@@ -7,11 +7,10 @@
|
||||
* E2E tests are disabled by default. Set E2E=1 environment variable to enable.
|
||||
*/
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { getOAuthApiKey } from "@oh-my-pi/pi-ai/utils/oauth";
|
||||
import type { OAuthCredentials, OAuthProvider } from "@oh-my-pi/pi-ai/utils/oauth/types";
|
||||
import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import { getTestAuthPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
/**
|
||||
* E2E tests require explicit opt-in via E2E=1 environment variable.
|
||||
@@ -28,7 +27,7 @@ export function e2eApiKey(envVar: string): string | undefined {
|
||||
return Bun.env[envVar];
|
||||
}
|
||||
|
||||
const AUTH_PATH = path.join(os.homedir(), ".omp", "agent", "testauth.db");
|
||||
const AUTH_PATH = getTestAuthPath();
|
||||
|
||||
type ApiKeyCredential = {
|
||||
type: "api_key";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- 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
|
||||
@@ -9,6 +8,8 @@
|
||||
|
||||
### Changed
|
||||
|
||||
- Moved directory path utilities from `src/config.ts` to `@oh-my-pi/pi-utils/dirs` for shared use across packages
|
||||
- Updated imports throughout codebase to use centralized directory path functions from `@oh-my-pi/pi-utils/dirs`
|
||||
- Updated interactive bash terminal UI label from 'InteractiveTerm' to 'Console' for clarity
|
||||
- Enhanced bash execution environment with comprehensive non-interactive defaults for pagers, editors, and package managers to prevent command blocking and interactive prompts
|
||||
- Updated custom models configuration to use `~/.omp/agent/models.yml` (YAML format) while maintaining backward compatibility with legacy `models.json`
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* lightweight CLI runner from pi-utils.
|
||||
*/
|
||||
import { type CommandEntry, run } from "@oh-my-pi/pi-utils/cli";
|
||||
import { APP_NAME, VERSION } from "./config";
|
||||
import { APP_NAME, VERSION } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
// Detect known Bun errata that cause TUI crashes (e.g. Bun.stringWidth mishandling OSC sequences).
|
||||
if (Bun.stringWidth("\x1b[0m\x1b]8;;\x07") !== 0) {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
||||
import { logger } from "@oh-my-pi/pi-utils";
|
||||
import { APP_NAME, CONFIG_DIR_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME, CONFIG_DIR_NAME } from "../config";
|
||||
import { BUILTIN_TOOLS } from "../tools";
|
||||
|
||||
export type Mode = "text" | "json" | "rpc";
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* Handles `omp config <command>` subcommands for managing settings.
|
||||
* Uses settings-defs as the source of truth for available settings.
|
||||
*/
|
||||
|
||||
import { APP_NAME, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME, getAgentDir } from "../config";
|
||||
import {
|
||||
getDefault,
|
||||
getEnumValues,
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
import * as path from "node:path";
|
||||
import { grep } from "@oh-my-pi/pi-natives";
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME } from "../config";
|
||||
|
||||
export interface GrepCommandArgs {
|
||||
pattern: string;
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*
|
||||
* Handles `omp jupyter` subcommand for managing the shared Python gateway.
|
||||
*/
|
||||
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME } from "../config";
|
||||
import { getGatewayStatus, shutdownSharedGateway } from "../ipy/gateway-coordinator";
|
||||
|
||||
export type JupyterAction = "kill" | "status";
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*
|
||||
* Handles `omp plugin <command>` subcommands for plugin lifecycle management.
|
||||
*/
|
||||
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME } from "../config";
|
||||
import { PluginManager, parseSettingValue, validateSetting } from "../extensibility/plugins";
|
||||
import { theme } from "../modes/theme/theme";
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
*
|
||||
* Handles `omp setup <component>` to install dependencies for optional features.
|
||||
*/
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { APP_NAME, getPythonEnvDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { $ } from "bun";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME, CONFIG_DIR_NAME } from "../config";
|
||||
import { theme } from "../modes/theme/theme";
|
||||
|
||||
export type SetupComponent = "python";
|
||||
@@ -23,7 +22,7 @@ export interface SetupCommandArgs {
|
||||
const VALID_COMPONENTS: SetupComponent[] = ["python"];
|
||||
|
||||
const PYTHON_PACKAGES = ["jupyter_kernel_gateway", "ipykernel"];
|
||||
const MANAGED_PYTHON_ENV = path.join(os.homedir(), CONFIG_DIR_NAME, "python-env");
|
||||
const MANAGED_PYTHON_ENV = getPythonEnvDir();
|
||||
|
||||
/**
|
||||
* Parse setup subcommand arguments.
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import * as path from "node:path";
|
||||
import { createInterface } from "node:readline/promises";
|
||||
import { Shell } from "@oh-my-pi/pi-natives";
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME } from "../config";
|
||||
import { Settings } from "../config/settings";
|
||||
import { getOrCreateSnapshot } from "../utils/shell-snapshot";
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*
|
||||
* Handles `omp stats` subcommand for viewing AI usage statistics.
|
||||
*/
|
||||
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME } from "../config";
|
||||
import { openPath } from "../utils/open";
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -9,8 +9,8 @@ import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { pipeline } from "node:stream/promises";
|
||||
import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import { APP_NAME, VERSION } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME, VERSION } from "../config";
|
||||
import { theme } from "../modes/theme/theme";
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
*
|
||||
* Handles `omp q`/`omp web-search` subcommands for testing web search providers.
|
||||
*/
|
||||
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME } from "../config";
|
||||
import { initTheme, theme } from "../modes/theme/theme";
|
||||
import { runSearchQuery, type SearchParams } from "../web/search/index";
|
||||
import { renderSearchResult } from "../web/search/render";
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* Root command for the coding agent CLI.
|
||||
*/
|
||||
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { parseArgs } from "../cli/args";
|
||||
import { APP_NAME } from "../config";
|
||||
import { runRootCommand } from "../main";
|
||||
|
||||
export default class Index extends Command {
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { $env, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { CONFIG_DIR_NAME, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
import { Value } from "@sinclair/typebox/value";
|
||||
import { Ajv, type ErrorObject, type ValidateFunction } from "ajv";
|
||||
import { JSONC, YAML } from "bun";
|
||||
// Embed package.json at build time for config
|
||||
import packageJson from "../package.json" with { type: "json" };
|
||||
|
||||
// =============================================================================
|
||||
// App Config (from embedded package.json)
|
||||
// =============================================================================
|
||||
|
||||
export const APP_NAME: string = (packageJson as { ompConfig?: { name?: string } }).ompConfig?.name || "omp";
|
||||
export const CONFIG_DIR_NAME: string =
|
||||
(packageJson as { ompConfig?: { configDir?: string } }).ompConfig?.configDir || ".omp";
|
||||
export const VERSION: string = (packageJson as { version: string }).version;
|
||||
|
||||
const priorityList = [
|
||||
{ dir: ".omp", globalAgentDir: ".omp/agent" },
|
||||
{ dir: CONFIG_DIR_NAME, globalAgentDir: `${CONFIG_DIR_NAME}/agent` },
|
||||
{ dir: ".claude" },
|
||||
{ dir: ".codex" },
|
||||
{ dir: ".gemini" },
|
||||
@@ -253,55 +243,6 @@ export class ConfigFile<T> implements IConfigFile<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the agent config directory (e.g., ~/.omp/agent/) */
|
||||
export function getAgentDir(): string {
|
||||
return $env.PI_CODING_AGENT_DIR || path.join(os.homedir(), CONFIG_DIR_NAME, "agent");
|
||||
}
|
||||
|
||||
/** Get path to user's custom themes directory */
|
||||
export function getCustomThemesDir(): string {
|
||||
return path.join(getAgentDir(), "themes");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to agent.db (SQLite database for settings and auth storage).
|
||||
* @param agentDir - Base agent directory, defaults to ~/.omp/agent
|
||||
* @returns Absolute path to the agent.db file
|
||||
*/
|
||||
export function getAgentDbPath(agentDir: string = getAgentDir()): string {
|
||||
return path.join(agentDir, "agent.db");
|
||||
}
|
||||
|
||||
/** Get path to tools directory */
|
||||
export function getToolsDir(): string {
|
||||
return path.join(getAgentDir(), "tools");
|
||||
}
|
||||
|
||||
/** Get path to slash commands directory */
|
||||
export function getCommandsDir(): string {
|
||||
return path.join(getAgentDir(), "commands");
|
||||
}
|
||||
|
||||
/** Get path to prompts directory */
|
||||
export function getPromptsDir(): string {
|
||||
return path.join(getAgentDir(), "prompts");
|
||||
}
|
||||
|
||||
/** Get path to content-addressed blob store directory */
|
||||
export function getBlobsDir(): string {
|
||||
return path.join(getAgentDir(), "blobs");
|
||||
}
|
||||
|
||||
/** Get path to sessions directory */
|
||||
export function getSessionsDir(): string {
|
||||
return path.join(getAgentDir(), "sessions");
|
||||
}
|
||||
|
||||
/** Get path to debug log file */
|
||||
export function getDebugLogPath(): string {
|
||||
return path.join(getAgentDir(), `${APP_NAME}-debug.log`);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Multi-Config Directory Helpers
|
||||
// =============================================================================
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
setEditorKeybindings,
|
||||
} from "@oh-my-pi/pi-tui";
|
||||
import { logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "../config";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
/**
|
||||
* Application-level actions (coding agent specific).
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { logger } from "@oh-my-pi/pi-utils";
|
||||
import { getProjectPromptsDir, getPromptsDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import Handlebars from "handlebars";
|
||||
import { CONFIG_DIR_NAME, getPromptsDir } from "../config";
|
||||
import { computeLineHash } from "../patch/hashline";
|
||||
import { jtdToTypeScript } from "../tools/jtd-to-typescript";
|
||||
import { parseFrontmatter } from "../utils/frontmatter";
|
||||
@@ -468,7 +468,7 @@ export interface LoadPromptTemplatesOptions {
|
||||
/**
|
||||
* Load all prompt templates from:
|
||||
* 1. Global: agentDir/prompts/
|
||||
* 2. Project: cwd/{CONFIG_DIR_NAME}/prompts/
|
||||
* 2. Project: cwd/.omp/prompts/
|
||||
*/
|
||||
export async function loadPromptTemplates(options: LoadPromptTemplatesOptions = {}): Promise<PromptTemplate[]> {
|
||||
const resolvedCwd = options.cwd ?? process.cwd();
|
||||
@@ -481,8 +481,8 @@ export async function loadPromptTemplates(options: LoadPromptTemplatesOptions =
|
||||
const globalPromptsDir = options.agentDir ? path.join(options.agentDir, "prompts") : resolvedAgentDir;
|
||||
templates.push(...(await loadTemplatesFromDir(globalPromptsDir, "user")));
|
||||
|
||||
// 2. Load project templates from cwd/{CONFIG_DIR_NAME}/prompts/
|
||||
const projectPromptsDir = path.resolve(resolvedCwd, CONFIG_DIR_NAME, "prompts");
|
||||
// 2. Load project templates from cwd/.omp/prompts/
|
||||
const projectPromptsDir = getProjectPromptsDir(resolvedCwd);
|
||||
templates.push(...(await loadTemplatesFromDir(projectPromptsDir, "project")));
|
||||
|
||||
return templates;
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent, logger, procmgr } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { YAML } from "bun";
|
||||
import { type Settings as SettingsCapabilityItem, settingsCapability } from "../capability/settings";
|
||||
import { getAgentDbPath, getAgentDir } from "../config";
|
||||
import type { ModelRole } from "../config/model-registry";
|
||||
import { loadCapability } from "../discovery";
|
||||
import { setColorBlindMode, setSymbolPreset, setTheme } from "../modes/theme/theme";
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import { getWorkProfile } from "@oh-my-pi/pi-natives";
|
||||
import { Container, Loader, type SelectItem, SelectList, Spacer, Text } from "@oh-my-pi/pi-tui";
|
||||
import { getSessionsDir } from "../config";
|
||||
import { getSessionsDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { DynamicBorder } from "../modes/components/dynamic-border";
|
||||
import { getSelectListTheme, getSymbolTheme, theme } from "../modes/theme/theme";
|
||||
import type { InteractiveModeContext } from "../modes/types";
|
||||
|
||||
@@ -4,24 +4,13 @@
|
||||
* Creates a .tar.gz archive with session data, logs, system info, and optional profiling data.
|
||||
*/
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import type { WorkProfile } from "@oh-my-pi/pi-natives";
|
||||
import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import { getLogPath, getReportsDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { CpuProfile, HeapSnapshot } from "./profiler";
|
||||
import { collectSystemInfo, sanitizeEnv } from "./system-info";
|
||||
|
||||
/** Reports directory path */
|
||||
export function getReportsDir(): string {
|
||||
return path.join(os.homedir(), ".omp", "reports");
|
||||
}
|
||||
|
||||
/** Get today's log file path */
|
||||
function getLogPath(): string {
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
return path.join(os.homedir(), ".omp", "logs", `omp.${today}.log`);
|
||||
}
|
||||
|
||||
/** Read last N lines from a file */
|
||||
async function readLastLines(filePath: string, n: number): Promise<string> {
|
||||
try {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* System information collection for debug reports.
|
||||
*/
|
||||
import * as os from "node:os";
|
||||
import { VERSION } from "../config";
|
||||
import { VERSION } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
export interface SystemInfo {
|
||||
os: string;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
||||
import { CONFIG_DIR_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { readDirEntries, readFile } from "../capability/fs";
|
||||
import type { Skill, SkillFrontmatter } from "../capability/skill";
|
||||
import type { LoadContext, LoadResult, SourceMeta } from "../capability/types";
|
||||
@@ -39,9 +40,9 @@ export function expandPath(p: string): string {
|
||||
*/
|
||||
export const SOURCE_PATHS = {
|
||||
native: {
|
||||
userBase: ".omp",
|
||||
userAgent: ".omp/agent",
|
||||
projectDir: ".omp",
|
||||
userBase: CONFIG_DIR_NAME,
|
||||
userAgent: `${CONFIG_DIR_NAME}/agent`,
|
||||
projectDir: CONFIG_DIR_NAME,
|
||||
},
|
||||
claude: {
|
||||
userBase: ".claude",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { getAgentDir } from "../config";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
export interface CustomShareResult {
|
||||
/** URL to display/open (optional - script may handle everything itself) */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as path from "node:path";
|
||||
import type { AgentState } from "@oh-my-pi/pi-agent-core";
|
||||
import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import { APP_NAME } from "../../config";
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { getResolvedThemeColors, getThemeExportColors } from "../../modes/theme/theme";
|
||||
import { type SessionEntry, type SessionHeader, SessionManager } from "../../session/session-manager";
|
||||
// Pre-generated template (created by scripts/generate-template.ts at publish time)
|
||||
|
||||
@@ -8,8 +8,9 @@ import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import * as piCodingAgent from "@oh-my-pi/pi-coding-agent";
|
||||
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import * as typebox from "@sinclair/typebox";
|
||||
import { getAgentDir, getConfigDirs } from "../../config";
|
||||
import { getConfigDirs } from "../../config";
|
||||
import { execCommand } from "../../exec/exec";
|
||||
import { ReviewCommand } from "./bundled/review";
|
||||
import type {
|
||||
|
||||
@@ -14,13 +14,6 @@ export {
|
||||
} from "./loader";
|
||||
export { PluginManager, parseSettingValue, validateSetting } from "./manager";
|
||||
export { extractPackageName, formatPluginSpec, parsePluginSpec } from "./parser";
|
||||
export {
|
||||
getPluginsDir,
|
||||
getPluginsLockfile,
|
||||
getPluginsNodeModules,
|
||||
getPluginsPackageJson,
|
||||
getProjectPluginOverrides,
|
||||
} from "./paths";
|
||||
export type {
|
||||
BooleanSetting,
|
||||
DoctorCheck,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "../../config";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { InstalledPlugin } from "./types";
|
||||
|
||||
const PLUGINS_DIR = path.join(getAgentDir(), "plugins");
|
||||
|
||||
@@ -7,12 +7,8 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import {
|
||||
getAllProjectPluginOverridePaths,
|
||||
getPluginsLockfile,
|
||||
getPluginsNodeModules,
|
||||
getPluginsPackageJson,
|
||||
} from "./paths";
|
||||
import { getPluginsLockfile, getPluginsNodeModules, getPluginsPackageJson } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { getConfigDirPaths } from "../../config";
|
||||
import type { InstalledPlugin, PluginManifest, PluginRuntimeConfig, ProjectPluginOverrides } from "./types";
|
||||
|
||||
// =============================================================================
|
||||
@@ -36,7 +32,7 @@ async function loadRuntimeConfig(): Promise<PluginRuntimeConfig> {
|
||||
* Load project-local plugin overrides (checks .omp and .pi directories).
|
||||
*/
|
||||
async function loadProjectOverrides(cwd: string): Promise<ProjectPluginOverrides> {
|
||||
for (const overridesPath of getAllProjectPluginOverridePaths(cwd)) {
|
||||
for (const overridesPath of getConfigDirPaths("plugin-overrides.json", { user: false, cwd })) {
|
||||
try {
|
||||
return await Bun.file(overridesPath).json();
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { extractPackageName, parsePluginSpec } from "./parser";
|
||||
import {
|
||||
getPluginsDir,
|
||||
getPluginsLockfile,
|
||||
getPluginsNodeModules,
|
||||
getPluginsPackageJson,
|
||||
getProjectPluginOverrides,
|
||||
} from "./paths";
|
||||
getProjectPluginOverridesPath,
|
||||
} from "@oh-my-pi/pi-utils/dirs";
|
||||
import { extractPackageName, parsePluginSpec } from "./parser";
|
||||
import type {
|
||||
DoctorCheck,
|
||||
DoctorOptions,
|
||||
@@ -82,7 +82,7 @@ export class PluginManager {
|
||||
}
|
||||
|
||||
async #loadProjectOverrides(): Promise<ProjectPluginOverrides> {
|
||||
const overridesPath = getProjectPluginOverrides(this.#cwd);
|
||||
const overridesPath = getProjectPluginOverridesPath(this.#cwd);
|
||||
try {
|
||||
return await Bun.file(overridesPath).json();
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { CONFIG_DIR_NAME, getConfigDirPaths } from "../../config";
|
||||
|
||||
// =============================================================================
|
||||
// Plugin Directory Paths
|
||||
// =============================================================================
|
||||
|
||||
/** Root plugin directory: ~/.omp/plugins (not under agent/) */
|
||||
export function getPluginsDir(): string {
|
||||
return path.join(os.homedir(), CONFIG_DIR_NAME, "plugins");
|
||||
}
|
||||
|
||||
/** Where npm installs packages: ~/.omp/plugins/node_modules */
|
||||
export function getPluginsNodeModules(): string {
|
||||
return path.join(getPluginsDir(), "node_modules");
|
||||
}
|
||||
|
||||
/** Plugin manifest: ~/.omp/plugins/package.json */
|
||||
export function getPluginsPackageJson(): string {
|
||||
return path.join(getPluginsDir(), "package.json");
|
||||
}
|
||||
|
||||
/** Plugin lock file: ~/.omp/plugins/omp-plugins.lock.json */
|
||||
export function getPluginsLockfile(): string {
|
||||
return path.join(getPluginsDir(), "omp-plugins.lock.json");
|
||||
}
|
||||
|
||||
/** Project-local plugin overrides: .omp/plugin-overrides.json (primary) */
|
||||
export function getProjectPluginOverrides(cwd: string): string {
|
||||
return path.join(cwd, CONFIG_DIR_NAME, "plugin-overrides.json");
|
||||
}
|
||||
|
||||
/** All possible project plugin override paths (primary + legacy) */
|
||||
export function getAllProjectPluginOverridePaths(cwd: string): string[] {
|
||||
return getConfigDirPaths("plugin-overrides.json", { user: false, cwd });
|
||||
}
|
||||
@@ -7,7 +7,7 @@ export { StringEnum } from "@oh-my-pi/pi-ai";
|
||||
export { Container, Markdown, Spacer, Text } from "@oh-my-pi/pi-tui";
|
||||
// Logging
|
||||
export { logger } from "@oh-my-pi/pi-utils";
|
||||
export { getAgentDir, VERSION } from "./config";
|
||||
export { getAgentDir, VERSION } from "@oh-my-pi/pi-utils/dirs";
|
||||
export { formatKeyHint, formatKeyHints } from "./config/keybindings";
|
||||
export { ModelRegistry } from "./config/model-registry";
|
||||
// Prompt templates
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as path from "node:path";
|
||||
import { $env, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "../config";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { OutputSink } from "../session/streaming-output";
|
||||
import { time } from "../utils/timings";
|
||||
import { shutdownSharedGateway } from "./gateway-coordinator";
|
||||
|
||||
@@ -2,8 +2,8 @@ import * as fs from "node:fs";
|
||||
import { createServer } from "node:net";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent, logger, procmgr } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { Subprocess } from "bun";
|
||||
import { getAgentDir } from "../config";
|
||||
import { Settings } from "../config/settings";
|
||||
import { getOrCreateSnapshot } from "../utils/shell-snapshot";
|
||||
import { time } from "../utils/timings";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { getAgentModulesDir, getProjectModulesDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
export type PythonModuleSource = "user" | "project";
|
||||
|
||||
@@ -26,8 +26,8 @@ export interface PythonModuleExecutor {
|
||||
export interface DiscoverPythonModulesOptions {
|
||||
/** Working directory for project-level modules. Default: process.cwd() */
|
||||
cwd?: string;
|
||||
/** Home directory for user-level modules. Default: os.homedir() */
|
||||
homeDir?: string;
|
||||
/** Agent directory for user-level modules. Default: from getAgentDir() */
|
||||
agentDir?: string;
|
||||
}
|
||||
|
||||
interface ModuleCandidate {
|
||||
@@ -66,10 +66,9 @@ async function readModuleContent(candidate: ModuleCandidate): Promise<PythonModu
|
||||
*/
|
||||
export async function discoverPythonModules(options: DiscoverPythonModulesOptions = {}): Promise<PythonModuleEntry[]> {
|
||||
const cwd = options.cwd ?? process.cwd();
|
||||
const homeDir = options.homeDir ?? os.homedir();
|
||||
|
||||
const userDir = path.join(homeDir, ".omp", "agent", "modules");
|
||||
const projectDir = path.resolve(cwd, ".omp", "modules");
|
||||
const userDir = getAgentModulesDir(options.agentDir);
|
||||
const projectDir = getProjectModulesDir(cwd);
|
||||
|
||||
const userCandidates = await listModuleCandidates(userDir, "user");
|
||||
const projectCandidates = await listModuleCandidates(projectDir, "project");
|
||||
|
||||
@@ -5,11 +5,10 @@
|
||||
* for both the shared gateway and local kernel spawning.
|
||||
*/
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { $env } from "@oh-my-pi/pi-utils";
|
||||
import { CONFIG_DIR_NAME } from "../config";
|
||||
import { getPythonEnvDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
const DEFAULT_ENV_ALLOWLIST = new Set([
|
||||
"PATH",
|
||||
@@ -106,7 +105,7 @@ function resolvePathKey(env: Record<string, string | undefined>): string {
|
||||
}
|
||||
|
||||
function resolveManagedPythonEnv(): string {
|
||||
return path.join(os.homedir(), CONFIG_DIR_NAME, "python-env");
|
||||
return getPythonEnvDir();
|
||||
}
|
||||
|
||||
function resolveManagedPythonCandidate(): { venvPath: string; pythonPath: string } {
|
||||
|
||||
@@ -10,12 +10,13 @@ import * as path from "node:path";
|
||||
import { createInterface } from "node:readline/promises";
|
||||
import { type ImageContent, supportsXhigh } from "@oh-my-pi/pi-ai";
|
||||
import { $env, postmortem } from "@oh-my-pi/pi-utils";
|
||||
import { VERSION } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import type { Args } from "./cli/args";
|
||||
import { processFileArguments } from "./cli/file-processor";
|
||||
import { listModels } from "./cli/list-models";
|
||||
import { selectSession } from "./cli/session-picker";
|
||||
import { findConfigFile, VERSION } from "./config";
|
||||
import { findConfigFile } from "./config";
|
||||
import { ModelRegistry, ModelsConfigFile } from "./config/model-registry";
|
||||
import { parseModelPattern, parseModelString, resolveModelScope, type ScopedModel } from "./config/model-resolver";
|
||||
import { Settings, settings } from "./config/settings";
|
||||
|
||||
@@ -4,24 +4,12 @@
|
||||
* Utilities for reading/writing .omp/mcp.json files at user or project level.
|
||||
*/
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
|
||||
import { validateServerConfig } from "./config";
|
||||
import type { MCPConfigFile, MCPServerConfig } from "./types";
|
||||
|
||||
/**
|
||||
* Get the path to the MCP config file.
|
||||
* @param scope - "user" for ~/.omp/mcp.json or "project" for .omp/mcp.json
|
||||
* @param cwd - Current working directory (used for project scope)
|
||||
*/
|
||||
export function getMCPConfigPath(scope: "user" | "project", cwd: string): string {
|
||||
if (scope === "user") {
|
||||
return path.join(os.homedir(), ".omp", "mcp.json");
|
||||
}
|
||||
return path.join(cwd, ".omp", "mcp.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an MCP config file.
|
||||
* Returns empty config if file doesn't exist.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type Component, padding, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
||||
import { APP_NAME } from "../../config";
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { theme } from "../../modes/theme/theme";
|
||||
|
||||
export interface RecentSession {
|
||||
|
||||
@@ -4,16 +4,11 @@
|
||||
* Handles /mcp subcommands for managing MCP servers.
|
||||
*/
|
||||
import { Spacer, Text } from "@oh-my-pi/pi-tui";
|
||||
import { getMCPConfigPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { SourceMeta } from "../../capability/types";
|
||||
import { analyzeAuthError, discoverOAuthEndpoints, MCPManager } from "../../mcp";
|
||||
import { connectToServer, disconnectServer, listTools } from "../../mcp/client";
|
||||
import {
|
||||
addMCPServer,
|
||||
getMCPConfigPath,
|
||||
readMCPConfigFile,
|
||||
removeMCPServer,
|
||||
updateMCPServer,
|
||||
} from "../../mcp/config-writer";
|
||||
import { addMCPServer, readMCPConfigFile, removeMCPServer, updateMCPServer } from "../../mcp/config-writer";
|
||||
import { MCPOAuthFlow } from "../../mcp/oauth-flow";
|
||||
import type { MCPServerConfig, MCPServerConnection } from "../../mcp/types";
|
||||
import type { OAuthCredential } from "../../session/auth-storage";
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
||||
import type { OAuthProvider } from "@oh-my-pi/pi-ai";
|
||||
import type { Component } from "@oh-my-pi/pi-tui";
|
||||
import { Input, Loader, Spacer, Text } from "@oh-my-pi/pi-tui";
|
||||
import { getAgentDbPath } from "../../config";
|
||||
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { MODEL_ROLES } from "../../config/model-registry";
|
||||
import { settings } from "../../config/settings";
|
||||
import { DebugSelectorComponent } from "../../debug";
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
TUI,
|
||||
} from "@oh-my-pi/pi-tui";
|
||||
import { $env, isEnoent, logger, postmortem } from "@oh-my-pi/pi-utils";
|
||||
import { APP_NAME } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { APP_NAME } from "../config";
|
||||
import { KeybindingsManager } from "../config/keybindings";
|
||||
import { renderPromptTemplate } from "../config/prompt-templates";
|
||||
import { type Settings, settings } from "../config/settings";
|
||||
|
||||
@@ -7,10 +7,10 @@ import {
|
||||
} from "@oh-my-pi/pi-natives";
|
||||
import type { EditorTheme, MarkdownTheme, SelectListTheme, SymbolTheme } from "@oh-my-pi/pi-tui";
|
||||
import { adjustHsv, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getCustomThemesDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { type Static, Type } from "@sinclair/typebox";
|
||||
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
||||
import chalk from "chalk";
|
||||
import { getCustomThemesDir } from "../../config";
|
||||
// Embed theme JSON files at build time
|
||||
import darkThemeJson from "./dark.json" with { type: "json" };
|
||||
import { defaultThemes } from "./defaults";
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
||||
import { StringEnum } from "@oh-my-pi/pi-ai";
|
||||
import { invalidateFsScanCache } from "@oh-my-pi/pi-natives";
|
||||
import { type Static, Type } from "@sinclair/typebox";
|
||||
import { renderPromptTemplate } from "../config/prompt-templates";
|
||||
import {
|
||||
@@ -25,6 +24,11 @@ import hashlineDescription from "../prompts/tools/hashline.md" with { type: "tex
|
||||
import patchDescription from "../prompts/tools/patch.md" with { type: "text" };
|
||||
import replaceDescription from "../prompts/tools/replace.md" with { type: "text" };
|
||||
import type { ToolSession } from "../tools";
|
||||
import {
|
||||
invalidateFsScanAfterDelete,
|
||||
invalidateFsScanAfterRename,
|
||||
invalidateFsScanAfterWrite,
|
||||
} from "../tools/fs-cache-invalidation";
|
||||
import { outputMeta } from "../tools/output-meta";
|
||||
import { enforcePlanModeWrite, resolvePlanPath } from "../tools/plan-mode-guard";
|
||||
import { applyPatch } from "./applicator";
|
||||
@@ -531,7 +535,7 @@ export class EditTool implements AgentTool<TInput> {
|
||||
|
||||
const finalContent = bom + restoreLineEndings(result.content, originalEnding);
|
||||
const diagnostics = await this.#writethrough(absolutePath, finalContent, signal, file, batchRequest);
|
||||
invalidateFsScanCache(absolutePath);
|
||||
invalidateFsScanAfterWrite(absolutePath);
|
||||
const diffResult = generateDiffString(originalNormalized, result.content);
|
||||
|
||||
const normative = buildNormativeUpdateInput({
|
||||
@@ -589,8 +593,13 @@ export class EditTool implements AgentTool<TInput> {
|
||||
fuzzyThreshold: this.#fuzzyThreshold,
|
||||
allowFuzzy: this.#allowFuzzy,
|
||||
});
|
||||
invalidateFsScanCache(resolvedPath);
|
||||
if (resolvedRename) invalidateFsScanCache(resolvedRename);
|
||||
if (resolvedRename) {
|
||||
invalidateFsScanAfterRename(resolvedPath, resolvedRename);
|
||||
} else if (result.change.type === "delete") {
|
||||
invalidateFsScanAfterDelete(resolvedPath);
|
||||
} else {
|
||||
invalidateFsScanAfterWrite(resolvedPath);
|
||||
}
|
||||
const effRename = result.change.newPath ? rename : undefined;
|
||||
|
||||
// Generate diff for display
|
||||
@@ -713,7 +722,7 @@ export class EditTool implements AgentTool<TInput> {
|
||||
|
||||
const finalContent = bom + restoreLineEndings(result.content, originalEnding);
|
||||
const diagnostics = await this.#writethrough(absolutePath, finalContent, signal, file, batchRequest);
|
||||
invalidateFsScanCache(absolutePath);
|
||||
invalidateFsScanAfterWrite(absolutePath);
|
||||
const diffResult = generateDiffString(normalizedContent, result.content);
|
||||
|
||||
const resultText =
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type Thinkin
|
||||
import { type Message, type Model, supportsXhigh } from "@oh-my-pi/pi-ai";
|
||||
import type { Component } from "@oh-my-pi/pi-tui";
|
||||
import { $env, logger, postmortem } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import chalk from "chalk";
|
||||
import { loadCapability } from "./capability";
|
||||
import { type Rule, ruleCapability } from "./capability/rule";
|
||||
import { getAgentDbPath, getAgentDir } from "./config";
|
||||
import { ModelRegistry } from "./config/model-registry";
|
||||
import { formatModelString, parseModelString } from "./config/model-resolver";
|
||||
import { loadPromptTemplates as loadPromptTemplatesInternal, type PromptTemplate } from "./config/prompt-templates";
|
||||
|
||||
@@ -29,8 +29,8 @@ import type {
|
||||
} from "@oh-my-pi/pi-ai";
|
||||
import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@oh-my-pi/pi-ai";
|
||||
import { abortableSleep, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { Rule } from "../capability/rule";
|
||||
import { getAgentDbPath } from "../config";
|
||||
import { MODEL_ROLE_IDS, type ModelRegistry, type ModelRole } from "../config/model-registry";
|
||||
import { expandRoleAlias, parseModelString } from "../config/model-resolver";
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Database, type Statement } from "bun:sqlite";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath } from "../config";
|
||||
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { RawSettings as Settings } from "../config/settings";
|
||||
import type { AuthCredential } from "./auth-storage";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Database, type Statement } from "bun:sqlite";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDir } from "../config";
|
||||
import { getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
export interface HistoryEntry {
|
||||
id: number;
|
||||
|
||||
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
||||
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
||||
import type { ImageContent, Message, TextContent, Usage } from "@oh-my-pi/pi-ai";
|
||||
import { isEnoent, logger, parseJsonlLenient, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { getBlobsDir, getAgentDir as getDefaultAgentDir } from "../config";
|
||||
import { getBlobsDir, getAgentDir as getDefaultAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { type BlobPutResult, BlobStore, externalizeImageData, isBlobRef, resolveImageData } from "./blob-store";
|
||||
import {
|
||||
type BashExecutionMessage,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getRemoteHostDir, getSshControlDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { $ } from "bun";
|
||||
import { CONFIG_DIR_NAME } from "../config";
|
||||
|
||||
export interface SSHConnectionTarget {
|
||||
name: string;
|
||||
@@ -25,9 +24,9 @@ export interface SSHHostInfo {
|
||||
compatEnabled: boolean;
|
||||
}
|
||||
|
||||
const CONTROL_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "ssh-control");
|
||||
const CONTROL_DIR = getSshControlDir();
|
||||
const CONTROL_PATH = path.join(CONTROL_DIR, "%h.sock");
|
||||
const HOST_INFO_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "remote-host");
|
||||
const HOST_INFO_DIR = getRemoteHostDir();
|
||||
const HOST_INFO_VERSION = 2;
|
||||
|
||||
const activeHosts = new Map<string, SSHConnectionTarget>();
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { getRemoteDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { $ } from "bun";
|
||||
import { CONFIG_DIR_NAME } from "../config";
|
||||
import { getControlDir, getControlPathTemplate, type SSHConnectionTarget } from "./connection-manager";
|
||||
|
||||
const REMOTE_DIR = path.join(os.homedir(), CONFIG_DIR_NAME, "remote");
|
||||
const REMOTE_DIR = getRemoteDir();
|
||||
const CONTROL_DIR = getControlDir();
|
||||
const CONTROL_PATH = getControlPathTemplate();
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
* System prompt construction and project context loading
|
||||
*/
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { getSystemInfo as getNativeSystemInfo, type SystemInfo } from "@oh-my-pi/pi-natives";
|
||||
import { $env, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getGpuCachePath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { $ } from "bun";
|
||||
import { contextFileCapability } from "./capability/context-file";
|
||||
import { systemPromptCapability } from "./capability/system-prompt";
|
||||
@@ -272,7 +272,7 @@ interface GpuCache {
|
||||
}
|
||||
|
||||
function getSystemInfoCachePath(): string {
|
||||
return path.join(os.homedir(), ".omp", "gpu_cache.json");
|
||||
return getGpuCachePath();
|
||||
}
|
||||
|
||||
async function loadGpuCache(): Promise<GpuCache | null> {
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import path from "node:path";
|
||||
import { isEnoent, Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { getWorktreeDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { $ } from "bun";
|
||||
|
||||
export interface WorktreeBaseline {
|
||||
@@ -30,7 +31,7 @@ export async function getRepoRoot(cwd: string): Promise<string> {
|
||||
export async function ensureWorktree(baseCwd: string, id: string): Promise<string> {
|
||||
const repoRoot = await getRepoRoot(baseCwd);
|
||||
const encodedProject = getEncodedProjectName(repoRoot);
|
||||
const worktreeDir = path.join(os.homedir(), ".omp", "wt", encodedProject, id);
|
||||
const worktreeDir = getWorktreeDir(encodedProject, id);
|
||||
await fs.mkdir(path.dirname(worktreeDir), { recursive: true });
|
||||
await $`git worktree remove -f ${worktreeDir}`.cwd(repoRoot).quiet().nothrow();
|
||||
await fs.rm(worktreeDir, { recursive: true, force: true });
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
||||
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
||||
@@ -7,8 +6,8 @@ import { FileType, glob } from "@oh-my-pi/pi-natives";
|
||||
import type { Component } from "@oh-my-pi/pi-tui";
|
||||
import { Text } from "@oh-my-pi/pi-tui";
|
||||
import { ptree, untilAborted } from "@oh-my-pi/pi-utils";
|
||||
import { getRemoteDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { type Static, Type } from "@sinclair/typebox";
|
||||
import { CONFIG_DIR_NAME } from "../config";
|
||||
import { renderPromptTemplate } from "../config/prompt-templates";
|
||||
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
||||
import { getLanguageFromPath, type Theme } from "../modes/theme/theme";
|
||||
@@ -40,7 +39,7 @@ import {
|
||||
const CONVERTIBLE_EXTENSIONS = new Set([".pdf", ".doc", ".docx", ".ppt", ".pptx", ".xls", ".xlsx", ".rtf", ".epub"]);
|
||||
|
||||
// Remote mount path prefix (sshfs mounts) - skip fuzzy matching to avoid hangs
|
||||
const REMOTE_MOUNT_PREFIX = path.join(os.homedir(), CONFIG_DIR_NAME, "remote") + path.sep;
|
||||
const REMOTE_MOUNT_PREFIX = getRemoteDir() + path.sep;
|
||||
|
||||
function isRemoteMountPath(absolutePath: string): boolean {
|
||||
return absolutePath.startsWith(REMOTE_MOUNT_PREFIX);
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { logger, ptree, TempDir } from "@oh-my-pi/pi-utils";
|
||||
import { APP_NAME, getToolsDir } from "../config";
|
||||
import { APP_NAME, getToolsDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
const TOOLS_DIR = getToolsDir();
|
||||
const TOOL_DOWNLOAD_TIMEOUT_MS = 15000;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
import { buildAnthropicHeaders as buildProviderAnthropicHeaders, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
||||
import { $env, logger } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath, getAgentDir } from "../../config";
|
||||
import { getAgentDbPath, getAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { AgentStorage } from "../../session/agent-storage";
|
||||
import type { AuthCredential } from "../../session/auth-storage";
|
||||
import type { AnthropicAuthConfig, AnthropicOAuthCredential, ModelsJson } from "./types";
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
*/
|
||||
import * as os from "node:os";
|
||||
import { readSseJson } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import packageJson from "../../../../package.json" with { type: "json" };
|
||||
import { getAgentDbPath } from "../../../config";
|
||||
import { AgentStorage } from "../../../session/agent-storage";
|
||||
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
||||
import { SearchProviderError } from "../../../web/search/types";
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Returns synthesized answers with citations and source metadata from grounding chunks.
|
||||
*/
|
||||
import { refreshGoogleCloudToken } from "@oh-my-pi/pi-ai";
|
||||
import { getAgentDbPath } from "../../../config";
|
||||
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { AgentStorage } from "../../../session/agent-storage";
|
||||
import type { SearchCitation, SearchResponse, SearchSource } from "../../../web/search/types";
|
||||
import { SearchProviderError } from "../../../web/search/types";
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { getEnvApiKey } from "@oh-my-pi/pi-ai";
|
||||
import { readSseJson } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentDbPath } from "../../../config";
|
||||
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { AgentStorage } from "../../../session/agent-storage";
|
||||
import type {
|
||||
PerplexityMessageOutput,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
import { Agent } from "@oh-my-pi/pi-agent-core";
|
||||
import { getModel } from "@oh-my-pi/pi-ai/models";
|
||||
import { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
|
||||
@@ -12,6 +11,7 @@ import { AgentSession } from "@oh-my-pi/pi-coding-agent/session/agent-session";
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { TempDir } from "@oh-my-pi/pi-utils";
|
||||
import { getProjectAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
/**
|
||||
* Regression test: auto-compaction completion should resume the agent loop when
|
||||
@@ -30,7 +30,7 @@ describe("AgentSession auto-compaction queue resume", () => {
|
||||
|
||||
// Provide an extension that short-circuits compaction so the test doesn't
|
||||
// make any LLM calls.
|
||||
const extensionsDir = path.join(tempDir.path(), ".omp", "extensions");
|
||||
const extensionsDir = path.join(getProjectAgentDir(tempDir.path()), "extensions");
|
||||
fs.mkdirSync(extensionsDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(extensionsDir, "compaction-short-circuit.ts"),
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { loadPythonModules, type PythonModuleExecutor } from "@oh-my-pi/pi-coding-agent/ipy/modules";
|
||||
import { TempDir } from "@oh-my-pi/pi-utils";
|
||||
import { getAgentModulesDir, getProjectModulesDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
const fixturesDir = path.resolve(import.meta.dir, "../../test/fixtures/python-modules");
|
||||
|
||||
@@ -26,11 +27,11 @@ describe("python modules", () => {
|
||||
|
||||
it("loads modules in sorted order with silent execution", async () => {
|
||||
tempRoot = TempDir.createSync("@omp-python-modules-");
|
||||
const homeDir = path.join(tempRoot.path(), "home");
|
||||
const agentDir = path.join(tempRoot.path(), "agent");
|
||||
const cwd = path.join(tempRoot.path(), "project");
|
||||
|
||||
await writeModule(path.join(homeDir, ".omp", "agent", "modules"), "beta.py", "user-omp");
|
||||
await writeModule(path.join(homeDir, ".omp", "agent", "modules"), "alpha.py", "user-omp");
|
||||
await writeModule(getAgentModulesDir(agentDir), "beta.py", "user-omp");
|
||||
await writeModule(getAgentModulesDir(agentDir), "alpha.py", "user-omp");
|
||||
|
||||
const calls: Array<{ name: string; options?: { silent?: boolean; storeHistory?: boolean } }> = [];
|
||||
const executor: PythonModuleExecutor = {
|
||||
@@ -41,7 +42,7 @@ describe("python modules", () => {
|
||||
},
|
||||
};
|
||||
|
||||
await loadPythonModules(executor, { cwd, homeDir });
|
||||
await loadPythonModules(executor, { cwd, agentDir });
|
||||
expect(calls.map(call => call.name)).toEqual(["alpha", "beta"]);
|
||||
for (const call of calls) {
|
||||
expect(call.options).toEqual({ silent: true, storeHistory: false });
|
||||
@@ -50,11 +51,11 @@ describe("python modules", () => {
|
||||
|
||||
it("fails fast when a module fails to execute", async () => {
|
||||
tempRoot = TempDir.createSync("@omp-python-modules-");
|
||||
const homeDir = path.join(tempRoot.path(), "home");
|
||||
const agentDir = path.join(tempRoot.path(), "agent");
|
||||
const cwd = path.join(tempRoot.path(), "project");
|
||||
|
||||
await writeModule(path.join(homeDir, ".omp", "agent", "modules"), "alpha.py", "user-omp");
|
||||
await writeModule(path.join(cwd, ".omp", "modules"), "beta.py", "project-omp");
|
||||
await writeModule(getAgentModulesDir(agentDir), "alpha.py", "user-omp");
|
||||
await writeModule(getProjectModulesDir(cwd), "beta.py", "project-omp");
|
||||
|
||||
const executor: PythonModuleExecutor = {
|
||||
execute: async (code: string) => {
|
||||
@@ -69,6 +70,6 @@ describe("python modules", () => {
|
||||
},
|
||||
};
|
||||
|
||||
await expect(loadPythonModules(executor, { cwd, homeDir })).rejects.toThrow("Failed to load Python module");
|
||||
await expect(loadPythonModules(executor, { cwd, agentDir })).rejects.toThrow("Failed to load Python module");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { discoverAndLoadExtensions, loadExtensions } from "@oh-my-pi/pi-coding-agent/extensibility/extensions/loader";
|
||||
import { TempDir } from "@oh-my-pi/pi-utils";
|
||||
import { getProjectAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
@@ -13,7 +14,7 @@ describe("extensions discovery", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
tempDir = TempDir.createSync("@pi-ext-test-");
|
||||
extensionsDir = path.join(tempDir.path(), ".omp", "extensions");
|
||||
extensionsDir = path.join(getProjectAgentDir(tempDir.path()), "extensions");
|
||||
fs.mkdirSync(extensionsDir, { recursive: true });
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ExtensionRunner } from "@oh-my-pi/pi-coding-agent/extensibility/extensi
|
||||
import { AuthStorage } from "@oh-my-pi/pi-coding-agent/session/auth-storage";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { logger, TempDir } from "@oh-my-pi/pi-utils";
|
||||
import { getProjectAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
describe("ExtensionRunner", () => {
|
||||
let tempDir: TempDir;
|
||||
@@ -20,7 +21,7 @@ describe("ExtensionRunner", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
tempDir = TempDir.createSync("@pi-runner-test-");
|
||||
extensionsDir = path.join(tempDir.path(), ".omp", "extensions");
|
||||
extensionsDir = path.join(getProjectAgentDir(tempDir.path()), "extensions");
|
||||
fs.mkdirSync(extensionsDir, { recursive: true });
|
||||
sessionManager = SessionManager.inMemory();
|
||||
const authStorage = await AuthStorage.create(path.join(tempDir.path(), "testauth.db"));
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
||||
import type { Skill } from "@oh-my-pi/pi-coding-agent/sdk";
|
||||
import { createAgentSession } from "@oh-my-pi/pi-coding-agent/sdk";
|
||||
import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
||||
import { getProjectAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
|
||||
describe("createAgentSession skills option", () => {
|
||||
let tempDir: string;
|
||||
@@ -13,7 +14,7 @@ describe("createAgentSession skills option", () => {
|
||||
beforeEach(() => {
|
||||
tempDir = path.join(os.tmpdir(), `pi-sdk-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
||||
// Create skill in .omp/skills/ for native project-level discovery
|
||||
skillsDir = path.join(tempDir, ".omp", "skills", "test-skill");
|
||||
skillsDir = path.join(getProjectAgentDir(tempDir), "skills", "test-skill");
|
||||
fs.mkdirSync(skillsDir, { recursive: true });
|
||||
|
||||
// Create a test skill in the pi skills directory
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { _resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
|
||||
import { Snowflake } from "@oh-my-pi/pi-utils";
|
||||
import { getProjectAgentDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { YAML } from "bun";
|
||||
|
||||
describe("Settings", () => {
|
||||
@@ -24,7 +25,7 @@ describe("Settings", () => {
|
||||
fs.rmSync(testDir, { recursive: true });
|
||||
}
|
||||
fs.mkdirSync(agentDir, { recursive: true });
|
||||
fs.mkdirSync(path.join(projectDir, ".omp"), { recursive: true });
|
||||
fs.mkdirSync(getProjectAgentDir(projectDir), { recursive: true });
|
||||
});
|
||||
|
||||
const getConfigPath = () => path.join(agentDir, "config.yml");
|
||||
|
||||
+66
-65
@@ -1,66 +1,67 @@
|
||||
{
|
||||
"name": "@oh-my-pi/omp-stats",
|
||||
"version": "12.0.0",
|
||||
"description": "Local observability dashboard for pi AI usage statistics",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"bin": {
|
||||
"omp-stats": "./src/index.ts"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"import": "./src/index.ts"
|
||||
},
|
||||
"./src/server": {
|
||||
"types": "./src/server.ts",
|
||||
"import": "./src/server.ts"
|
||||
},
|
||||
"./src/db": {
|
||||
"types": "./src/db.ts",
|
||||
"import": "./src/db.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"public",
|
||||
"README.md"
|
||||
],
|
||||
"keywords": [
|
||||
"ai",
|
||||
"observability",
|
||||
"metrics",
|
||||
"dashboard",
|
||||
"llm",
|
||||
"statistics"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
"check": "tsgo -p tsconfig.json && tsgo -p tsconfig.client.json",
|
||||
"build": "bun run build.ts"
|
||||
},
|
||||
"author": "Can Bölük",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/can1357/oh-my-pi.git",
|
||||
"directory": "packages/stats"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oh-my-pi/pi-ai": "workspace:*",
|
||||
"date-fns": "^4.1.0",
|
||||
"lucide-react": "^0.563.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"recharts": "^3.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.9",
|
||||
"@types/react": "^19.2.10",
|
||||
"@types/react-dom": "^19.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.3.7"
|
||||
}
|
||||
}
|
||||
"name": "@oh-my-pi/omp-stats",
|
||||
"version": "12.0.0",
|
||||
"description": "Local observability dashboard for pi AI usage statistics",
|
||||
"type": "module",
|
||||
"main": "./src/index.ts",
|
||||
"types": "./src/index.ts",
|
||||
"bin": {
|
||||
"omp-stats": "./src/index.ts"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"import": "./src/index.ts"
|
||||
},
|
||||
"./src/server": {
|
||||
"types": "./src/server.ts",
|
||||
"import": "./src/server.ts"
|
||||
},
|
||||
"./src/db": {
|
||||
"types": "./src/db.ts",
|
||||
"import": "./src/db.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"public",
|
||||
"README.md"
|
||||
],
|
||||
"keywords": [
|
||||
"ai",
|
||||
"observability",
|
||||
"metrics",
|
||||
"dashboard",
|
||||
"llm",
|
||||
"statistics"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
"check": "tsgo -p tsconfig.json && tsgo -p tsconfig.client.json",
|
||||
"build": "bun run build.ts"
|
||||
},
|
||||
"author": "Can Bölük",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/can1357/oh-my-pi.git",
|
||||
"directory": "packages/stats"
|
||||
},
|
||||
"dependencies": {
|
||||
"@oh-my-pi/pi-ai": "workspace:*",
|
||||
"date-fns": "^4.1.0",
|
||||
"lucide-react": "^0.563.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"recharts": "^3.7.0",
|
||||
"@oh-my-pi/pi-utils": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.3.9",
|
||||
"@types/react": "^19.2.10",
|
||||
"@types/react-dom": "^19.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"bun": ">=1.3.7"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Database } from "bun:sqlite";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { getConfigRootDir, getStatsDbPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type {
|
||||
AggregatedStats,
|
||||
FolderStats,
|
||||
@@ -12,7 +11,7 @@ import type {
|
||||
TimeSeriesPoint,
|
||||
} from "./types";
|
||||
|
||||
const DB_PATH = path.join(os.homedir(), ".omp", "stats.db");
|
||||
const DB_PATH = getStatsDbPath();
|
||||
|
||||
let db: Database | null = null;
|
||||
|
||||
@@ -23,7 +22,7 @@ export async function initDb(): Promise<Database> {
|
||||
if (db) return db;
|
||||
|
||||
// Ensure directory exists
|
||||
await fs.mkdir(path.join(os.homedir(), ".omp"), { recursive: true });
|
||||
await fs.mkdir(getConfigRootDir(), { recursive: true });
|
||||
|
||||
db = new Database(DB_PATH);
|
||||
db.exec("PRAGMA journal_mode = WAL");
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
||||
import { getSessionsDir } from "@oh-my-pi/pi-utils/dirs";
|
||||
import type { MessageStats, SessionEntry, SessionMessageEntry } from "./types";
|
||||
|
||||
const SESSIONS_DIR = path.join(os.homedir(), ".omp", "agent", "sessions");
|
||||
|
||||
/**
|
||||
* Extract folder name from session filename.
|
||||
* Session files are named like: --work--pi--/timestamp_uuid.jsonl
|
||||
@@ -95,8 +93,9 @@ export async function parseSessionFile(
|
||||
*/
|
||||
export async function listSessionFolders(): Promise<string[]> {
|
||||
try {
|
||||
const entries = await fs.readdir(SESSIONS_DIR, { withFileTypes: true });
|
||||
return entries.filter(e => e.isDirectory()).map(e => path.join(SESSIONS_DIR, e.name));
|
||||
const sessionsDir = getSessionsDir();
|
||||
const entries = await fs.readdir(sessionsDir, { withFileTypes: true });
|
||||
return entries.filter(e => e.isDirectory()).map(e => path.join(sessionsDir, e.name));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
@@ -129,13 +128,6 @@ export async function listAllSessionFiles(): Promise<string[]> {
|
||||
return allFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session directory path.
|
||||
*/
|
||||
export function getSessionsDir(): string {
|
||||
return SESSIONS_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific entry in a session file.
|
||||
*/
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* Minimal TUI implementation with differential rendering
|
||||
*/
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { getCrashLogPath, getDebugLogPath } from "@oh-my-pi/pi-utils/dirs";
|
||||
import { isKeyRelease, matchesKey } from "./keys";
|
||||
import type { Terminal } from "./terminal";
|
||||
import { setCellDimensions, TERMINAL } from "./terminal-capabilities";
|
||||
@@ -872,7 +872,7 @@ export class TUI extends Container {
|
||||
const debugRedraw = process.env.PI_DEBUG_REDRAW === "1";
|
||||
const logRedraw = (reason: string): void => {
|
||||
if (!debugRedraw) return;
|
||||
const logPath = path.join(os.homedir(), ".omp", "agent", "omp-debug.log");
|
||||
const logPath = getDebugLogPath();
|
||||
const msg = `[${new Date().toISOString()}] fullRender: ${reason} (prev=${this.#previousLines.length}, new=${newLines.length}, height=${height})\n`;
|
||||
fs.appendFileSync(logPath, msg);
|
||||
};
|
||||
@@ -1018,7 +1018,7 @@ export class TUI extends Container {
|
||||
const isImage = TERMINAL.isImageLine(line);
|
||||
if (!isImage && visibleWidth(line) > width) {
|
||||
// Log all lines to crash file for debugging
|
||||
const crashLogPath = path.join(os.homedir(), ".omp", "agent", "omp-crash.log");
|
||||
const crashLogPath = getCrashLogPath();
|
||||
const crashData = [
|
||||
`Crash at ${new Date().toISOString()}`,
|
||||
`Terminal width: ${width}`,
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
"./cli": {
|
||||
"types": "./src/cli.ts",
|
||||
"import": "./src/cli.ts"
|
||||
},
|
||||
"./dirs": {
|
||||
"types": "./src/dirs.ts",
|
||||
"import": "./src/dirs.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/**
|
||||
* Centralized path helpers for omp config directories.
|
||||
*
|
||||
* Uses PI_CONFIG_DIR (default ".omp") for the config root and
|
||||
* PI_CODING_AGENT_DIR to override the agent directory.
|
||||
*/
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import packageJson from "../../coding-agent/package.json" with { type: "json" };
|
||||
import { $env } from "./env";
|
||||
|
||||
/** App name (e.g. "omp") */
|
||||
export const APP_NAME: string = packageJson.ompConfig?.name || "omp";
|
||||
|
||||
/** Config directory name (e.g. ".omp") */
|
||||
export const CONFIG_DIR_NAME: string = packageJson.ompConfig?.configDir || ".omp";
|
||||
|
||||
/** Version (e.g. "1.0.0") */
|
||||
export const VERSION: string = packageJson.version;
|
||||
|
||||
/**
|
||||
* Get the config root directory (~/.omp).
|
||||
*/
|
||||
export function getConfigRootDir(): string {
|
||||
return path.join(os.homedir(), $env.PI_CONFIG_DIR || CONFIG_DIR_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the agent directory.
|
||||
*/
|
||||
export function getAgentDir(): string {
|
||||
return $env.PI_CODING_AGENT_DIR || path.join(getConfigRootDir(), "agent");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the project-local agent directory.
|
||||
*/
|
||||
export function getProjectAgentDir(cwd: string = process.cwd()): string {
|
||||
return path.join(cwd, CONFIG_DIR_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reports directory (~/.omp/reports).
|
||||
*/
|
||||
export function getReportsDir(): string {
|
||||
return path.join(getConfigRootDir(), "reports");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logs directory (~/.omp/logs).
|
||||
*/
|
||||
export function getLogsDir(): string {
|
||||
return path.join(getConfigRootDir(), "logs");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to today's log file.
|
||||
*/
|
||||
export function getLogPath(date?: string): string {
|
||||
const today = date ?? new Date().toISOString().slice(0, 10);
|
||||
return path.join(getLogsDir(), `omp.${today}.log`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user-level Python modules directory (~/.omp/agent/modules).
|
||||
*/
|
||||
export function getAgentModulesDir(agentDir: string = getAgentDir()): string {
|
||||
return path.join(agentDir, "modules");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project-level Python modules directory (.omp/modules).
|
||||
*/
|
||||
export function getProjectModulesDir(cwd: string = process.cwd()): string {
|
||||
return path.join(getProjectAgentDir(cwd), "modules");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project-level prompts directory (.omp/prompts).
|
||||
*/
|
||||
export function getProjectPromptsDir(cwd: string = process.cwd()): string {
|
||||
return path.join(getProjectAgentDir(cwd), "prompts");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugins directory (~/.omp/plugins).
|
||||
*/
|
||||
export function getPluginsDir(): string {
|
||||
return path.join(getConfigRootDir(), "plugins");
|
||||
}
|
||||
|
||||
/** Where npm installs packages: ~/.omp/plugins/node_modules */
|
||||
export function getPluginsNodeModules(): string {
|
||||
return path.join(getPluginsDir(), "node_modules");
|
||||
}
|
||||
|
||||
/** Plugin manifest: ~/.omp/plugins/package.json */
|
||||
export function getPluginsPackageJson(): string {
|
||||
return path.join(getPluginsDir(), "package.json");
|
||||
}
|
||||
|
||||
/** Plugin lock file: ~/.omp/plugins/omp-plugins.lock.json */
|
||||
export function getPluginsLockfile(): string {
|
||||
return path.join(getPluginsDir(), "omp-plugins.lock.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remote mount directory (~/.omp/remote).
|
||||
*/
|
||||
export function getRemoteDir(): string {
|
||||
return path.join(getConfigRootDir(), "remote");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SSH control socket directory (~/.omp/ssh-control).
|
||||
*/
|
||||
export function getSshControlDir(): string {
|
||||
return path.join(getConfigRootDir(), "ssh-control");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remote host info directory (~/.omp/remote-host).
|
||||
*/
|
||||
export function getRemoteHostDir(): string {
|
||||
return path.join(getConfigRootDir(), "remote-host");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the managed Python venv directory (~/.omp/python-env).
|
||||
*/
|
||||
export function getPythonEnvDir(): string {
|
||||
return path.join(getConfigRootDir(), "python-env");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project-level plugin overrides path (.omp/plugin-overrides.json).
|
||||
*/
|
||||
export function getProjectPluginOverridesPath(cwd: string = process.cwd()): string {
|
||||
return path.join(getProjectAgentDir(cwd), "plugin-overrides.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the MCP config file path.
|
||||
* @param scope - "user" for ~/.omp/mcp.json or "project" for .omp/mcp.json
|
||||
*/
|
||||
export function getMCPConfigPaths(scope: "user" | "project", cwd: string = process.cwd()): string[] {
|
||||
if (scope === "user") {
|
||||
return [path.join(getAgentDir(), "mcp.json"), path.join(getAgentDir(), ".mcp.json")];
|
||||
}
|
||||
return [path.join(getProjectAgentDir(cwd), "mcp.json"), path.join(getProjectAgentDir(cwd), ".mcp.json")];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primary MCP config file path (first candidate).
|
||||
* @param scope - "user" for ~/.omp/agent/mcp.json or "project" for .omp/mcp.json
|
||||
*/
|
||||
export function getMCPConfigPath(scope: "user" | "project", cwd: string = process.cwd()): string {
|
||||
return getMCPConfigPaths(scope, cwd)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the worktree base directory (~/.omp/wt).
|
||||
*/
|
||||
export function getWorktreeBaseDir(): string {
|
||||
return path.join(getConfigRootDir(), "wt");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to a worktree directory.
|
||||
*/
|
||||
export function getWorktreeDir(encodedProject: string, id: string): string {
|
||||
return path.join(getWorktreeBaseDir(), encodedProject, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the GPU cache path (~/.omp/gpu_cache.json).
|
||||
*/
|
||||
export function getGpuCachePath(): string {
|
||||
return path.join(getConfigRootDir(), "gpu_cache.json");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test auth database path (~/.omp/agent/testauth.db).
|
||||
*/
|
||||
export function getTestAuthPath(): string {
|
||||
return path.join(getAgentDir(), "testauth.db");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sessions directory (~/.omp/agent/sessions).
|
||||
*/
|
||||
export function getSessionsDir(): string {
|
||||
return path.join(getAgentDir(), "sessions");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the natives directory (~/.omp/natives).
|
||||
*/
|
||||
export function getNativesDir(): string {
|
||||
return path.join(getConfigRootDir(), "natives");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stats database path (~/.omp/stats.db).
|
||||
*/
|
||||
export function getStatsDbPath(): string {
|
||||
return path.join(getConfigRootDir(), "stats.db");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the crash log path (~/.omp/agent/omp-crash.log).
|
||||
*/
|
||||
export function getCrashLogPath(): string {
|
||||
return path.join(getAgentDir(), "omp-crash.log");
|
||||
}
|
||||
|
||||
/** Gets the path to agent.db (SQLite database for settings and auth storage) */
|
||||
export function getAgentDbPath(agentDir: string = getAgentDir()): string {
|
||||
return path.join(agentDir, "agent.db");
|
||||
}
|
||||
|
||||
/** Get path to user's custom themes directory */
|
||||
export function getCustomThemesDir(): string {
|
||||
return path.join(getAgentDir(), "themes");
|
||||
}
|
||||
|
||||
/** Get path to tools directory */
|
||||
export function getToolsDir(): string {
|
||||
return path.join(getAgentDir(), "tools");
|
||||
}
|
||||
|
||||
/** Get path to slash commands directory */
|
||||
export function getCommandsDir(): string {
|
||||
return path.join(getAgentDir(), "commands");
|
||||
}
|
||||
|
||||
/** Get path to prompts directory */
|
||||
export function getPromptsDir(): string {
|
||||
return path.join(getAgentDir(), "prompts");
|
||||
}
|
||||
|
||||
/** Get path to content-addressed blob store directory */
|
||||
export function getBlobsDir(): string {
|
||||
return path.join(getAgentDir(), "blobs");
|
||||
}
|
||||
|
||||
/** Get path to debug log file */
|
||||
export function getDebugLogPath(): string {
|
||||
return path.join(getAgentDir(), `${APP_NAME}-debug.log`);
|
||||
}
|
||||
@@ -5,15 +5,9 @@
|
||||
* Each log entry includes process.pid for traceability.
|
||||
*/
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import winston from "winston";
|
||||
import DailyRotateFile from "winston-daily-rotate-file";
|
||||
|
||||
/** Get the logs directory (~/.omp/logs/) */
|
||||
function getLogsDir(): string {
|
||||
return path.join(os.homedir(), ".omp", "logs");
|
||||
}
|
||||
import { getLogsDir } from "./dirs";
|
||||
|
||||
/** Ensure logs directory exists */
|
||||
function ensureLogsDir(): string {
|
||||
|
||||
Reference in New Issue
Block a user