oh-my-opencode 4.19.2 → 4.19.4
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/.agents/command/get-unpublished-changes.md +2 -0
- package/.agents/command/omomomo.md +1 -1
- package/.agents/command/publish.md +102 -28
- package/.agents/skills/get-unpublished-changes/SKILL.md +2 -0
- package/.agents/skills/hyperplan/SKILL.md +3 -3
- package/.agents/skills/omomomo/SKILL.md +1 -1
- package/.agents/skills/publish/SKILL.md +27 -6
- package/.opencode/command/get-unpublished-changes.md +2 -0
- package/.opencode/command/omomomo.md +1 -1
- package/.opencode/command/publish.md +102 -28
- package/.opencode/skills/hyperplan/SKILL.md +3 -3
- package/THIRD-PARTY-NOTICES.md +7 -7
- package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
- package/dist/agents/sisyphus-runtime-prompt-reconciler.d.ts +1 -1
- package/dist/cli/config-migrate.d.ts +8 -0
- package/dist/cli/doctor/checks/deprecated-reasoning-keys.d.ts +2 -0
- package/dist/cli/doctor/checks/legacy-config-leftovers.d.ts +10 -0
- package/dist/cli/doctor/checks/model-resolution-config.d.ts +1 -1
- package/dist/cli/doctor/checks/tools-lsp.d.ts +1 -1
- package/dist/cli/doctor/framework/constants.d.ts +1 -1
- package/dist/cli/index.js +95684 -92814
- package/dist/cli-node/index.js +95650 -92780
- package/dist/config/schema/agent-overrides.d.ts +800 -0
- package/dist/config/schema/categories.d.ts +132 -0
- package/dist/config/schema/fallback-models.d.ts +50 -0
- package/dist/config/schema/oh-my-opencode-config.d.ts +818 -2
- package/dist/config/validate.d.ts +2 -1
- package/dist/config-migration/deep-diff.d.ts +1 -0
- package/dist/config-migration/discovery-paths.d.ts +11 -0
- package/dist/config-migration/discovery-roots.d.ts +7 -0
- package/dist/config-migration/discovery.d.ts +4 -0
- package/dist/config-migration/index.d.ts +10 -0
- package/dist/config-migration/legacy-history.d.ts +3 -0
- package/dist/config-migration/migration-executor.d.ts +4 -0
- package/dist/config-migration/migration-plans.d.ts +15 -0
- package/dist/config-migration/reasoning-unification.d.ts +3 -0
- package/dist/config-migration/record-values.d.ts +4 -0
- package/dist/config-migration/schema-url.d.ts +1 -0
- package/dist/config-migration/transform-config-jsonc.d.ts +2 -0
- package/dist/config-migration/transform-opencode.d.ts +2 -0
- package/dist/config-migration/transform-types.d.ts +24 -0
- package/dist/config-migration/types.d.ts +39 -0
- package/dist/features/builtin-commands/templates/hyperplan.d.ts +1 -1
- package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +2 -0
- package/dist/hooks/auto-update-checker/constants.d.ts +3 -3
- package/dist/hooks/codegraph-bootstrap/command-runner.d.ts +1 -0
- package/dist/hooks/model-fallback/next-fallback.d.ts +1 -0
- package/dist/hooks/runtime-fallback/constants.d.ts +1 -1
- package/dist/index.js +12368 -9788
- package/dist/oh-my-opencode.schema.json +1672 -8
- package/dist/plugin-config/omo-config-chain.d.ts +14 -0
- package/dist/plugin-config/unknown-key-diagnostics.d.ts +5 -0
- package/dist/plugin-config.d.ts +2 -2
- package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +1 -0
- package/dist/shared/agent-display-names.d.ts +1 -1
- package/dist/shared/agent-variant.d.ts +11 -0
- package/dist/shared/jsonc-parser.d.ts +1 -1
- package/dist/shared/migration.d.ts +5 -1
- package/dist/shared/opencode-config-dir-types.d.ts +0 -1
- package/dist/shared/plugin-identity.d.ts +6 -8
- package/dist/shared/project-discovery-dirs.d.ts +0 -1
- package/dist/shared/session-prompt-params-helpers.d.ts +6 -1
- package/dist/skills/coding-agent-sessions/SKILL.md +4 -3
- package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
- package/dist/skills/data-scientist/SKILL.md +243 -0
- package/dist/skills/data-scientist/references/common-scenarios.md +176 -0
- package/dist/skills/data-scientist/references/execution-templates.md +197 -0
- package/dist/skills/data-scientist/references/integration-patterns.md +153 -0
- package/dist/skills/data-scientist/references/performance-benchmarks.md +37 -0
- package/dist/skills/data-scientist/references/uv-setup.md +78 -0
- package/dist/skills/data-scientist/scripts/quick-query.py +111 -0
- package/dist/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
- package/dist/skills/data-scientist/scripts/setup-uv.sh +60 -0
- package/dist/skills/debugging/SKILL.md +1 -1
- package/dist/skills/frontend/ATTRIBUTION.md +7 -0
- package/dist/skills/frontend/SKILL.md +4 -1
- package/dist/skills/frontend/references/design/README.md +8 -0
- package/dist/skills/frontend/references/design/_INDEX.md +14 -1
- package/dist/skills/frontend/references/design/interaction-skill.md +144 -0
- package/dist/skills/programming/SKILL.md +1 -2
- package/dist/skills/ulw-plan/SKILL.md +1 -1
- package/dist/skills/ulw-research/SKILL.md +122 -11
- package/dist/startup-migration.d.ts +28 -0
- package/dist/testing/create-plugin-module.d.ts +4 -0
- package/dist/tools/delegate-task/builtin-categories.d.ts +1 -0
- package/dist/tools/delegate-task/builtin-category-definition.d.ts +1 -0
- package/dist/tools/delegate-task/constants.d.ts +1 -1
- package/dist/tui.js +6040 -5443
- package/package.json +13 -13
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +21 -5
- package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +11 -2
- package/packages/omo-codex/THIRD-PARTY-NOTICES.md +2 -2
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +21 -3
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +11 -5
- package/packages/omo-codex/plugin/components/codegraph/NOTICE +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +10373 -2570
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +7502 -574
- package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
- package/packages/omo-codex/plugin/components/codegraph/src/hook-input.ts +33 -0
- package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +36 -10
- package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +164 -159
- package/packages/omo-codex/plugin/components/codegraph/src/post-tool-use-hook.ts +34 -0
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +20 -2
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-command.ts +106 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-cooldown.ts +145 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +21 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-lock.ts +139 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-outcome.ts +15 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-paths.ts +32 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-project.ts +109 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker-result.ts +148 -0
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +126 -175
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +4 -4
- package/packages/omo-codex/plugin/components/codegraph/test/hook-session-start-guard.test.ts +160 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +12 -138
- package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +34 -24
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +3 -3
- package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +2 -2
- package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +5 -5
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -3
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +45 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +2 -2
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +70 -13
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-project.test.ts +58 -0
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-state.test.ts +104 -0
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-trust-boundary.test.ts +4 -2
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-cooldown.test.ts +116 -0
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +58 -57
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +32 -15
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +17 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +42 -41
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -0
- package/packages/omo-codex/plugin/shared/src/config-loader.ts +165 -16
- package/packages/omo-codex/plugin/shared/src/config-migration.ts +162 -0
- package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +186 -9
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +4 -3
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
- package/packages/omo-codex/plugin/skills/data-scientist/SKILL.md +243 -0
- package/packages/omo-codex/plugin/skills/data-scientist/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/data-scientist/references/common-scenarios.md +176 -0
- package/packages/omo-codex/plugin/skills/data-scientist/references/execution-templates.md +197 -0
- package/packages/omo-codex/plugin/skills/data-scientist/references/integration-patterns.md +153 -0
- package/packages/omo-codex/plugin/skills/data-scientist/references/performance-benchmarks.md +37 -0
- package/packages/omo-codex/plugin/skills/data-scientist/references/uv-setup.md +78 -0
- package/packages/omo-codex/plugin/skills/data-scientist/scripts/quick-query.py +111 -0
- package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
- package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.sh +60 -0
- package/packages/omo-codex/plugin/skills/debugging/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +7 -0
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +8 -0
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +14 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/interaction-skill.md +144 -0
- package/packages/omo-codex/plugin/skills/programming/SKILL.md +1 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +17 -0
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +121 -11
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +7 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +12 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +24 -5
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +4 -3
- package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
- package/packages/shared-skills/skills/data-scientist/SKILL.md +243 -0
- package/packages/shared-skills/skills/data-scientist/references/common-scenarios.md +176 -0
- package/packages/shared-skills/skills/data-scientist/references/execution-templates.md +197 -0
- package/packages/shared-skills/skills/data-scientist/references/integration-patterns.md +153 -0
- package/packages/shared-skills/skills/data-scientist/references/performance-benchmarks.md +37 -0
- package/packages/shared-skills/skills/data-scientist/references/uv-setup.md +78 -0
- package/packages/shared-skills/skills/data-scientist/scripts/quick-query.py +111 -0
- package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
- package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.sh +60 -0
- package/packages/shared-skills/skills/debugging/SKILL.md +1 -1
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +7 -0
- package/packages/shared-skills/skills/frontend/SKILL.md +4 -1
- package/packages/shared-skills/skills/frontend/references/design/README.md +8 -0
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +14 -1
- package/packages/shared-skills/skills/frontend/references/design/interaction-skill.md +144 -0
- package/packages/shared-skills/skills/programming/SKILL.md +1 -2
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +1 -1
- package/packages/shared-skills/skills/ulw-research/SKILL.md +122 -11
- package/dist/plugin-config/layered-config-loader.d.ts +0 -2
- package/dist/plugin-config/single-config-loader.d.ts +0 -4
- package/dist/shared/migrate-legacy-config-file.d.ts +0 -1
- package/dist/shared/migration/config-migration.d.ts +0 -1
|
@@ -2,45 +2,194 @@ import { homedir } from "node:os"
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
loadOmoConfig,
|
|
5
|
+
mergeOmoConfigRecords,
|
|
6
|
+
OmoConfigSchema,
|
|
5
7
|
type LoadOmoConfigOptions,
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
type MigrationEnvironment,
|
|
9
|
+
type MigrationFileSystem,
|
|
10
|
+
type OmoConfig,
|
|
11
|
+
type OmoConfigEnv,
|
|
12
|
+
type OmoConfigSource,
|
|
13
|
+
} from "../../../../omo-config-core/src/index.ts"
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
import {
|
|
16
|
+
runCodexConfigMigration,
|
|
17
|
+
type CodexConfigMigrationOptions,
|
|
18
|
+
type CodexConfigMigrationResult,
|
|
19
|
+
} from "./config-migration.ts"
|
|
20
|
+
|
|
21
|
+
export type CodexOmoConfigOptions = Omit<LoadOmoConfigOptions, "fileSystem" | "harness"> & {
|
|
22
|
+
readonly fileSystem?: MigrationFileSystem
|
|
23
|
+
readonly homeDir?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type CodexStartupMigrationOptions = CodexConfigMigrationOptions
|
|
27
|
+
export type CodexStartupMigrationResult = CodexConfigMigrationResult
|
|
28
|
+
|
|
29
|
+
type CodexCodegraphConfig = {
|
|
30
|
+
readonly auto_provision?: boolean
|
|
31
|
+
readonly daemon?: boolean
|
|
32
|
+
readonly enabled?: boolean
|
|
33
|
+
readonly excluded_roots?: readonly string[]
|
|
34
|
+
readonly install_dir?: string
|
|
35
|
+
readonly session_start_cooldown_ms?: number
|
|
36
|
+
readonly telemetry?: boolean
|
|
37
|
+
readonly watch_debounce_ms?: number
|
|
38
|
+
}
|
|
11
39
|
|
|
12
|
-
|
|
40
|
+
type CodexResolvedConfig = Omit<OmoConfig, "codegraph"> & {
|
|
41
|
+
readonly codegraph?: CodexCodegraphConfig
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type CodexOmoConfig = CodexResolvedConfig & {
|
|
13
45
|
readonly sources: readonly OmoConfigSource[]
|
|
14
46
|
readonly trustedCodegraphInstallDir?: string
|
|
15
47
|
readonly warnings: readonly string[]
|
|
16
48
|
}
|
|
17
49
|
|
|
50
|
+
const ENV_BOOLEAN_SETTINGS = [
|
|
51
|
+
["auto_provision", "AUTO_PROVISION"],
|
|
52
|
+
["enabled", "ENABLED"],
|
|
53
|
+
["telemetry", "TELEMETRY"],
|
|
54
|
+
] as const
|
|
55
|
+
|
|
56
|
+
function resolveHomeDir(options: CodexOmoConfigOptions): string {
|
|
57
|
+
const env = options.env ?? process.env
|
|
58
|
+
return options.homeDir ?? env.HOME ?? env.USERPROFILE ?? homedir()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function environmentWithHome(env: OmoConfigEnv, homeDir: string): OmoConfigEnv {
|
|
62
|
+
return { ...env, HOME: homeDir }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function migrationEnvironment(homeDir: string, env: OmoConfigEnv): MigrationEnvironment {
|
|
66
|
+
return {
|
|
67
|
+
HOME: homeDir,
|
|
68
|
+
...(env.USERPROFILE === undefined ? {} : { USERPROFILE: env.USERPROFILE }),
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Runs only the legacy config.jsonc transaction before Codex reads omo.jsonc. */
|
|
73
|
+
export function runCodexStartupMigration(options: CodexStartupMigrationOptions): CodexStartupMigrationResult {
|
|
74
|
+
return runCodexConfigMigration(options)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseBoolean(value: string): boolean | undefined {
|
|
78
|
+
const normalized = value.trim().toLowerCase()
|
|
79
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true
|
|
80
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false
|
|
81
|
+
return undefined
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function envOverrides(env: OmoConfigEnv, warnings: string[]): Record<string, unknown> {
|
|
85
|
+
const codegraph: Record<string, unknown> = {}
|
|
86
|
+
for (const prefix of ["OMO", "CODEX"]) {
|
|
87
|
+
for (const [setting, suffix] of ENV_BOOLEAN_SETTINGS) {
|
|
88
|
+
const name = `${prefix}_CODEGRAPH_${suffix}`
|
|
89
|
+
const rawValue = env[name]
|
|
90
|
+
if (rawValue === undefined) continue
|
|
91
|
+
const value = parseBoolean(rawValue)
|
|
92
|
+
if (value === undefined) warnings.push(`${name} has invalid boolean value "${rawValue}"`)
|
|
93
|
+
else codegraph[setting] = value
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const installDir = env[`${prefix}_CODEGRAPH_INSTALL_DIR`]
|
|
97
|
+
if (installDir !== undefined) codegraph["install_dir"] = installDir
|
|
98
|
+
const cooldown = env[`${prefix}_CODEGRAPH_SESSION_START_COOLDOWN_MS`]
|
|
99
|
+
if (cooldown !== undefined) {
|
|
100
|
+
const value = Number(cooldown)
|
|
101
|
+
if (!Number.isFinite(value) || value < 60_000) {
|
|
102
|
+
warnings.push(`${prefix}_CODEGRAPH_SESSION_START_COOLDOWN_MS has invalid number value "${cooldown}"`)
|
|
103
|
+
} else codegraph["session_start_cooldown_ms"] = value
|
|
104
|
+
}
|
|
105
|
+
const debounce = env[`${prefix}_CODEGRAPH_WATCH_DEBOUNCE_MS`]
|
|
106
|
+
if (debounce !== undefined) {
|
|
107
|
+
const value = Number(debounce)
|
|
108
|
+
if (!Number.isFinite(value) || value < 0) warnings.push(`${prefix}_CODEGRAPH_WATCH_DEBOUNCE_MS has invalid number value "${debounce}"`)
|
|
109
|
+
else codegraph["watch_debounce_ms"] = value
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return Object.keys(codegraph).length === 0 ? {} : { codegraph }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function migrationWarnings(result: CodexStartupMigrationResult): readonly string[] {
|
|
116
|
+
const warnings: string[] = []
|
|
117
|
+
if (result.error !== undefined) warnings.push(`omo-codex: configuration migration: ${result.error}`)
|
|
118
|
+
if (result.journalResumed) warnings.push("omo-codex: recovered an interrupted configuration migration")
|
|
119
|
+
if (result.migratedFrom.length > 0) {
|
|
120
|
+
warnings.push(`omo-codex: migrated legacy configuration from ${result.migratedFrom.join(", ")}`)
|
|
121
|
+
}
|
|
122
|
+
for (const migration of result.results) {
|
|
123
|
+
for (const diagnostic of migration.diagnostics) {
|
|
124
|
+
warnings.push(`omo-codex: configuration migration: ${diagnostic}`)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return warnings
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function applicabilityWarnings(config: OmoConfig): readonly string[] {
|
|
131
|
+
return config.codegraph?.watch_debounce_ms === undefined
|
|
132
|
+
? []
|
|
133
|
+
: ["codegraph.watch_debounce_ms is not supported for harness codex"]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function codexCodegraphConfig(value: OmoConfig["codegraph"]): CodexCodegraphConfig | undefined {
|
|
137
|
+
if (value === undefined) return undefined
|
|
138
|
+
return {
|
|
139
|
+
auto_provision: value.auto_provision,
|
|
140
|
+
daemon: value.daemon,
|
|
141
|
+
enabled: value.enabled,
|
|
142
|
+
telemetry: value.telemetry,
|
|
143
|
+
...(value.excluded_roots === undefined ? {} : { excluded_roots: value.excluded_roots }),
|
|
144
|
+
...(value.install_dir === undefined ? {} : { install_dir: value.install_dir }),
|
|
145
|
+
...(value.session_start_cooldown_ms === undefined ? {} : { session_start_cooldown_ms: value.session_start_cooldown_ms }),
|
|
146
|
+
...(value.watch_debounce_ms === undefined ? {} : { watch_debounce_ms: value.watch_debounce_ms }),
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
18
150
|
export function getCodexOmoConfig(options: CodexOmoConfigOptions = {}): CodexOmoConfig {
|
|
19
151
|
const env = options.env ?? process.env
|
|
20
152
|
const homeDir = resolveHomeDir(options)
|
|
153
|
+
const environment = environmentWithHome(env, homeDir)
|
|
154
|
+
const migration = runCodexStartupMigration({
|
|
155
|
+
cwd: options.cwd ?? process.cwd(),
|
|
156
|
+
environment,
|
|
157
|
+
env: migrationEnvironment(homeDir, environment),
|
|
158
|
+
...(options.fileSystem === undefined ? {} : { fileSystem: options.fileSystem }),
|
|
159
|
+
...(options.platform === undefined ? {} : { platform: options.platform }),
|
|
160
|
+
})
|
|
21
161
|
const result = loadOmoConfig({
|
|
22
162
|
...(options.cwd === undefined ? {} : { cwd: options.cwd }),
|
|
23
|
-
env,
|
|
24
|
-
|
|
163
|
+
env: environment,
|
|
164
|
+
...(options.fileSystem === undefined ? {} : { fileSystem: options.fileSystem }),
|
|
25
165
|
harness: "codex",
|
|
166
|
+
...(options.platform === undefined ? {} : { platform: options.platform }),
|
|
167
|
+
...(options.profile === undefined ? {} : { profile: options.profile }),
|
|
26
168
|
})
|
|
27
169
|
const trustedConfig = loadOmoConfig({
|
|
28
170
|
cwd: homeDir,
|
|
29
|
-
env,
|
|
30
|
-
|
|
171
|
+
env: environment,
|
|
172
|
+
...(options.fileSystem === undefined ? {} : { fileSystem: options.fileSystem }),
|
|
31
173
|
harness: "codex",
|
|
174
|
+
...(options.platform === undefined ? {} : { platform: options.platform }),
|
|
175
|
+
...(options.profile === undefined ? {} : { profile: options.profile }),
|
|
32
176
|
})
|
|
177
|
+
const envWarnings: string[] = []
|
|
178
|
+
const config = OmoConfigSchema.parse(mergeOmoConfigRecords(result.config, envOverrides(env, envWarnings)))
|
|
33
179
|
const trustedCodegraphInstallDir = trustedConfig.config.codegraph?.install_dir
|
|
180
|
+
const { codegraph, ...resolvedConfig } = config
|
|
181
|
+
const codexCodegraph = codexCodegraphConfig(codegraph)
|
|
34
182
|
|
|
35
183
|
return {
|
|
36
|
-
...
|
|
184
|
+
...resolvedConfig,
|
|
185
|
+
...(codexCodegraph === undefined ? {} : { codegraph: codexCodegraph }),
|
|
37
186
|
sources: result.sources,
|
|
38
187
|
...(trustedCodegraphInstallDir === undefined ? {} : { trustedCodegraphInstallDir }),
|
|
39
|
-
warnings:
|
|
188
|
+
warnings: [
|
|
189
|
+
...migrationWarnings(migration),
|
|
190
|
+
...result.diagnostics.map((diagnostic) => diagnostic.message),
|
|
191
|
+
...envWarnings,
|
|
192
|
+
...applicabilityWarnings(config),
|
|
193
|
+
],
|
|
40
194
|
}
|
|
41
195
|
}
|
|
42
|
-
|
|
43
|
-
function resolveHomeDir(options: CodexOmoConfigOptions): string {
|
|
44
|
-
const env = options.env ?? process.env
|
|
45
|
-
return options.homeDir ?? env["HOME"] ?? env["USERPROFILE"] ?? homedir()
|
|
46
|
-
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { existsSync } from "node:fs"
|
|
2
|
+
import { posix, win32 } from "node:path"
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
runMigrations,
|
|
6
|
+
type LoadedMigrationSource,
|
|
7
|
+
type MigrationBoundary,
|
|
8
|
+
type MigrationClock,
|
|
9
|
+
type MigrationEnvironment,
|
|
10
|
+
type MigrationFileSystem,
|
|
11
|
+
type MigrationPlan,
|
|
12
|
+
type MigrationRunResult,
|
|
13
|
+
type MigrationTransformResult,
|
|
14
|
+
} from "../../../../omo-config-core/src/index.ts"
|
|
15
|
+
|
|
16
|
+
const MIGRATION_ID = "2026-07-codex-config-jsonc"
|
|
17
|
+
const OMO_SCHEMA_URL = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/omo.schema.json"
|
|
18
|
+
|
|
19
|
+
type DiscoveryFileSystem = {
|
|
20
|
+
readonly existsSync: (path: string) => boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type PathOperations = {
|
|
24
|
+
readonly join: (...paths: string[]) => string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type CodexConfigMigrationOptions = {
|
|
28
|
+
readonly backupTimestamp?: string
|
|
29
|
+
readonly clock?: MigrationClock
|
|
30
|
+
readonly cwd: string
|
|
31
|
+
readonly discoveryFileSystem?: DiscoveryFileSystem
|
|
32
|
+
readonly env?: MigrationEnvironment
|
|
33
|
+
readonly environment?: Readonly<Record<string, string | undefined>>
|
|
34
|
+
readonly fileSystem?: MigrationFileSystem
|
|
35
|
+
readonly homeDir?: string
|
|
36
|
+
readonly isProcessAlive?: (pid: number) => boolean
|
|
37
|
+
readonly onBoundary?: (boundary: MigrationBoundary) => void
|
|
38
|
+
readonly pathOperations?: PathOperations
|
|
39
|
+
readonly pid?: number
|
|
40
|
+
readonly platform?: NodeJS.Platform
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type CodexConfigMigrationResult = {
|
|
44
|
+
readonly error?: string
|
|
45
|
+
readonly journalResumed: boolean
|
|
46
|
+
readonly migratedFrom: readonly string[]
|
|
47
|
+
readonly results: readonly MigrationRunResult[]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
51
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function recordAt(value: Readonly<Record<string, unknown>>, key: string): Record<string, unknown> | undefined {
|
|
55
|
+
const candidate = value[key]
|
|
56
|
+
return isRecord(candidate) ? candidate : undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function migrationHistory(sources: readonly LoadedMigrationSource[], configPath: string): Record<string, readonly string[]> {
|
|
60
|
+
const history: string[] = []
|
|
61
|
+
for (const source of sources) {
|
|
62
|
+
if (!isRecord(source.value)) continue
|
|
63
|
+
for (const key of ["_migrations", "appliedMigrations"]) {
|
|
64
|
+
const values = source.value[key]
|
|
65
|
+
if (!Array.isArray(values)) continue
|
|
66
|
+
for (const value of values) {
|
|
67
|
+
if (typeof value === "string" && !history.includes(value)) history.push(value)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return history.length === 0 ? {} : { [configPath]: history }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function transformConfigJsonc(configPath: string, sources: readonly LoadedMigrationSource[]): MigrationTransformResult {
|
|
75
|
+
const config = sources.find((source) => source.path === configPath)
|
|
76
|
+
const legacy = config === undefined || !isRecord(config.value) ? {} : config.value
|
|
77
|
+
const omo = recordAt(legacy, "[omo]")
|
|
78
|
+
const senpi = recordAt(legacy, "[senpi]")
|
|
79
|
+
const history = migrationHistory(sources, configPath)
|
|
80
|
+
return {
|
|
81
|
+
diagnostics: omo !== undefined && senpi !== undefined
|
|
82
|
+
? ["conflict: [senpi] legacy [omo] kept [senpi]"]
|
|
83
|
+
: [],
|
|
84
|
+
document: {
|
|
85
|
+
$schema: OMO_SCHEMA_URL,
|
|
86
|
+
...(recordAt(legacy, "codegraph") === undefined ? {} : { codegraph: recordAt(legacy, "codegraph") }),
|
|
87
|
+
...(recordAt(legacy, "[opencode]") === undefined ? {} : { "[opencode]": recordAt(legacy, "[opencode]") }),
|
|
88
|
+
...(recordAt(legacy, "[codex]") === undefined ? {} : { "[codex]": recordAt(legacy, "[codex]") }),
|
|
89
|
+
...(senpi === undefined && omo === undefined ? {} : { "[senpi]": senpi ?? omo }),
|
|
90
|
+
...(Object.keys(history).length === 0 ? {} : { legacy_migrations: history }),
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function timestamp(value: string | undefined): string {
|
|
96
|
+
return value ?? new Date().toISOString().replace(/[:.]/g, "-")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function sourceExists(options: CodexConfigMigrationOptions, path: string): boolean {
|
|
100
|
+
return (options.discoveryFileSystem ?? options.fileSystem ?? { existsSync }).existsSync(path)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function migrationPlan(homeDir: string, options: CodexConfigMigrationOptions): MigrationPlan | undefined {
|
|
104
|
+
const platform = options.platform ?? process.platform
|
|
105
|
+
const paths = options.pathOperations ?? (platform === "win32" ? win32 : posix)
|
|
106
|
+
const configPath = paths.join(homeDir, ".omo", "config.jsonc")
|
|
107
|
+
const sidecarPath = `${configPath}.migrations.json`
|
|
108
|
+
const sourcePaths = [configPath, sidecarPath].filter((path) => sourceExists(options, path))
|
|
109
|
+
if (sourcePaths.length === 0) return undefined
|
|
110
|
+
const backupRoot = paths.join(homeDir, ".omo", `migration-backup-${timestamp(options.backupTimestamp)}-opencode-config`, ".omo")
|
|
111
|
+
return {
|
|
112
|
+
id: MIGRATION_ID,
|
|
113
|
+
sources: sourcePaths.map((path) => ({
|
|
114
|
+
backupPath: paths.join(backupRoot, path === configPath ? "config.jsonc" : "config.jsonc.migrations.json"),
|
|
115
|
+
path,
|
|
116
|
+
})),
|
|
117
|
+
targetPath: paths.join(homeDir, ".omo", "omo.jsonc"),
|
|
118
|
+
transform: (sources) => transformConfigJsonc(configPath, sources),
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function migratedSources(results: readonly MigrationRunResult[]): readonly string[] {
|
|
123
|
+
return [...new Set(results.flatMap((result) => result.status === "migrated"
|
|
124
|
+
? result.preview?.backupMoves.map((move) => move.from) ?? []
|
|
125
|
+
: []))].sort()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function runCodexConfigMigration(options: CodexConfigMigrationOptions): CodexConfigMigrationResult {
|
|
129
|
+
const environment = options.environment ?? process.env
|
|
130
|
+
const homeDir = options.homeDir ?? environment["HOME"] ?? environment["USERPROFILE"]
|
|
131
|
+
if (homeDir === undefined || homeDir.length === 0) {
|
|
132
|
+
return { error: "Cannot migrate configuration because no home directory is available", journalResumed: false, migratedFrom: [], results: [] }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const batch = runMigrations({
|
|
137
|
+
...(options.clock === undefined ? {} : { clock: options.clock }),
|
|
138
|
+
discover: () => {
|
|
139
|
+
const plan = migrationPlan(homeDir, options)
|
|
140
|
+
return plan === undefined ? [] : [plan]
|
|
141
|
+
},
|
|
142
|
+
...(options.env === undefined ? { env: { HOME: homeDir } } : { env: options.env }),
|
|
143
|
+
...(options.fileSystem === undefined ? {} : { fileSystem: options.fileSystem }),
|
|
144
|
+
...(options.isProcessAlive === undefined ? {} : { isProcessAlive: options.isProcessAlive }),
|
|
145
|
+
...(options.onBoundary === undefined ? {} : { onBoundary: options.onBoundary }),
|
|
146
|
+
...(options.pid === undefined ? {} : { pid: options.pid }),
|
|
147
|
+
})
|
|
148
|
+
return {
|
|
149
|
+
...(batch.status === "locked" ? { error: "Configuration migration is already running" } : {}),
|
|
150
|
+
journalResumed: batch.journalResumed,
|
|
151
|
+
migratedFrom: migratedSources(batch.results),
|
|
152
|
+
results: batch.results,
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return {
|
|
156
|
+
error: error instanceof Error ? error.message : String(error),
|
|
157
|
+
journalResumed: false,
|
|
158
|
+
migratedFrom: [],
|
|
159
|
+
results: [],
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { afterEach, describe, expect, it } from "bun:test"
|
|
2
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs"
|
|
3
3
|
import { tmpdir } from "node:os"
|
|
4
|
-
import { join } from "node:path"
|
|
4
|
+
import { join, posix } from "node:path"
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { parse } from "jsonc-parser"
|
|
7
|
+
|
|
8
|
+
import { runMigrations, type MigrationBoundary } from "../../../../omo-config-core/src/index.ts"
|
|
9
|
+
import { createLegacyConfigMigrationPlans } from "../../../../omo-opencode/src/config-migration/index.ts"
|
|
10
|
+
|
|
11
|
+
import { getCodexOmoConfig, runCodexStartupMigration } from "../src/config-loader.ts"
|
|
7
12
|
|
|
8
13
|
const temporaryDirectories: string[] = []
|
|
9
14
|
|
|
@@ -14,11 +19,51 @@ function createTemporaryDirectory(prefix: string): string {
|
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
function writeOmoConfig(homeDir: string, content: string): void {
|
|
22
|
+
const configDir = join(homeDir, ".omo")
|
|
23
|
+
mkdirSync(configDir, { recursive: true })
|
|
24
|
+
writeFileSync(join(configDir, "omo.jsonc"), content)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function writeLegacyOmoConfig(homeDir: string, content: string): void {
|
|
17
28
|
const configDir = join(homeDir, ".omo")
|
|
18
29
|
mkdirSync(configDir, { recursive: true })
|
|
19
30
|
writeFileSync(join(configDir, "config.jsonc"), content)
|
|
20
31
|
}
|
|
21
32
|
|
|
33
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
34
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function readOmoJson(homeDir: string): Record<string, unknown> {
|
|
38
|
+
const value: unknown = parse(readFileSync(join(homeDir, ".omo", "omo.jsonc"), "utf-8"))
|
|
39
|
+
if (!isRecord(value)) throw new Error("Expected an omo JSONC object")
|
|
40
|
+
return value
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function writeOpenCodeConfig(homeDir: string): void {
|
|
44
|
+
const configDir = join(homeDir, ".config", "opencode")
|
|
45
|
+
mkdirSync(configDir, { recursive: true })
|
|
46
|
+
writeFileSync(join(configDir, "oh-my-openagent.jsonc"), '{"agents":{"finder":{"model":"provider/finder"}}}')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function runOpenCodeMigration(homeDir: string, cwd: string, onBoundary?: (boundary: MigrationBoundary) => void): void {
|
|
50
|
+
runMigrations({
|
|
51
|
+
discover: () => createLegacyConfigMigrationPlans({
|
|
52
|
+
cwd,
|
|
53
|
+
environment: { HOME: homeDir, XDG_CONFIG_HOME: join(homeDir, ".config") },
|
|
54
|
+
homeDir,
|
|
55
|
+
pathOperations: posix,
|
|
56
|
+
}),
|
|
57
|
+
env: { HOME: homeDir },
|
|
58
|
+
...(onBoundary === undefined ? {} : { onBoundary }),
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function migrationIds(homeDir: string): readonly string[] {
|
|
63
|
+
const migrations = readOmoJson(homeDir)["_migrations"]
|
|
64
|
+
return Array.isArray(migrations) ? migrations.filter((entry): entry is string => typeof entry === "string") : []
|
|
65
|
+
}
|
|
66
|
+
|
|
22
67
|
afterEach(() => {
|
|
23
68
|
for (const directory of temporaryDirectories.splice(0)) {
|
|
24
69
|
rmSync(directory, { recursive: true, force: true })
|
|
@@ -86,6 +131,20 @@ describe("getCodexOmoConfig", () => {
|
|
|
86
131
|
expect(result.codegraph?.excluded_roots).toEqual(["/tmp/omo-research", "/private/tmp/omo-research"])
|
|
87
132
|
})
|
|
88
133
|
|
|
134
|
+
it("#given codex SOT sets the SessionStart cooldown #when loading config #then the configured base interval is returned", () => {
|
|
135
|
+
// given
|
|
136
|
+
const homeDir = createTemporaryDirectory("omo-codex-shared-cooldown-home-")
|
|
137
|
+
const cwd = createTemporaryDirectory("omo-codex-shared-cooldown-project-")
|
|
138
|
+
writeOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { session_start_cooldown_ms: 900_000 } } }))
|
|
139
|
+
|
|
140
|
+
// when
|
|
141
|
+
const result = getCodexOmoConfig({ cwd, homeDir, env: {} })
|
|
142
|
+
|
|
143
|
+
// then
|
|
144
|
+
expect(result.codegraph?.session_start_cooldown_ms).toBe(900_000)
|
|
145
|
+
expect(result.warnings).toEqual([])
|
|
146
|
+
})
|
|
147
|
+
|
|
89
148
|
it("#given no codegraph.daemon key #when loading config #then daemon defaults to on", () => {
|
|
90
149
|
// given
|
|
91
150
|
const homeDir = createTemporaryDirectory("omo-codex-shared-daemon-default-home-")
|
|
@@ -125,24 +184,24 @@ describe("getCodexOmoConfig", () => {
|
|
|
125
184
|
|
|
126
185
|
// then
|
|
127
186
|
expect(result.codegraph?.daemon).toBe(true)
|
|
128
|
-
expect(result.warnings).toContain("
|
|
187
|
+
expect(result.warnings).toContain(`Invalid omo config at ${join(homeDir, ".omo", "omo.jsonc")}: [codex].codegraph.daemon`)
|
|
129
188
|
})
|
|
130
189
|
|
|
131
190
|
it("#given legacy env override and SOT value #when loading config #then env wins over the SOT", () => {
|
|
132
191
|
// given
|
|
133
192
|
const homeDir = createTemporaryDirectory("omo-codex-shared-env-")
|
|
134
193
|
const cwd = createTemporaryDirectory("omo-codex-project-env-")
|
|
135
|
-
writeOmoConfig(homeDir, JSON.stringify({ codegraph: { enabled:
|
|
194
|
+
writeOmoConfig(homeDir, JSON.stringify({ codegraph: { enabled: false } }))
|
|
136
195
|
|
|
137
196
|
// when
|
|
138
197
|
const result = getCodexOmoConfig({
|
|
139
198
|
cwd,
|
|
140
199
|
homeDir,
|
|
141
|
-
env: { CODEX_CODEGRAPH_ENABLED: "
|
|
200
|
+
env: { CODEX_CODEGRAPH_ENABLED: "1" },
|
|
142
201
|
})
|
|
143
202
|
|
|
144
203
|
// then
|
|
145
|
-
expect(result.codegraph?.enabled).toBe(
|
|
204
|
+
expect(result.codegraph?.enabled).toBe(true)
|
|
146
205
|
})
|
|
147
206
|
|
|
148
207
|
it("#given no SOT files #when loading config #then returns built-in defaults and missing global source", () => {
|
|
@@ -163,8 +222,8 @@ describe("getCodexOmoConfig", () => {
|
|
|
163
222
|
expect(result.sources).toContainEqual({
|
|
164
223
|
exists: false,
|
|
165
224
|
loaded: false,
|
|
166
|
-
path: join(homeDir, ".omo", "
|
|
167
|
-
scope: "
|
|
225
|
+
path: join(homeDir, ".omo", "omo.jsonc"),
|
|
226
|
+
scope: "user",
|
|
168
227
|
})
|
|
169
228
|
})
|
|
170
229
|
|
|
@@ -180,4 +239,122 @@ describe("getCodexOmoConfig", () => {
|
|
|
180
239
|
// then
|
|
181
240
|
expect(result.warnings).toContain("codegraph.watch_debounce_ms is not supported for harness codex")
|
|
182
241
|
})
|
|
242
|
+
|
|
243
|
+
it("#given only legacy config.jsonc #when codex starts #then migrates once with a backup before loading the unified codex view", () => {
|
|
244
|
+
// given
|
|
245
|
+
const homeDir = createTemporaryDirectory("omo-codex-legacy-upgrade-")
|
|
246
|
+
writeLegacyOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: false } } }))
|
|
247
|
+
const legacyPath = join(homeDir, ".omo", "config.jsonc")
|
|
248
|
+
|
|
249
|
+
// when
|
|
250
|
+
const first = getCodexOmoConfig({ cwd: homeDir, homeDir, env: {} })
|
|
251
|
+
const backupDirectories = readdirSync(join(homeDir, ".omo")).filter((entry) => entry.startsWith("migration-backup-"))
|
|
252
|
+
const second = getCodexOmoConfig({ cwd: homeDir, homeDir, env: {} })
|
|
253
|
+
|
|
254
|
+
// then
|
|
255
|
+
expect(first.codegraph?.daemon).toBe(false)
|
|
256
|
+
expect(first.warnings.some((warning) => warning.startsWith("omo-codex: migrated legacy configuration from ")
|
|
257
|
+
&& warning.endsWith("/.omo/config.jsonc"))).toBe(true)
|
|
258
|
+
expect(readOmoJson(homeDir)["_migrations"]).toEqual(["2026-07-codex-config-jsonc"])
|
|
259
|
+
expect(existsSync(legacyPath)).toBe(false)
|
|
260
|
+
expect(backupDirectories).toHaveLength(1)
|
|
261
|
+
expect(existsSync(join(homeDir, ".omo", backupDirectories[0] ?? "", ".omo", "config.jsonc"))).toBe(true)
|
|
262
|
+
expect(second.warnings.some((warning) => warning.startsWith("omo-codex: migrated legacy configuration from "))).toBe(false)
|
|
263
|
+
expect(readdirSync(join(homeDir, ".omo")).filter((entry) => entry.startsWith("migration-backup-"))).toEqual(backupDirectories)
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it("#given a legacy migration conflict #when codex starts #then exposes the migration summary and conflict through loader warnings", () => {
|
|
267
|
+
// given
|
|
268
|
+
const homeDir = createTemporaryDirectory("omo-codex-legacy-conflict-")
|
|
269
|
+
writeOmoConfig(homeDir, JSON.stringify({ codegraph: { enabled: true } }))
|
|
270
|
+
writeLegacyOmoConfig(homeDir, JSON.stringify({ codegraph: { enabled: false } }))
|
|
271
|
+
|
|
272
|
+
// when
|
|
273
|
+
const result = getCodexOmoConfig({ cwd: homeDir, homeDir, env: {} })
|
|
274
|
+
|
|
275
|
+
// then
|
|
276
|
+
expect(result.warnings.some((warning) => warning.startsWith("omo-codex: migrated legacy configuration from ")
|
|
277
|
+
&& warning.endsWith("/.omo/config.jsonc"))).toBe(true)
|
|
278
|
+
expect(result.warnings).toContain("omo-codex: configuration migration: skipped: codegraph.enabled legacy=false kept=true")
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
it("#given overlapping [omo] and [senpi] blocks in legacy config #when Codex migrates #then the transform conflict is reported", () => {
|
|
282
|
+
// given
|
|
283
|
+
const homeDir = createTemporaryDirectory("omo-codex-legacy-senpi-conflict-")
|
|
284
|
+
writeLegacyOmoConfig(homeDir, JSON.stringify({
|
|
285
|
+
"[omo]": { agents: { oracle: { model: "legacy" } } },
|
|
286
|
+
"[senpi]": { agents: { oracle: { model: "current" } } },
|
|
287
|
+
}))
|
|
288
|
+
|
|
289
|
+
// when
|
|
290
|
+
const result = runCodexStartupMigration({ cwd: homeDir, environment: { HOME: homeDir }, homeDir })
|
|
291
|
+
|
|
292
|
+
// then
|
|
293
|
+
expect(result.results[0]?.diagnostics).toContain("conflict: [senpi] legacy [omo] kept [senpi]")
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
it("#given a malformed migration journal #when codex starts #then exposes the recovery failure through loader warnings", () => {
|
|
297
|
+
// given
|
|
298
|
+
const homeDir = createTemporaryDirectory("omo-codex-recovery-warning-")
|
|
299
|
+
const migrationDir = join(homeDir, ".omo")
|
|
300
|
+
mkdirSync(migrationDir, { recursive: true })
|
|
301
|
+
writeFileSync(join(migrationDir, ".migration-journal.json"), "not-json")
|
|
302
|
+
|
|
303
|
+
// when
|
|
304
|
+
const result = getCodexOmoConfig({ cwd: homeDir, homeDir, env: {} })
|
|
305
|
+
|
|
306
|
+
// then
|
|
307
|
+
expect(result.warnings.some((warning) => warning.startsWith("omo-codex: configuration migration: "))).toBe(true)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
it("#given codex starts before opencode #when both legacy source groups exist #then each distinct migration id is applied once", () => {
|
|
311
|
+
// given
|
|
312
|
+
const homeDir = createTemporaryDirectory("omo-codex-order-codex-first-")
|
|
313
|
+
writeLegacyOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: false } } }))
|
|
314
|
+
writeOpenCodeConfig(homeDir)
|
|
315
|
+
const openCodeSource = join(homeDir, ".config", "opencode", "oh-my-openagent.jsonc")
|
|
316
|
+
|
|
317
|
+
// when
|
|
318
|
+
getCodexOmoConfig({ cwd: homeDir, homeDir, env: {} })
|
|
319
|
+
const remainsAfterCodexStartup = existsSync(openCodeSource)
|
|
320
|
+
runOpenCodeMigration(homeDir, homeDir)
|
|
321
|
+
|
|
322
|
+
// then
|
|
323
|
+
expect(remainsAfterCodexStartup).toBe(true)
|
|
324
|
+
expect(existsSync(openCodeSource)).toBe(false)
|
|
325
|
+
expect(migrationIds(homeDir)).toEqual(["2026-07-codex-config-jsonc", "2026-07-opencode-config-unification"])
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
it("#given opencode starts before codex #when both legacy source groups exist #then each distinct migration id is applied once", () => {
|
|
329
|
+
// given
|
|
330
|
+
const homeDir = createTemporaryDirectory("omo-codex-order-opencode-first-")
|
|
331
|
+
writeLegacyOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: false } } }))
|
|
332
|
+
writeOpenCodeConfig(homeDir)
|
|
333
|
+
|
|
334
|
+
// when
|
|
335
|
+
runOpenCodeMigration(homeDir, homeDir)
|
|
336
|
+
getCodexOmoConfig({ cwd: homeDir, homeDir, env: {} })
|
|
337
|
+
|
|
338
|
+
// then
|
|
339
|
+
expect(migrationIds(homeDir)).toEqual(["2026-07-opencode-config-unification", "2026-07-codex-config-jsonc"])
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
it("#given concurrent codex and opencode starts #when opencode owns the shared lock #then both migration ids are applied exactly once", () => {
|
|
343
|
+
// given
|
|
344
|
+
const homeDir = createTemporaryDirectory("omo-codex-order-concurrent-")
|
|
345
|
+
writeLegacyOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: false } } }))
|
|
346
|
+
writeOpenCodeConfig(homeDir)
|
|
347
|
+
let codexResult: ReturnType<typeof runCodexStartupMigration> | undefined
|
|
348
|
+
|
|
349
|
+
// when
|
|
350
|
+
runOpenCodeMigration(homeDir, homeDir, (boundary) => {
|
|
351
|
+
if (boundary !== "journal-written" || codexResult !== undefined) return
|
|
352
|
+
codexResult = runCodexStartupMigration({ cwd: homeDir, homeDir })
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
// then
|
|
356
|
+
if (codexResult === undefined) throw new Error("Expected concurrent Codex migration attempt")
|
|
357
|
+
expect(codexResult.error).toBe("Configuration migration is already running")
|
|
358
|
+
expect(migrationIds(homeDir)).toEqual(["2026-07-opencode-config-unification", "2026-07-codex-config-jsonc"])
|
|
359
|
+
})
|
|
183
360
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: coding-agent-sessions
|
|
3
|
-
description: "MUST USE when asked to find, read, list, search, inspect, fetch, export, or reconstruct coding-agent sessions across Codex, Claude Code/Desktop, OpenCode, Senpi/pi, oh-my-pi (omp), gajae-code (gjc), OpenClaw, Factory Droid, Amp, Gemini/Kimi/Qwen CLIs, Codebuff, Roo/Kilo/Cline, Kodu, Cursor CLI, Aider, or unknown local agent logs. Covers transcripts, session IDs, rollout JSONL, state SQLite, Claude projects/pre-compact histories, OpenCode messages/parts, child/subagent linkage, cwd/model/time/token filters, archives, and cost clues. Expands fuzzy recall into parallel query lanes and first probes known stores so absent platforms are skipped cheaply. Triggers: coding agent sessions, Codex/Claude/OpenCode/Senpi/pi/oh-my-pi/omp/gajae-code/gjc/OpenClaw/Droid/Amp/Kodu/Cursor/Aider sessions, transcript search, session history, session ID, read transcript, token usage, subagent sessions, what did I do yesterday, did we already do this."
|
|
3
|
+
description: "MUST USE when asked to find, read, list, search, inspect, fetch, export, or reconstruct coding-agent sessions across Codex, Claude Code/Desktop, OpenCode, Senpi/pi, oh-my-pi (omp), gajae-code (gjc), OpenClaw, Factory Droid, Amp, Gemini/Kimi/Qwen CLIs, Codebuff, Roo/Kilo/Cline, Kodu, Cursor CLI, Aider, Aside browser-agent sessions, or unknown local agent logs. Covers transcripts, session IDs, rollout JSONL, state SQLite, Claude projects/pre-compact histories, OpenCode messages/parts, child/subagent linkage, cwd/model/time/token filters, archives, and cost clues. Expands fuzzy recall into parallel query lanes and first probes known stores so absent platforms are skipped cheaply. Triggers: coding agent sessions, Codex/Claude/OpenCode/Senpi/pi/oh-my-pi/omp/gajae-code/gjc/OpenClaw/Droid/Amp/Kodu/Cursor/Aider/Aside sessions, transcript search, session history, session ID, read transcript, token usage, subagent sessions, what did I do yesterday, did we already do this."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Coding Agent Sessions
|
|
@@ -18,7 +18,7 @@ Find local coding-agent sessions across agent products before answering from mem
|
|
|
18
18
|
| Senpi / pi coding-agent logs | `references/senpi.md` |
|
|
19
19
|
| oh-my-pi (`omp`, `~/.omp`) and gajae-code (`gjc`, `~/.gjc`) logs | `references/senpi.md` |
|
|
20
20
|
| OpenCode / oh-my-openagent (formerly oh-my-opencode) storage | `references/opencode.md` |
|
|
21
|
-
| OpenClaw, Droid, Amp, Gemini, Kimi, Qwen, Codebuff, Roo/Kilo/Cline, Kodu, Cursor CLI, Aider, Kiro, Goose, Hermes, Crush, Zed | `references/all-platforms.md` |
|
|
21
|
+
| OpenClaw, Droid, Amp, Gemini, Kimi, Qwen, Codebuff, Roo/Kilo/Cline, Kodu, Cursor CLI, Aider, Kiro, Goose, Hermes, Crush, Zed, Aside | `references/all-platforms.md` |
|
|
22
22
|
| Unknown / "any session" / cross-agent search | `references/all-platforms.md` |
|
|
23
23
|
|
|
24
24
|
2. **Run the broad finder first unless the user gave an exact file path. For fuzzy recall, expand the query first.**
|
|
@@ -51,7 +51,7 @@ The finder prints JSON for stdout and `jq`. Every result includes:
|
|
|
51
51
|
|
|
52
52
|
| Field | Meaning |
|
|
53
53
|
|---|---|
|
|
54
|
-
| `platform` | Registered platform key such as `codex`, `claude`, `senpi`, `oh-my-pi`, `gajae-code`, `opencode`, `openclaw`, `droid`, `amp`, `kodu`, `cursor-cli`, `aider`, `roo-code`, `kilo-code`, `kilo-cli`, or `
|
|
54
|
+
| `platform` | Registered platform key such as `codex`, `claude`, `senpi`, `oh-my-pi`, `gajae-code`, `opencode`, `openclaw`, `droid`, `amp`, `kodu`, `cursor-cli`, `aider`, `roo-code`, `kilo-code`, `kilo-cli`, `kiro`, or `aside` |
|
|
55
55
|
| `id` | Session ID or stable file-derived ID |
|
|
56
56
|
| `path` | Raw transcript/index file |
|
|
57
57
|
| `cwd` | Working directory when recoverable |
|
|
@@ -122,6 +122,7 @@ Use `references/codex.md` for Codex storage details.
|
|
|
122
122
|
| Missing oh-my-pi / gajae-code sessions | Those stores live in `~/.omp/agent/sessions` and `~/.gjc/agent/sessions`. For a custom `PI_CONFIG_DIR` / `PI_CODING_AGENT_DIR`, pass that agent dir with `--root`. |
|
|
123
123
|
| Missing OpenCode sessions | Pass the data dir that contains `messages/` and `parts/`, often `~/.opencode` or `~/.local/share/opencode`. |
|
|
124
124
|
| Missing Claude sessions | Search `~/.claude/projects`, `~/.claude/transcripts`, and `~/.claude/pre-compact-session-histories`; use `--root` for nonstandard config dirs. |
|
|
125
|
+
| Missing Aside sessions | The Aside browser agent stores per-user data under `~/.aside/u/<n>/` (`sessions/<date>_<id>/messages.jsonl` transcripts + a `state.db` index; `agents/*/sessions/` is just a hardlink mirror). Pass `--root` for a nonstandard `.aside` dir or an exported user dir. |
|
|
125
126
|
| Missing optional platform sessions | Check `references/all-platforms.md` for the exact local store. For project-local tools such as Aider, pass `--root /path/to/workspace` if the repo is outside the bounded default roots. |
|
|
126
127
|
| Date filter misses local sessions | Timestamps are compared as UTC instants when parseable; otherwise file mtime is used. |
|
|
127
128
|
| Search is slow | Narrow with repeated `--platform` flags or date/cwd filters. Optional stores are probed before parsing; avoid passing your whole home as `--root` unless you really want every bounded project-local scan. |
|