oh-my-codex 0.18.11 → 0.18.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +9 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +668 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/completion-gate.d.ts +10 -0
- package/dist/autopilot/completion-gate.d.ts.map +1 -0
- package/dist/autopilot/completion-gate.js +154 -0
- package/dist/autopilot/completion-gate.js.map +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +42 -21
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +46 -3
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +35 -0
- package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +317 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +120 -2
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +217 -1
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +101 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/session-search-help.test.js +3 -2
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/session-search.test.js +64 -2
- package/dist/cli/__tests__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +289 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +290 -17
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +74 -0
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +45 -0
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +93 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +157 -3
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/version-sync-contract.test.js +2 -0
- package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +90 -12
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +13 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +439 -46
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/project-runtime-codex-homes.d.ts +6 -0
- package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -0
- package/dist/cli/project-runtime-codex-homes.js +27 -0
- package/dist/cli/project-runtime-codex-homes.js.map +1 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +8 -1
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/setup.d.ts +2 -2
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +482 -126
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +79 -8
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/update.d.ts +1 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +42 -10
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +73 -29
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +14 -0
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +54 -51
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +1 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +1 -1
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/best-practice-research-skill.test.js +12 -0
- package/dist/hooks/__tests__/best-practice-research-skill.test.js.map +1 -1
- package/dist/hud/__tests__/authority.test.js +45 -12
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +95 -0
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +6 -6
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +2 -2
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +17 -2
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/index.js +1 -4
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +42 -0
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +6 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +5 -4
- package/dist/hud/tmux.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +31 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -0
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +32 -0
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/modes/__tests__/base-autopilot-gates.test.d.ts +2 -0
- package/dist/modes/__tests__/base-autopilot-gates.test.d.ts.map +1 -0
- package/dist/modes/__tests__/base-autopilot-gates.test.js +154 -0
- package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -0
- package/dist/modes/base.d.ts +4 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +71 -1
- package/dist/modes/base.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +144 -3
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +109 -0
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +11 -4
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/code-review.d.ts +2 -0
- package/dist/pipeline/stages/code-review.d.ts.map +1 -1
- package/dist/pipeline/stages/code-review.js +2 -0
- package/dist/pipeline/stages/code-review.js.map +1 -1
- package/dist/pipeline/stages/ultraqa.d.ts +3 -0
- package/dist/pipeline/stages/ultraqa.d.ts.map +1 -1
- package/dist/pipeline/stages/ultraqa.js +3 -0
- package/dist/pipeline/stages/ultraqa.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.d.ts +2 -0
- package/dist/ralplan/__tests__/consensus-gate.test.d.ts.map +1 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js +631 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -0
- package/dist/ralplan/consensus-gate.d.ts +9 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +287 -65
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +481 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +145 -25
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts +1 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +130 -0
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/session-history/__tests__/search.test.js +166 -0
- package/dist/session-history/__tests__/search.test.js.map +1 -1
- package/dist/session-history/search.d.ts +7 -0
- package/dist/session-history/search.d.ts.map +1 -1
- package/dist/session-history/search.js +83 -24
- package/dist/session-history/search.js.map +1 -1
- package/dist/sidecar/__tests__/collector.test.js +60 -0
- package/dist/sidecar/__tests__/collector.test.js.map +1 -1
- package/dist/sidecar/collector.d.ts.map +1 -1
- package/dist/sidecar/collector.js +3 -6
- package/dist/sidecar/collector.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +622 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +82 -0
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +31 -9
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +41 -1
- package/dist/state/skill-active.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +81 -57
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/runtime.js +4 -4
- package/dist/team/runtime.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +23 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/__tests__/version.test.js +27 -0
- package/dist/utils/__tests__/version.test.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +4 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +7 -2
- package/dist/utils/version.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +4 -2
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +71 -3
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +53 -2
- package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +6 -1
- package/skills/best-practice-research/SKILL.md +6 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +615 -0
- package/src/scripts/codex-native-hook.ts +162 -32
- package/src/scripts/codex-native-pre-post.ts +137 -0
package/dist/cli/index.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Multi-agent orchestration for OpenAI Codex CLI
|
|
4
4
|
*/
|
|
5
5
|
import { execFileSync, spawn } from "child_process";
|
|
6
|
-
import { basename,
|
|
7
|
-
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
6
|
+
import { basename, dirname, join, posix, resolve, win32 } from "path";
|
|
7
|
+
import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "fs";
|
|
8
8
|
import { copyFile, cp, lstat, mkdir, readFile, readdir, rm, symlink, writeFile } from "fs/promises";
|
|
9
9
|
import { constants as osConstants, homedir } from "os";
|
|
10
10
|
import { createHash } from "crypto";
|
|
@@ -41,6 +41,7 @@ import { MADMAX_FLAG, CODEX_BYPASS_FLAG, HIGH_REASONING_FLAG, XHIGH_REASONING_FL
|
|
|
41
41
|
import { getBaseStateDir, getStateDir, listModeStateFilesWithScopePreference, } from "../mcp/state-paths.js";
|
|
42
42
|
import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
|
|
43
43
|
import { readPersistedSetupPreferences, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, } from "./codex-home.js";
|
|
44
|
+
import { discoverProjectRuntimeCodexHomes } from "./project-runtime-codex-homes.js";
|
|
44
45
|
import { escapeTomlString, readTopLevelTomlString, upsertTopLevelTomlString } from "../utils/toml.js";
|
|
45
46
|
export { readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, } from "./codex-home.js";
|
|
46
47
|
import { SKILL_ACTIVE_STATE_MODE, extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, syncCanonicalSkillStateForMode, } from "../state/skill-active.js";
|
|
@@ -107,10 +108,10 @@ Usage:
|
|
|
107
108
|
omx auth Manage Codex OAuth auth slots (add|list|use)
|
|
108
109
|
omx question OMX-owned blocking question UI entrypoint for agent-invoked user questions
|
|
109
110
|
omx adapt Scaffold OMX-owned adapter foundations for persistent external targets
|
|
110
|
-
omx resume Resume
|
|
111
|
+
omx resume Resume Codex sessions (supports --project and --codex-home <path>)
|
|
111
112
|
omx explore DEPRECATED compatibility command; use normal repo inspection or omx sparkshell
|
|
112
113
|
omx api Run native omx-api localhost gateway commands (serve|status|stop|generate)
|
|
113
|
-
omx session Search prior local session transcripts
|
|
114
|
+
omx session Search prior local session transcripts (--codex-home <path> escape hatch)
|
|
114
115
|
omx agents-init [path]
|
|
115
116
|
Bootstrap lightweight AGENTS.md files for a repo/subtree
|
|
116
117
|
omx agents Manage Codex native agent TOML files
|
|
@@ -563,14 +564,124 @@ const PROJECT_LAUNCH_PERSISTED_RUNTIME_ENTRY_NAMES = new Set([
|
|
|
563
564
|
// log the contents.
|
|
564
565
|
"auth.json",
|
|
565
566
|
]);
|
|
567
|
+
const PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES = new Set([
|
|
568
|
+
"sessions",
|
|
569
|
+
"history.jsonl",
|
|
570
|
+
"session_index.jsonl",
|
|
571
|
+
]);
|
|
566
572
|
// Mirroring these files into the runtime CODEX_HOME would cause Codex to load
|
|
567
573
|
// them as user-scope config alongside the canonical project-scope copies under
|
|
568
574
|
// <cwd>/.codex, duplicating every native hook and asking the user to re-trust
|
|
569
575
|
// hooks on every launch. See GH issue #2470.
|
|
570
576
|
const PROJECT_LAUNCH_RUNTIME_SKIPPED_ENTRY_NAMES = new Set(["hooks.json"]);
|
|
577
|
+
function shouldMirrorProjectLaunchRuntimeEntry(entryName, includeHistoryArtifacts) {
|
|
578
|
+
if (PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES.has(entryName))
|
|
579
|
+
return true;
|
|
580
|
+
if (isCodexSqliteArtifact(entryName))
|
|
581
|
+
return includeHistoryArtifacts;
|
|
582
|
+
return true;
|
|
583
|
+
}
|
|
571
584
|
function shouldPersistProjectLaunchRuntimeEntry(entryName) {
|
|
572
585
|
return PROJECT_LAUNCH_PERSISTED_RUNTIME_ENTRY_NAMES.has(entryName);
|
|
573
586
|
}
|
|
587
|
+
function uniqueJsonlLines(contents) {
|
|
588
|
+
const seen = new Set();
|
|
589
|
+
const lines = [];
|
|
590
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
591
|
+
if (line === "" || seen.has(line))
|
|
592
|
+
continue;
|
|
593
|
+
seen.add(line);
|
|
594
|
+
lines.push(line);
|
|
595
|
+
}
|
|
596
|
+
return lines;
|
|
597
|
+
}
|
|
598
|
+
async function persistProjectLaunchRuntimeJsonlArtifact(source, destination) {
|
|
599
|
+
const existing = existsSync(destination) ? await readFile(destination, "utf-8").catch(() => "") : "";
|
|
600
|
+
const sourceContents = await readFile(source, "utf-8");
|
|
601
|
+
const separator = existing === "" || existing.endsWith("\n") || sourceContents === "" ? "" : "\n";
|
|
602
|
+
const lines = uniqueJsonlLines(`${existing}${separator}${sourceContents}`);
|
|
603
|
+
await writeFile(destination, lines.length > 0 ? `${lines.join("\n")}\n` : "", "utf-8");
|
|
604
|
+
}
|
|
605
|
+
async function persistProjectLaunchRuntimeHistoryArtifacts(runtimeCodexHome, projectCodexHome) {
|
|
606
|
+
if (!runtimeCodexHome || !projectCodexHome)
|
|
607
|
+
return;
|
|
608
|
+
if (!existsSync(runtimeCodexHome))
|
|
609
|
+
return;
|
|
610
|
+
await mkdir(projectCodexHome, { recursive: true });
|
|
611
|
+
for (const entryName of PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES) {
|
|
612
|
+
const source = join(runtimeCodexHome, entryName);
|
|
613
|
+
if (!existsSync(source))
|
|
614
|
+
continue;
|
|
615
|
+
const sourceStat = await lstat(source);
|
|
616
|
+
if (sourceStat.isSymbolicLink())
|
|
617
|
+
continue;
|
|
618
|
+
const destination = join(projectCodexHome, entryName);
|
|
619
|
+
if (sourceStat.isDirectory()) {
|
|
620
|
+
await cp(source, destination, { recursive: true, force: true, verbatimSymlinks: true });
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
if (entryName === "history.jsonl" || entryName === "session_index.jsonl") {
|
|
624
|
+
await persistProjectLaunchRuntimeJsonlArtifact(source, destination);
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (sourceStat.isFile()) {
|
|
628
|
+
await copyFile(source, destination);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
async function ensureProjectLaunchRuntimeHistoryLinks(runtimeCodexHome, projectCodexHome) {
|
|
633
|
+
await mkdir(projectCodexHome, { recursive: true });
|
|
634
|
+
for (const entryName of PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES) {
|
|
635
|
+
const runtimeEntry = join(runtimeCodexHome, entryName);
|
|
636
|
+
if (existsSync(runtimeEntry))
|
|
637
|
+
continue;
|
|
638
|
+
const projectEntry = join(projectCodexHome, entryName);
|
|
639
|
+
if (entryName === "sessions") {
|
|
640
|
+
await mkdir(projectEntry, { recursive: true });
|
|
641
|
+
}
|
|
642
|
+
else if (!existsSync(projectEntry)) {
|
|
643
|
+
await writeFile(projectEntry, "");
|
|
644
|
+
}
|
|
645
|
+
await linkOrCopyCodexHomeEntry(projectEntry, runtimeEntry);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
async function materializeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, sourceCodexHome) {
|
|
649
|
+
for (const entryName of PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES) {
|
|
650
|
+
const source = join(sourceCodexHome, entryName);
|
|
651
|
+
if (!existsSync(source))
|
|
652
|
+
continue;
|
|
653
|
+
const destination = join(runtimeCodexHome, entryName);
|
|
654
|
+
await rm(destination, { recursive: true, force: true });
|
|
655
|
+
const sourceStat = await lstat(source);
|
|
656
|
+
if (sourceStat.isDirectory()) {
|
|
657
|
+
await cp(source, destination, { recursive: true, force: true, dereference: true });
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
await copyFile(source, destination);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
async function mergeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, sourceCodexHome) {
|
|
664
|
+
for (const entryName of PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES) {
|
|
665
|
+
const source = join(sourceCodexHome, entryName);
|
|
666
|
+
if (!existsSync(source))
|
|
667
|
+
continue;
|
|
668
|
+
const destination = join(runtimeCodexHome, entryName);
|
|
669
|
+
const sourceStat = await lstat(source);
|
|
670
|
+
if (sourceStat.isDirectory()) {
|
|
671
|
+
await mkdir(destination, { recursive: true });
|
|
672
|
+
await cp(source, destination, { recursive: true, force: true, dereference: true });
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (existsSync(destination)) {
|
|
676
|
+
const existing = await readFile(destination, "utf-8").catch(() => "");
|
|
677
|
+
const addition = await readFile(source, "utf-8");
|
|
678
|
+
const separator = existing === "" || existing.endsWith("\n") || addition === "" ? "" : "\n";
|
|
679
|
+
await writeFile(destination, `${existing}${separator}${addition}`, "utf-8");
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
await copyFile(source, destination);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
574
685
|
export async function persistProjectLaunchRuntimeAuthState(runtimeCodexHome, projectCodexHome) {
|
|
575
686
|
if (!runtimeCodexHome || !projectCodexHome)
|
|
576
687
|
return;
|
|
@@ -587,10 +698,12 @@ export async function prepareRuntimeCodexHomeForProjectLaunch(cwd, sessionId, pr
|
|
|
587
698
|
const runtimeCodexHome = runtimeCodexHomePath(cwd, sessionId);
|
|
588
699
|
await rm(runtimeCodexHome, { recursive: true, force: true });
|
|
589
700
|
await mkdir(runtimeCodexHome, { recursive: true });
|
|
590
|
-
if (!existsSync(projectCodexHome))
|
|
701
|
+
if (!existsSync(projectCodexHome)) {
|
|
702
|
+
await ensureProjectLaunchRuntimeHistoryLinks(runtimeCodexHome, projectCodexHome);
|
|
591
703
|
return runtimeCodexHome;
|
|
704
|
+
}
|
|
592
705
|
for (const entry of await readdir(projectCodexHome, { withFileTypes: true })) {
|
|
593
|
-
if (
|
|
706
|
+
if (!shouldMirrorProjectLaunchRuntimeEntry(entry.name, options.includeHistoryArtifacts === true))
|
|
594
707
|
continue;
|
|
595
708
|
if (PROJECT_LAUNCH_RUNTIME_SKIPPED_ENTRY_NAMES.has(entry.name))
|
|
596
709
|
continue;
|
|
@@ -608,6 +721,13 @@ export async function prepareRuntimeCodexHomeForProjectLaunch(cwd, sessionId, pr
|
|
|
608
721
|
}
|
|
609
722
|
await linkOrCopyCodexHomeEntry(source, destination);
|
|
610
723
|
}
|
|
724
|
+
await ensureProjectLaunchRuntimeHistoryLinks(runtimeCodexHome, projectCodexHome);
|
|
725
|
+
if (options.includeHistoryArtifacts === true && (options.extraHistoryCodexHomes?.length ?? 0) > 0) {
|
|
726
|
+
await materializeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, projectCodexHome);
|
|
727
|
+
for (const extraCodexHome of options.extraHistoryCodexHomes ?? []) {
|
|
728
|
+
await mergeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, extraCodexHome);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
611
731
|
return runtimeCodexHome;
|
|
612
732
|
}
|
|
613
733
|
function resolveProjectSqliteHomeForLaunch(projectCodexHome, env) {
|
|
@@ -619,7 +739,7 @@ function resolveProjectSqliteHomeForLaunch(projectCodexHome, env) {
|
|
|
619
739
|
export async function prepareCodexHomeForLaunch(cwd, sessionId, env = process.env, options = {}) {
|
|
620
740
|
const projectLocalCodexHomeForCleanup = resolveProjectLocalCodexHomeForLaunch(cwd, env);
|
|
621
741
|
if (projectLocalCodexHomeForCleanup) {
|
|
622
|
-
const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(cwd, sessionId, projectLocalCodexHomeForCleanup, { includeHistoryArtifacts: options.includeHistoryArtifacts });
|
|
742
|
+
const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(cwd, sessionId, projectLocalCodexHomeForCleanup, { includeHistoryArtifacts: options.includeHistoryArtifacts, extraHistoryCodexHomes: options.extraHistoryCodexHomes });
|
|
623
743
|
return {
|
|
624
744
|
codexHomeOverride: runtimeCodexHome,
|
|
625
745
|
sqliteHomeOverride: resolveProjectSqliteHomeForLaunch(projectLocalCodexHomeForCleanup, env),
|
|
@@ -632,6 +752,81 @@ export async function prepareCodexHomeForLaunch(cwd, sessionId, env = process.en
|
|
|
632
752
|
projectLocalCodexHomeForCleanup,
|
|
633
753
|
};
|
|
634
754
|
}
|
|
755
|
+
export function parseResumeCodexHomeSelection(args) {
|
|
756
|
+
const nextArgs = [];
|
|
757
|
+
let explicitCodexHome;
|
|
758
|
+
let projectOnly = false;
|
|
759
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
760
|
+
const arg = args[index];
|
|
761
|
+
if (arg === "--codex-home") {
|
|
762
|
+
const value = args[index + 1];
|
|
763
|
+
if (!value || value.startsWith("-")) {
|
|
764
|
+
throw new Error("Missing value after --codex-home.");
|
|
765
|
+
}
|
|
766
|
+
explicitCodexHome = value;
|
|
767
|
+
index += 1;
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (arg.startsWith("--codex-home=")) {
|
|
771
|
+
explicitCodexHome = arg.slice("--codex-home=".length);
|
|
772
|
+
if (explicitCodexHome.trim() === "") {
|
|
773
|
+
throw new Error("Missing value after --codex-home.");
|
|
774
|
+
}
|
|
775
|
+
continue;
|
|
776
|
+
}
|
|
777
|
+
if (arg === "--project") {
|
|
778
|
+
projectOnly = true;
|
|
779
|
+
continue;
|
|
780
|
+
}
|
|
781
|
+
nextArgs.push(arg);
|
|
782
|
+
}
|
|
783
|
+
return {
|
|
784
|
+
args: nextArgs,
|
|
785
|
+
explicitCodexHome,
|
|
786
|
+
projectOnly,
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
async function prepareResumeCodexHomeForLaunch(cwd, sessionId, args, env = process.env) {
|
|
790
|
+
const selection = parseResumeCodexHomeSelection(args);
|
|
791
|
+
if (selection.explicitCodexHome) {
|
|
792
|
+
return {
|
|
793
|
+
args: selection.args,
|
|
794
|
+
prepared: {
|
|
795
|
+
codexHomeOverride: resolve(selection.explicitCodexHome),
|
|
796
|
+
},
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
const projectHomes = await discoverProjectRuntimeCodexHomes(cwd);
|
|
800
|
+
if (selection.projectOnly) {
|
|
801
|
+
if (projectHomes.length === 0) {
|
|
802
|
+
const emptyRuntimeCodexHome = runtimeCodexHomePath(cwd, sessionId);
|
|
803
|
+
await rm(emptyRuntimeCodexHome, { recursive: true, force: true });
|
|
804
|
+
await mkdir(join(emptyRuntimeCodexHome, "sessions"), { recursive: true });
|
|
805
|
+
return {
|
|
806
|
+
args: selection.args,
|
|
807
|
+
prepared: {
|
|
808
|
+
codexHomeOverride: emptyRuntimeCodexHome,
|
|
809
|
+
runtimeCodexHomeForCleanup: emptyRuntimeCodexHome,
|
|
810
|
+
},
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
const runtimeCodexHome = await prepareRuntimeCodexHomeForProjectLaunch(cwd, sessionId, projectHomes[0].path, {
|
|
814
|
+
includeHistoryArtifacts: true,
|
|
815
|
+
extraHistoryCodexHomes: projectHomes.slice(1).map((home) => home.path),
|
|
816
|
+
});
|
|
817
|
+
return {
|
|
818
|
+
args: selection.args,
|
|
819
|
+
prepared: {
|
|
820
|
+
codexHomeOverride: runtimeCodexHome,
|
|
821
|
+
},
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
const prepared = await prepareCodexHomeForLaunch(cwd, sessionId, env, {
|
|
825
|
+
includeHistoryArtifacts: true,
|
|
826
|
+
extraHistoryCodexHomes: projectHomes.map((home) => home.path),
|
|
827
|
+
});
|
|
828
|
+
return { args: selection.args, prepared };
|
|
829
|
+
}
|
|
635
830
|
export async function persistProjectLaunchRuntimeProjectTrustState(runtimeCodexHome, projectCodexHome) {
|
|
636
831
|
if (!runtimeCodexHome || !projectCodexHome)
|
|
637
832
|
return;
|
|
@@ -654,6 +849,7 @@ export async function cleanupRuntimeCodexHome(runtimeCodexHomeForCleanup, projec
|
|
|
654
849
|
if (!runtimeCodexHomeForCleanup)
|
|
655
850
|
return;
|
|
656
851
|
await persistProjectLaunchRuntimeAuthState(runtimeCodexHomeForCleanup, projectCodexHomeForPersistence);
|
|
852
|
+
await persistProjectLaunchRuntimeHistoryArtifacts(runtimeCodexHomeForCleanup, projectCodexHomeForPersistence);
|
|
657
853
|
await persistProjectLaunchRuntimeProjectTrustState(runtimeCodexHomeForCleanup, projectCodexHomeForPersistence);
|
|
658
854
|
await rm(runtimeCodexHomeForCleanup, { recursive: true, force: true });
|
|
659
855
|
}
|
|
@@ -774,7 +970,10 @@ function tmuxPaneBelongsToSession(paneId, sessionName) {
|
|
|
774
970
|
}
|
|
775
971
|
}
|
|
776
972
|
function buildDetachedHistoryPruneHookCommand(leaderPaneId) {
|
|
777
|
-
|
|
973
|
+
// The leader pane can be gone by the time the hook fires (e.g. crashed
|
|
974
|
+
// leader with a lingering session); suppress errors so tmux does not queue
|
|
975
|
+
// "(null):0: can't find pane" for the next attaching client.
|
|
976
|
+
return `if-shell -F '#{==:#{session_attached},0}' 'run-shell -b "tmux clear-history -t ${leaderPaneId} >/dev/null 2>&1 || true"'`;
|
|
778
977
|
}
|
|
779
978
|
function buildDetachedHistoryPruneHookSlot(sessionName, leaderPaneId) {
|
|
780
979
|
const key = `${sessionName}:${leaderPaneId}:omx-history-prune`;
|
|
@@ -992,8 +1191,11 @@ function runCodexBlocking(cwd, launchArgs, codexEnv) {
|
|
|
992
1191
|
}
|
|
993
1192
|
}
|
|
994
1193
|
}
|
|
995
|
-
export function
|
|
996
|
-
return
|
|
1194
|
+
export function omxRuntimeCommandShimFileName(platform = process.platform) {
|
|
1195
|
+
return platform === "win32" ? "omx.cmd" : "omx";
|
|
1196
|
+
}
|
|
1197
|
+
export function omxRuntimeCommandShimPath(cwd, platform = process.platform) {
|
|
1198
|
+
return join(omxRoot(cwd), "runtime", "bin", omxRuntimeCommandShimFileName(platform));
|
|
997
1199
|
}
|
|
998
1200
|
function ensureRuntimeShimDirectory(path) {
|
|
999
1201
|
if (existsSync(path)) {
|
|
@@ -1008,15 +1210,22 @@ function ensureRuntimeShimDirectory(path) {
|
|
|
1008
1210
|
}
|
|
1009
1211
|
mkdirSync(path, { mode: 0o700 });
|
|
1010
1212
|
}
|
|
1011
|
-
function buildOmxRuntimeCommandShim(nodePath, omxBin) {
|
|
1213
|
+
function buildOmxRuntimeCommandShim(nodePath, omxBin, platform = process.platform) {
|
|
1214
|
+
if (platform === "win32") {
|
|
1215
|
+
return [
|
|
1216
|
+
"@echo off",
|
|
1217
|
+
`"${nodePath}" "${omxBin}" %*`,
|
|
1218
|
+
"",
|
|
1219
|
+
].join("\r\n");
|
|
1220
|
+
}
|
|
1012
1221
|
return [
|
|
1013
1222
|
"#!/bin/sh",
|
|
1014
1223
|
`exec ${quoteShellArg(nodePath)} ${quoteShellArg(omxBin)} "$@"`,
|
|
1015
1224
|
"",
|
|
1016
1225
|
].join("\n");
|
|
1017
1226
|
}
|
|
1018
|
-
export function ensureOmxRuntimeCommandShim(cwd, omxBin, nodePath = process.execPath) {
|
|
1019
|
-
const shimPath = omxRuntimeCommandShimPath(cwd);
|
|
1227
|
+
export function ensureOmxRuntimeCommandShim(cwd, omxBin, nodePath = process.execPath, platform = process.platform) {
|
|
1228
|
+
const shimPath = omxRuntimeCommandShimPath(cwd, platform);
|
|
1020
1229
|
const shimDir = dirname(shimPath);
|
|
1021
1230
|
const rootDir = omxRoot(cwd);
|
|
1022
1231
|
const runtimeDir = dirname(shimDir);
|
|
@@ -1032,24 +1241,53 @@ export function ensureOmxRuntimeCommandShim(cwd, omxBin, nodePath = process.exec
|
|
|
1032
1241
|
rmSync(shimPath, { force: true });
|
|
1033
1242
|
}
|
|
1034
1243
|
}
|
|
1035
|
-
writeFileSync(shimPath, buildOmxRuntimeCommandShim(nodePath, omxBin), {
|
|
1244
|
+
writeFileSync(shimPath, buildOmxRuntimeCommandShim(nodePath, omxBin, platform), {
|
|
1036
1245
|
encoding: "utf-8",
|
|
1037
1246
|
mode: 0o700,
|
|
1038
1247
|
});
|
|
1039
|
-
|
|
1248
|
+
if (platform !== "win32") {
|
|
1249
|
+
chmodSync(shimPath, 0o700);
|
|
1250
|
+
}
|
|
1040
1251
|
return shimDir;
|
|
1041
1252
|
}
|
|
1042
|
-
export function prependOmxRuntimeCommandShimToEnv(cwd, env, omxBin, nodePath = process.execPath) {
|
|
1043
|
-
const shimDir = ensureOmxRuntimeCommandShim(cwd, omxBin, nodePath);
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1253
|
+
export function prependOmxRuntimeCommandShimToEnv(cwd, env, omxBin, nodePath = process.execPath, platform = process.platform) {
|
|
1254
|
+
const shimDir = ensureOmxRuntimeCommandShim(cwd, omxBin, nodePath, platform);
|
|
1255
|
+
const pathDelimiter = platform === "win32" ? win32.delimiter : posix.delimiter;
|
|
1256
|
+
const result = { ...env };
|
|
1257
|
+
if (platform === "win32") {
|
|
1258
|
+
// Windows env var names are case-insensitive; the inherited key is usually
|
|
1259
|
+
// `Path`, not `PATH`. Find every case variant, preserve the existing value,
|
|
1260
|
+
// prepend the shim directory, and collapse to a single key so the child does
|
|
1261
|
+
// not see an empty `PATH` shadowing the real `Path` (which drops System32,
|
|
1262
|
+
// WindowsPowerShell, etc.).
|
|
1263
|
+
const pathVariants = Object.keys(result).filter((key) => key.toLowerCase() === "path");
|
|
1264
|
+
let pathKey = "Path";
|
|
1265
|
+
let currentPath = "";
|
|
1266
|
+
for (const variant of pathVariants) {
|
|
1267
|
+
const value = result[variant];
|
|
1268
|
+
if (typeof value === "string" && value.length > 0) {
|
|
1269
|
+
pathKey = variant;
|
|
1270
|
+
currentPath = value;
|
|
1271
|
+
break;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
for (const variant of pathVariants) {
|
|
1275
|
+
delete result[variant];
|
|
1276
|
+
}
|
|
1277
|
+
result[pathKey] = currentPath
|
|
1278
|
+
? `${shimDir}${pathDelimiter}${currentPath}`
|
|
1279
|
+
: shimDir;
|
|
1280
|
+
}
|
|
1281
|
+
else {
|
|
1282
|
+
const currentPath = typeof result.PATH === "string" ? result.PATH : "";
|
|
1283
|
+
result.PATH = currentPath ? `${shimDir}${pathDelimiter}${currentPath}` : shimDir;
|
|
1284
|
+
}
|
|
1285
|
+
result.OMX_ENTRY_PATH = omxBin;
|
|
1286
|
+
result.OMX_STARTUP_CWD =
|
|
1287
|
+
typeof result.OMX_STARTUP_CWD === "string" && result.OMX_STARTUP_CWD.trim()
|
|
1288
|
+
? result.OMX_STARTUP_CWD
|
|
1289
|
+
: cwd;
|
|
1290
|
+
return result;
|
|
1053
1291
|
}
|
|
1054
1292
|
export function buildHudPaneCleanupTargets(existingPaneIds, createdPaneId, leaderPaneId) {
|
|
1055
1293
|
const targets = new Set(existingPaneIds.filter((id) => id.startsWith("%")));
|
|
@@ -1656,7 +1894,31 @@ async function showStatus() {
|
|
|
1656
1894
|
const { readFile } = await import("fs/promises");
|
|
1657
1895
|
const cwd = process.cwd();
|
|
1658
1896
|
try {
|
|
1659
|
-
|
|
1897
|
+
let refs = await listModeStateFilesWithScopePreference(cwd);
|
|
1898
|
+
// Reconcile with hook-visible run-dir state when the worktree-scoped state
|
|
1899
|
+
// list reports no active workflow mode (parity with `omx cancel`). This
|
|
1900
|
+
// surfaces detached/madmax sessions whose state lives under the run dir.
|
|
1901
|
+
const hasActiveWorkflowMode = async (candidate) => {
|
|
1902
|
+
for (const ref of candidate) {
|
|
1903
|
+
const mode = basename(ref.path).replace("-state.json", "");
|
|
1904
|
+
if (mode === SKILL_ACTIVE_STATE_MODE)
|
|
1905
|
+
continue;
|
|
1906
|
+
try {
|
|
1907
|
+
const parsed = JSON.parse(await readFile(ref.path, "utf-8"));
|
|
1908
|
+
if (parsed.active === true)
|
|
1909
|
+
return true;
|
|
1910
|
+
}
|
|
1911
|
+
catch {
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return false;
|
|
1916
|
+
};
|
|
1917
|
+
if (!(await hasActiveWorkflowMode(refs))) {
|
|
1918
|
+
const runDirRefs = await listHookVisibleRunDirStateRefs(cwd);
|
|
1919
|
+
if (await hasActiveWorkflowMode(runDirRefs))
|
|
1920
|
+
refs = runDirRefs;
|
|
1921
|
+
}
|
|
1660
1922
|
const states = refs.map((ref) => ref.path);
|
|
1661
1923
|
const ultragoalState = await readUltragoalState(cwd).catch(() => null);
|
|
1662
1924
|
if (states.length === 0) {
|
|
@@ -1825,7 +2087,7 @@ export async function launchWithHud(args) {
|
|
|
1825
2087
|
const { launchPolicy, effectiveExplicitLaunchPolicy } = resolveTmuxAwareLaunchPolicy(explicitLaunchPolicy, isNativeWindows());
|
|
1826
2088
|
const enableNotifyFallbackAuthority = launchPolicy === "direct";
|
|
1827
2089
|
const workerSparkModel = resolveWorkerSparkModel(notifyTempResult.passthroughArgs, persistentCodexHomeForLaunch);
|
|
1828
|
-
|
|
2090
|
+
let normalizedArgs = normalizeCodexLaunchArgs(notifyTempResult.passthroughArgs);
|
|
1829
2091
|
let cwd = launchCwd;
|
|
1830
2092
|
let worktreeDirty = false;
|
|
1831
2093
|
let ensuredLaunchWorktree;
|
|
@@ -1883,7 +2145,13 @@ export async function launchWithHud(args) {
|
|
|
1883
2145
|
catch {
|
|
1884
2146
|
// Non-fatal: repair failure must not block launch
|
|
1885
2147
|
}
|
|
1886
|
-
const
|
|
2148
|
+
const resumePrepared = normalizedArgs[0] === "resume"
|
|
2149
|
+
? await prepareResumeCodexHomeForLaunch(launchCwd, sessionId, normalizedArgs, process.env)
|
|
2150
|
+
: null;
|
|
2151
|
+
if (resumePrepared) {
|
|
2152
|
+
normalizedArgs = resumePrepared.args;
|
|
2153
|
+
}
|
|
2154
|
+
const preparedCodexHome = resumePrepared?.prepared ?? await prepareCodexHomeForLaunch(launchCwd, sessionId, process.env, {
|
|
1887
2155
|
includeHistoryArtifacts: normalizedArgs[0] === "resume",
|
|
1888
2156
|
});
|
|
1889
2157
|
const codexHomeOverride = preparedCodexHome.codexHomeOverride;
|
|
@@ -4321,28 +4589,144 @@ async function flushHookDerivedWatcherOnce(cwd) {
|
|
|
4321
4589
|
},
|
|
4322
4590
|
});
|
|
4323
4591
|
}
|
|
4592
|
+
// Canonicalize a path for comparing a registry `source_cwd` against the current
|
|
4593
|
+
// working directory. `process.cwd()` resolves symlinks (e.g. macOS `/var` ->
|
|
4594
|
+
// `/private/var`), so registry values must be canonicalized the same way or the
|
|
4595
|
+
// run-dir fallback never matches. Falls back to `resolve` when the path is
|
|
4596
|
+
// missing (realpathSync requires an existing target).
|
|
4597
|
+
function canonicalizePathForRunDirMatch(p) {
|
|
4598
|
+
try {
|
|
4599
|
+
return realpathSync(resolve(p));
|
|
4600
|
+
}
|
|
4601
|
+
catch {
|
|
4602
|
+
return resolve(p);
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
async function listHookVisibleRunDirStateRefs(cwd) {
|
|
4606
|
+
const runsRoot = resolveMadmaxRunsRoot(process.env);
|
|
4607
|
+
const registryPath = join(runsRoot, "registry.jsonl");
|
|
4608
|
+
const runDirs = new Set();
|
|
4609
|
+
const canonicalCwd = canonicalizePathForRunDirMatch(cwd);
|
|
4610
|
+
const canonicalRunsRoot = resolve(runsRoot);
|
|
4611
|
+
const addRecord = (raw) => {
|
|
4612
|
+
if (!raw || typeof raw !== "object")
|
|
4613
|
+
return;
|
|
4614
|
+
const record = raw;
|
|
4615
|
+
const sourceCwd = typeof record.source_cwd === "string" ? record.source_cwd.trim() : "";
|
|
4616
|
+
const runDir = typeof record.run_dir === "string"
|
|
4617
|
+
? record.run_dir.trim()
|
|
4618
|
+
: typeof record.cwd === "string"
|
|
4619
|
+
? record.cwd.trim()
|
|
4620
|
+
: "";
|
|
4621
|
+
if (!sourceCwd || !runDir)
|
|
4622
|
+
return;
|
|
4623
|
+
try {
|
|
4624
|
+
if (canonicalizePathForRunDirMatch(sourceCwd) !== canonicalCwd)
|
|
4625
|
+
return;
|
|
4626
|
+
const resolvedRunDir = resolve(runDir);
|
|
4627
|
+
if (resolvedRunDir !== canonicalRunsRoot
|
|
4628
|
+
&& !resolvedRunDir.startsWith(`${canonicalRunsRoot}/`)) {
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
runDirs.add(resolvedRunDir);
|
|
4632
|
+
}
|
|
4633
|
+
catch {
|
|
4634
|
+
return;
|
|
4635
|
+
}
|
|
4636
|
+
};
|
|
4637
|
+
try {
|
|
4638
|
+
const rawRegistry = await readFile(registryPath, "utf-8");
|
|
4639
|
+
for (const line of rawRegistry.split(/\r?\n/)) {
|
|
4640
|
+
const trimmed = line.trim();
|
|
4641
|
+
if (!trimmed)
|
|
4642
|
+
continue;
|
|
4643
|
+
try {
|
|
4644
|
+
addRecord(JSON.parse(trimmed));
|
|
4645
|
+
}
|
|
4646
|
+
catch {
|
|
4647
|
+
continue;
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
catch { }
|
|
4652
|
+
try {
|
|
4653
|
+
const activeDir = join(runsRoot, MADMAX_DETACHED_ACTIVE_DIR);
|
|
4654
|
+
const files = await readdir(activeDir).catch(() => []);
|
|
4655
|
+
for (const file of files) {
|
|
4656
|
+
if (!file.endsWith(".json"))
|
|
4657
|
+
continue;
|
|
4658
|
+
try {
|
|
4659
|
+
addRecord(JSON.parse(await readFile(join(activeDir, file), "utf-8")));
|
|
4660
|
+
}
|
|
4661
|
+
catch {
|
|
4662
|
+
continue;
|
|
4663
|
+
}
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
catch { }
|
|
4667
|
+
const refs = [];
|
|
4668
|
+
const seenPaths = new Set();
|
|
4669
|
+
for (const runDir of runDirs) {
|
|
4670
|
+
const stateDir = join(runDir, ".omx", "state");
|
|
4671
|
+
let sessionId;
|
|
4672
|
+
try {
|
|
4673
|
+
const session = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8"));
|
|
4674
|
+
if (typeof session.session_id === "string" && session.session_id.trim()) {
|
|
4675
|
+
sessionId = session.session_id.trim();
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
catch { }
|
|
4679
|
+
const candidateDirs = sessionId ? [join(stateDir, "sessions", sessionId), stateDir] : [stateDir];
|
|
4680
|
+
for (const dir of candidateDirs) {
|
|
4681
|
+
const files = await readdir(dir).catch(() => []);
|
|
4682
|
+
for (const file of files) {
|
|
4683
|
+
if (!file.endsWith("-state.json") || file === "session.json")
|
|
4684
|
+
continue;
|
|
4685
|
+
const path = join(dir, file);
|
|
4686
|
+
if (seenPaths.has(path))
|
|
4687
|
+
continue;
|
|
4688
|
+
seenPaths.add(path);
|
|
4689
|
+
refs.push({
|
|
4690
|
+
mode: file.slice(0, -"-state.json".length),
|
|
4691
|
+
path,
|
|
4692
|
+
scope: dir === stateDir ? "root" : "session",
|
|
4693
|
+
});
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
return refs.sort((a, b) => a.mode.localeCompare(b.mode));
|
|
4698
|
+
}
|
|
4324
4699
|
async function cancelModes() {
|
|
4325
4700
|
const { writeFile, readFile } = await import("fs/promises");
|
|
4326
4701
|
const cwd = process.cwd();
|
|
4327
4702
|
const nowIso = new Date().toISOString();
|
|
4328
4703
|
try {
|
|
4329
|
-
const
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4704
|
+
const loadStates = async (refs) => {
|
|
4705
|
+
const loaded = new Map();
|
|
4706
|
+
for (const ref of refs) {
|
|
4707
|
+
const content = await readFile(ref.path, "utf-8");
|
|
4708
|
+
let parsedState;
|
|
4709
|
+
try {
|
|
4710
|
+
parsedState = JSON.parse(content);
|
|
4711
|
+
}
|
|
4712
|
+
catch (err) {
|
|
4713
|
+
logCliOperationFailure(err);
|
|
4714
|
+
continue;
|
|
4715
|
+
}
|
|
4716
|
+
loaded.set(ref.mode, {
|
|
4717
|
+
path: ref.path,
|
|
4718
|
+
scope: ref.scope,
|
|
4719
|
+
state: parsedState,
|
|
4720
|
+
});
|
|
4340
4721
|
}
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4722
|
+
return loaded;
|
|
4723
|
+
};
|
|
4724
|
+
let states = await loadStates(await listModeStateFilesWithScopePreference(cwd));
|
|
4725
|
+
const hasActiveWorkflowMode = (entries) => [...entries.entries()].some(([mode, entry]) => mode !== SKILL_ACTIVE_STATE_MODE && entry.state.active === true);
|
|
4726
|
+
if (!hasActiveWorkflowMode(states)) {
|
|
4727
|
+
const runDirStates = await loadStates(await listHookVisibleRunDirStateRefs(cwd));
|
|
4728
|
+
if (hasActiveWorkflowMode(runDirStates))
|
|
4729
|
+
states = runDirStates;
|
|
4346
4730
|
}
|
|
4347
4731
|
const changed = new Set();
|
|
4348
4732
|
const reported = new Set();
|
|
@@ -4361,8 +4745,17 @@ async function cancelModes() {
|
|
|
4361
4745
|
entry.state.current_phase = phase;
|
|
4362
4746
|
entry.state.completed_at = nowIso;
|
|
4363
4747
|
entry.state.last_turn_at = nowIso;
|
|
4748
|
+
if (mode === SKILL_ACTIVE_STATE_MODE) {
|
|
4749
|
+
entry.state.phase = phase;
|
|
4750
|
+
const activeSkills = Array.isArray(entry.state.active_skills)
|
|
4751
|
+
? entry.state.active_skills
|
|
4752
|
+
: [];
|
|
4753
|
+
entry.state.active_skills = activeSkills.map((skill) => (skill && typeof skill === "object"
|
|
4754
|
+
? { ...skill, active: false, phase }
|
|
4755
|
+
: skill));
|
|
4756
|
+
}
|
|
4364
4757
|
changed.add(mode);
|
|
4365
|
-
if (reportIfWasActive && wasActive)
|
|
4758
|
+
if (reportIfWasActive && wasActive && mode !== SKILL_ACTIVE_STATE_MODE)
|
|
4366
4759
|
reported.add(mode);
|
|
4367
4760
|
};
|
|
4368
4761
|
const ralphLinksUltrawork = (state) => state.linked_ultrawork === true || state.linked_mode === "ultrawork";
|