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.
Files changed (241) hide show
  1. package/.agents/command/get-unpublished-changes.md +2 -0
  2. package/.agents/command/omomomo.md +1 -1
  3. package/.agents/command/publish.md +102 -28
  4. package/.agents/skills/get-unpublished-changes/SKILL.md +2 -0
  5. package/.agents/skills/hyperplan/SKILL.md +3 -3
  6. package/.agents/skills/omomomo/SKILL.md +1 -1
  7. package/.agents/skills/publish/SKILL.md +27 -6
  8. package/.opencode/command/get-unpublished-changes.md +2 -0
  9. package/.opencode/command/omomomo.md +1 -1
  10. package/.opencode/command/publish.md +102 -28
  11. package/.opencode/skills/hyperplan/SKILL.md +3 -3
  12. package/THIRD-PARTY-NOTICES.md +7 -7
  13. package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
  14. package/dist/agents/sisyphus-runtime-prompt-reconciler.d.ts +1 -1
  15. package/dist/cli/config-migrate.d.ts +8 -0
  16. package/dist/cli/doctor/checks/deprecated-reasoning-keys.d.ts +2 -0
  17. package/dist/cli/doctor/checks/legacy-config-leftovers.d.ts +10 -0
  18. package/dist/cli/doctor/checks/model-resolution-config.d.ts +1 -1
  19. package/dist/cli/doctor/checks/tools-lsp.d.ts +1 -1
  20. package/dist/cli/doctor/framework/constants.d.ts +1 -1
  21. package/dist/cli/index.js +95684 -92814
  22. package/dist/cli-node/index.js +95650 -92780
  23. package/dist/config/schema/agent-overrides.d.ts +800 -0
  24. package/dist/config/schema/categories.d.ts +132 -0
  25. package/dist/config/schema/fallback-models.d.ts +50 -0
  26. package/dist/config/schema/oh-my-opencode-config.d.ts +818 -2
  27. package/dist/config/validate.d.ts +2 -1
  28. package/dist/config-migration/deep-diff.d.ts +1 -0
  29. package/dist/config-migration/discovery-paths.d.ts +11 -0
  30. package/dist/config-migration/discovery-roots.d.ts +7 -0
  31. package/dist/config-migration/discovery.d.ts +4 -0
  32. package/dist/config-migration/index.d.ts +10 -0
  33. package/dist/config-migration/legacy-history.d.ts +3 -0
  34. package/dist/config-migration/migration-executor.d.ts +4 -0
  35. package/dist/config-migration/migration-plans.d.ts +15 -0
  36. package/dist/config-migration/reasoning-unification.d.ts +3 -0
  37. package/dist/config-migration/record-values.d.ts +4 -0
  38. package/dist/config-migration/schema-url.d.ts +1 -0
  39. package/dist/config-migration/transform-config-jsonc.d.ts +2 -0
  40. package/dist/config-migration/transform-opencode.d.ts +2 -0
  41. package/dist/config-migration/transform-types.d.ts +24 -0
  42. package/dist/config-migration/types.d.ts +39 -0
  43. package/dist/features/builtin-commands/templates/hyperplan.d.ts +1 -1
  44. package/dist/features/team-mode/tools/lifecycle-test-fixture.d.ts +2 -0
  45. package/dist/hooks/auto-update-checker/constants.d.ts +3 -3
  46. package/dist/hooks/codegraph-bootstrap/command-runner.d.ts +1 -0
  47. package/dist/hooks/model-fallback/next-fallback.d.ts +1 -0
  48. package/dist/hooks/runtime-fallback/constants.d.ts +1 -1
  49. package/dist/index.js +12368 -9788
  50. package/dist/oh-my-opencode.schema.json +1672 -8
  51. package/dist/plugin-config/omo-config-chain.d.ts +14 -0
  52. package/dist/plugin-config/unknown-key-diagnostics.d.ts +5 -0
  53. package/dist/plugin-config.d.ts +2 -2
  54. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +1 -0
  55. package/dist/shared/agent-display-names.d.ts +1 -1
  56. package/dist/shared/agent-variant.d.ts +11 -0
  57. package/dist/shared/jsonc-parser.d.ts +1 -1
  58. package/dist/shared/migration.d.ts +5 -1
  59. package/dist/shared/opencode-config-dir-types.d.ts +0 -1
  60. package/dist/shared/plugin-identity.d.ts +6 -8
  61. package/dist/shared/project-discovery-dirs.d.ts +0 -1
  62. package/dist/shared/session-prompt-params-helpers.d.ts +6 -1
  63. package/dist/skills/coding-agent-sessions/SKILL.md +4 -3
  64. package/dist/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  65. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  66. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  67. package/dist/skills/data-scientist/SKILL.md +243 -0
  68. package/dist/skills/data-scientist/references/common-scenarios.md +176 -0
  69. package/dist/skills/data-scientist/references/execution-templates.md +197 -0
  70. package/dist/skills/data-scientist/references/integration-patterns.md +153 -0
  71. package/dist/skills/data-scientist/references/performance-benchmarks.md +37 -0
  72. package/dist/skills/data-scientist/references/uv-setup.md +78 -0
  73. package/dist/skills/data-scientist/scripts/quick-query.py +111 -0
  74. package/dist/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  75. package/dist/skills/data-scientist/scripts/setup-uv.sh +60 -0
  76. package/dist/skills/debugging/SKILL.md +1 -1
  77. package/dist/skills/frontend/ATTRIBUTION.md +7 -0
  78. package/dist/skills/frontend/SKILL.md +4 -1
  79. package/dist/skills/frontend/references/design/README.md +8 -0
  80. package/dist/skills/frontend/references/design/_INDEX.md +14 -1
  81. package/dist/skills/frontend/references/design/interaction-skill.md +144 -0
  82. package/dist/skills/programming/SKILL.md +1 -2
  83. package/dist/skills/ulw-plan/SKILL.md +1 -1
  84. package/dist/skills/ulw-research/SKILL.md +122 -11
  85. package/dist/startup-migration.d.ts +28 -0
  86. package/dist/testing/create-plugin-module.d.ts +4 -0
  87. package/dist/tools/delegate-task/builtin-categories.d.ts +1 -0
  88. package/dist/tools/delegate-task/builtin-category-definition.d.ts +1 -0
  89. package/dist/tools/delegate-task/constants.d.ts +1 -1
  90. package/dist/tui.js +6040 -5443
  91. package/package.json +13 -13
  92. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +21 -5
  93. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +11 -2
  94. package/packages/omo-codex/THIRD-PARTY-NOTICES.md +2 -2
  95. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  96. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +21 -3
  97. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  98. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  99. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +11 -5
  100. package/packages/omo-codex/plugin/components/codegraph/NOTICE +1 -1
  101. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +10373 -2570
  102. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +7502 -574
  103. package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
  104. package/packages/omo-codex/plugin/components/codegraph/src/hook-input.ts +33 -0
  105. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +36 -10
  106. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +164 -159
  107. package/packages/omo-codex/plugin/components/codegraph/src/post-tool-use-hook.ts +34 -0
  108. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +20 -2
  109. package/packages/omo-codex/plugin/components/codegraph/src/session-start-command.ts +106 -0
  110. package/packages/omo-codex/plugin/components/codegraph/src/session-start-cooldown.ts +145 -0
  111. package/packages/omo-codex/plugin/components/codegraph/src/session-start-hook-runtime.ts +21 -0
  112. package/packages/omo-codex/plugin/components/codegraph/src/session-start-lock.ts +139 -0
  113. package/packages/omo-codex/plugin/components/codegraph/src/session-start-outcome.ts +15 -0
  114. package/packages/omo-codex/plugin/components/codegraph/src/session-start-paths.ts +32 -0
  115. package/packages/omo-codex/plugin/components/codegraph/src/session-start-project.ts +109 -0
  116. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker-result.ts +148 -0
  117. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +126 -175
  118. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +4 -4
  119. package/packages/omo-codex/plugin/components/codegraph/test/hook-session-start-guard.test.ts +160 -0
  120. package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +12 -138
  121. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +34 -24
  122. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +3 -3
  123. package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +2 -2
  124. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +5 -5
  125. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -3
  126. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +1 -1
  127. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +45 -0
  128. package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +2 -2
  129. package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +70 -13
  130. package/packages/omo-codex/plugin/components/codegraph/test/session-start-project.test.ts +58 -0
  131. package/packages/omo-codex/plugin/components/codegraph/test/session-start-state.test.ts +104 -0
  132. package/packages/omo-codex/plugin/components/codegraph/test/session-start-trust-boundary.test.ts +4 -2
  133. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-cooldown.test.ts +116 -0
  134. package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +58 -57
  135. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  136. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  137. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  138. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  139. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  140. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  141. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  142. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  143. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  144. package/packages/omo-codex/plugin/components/rules/dist/cli.js +32 -15
  145. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  146. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  147. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +1 -1
  148. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  149. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  150. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  151. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  152. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  153. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  154. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  155. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  156. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +1 -1
  157. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  158. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  159. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +17 -0
  160. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  161. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  164. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  165. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  166. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  167. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  170. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  171. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  172. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  173. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  174. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  175. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  176. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  177. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  178. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  179. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  180. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  181. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  182. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  183. package/packages/omo-codex/plugin/package-lock.json +42 -41
  184. package/packages/omo-codex/plugin/package.json +1 -1
  185. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -0
  186. package/packages/omo-codex/plugin/shared/src/config-loader.ts +165 -16
  187. package/packages/omo-codex/plugin/shared/src/config-migration.ts +162 -0
  188. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +186 -9
  189. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +4 -3
  190. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  191. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  192. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  193. package/packages/omo-codex/plugin/skills/data-scientist/SKILL.md +243 -0
  194. package/packages/omo-codex/plugin/skills/data-scientist/agents/openai.yaml +2 -0
  195. package/packages/omo-codex/plugin/skills/data-scientist/references/common-scenarios.md +176 -0
  196. package/packages/omo-codex/plugin/skills/data-scientist/references/execution-templates.md +197 -0
  197. package/packages/omo-codex/plugin/skills/data-scientist/references/integration-patterns.md +153 -0
  198. package/packages/omo-codex/plugin/skills/data-scientist/references/performance-benchmarks.md +37 -0
  199. package/packages/omo-codex/plugin/skills/data-scientist/references/uv-setup.md +78 -0
  200. package/packages/omo-codex/plugin/skills/data-scientist/scripts/quick-query.py +111 -0
  201. package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  202. package/packages/omo-codex/plugin/skills/data-scientist/scripts/setup-uv.sh +60 -0
  203. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +1 -1
  204. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +7 -0
  205. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -1
  206. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +8 -0
  207. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +14 -1
  208. package/packages/omo-codex/plugin/skills/frontend/references/design/interaction-skill.md +144 -0
  209. package/packages/omo-codex/plugin/skills/programming/SKILL.md +1 -2
  210. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +17 -0
  211. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +1 -1
  212. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +121 -11
  213. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +7 -0
  214. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +12 -0
  215. package/packages/omo-codex/scripts/install-dist/install-local.mjs +24 -5
  216. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +4 -3
  217. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +3 -1
  218. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/aside_scanner.py +140 -0
  219. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/scanners.py +3 -0
  220. package/packages/shared-skills/skills/data-scientist/SKILL.md +243 -0
  221. package/packages/shared-skills/skills/data-scientist/references/common-scenarios.md +176 -0
  222. package/packages/shared-skills/skills/data-scientist/references/execution-templates.md +197 -0
  223. package/packages/shared-skills/skills/data-scientist/references/integration-patterns.md +153 -0
  224. package/packages/shared-skills/skills/data-scientist/references/performance-benchmarks.md +37 -0
  225. package/packages/shared-skills/skills/data-scientist/references/uv-setup.md +78 -0
  226. package/packages/shared-skills/skills/data-scientist/scripts/quick-query.py +111 -0
  227. package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.ps1 +53 -0
  228. package/packages/shared-skills/skills/data-scientist/scripts/setup-uv.sh +60 -0
  229. package/packages/shared-skills/skills/debugging/SKILL.md +1 -1
  230. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +7 -0
  231. package/packages/shared-skills/skills/frontend/SKILL.md +4 -1
  232. package/packages/shared-skills/skills/frontend/references/design/README.md +8 -0
  233. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +14 -1
  234. package/packages/shared-skills/skills/frontend/references/design/interaction-skill.md +144 -0
  235. package/packages/shared-skills/skills/programming/SKILL.md +1 -2
  236. package/packages/shared-skills/skills/ulw-plan/SKILL.md +1 -1
  237. package/packages/shared-skills/skills/ulw-research/SKILL.md +122 -11
  238. package/dist/plugin-config/layered-config-loader.d.ts +0 -2
  239. package/dist/plugin-config/single-config-loader.d.ts +0 -4
  240. package/dist/shared/migrate-legacy-config-file.d.ts +0 -1
  241. 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
- } from "../../../../utils/src/omo-config/loader.ts"
7
- import type { OmoConfig } from "../../../../utils/src/omo-config.ts"
8
- import type { OmoConfigSource } from "../../../../utils/src/omo-config/resolve.ts"
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
- export type CodexOmoConfigOptions = Omit<LoadOmoConfigOptions, "harness">
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
- export type CodexOmoConfig = OmoConfig & {
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
- homeDir,
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
- homeDir,
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
- ...result.config,
184
+ ...resolvedConfig,
185
+ ...(codexCodegraph === undefined ? {} : { codegraph: codexCodegraph }),
37
186
  sources: result.sources,
38
187
  ...(trustedCodegraphInstallDir === undefined ? {} : { trustedCodegraphInstallDir }),
39
- warnings: result.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 { getCodexOmoConfig } from "../src/config-loader.ts"
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("config.[codex].codegraph.daemon must be a boolean")
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: true } }))
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: "0" },
200
+ env: { CODEX_CODEGRAPH_ENABLED: "1" },
142
201
  })
143
202
 
144
203
  // then
145
- expect(result.codegraph?.enabled).toBe(false)
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", "config.jsonc"),
167
- scope: "global",
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 `kiro` |
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. |