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/setup.js
CHANGED
|
@@ -8,9 +8,11 @@ import { existsSync } from "fs";
|
|
|
8
8
|
import { spawnSync } from "child_process";
|
|
9
9
|
import { createInterface } from "readline/promises";
|
|
10
10
|
import { homedir } from "os";
|
|
11
|
+
import TOML from "@iarna/toml";
|
|
12
|
+
import { createHash } from "crypto";
|
|
11
13
|
import { codexHome, codexConfigPath, codexPromptsDir, codexAgentsDir, userSkillsDir, omxStateDir, detectLegacySkillRootOverlap, omxPlansDir, omxLogsDir, } from "../utils/paths.js";
|
|
12
|
-
import { buildMergedConfig, getRootModelName, getRootTomlArray, hasLegacyOmxTeamRunTable, isOmxManagedNotifyCommand, sanitizePreviousNotifyCommand, stripExistingOmxBlocks, stripExistingSharedMcpRegistryBlock, mergeSharedMcpRegistryBlock, stripOmxEnvSettings, stripOmxFeatureFlags, stripOmxSeededBehavioralDefaults, upsertPluginModeRuntimeFeatureFlags, upsertManagedCodexHookTrustState, stripManagedCodexHookTrustState, OMX_PLUGIN_DEVELOPER_INSTRUCTIONS, hasFirstPartyOmxMcpRegistrations, extractFirstPartyOmxMcpSections, stripFirstPartyOmxMcpSections, } from "../config/generator.js";
|
|
13
|
-
import { buildManagedCodexHookTrustState, buildManagedCodexNativeHookWindowsShimContent, buildManagedCodexNativeHookWindowsShimPath, mergeManagedCodexHooksConfig, removeManagedCodexHooks, } from "../config/codex-hooks.js";
|
|
14
|
+
import { buildMergedConfig, getRootModelName, getRootTomlArray, hasLegacyOmxTeamRunTable, isOmxManagedNotifyCommand, sanitizePreviousNotifyCommand, stripExistingOmxBlocks, stripExistingSharedMcpRegistryBlock, mergeSharedMcpRegistryBlock, stripOmxEnvSettings, stripOmxFeatureFlags, stripOmxSeededBehavioralDefaults, upsertPluginModeRuntimeFeatureFlags, upsertManagedCodexHookTrustState, stripManagedCodexHookTrustState, OMX_DEVELOPER_INSTRUCTIONS, OMX_PLUGIN_DEVELOPER_INSTRUCTIONS, hasFirstPartyOmxMcpRegistrations, extractFirstPartyOmxMcpSections, stripFirstPartyOmxMcpSections, } from "../config/generator.js";
|
|
15
|
+
import { buildManagedCodexHookTrustState, buildManagedCodexNativeHookWindowsShimContent, buildManagedCodexNativeHookWindowsShimPath, mergeManagedCodexHooksConfig, extractCodexHooksJsonTrustState, removeManagedCodexHooks, } from "../config/codex-hooks.js";
|
|
14
16
|
import { getLegacyUnifiedMcpRegistryCandidate, getUnifiedMcpRegistryCandidates, loadUnifiedMcpRegistry, planClaudeCodeMcpSettingsSync, } from "../config/mcp-registry.js";
|
|
15
17
|
import { generateAgentToml } from "../agents/native-config.js";
|
|
16
18
|
import { AGENT_DEFINITIONS } from "../agents/definitions.js";
|
|
@@ -62,6 +64,7 @@ const PROJECT_GITIGNORE_ENTRIES = [
|
|
|
62
64
|
const LEGACY_PROJECT_GITIGNORE_ENTRIES = [".codex/"];
|
|
63
65
|
const SETUP_ONLY_INSTALLABLE_SKILLS = new Set(["wiki"]);
|
|
64
66
|
const DEFAULT_SETUP_MCP_MODE = "none";
|
|
67
|
+
const SKIP_NATIVE_AGENT_REFRESH_ENV = "OMX_SKIP_NATIVE_AGENT_REFRESH";
|
|
65
68
|
const HARD_DEPRECATED_SKILL_NAMES = new Set(["web-clone"]);
|
|
66
69
|
const TEAM_MODE_SKILL_NAMES = new Set(["team", "worker"]);
|
|
67
70
|
const TEAM_MODE_PROMPT_NAMES = new Set(["team-executor"]);
|
|
@@ -192,6 +195,68 @@ function getBackupContext(scope, projectRoot) {
|
|
|
192
195
|
baseRoot: homedir(),
|
|
193
196
|
};
|
|
194
197
|
}
|
|
198
|
+
function escapeTomlBasicString(value) {
|
|
199
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
200
|
+
}
|
|
201
|
+
function renderHooksJsonTrustStateToml(content) {
|
|
202
|
+
const trustState = extractCodexHooksJsonTrustState(content);
|
|
203
|
+
return Object.entries(trustState)
|
|
204
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
205
|
+
.flatMap(([key, state]) => [
|
|
206
|
+
`[hooks.state."${escapeTomlBasicString(key)}"]`,
|
|
207
|
+
`trusted_hash = "${escapeTomlBasicString(state.trusted_hash)}"`,
|
|
208
|
+
...(typeof state.enabled === "boolean" ? [`enabled = ${state.enabled}`] : []),
|
|
209
|
+
"",
|
|
210
|
+
])
|
|
211
|
+
.join("\n")
|
|
212
|
+
.trimEnd();
|
|
213
|
+
}
|
|
214
|
+
function existingHooksStateKeys(config) {
|
|
215
|
+
try {
|
|
216
|
+
const parsed = TOML.parse(config);
|
|
217
|
+
return new Set(Object.keys(parsed.hooks?.state ?? {}));
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return new Set();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function appendHooksJsonTrustStateToConfig(config, hooksContent) {
|
|
224
|
+
const existingKeys = existingHooksStateKeys(config);
|
|
225
|
+
const trustState = extractCodexHooksJsonTrustState(hooksContent);
|
|
226
|
+
const migratableContent = JSON.stringify({
|
|
227
|
+
state: Object.fromEntries(Object.entries(trustState).filter(([key]) => !existingKeys.has(key))),
|
|
228
|
+
});
|
|
229
|
+
const trustToml = renderHooksJsonTrustStateToml(migratableContent);
|
|
230
|
+
if (!trustToml)
|
|
231
|
+
return config;
|
|
232
|
+
const base = config.trimEnd();
|
|
233
|
+
return [
|
|
234
|
+
base,
|
|
235
|
+
base ? "" : null,
|
|
236
|
+
"# Migrated from legacy hooks.json state; kept in Codex config.toml because Codex 0.140 rejects top-level hooks.json state.",
|
|
237
|
+
trustToml,
|
|
238
|
+
"",
|
|
239
|
+
].filter((line) => line !== null).join("\n");
|
|
240
|
+
}
|
|
241
|
+
async function migrateLegacyHooksJsonTrustStateToConfig(configPath, hooksContent, backupContext, summary, options) {
|
|
242
|
+
const existingConfig = existsSync(configPath)
|
|
243
|
+
? await readFile(configPath, "utf-8")
|
|
244
|
+
: "";
|
|
245
|
+
const nextConfig = appendHooksJsonTrustStateToConfig(existingConfig, hooksContent);
|
|
246
|
+
if (nextConfig === existingConfig)
|
|
247
|
+
return;
|
|
248
|
+
if (await ensureBackup(configPath, existsSync(configPath), backupContext, options)) {
|
|
249
|
+
summary.backedUp += 1;
|
|
250
|
+
}
|
|
251
|
+
if (!options.dryRun) {
|
|
252
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
253
|
+
await writeFile(configPath, nextConfig);
|
|
254
|
+
}
|
|
255
|
+
summary.updated += 1;
|
|
256
|
+
if (options.verbose) {
|
|
257
|
+
console.log(` ${options.dryRun ? "would migrate" : "migrated"} legacy hooks.json trust state to ${configPath}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
195
260
|
async function ensureBackup(destinationPath, contentChanged, backupContext, options) {
|
|
196
261
|
if (!contentChanged || !existsSync(destinationPath))
|
|
197
262
|
return false;
|
|
@@ -517,41 +582,55 @@ async function promptForAgentsOverwrite(destinationPath) {
|
|
|
517
582
|
}
|
|
518
583
|
async function promptForPluginAgentsMdDefault(destinationPath) {
|
|
519
584
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
520
|
-
return
|
|
585
|
+
return !existsSync(destinationPath);
|
|
521
586
|
}
|
|
522
587
|
const rl = createInterface({
|
|
523
588
|
input: process.stdin,
|
|
524
589
|
output: process.stdout,
|
|
525
590
|
});
|
|
526
591
|
try {
|
|
527
|
-
const answer = (await rl.question(`Plugin mode: install OMX AGENTS.md defaults at "${destinationPath}"? [
|
|
592
|
+
const answer = (await rl.question(`Plugin mode: install/update OMX AGENTS.md defaults at "${destinationPath}"? [Y/n]: `))
|
|
528
593
|
.trim()
|
|
529
594
|
.toLowerCase();
|
|
530
|
-
return answer === "y" || answer === "yes";
|
|
595
|
+
return answer === "" || answer === "y" || answer === "yes";
|
|
531
596
|
}
|
|
532
597
|
finally {
|
|
533
598
|
rl.close();
|
|
534
599
|
}
|
|
535
600
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
601
|
+
const LEGACY_PLUGIN_DEVELOPER_INSTRUCTIONS = "You have oh-my-codex installed through Codex plugin mode. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing and $name workflow invocation. When spawning native subagents, set `agent_type` to an installed role and never omit it for OMX work. Registered Codex plugin marketplace surfaces supply OMX workflows and plugin-scoped companion resources when the plugin is installed; native agent roles are installed as setup-owned Codex agent TOML files in plugin mode so agent_type routing works. User-installed skills may still live under ~/.codex/skills. Use outcome-first, concise progress updates: state the target result, constraints, validation evidence, and stop condition before adding process detail.";
|
|
602
|
+
function normalizeDeveloperInstructionsText(value) {
|
|
603
|
+
return value.replace(/\r\n/g, "\n").trim();
|
|
604
|
+
}
|
|
605
|
+
function classifyPluginDeveloperInstructions(value) {
|
|
606
|
+
if (typeof value !== "string")
|
|
607
|
+
return "custom";
|
|
608
|
+
const normalized = normalizeDeveloperInstructionsText(value);
|
|
609
|
+
if (normalized ===
|
|
610
|
+
normalizeDeveloperInstructionsText(OMX_PLUGIN_DEVELOPER_INSTRUCTIONS)) {
|
|
611
|
+
return "current";
|
|
539
612
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
613
|
+
if (normalized ===
|
|
614
|
+
normalizeDeveloperInstructionsText(LEGACY_PLUGIN_DEVELOPER_INSTRUCTIONS)) {
|
|
615
|
+
return "current";
|
|
616
|
+
}
|
|
617
|
+
if (normalized === normalizeDeveloperInstructionsText(OMX_DEVELOPER_INSTRUCTIONS)) {
|
|
618
|
+
return "historical";
|
|
619
|
+
}
|
|
620
|
+
return "custom";
|
|
621
|
+
}
|
|
622
|
+
function readRootDeveloperInstructions(config) {
|
|
623
|
+
if (!rootHasTomlKey(config, "developer_instructions"))
|
|
624
|
+
return undefined;
|
|
544
625
|
try {
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
.toLowerCase();
|
|
548
|
-
return answer === "y" || answer === "yes";
|
|
626
|
+
const parsed = TOML.parse(config);
|
|
627
|
+
return parsed.developer_instructions;
|
|
549
628
|
}
|
|
550
|
-
|
|
551
|
-
|
|
629
|
+
catch {
|
|
630
|
+
return Symbol.for("omx.invalid-developer-instructions");
|
|
552
631
|
}
|
|
553
632
|
}
|
|
554
|
-
async function
|
|
633
|
+
async function askYesNoDefaultYes(question) {
|
|
555
634
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
556
635
|
return false;
|
|
557
636
|
}
|
|
@@ -560,15 +639,99 @@ async function promptForPluginDeveloperInstructionsOverwrite(configPath) {
|
|
|
560
639
|
output: process.stdout,
|
|
561
640
|
});
|
|
562
641
|
try {
|
|
563
|
-
const answer = (await rl.question(
|
|
564
|
-
|
|
565
|
-
.toLowerCase();
|
|
566
|
-
return answer === "y" || answer === "yes";
|
|
642
|
+
const answer = (await rl.question(question)).trim().toLowerCase();
|
|
643
|
+
return answer === "" || answer === "y" || answer === "yes";
|
|
567
644
|
}
|
|
568
645
|
finally {
|
|
569
646
|
rl.close();
|
|
570
647
|
}
|
|
571
648
|
}
|
|
649
|
+
function legacyPluginDeveloperInstructionsDecision(choice, state = "missing") {
|
|
650
|
+
if (choice === "refresh" || (choice === true && state === "historical")) {
|
|
651
|
+
return {
|
|
652
|
+
action: "update",
|
|
653
|
+
state: "historical",
|
|
654
|
+
reason: choice === "refresh"
|
|
655
|
+
? "legacy explicit refresh policy"
|
|
656
|
+
: "legacy boolean approval refreshed historical developer_instructions",
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
if (choice === true || choice === "preserve-or-add") {
|
|
660
|
+
return {
|
|
661
|
+
action: "add",
|
|
662
|
+
state: "missing",
|
|
663
|
+
reason: "legacy explicit add-if-missing policy",
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
return {
|
|
667
|
+
action: "preserve",
|
|
668
|
+
state,
|
|
669
|
+
reason: "legacy explicit skip policy",
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
async function resolvePluginDeveloperInstructionsDecision(configPath, options) {
|
|
673
|
+
const existing = existsSync(configPath)
|
|
674
|
+
? await readFile(configPath, "utf-8")
|
|
675
|
+
: "";
|
|
676
|
+
const value = readRootDeveloperInstructions(existing);
|
|
677
|
+
if (value === undefined) {
|
|
678
|
+
if (options.pluginDeveloperInstructionsPrompt) {
|
|
679
|
+
return legacyPluginDeveloperInstructionsDecision(await options.pluginDeveloperInstructionsPrompt(configPath), "missing");
|
|
680
|
+
}
|
|
681
|
+
const install = await askYesNoDefaultYes(`Plugin mode: add OMX developer_instructions bootstrap to "${configPath}"? [Y/n]: `);
|
|
682
|
+
return install
|
|
683
|
+
? {
|
|
684
|
+
action: "add",
|
|
685
|
+
state: "missing",
|
|
686
|
+
reason: "missing developer_instructions",
|
|
687
|
+
}
|
|
688
|
+
: {
|
|
689
|
+
action: "preserve",
|
|
690
|
+
state: "missing",
|
|
691
|
+
reason: "missing developer_instructions skipped",
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
const state = classifyPluginDeveloperInstructions(value);
|
|
695
|
+
if (state === "current") {
|
|
696
|
+
return {
|
|
697
|
+
action: "preserve",
|
|
698
|
+
state,
|
|
699
|
+
reason: "current OMX developer_instructions already installed",
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
if (state === "historical") {
|
|
703
|
+
const updateDecision = options.pluginDeveloperInstructionsPrompt
|
|
704
|
+
? legacyPluginDeveloperInstructionsDecision(await options.pluginDeveloperInstructionsPrompt(configPath), state)
|
|
705
|
+
: await askYesNoDefaultYes(`Plugin mode: update OMX developer_instructions bootstrap at "${configPath}"? [Y/n]: `)
|
|
706
|
+
? {
|
|
707
|
+
action: "update",
|
|
708
|
+
state,
|
|
709
|
+
reason: "recognized historical OMX developer_instructions",
|
|
710
|
+
}
|
|
711
|
+
: {
|
|
712
|
+
action: "preserve",
|
|
713
|
+
state,
|
|
714
|
+
reason: "historical OMX developer_instructions preserved",
|
|
715
|
+
};
|
|
716
|
+
const update = updateDecision.action === "update";
|
|
717
|
+
return update
|
|
718
|
+
? {
|
|
719
|
+
action: "update",
|
|
720
|
+
state,
|
|
721
|
+
reason: "recognized historical OMX developer_instructions",
|
|
722
|
+
}
|
|
723
|
+
: {
|
|
724
|
+
action: "preserve",
|
|
725
|
+
state,
|
|
726
|
+
reason: "historical OMX developer_instructions preserved",
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
return {
|
|
730
|
+
action: "preserve",
|
|
731
|
+
state: "custom",
|
|
732
|
+
reason: "custom or unknown developer_instructions preserved",
|
|
733
|
+
};
|
|
734
|
+
}
|
|
572
735
|
async function resolveSetupScope(projectRoot, requestedScope, persistedReviewDecision = "keep", persistedPreferences, setupScopePrompt) {
|
|
573
736
|
if (requestedScope) {
|
|
574
737
|
return { scope: requestedScope, source: "cli" };
|
|
@@ -898,7 +1061,15 @@ async function cleanupPluginModeLegacyPrompts(srcDir, dstDir, backupContext, opt
|
|
|
898
1061
|
await removeEmptyDirectoryIfPresent(dstDir, options);
|
|
899
1062
|
return summary;
|
|
900
1063
|
}
|
|
901
|
-
function
|
|
1064
|
+
function removeRootTomlKey(config, key) {
|
|
1065
|
+
const range = findRootTomlKeyRange(config, key);
|
|
1066
|
+
if (!range)
|
|
1067
|
+
return config;
|
|
1068
|
+
const before = config.slice(0, range.start);
|
|
1069
|
+
const after = config.slice(range.end).replace(/^\r?\n?/, "\n");
|
|
1070
|
+
return `${before}${after}`;
|
|
1071
|
+
}
|
|
1072
|
+
function stripPluginModeLegacyRootDefaults(config, developerInstructionsDecision) {
|
|
902
1073
|
const lines = config.split(/\r?\n/);
|
|
903
1074
|
const firstTableIndex = lines.findIndex((line) => /^\s*\[/.test(line));
|
|
904
1075
|
const boundary = firstTableIndex >= 0 ? firstTableIndex : lines.length;
|
|
@@ -918,14 +1089,15 @@ function stripPluginModeLegacyRootDefaults(config) {
|
|
|
918
1089
|
/^\s*model_reasoning_effort\s*=\s*"medium"\s*$/.test(line)) {
|
|
919
1090
|
continue;
|
|
920
1091
|
}
|
|
921
|
-
if (index < boundary &&
|
|
922
|
-
/^\s*developer_instructions\s*=/.test(line) &&
|
|
923
|
-
line.includes("You have oh-my-codex installed.")) {
|
|
924
|
-
continue;
|
|
925
|
-
}
|
|
926
1092
|
result.push(line);
|
|
927
1093
|
}
|
|
928
|
-
|
|
1094
|
+
let nextConfig = result.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
1095
|
+
if (developerInstructionsDecision.action === "update" &&
|
|
1096
|
+
developerInstructionsDecision.state === "historical" &&
|
|
1097
|
+
classifyPluginDeveloperInstructions(readRootDeveloperInstructions(nextConfig)) === "historical") {
|
|
1098
|
+
nextConfig = removeRootTomlKey(nextConfig, "developer_instructions");
|
|
1099
|
+
}
|
|
1100
|
+
return nextConfig;
|
|
929
1101
|
}
|
|
930
1102
|
function rootHasTomlKey(config, key) {
|
|
931
1103
|
const lines = config.split(/\r?\n/);
|
|
@@ -936,18 +1108,62 @@ function rootHasTomlKey(config, key) {
|
|
|
936
1108
|
return lines.slice(0, boundary).some((line) => pattern.test(line));
|
|
937
1109
|
}
|
|
938
1110
|
function replaceRootTomlKey(config, key, line) {
|
|
939
|
-
const
|
|
940
|
-
|
|
941
|
-
|
|
1111
|
+
const range = findRootTomlKeyRange(config, key);
|
|
1112
|
+
if (!range)
|
|
1113
|
+
return insertRootTomlKey(config, line);
|
|
1114
|
+
const before = config.slice(0, range.start);
|
|
1115
|
+
const after = config.slice(range.end).replace(/^\r?\n?/, "\n");
|
|
1116
|
+
return `${before}${line}${after}`.replace(/\n?$/, "\n");
|
|
1117
|
+
}
|
|
1118
|
+
function findRootTomlKeyRange(config, key) {
|
|
942
1119
|
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1120
|
+
const keyPattern = new RegExp(`^\\s*${escapedKey}\\s*=`);
|
|
1121
|
+
const nextRootKeyPattern = /^\s*[A-Za-z0-9_-]+\s*=/;
|
|
1122
|
+
const tablePattern = /^\s*\[/;
|
|
1123
|
+
const linePattern = /.*(?:\r?\n|$)/g;
|
|
1124
|
+
let match;
|
|
1125
|
+
let found = null;
|
|
1126
|
+
let inMultiline = false;
|
|
1127
|
+
let multilineDelimiter = null;
|
|
1128
|
+
while ((match = linePattern.exec(config)) && match[0] !== "") {
|
|
1129
|
+
const line = match[0];
|
|
1130
|
+
const lineStart = match.index;
|
|
1131
|
+
const lineEnd = lineStart + line.length;
|
|
1132
|
+
const trimmedLine = line.replace(/\r?\n$/, "");
|
|
1133
|
+
if (!found) {
|
|
1134
|
+
if (tablePattern.test(trimmedLine))
|
|
1135
|
+
return null;
|
|
1136
|
+
if (keyPattern.test(trimmedLine)) {
|
|
1137
|
+
found = { start: lineStart, end: lineEnd };
|
|
1138
|
+
const valuePart = trimmedLine.slice(trimmedLine.indexOf("=") + 1);
|
|
1139
|
+
const delimiter = valuePart.includes('"""')
|
|
1140
|
+
? '"""'
|
|
1141
|
+
: valuePart.includes("'''")
|
|
1142
|
+
? "'''"
|
|
1143
|
+
: null;
|
|
1144
|
+
if (delimiter && valuePart.split(delimiter).length - 1 === 1) {
|
|
1145
|
+
inMultiline = true;
|
|
1146
|
+
multilineDelimiter = delimiter;
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
return found;
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
found.end = lineEnd;
|
|
1155
|
+
if (inMultiline && multilineDelimiter) {
|
|
1156
|
+
if (trimmedLine.includes(multilineDelimiter)) {
|
|
1157
|
+
return found;
|
|
1158
|
+
}
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
if (tablePattern.test(trimmedLine) || nextRootKeyPattern.test(trimmedLine)) {
|
|
1162
|
+
found.end = lineStart;
|
|
1163
|
+
return found;
|
|
948
1164
|
}
|
|
949
1165
|
}
|
|
950
|
-
return
|
|
1166
|
+
return found;
|
|
951
1167
|
}
|
|
952
1168
|
function insertRootTomlKey(config, line) {
|
|
953
1169
|
const lines = config.trimEnd().split(/\r?\n/);
|
|
@@ -1041,6 +1257,7 @@ async function applyPluginModeHooksConfig(configPath, hooksPath, pkgRoot, codexH
|
|
|
1041
1257
|
const existingHooksContent = existsSync(hooksPath)
|
|
1042
1258
|
? await readFile(hooksPath, "utf-8")
|
|
1043
1259
|
: null;
|
|
1260
|
+
await migrateLegacyHooksJsonTrustStateToConfig(configPath, existingHooksContent, backupContext, summary, options);
|
|
1044
1261
|
if (options.pluginScopedHooks) {
|
|
1045
1262
|
await cleanupPluginModeManagedHooksJson(existingHooksContent, hooksPath, backupContext, summary, options);
|
|
1046
1263
|
}
|
|
@@ -1060,19 +1277,21 @@ async function applyPluginDeveloperInstructionsDefault(configPath, backupContext
|
|
|
1060
1277
|
const existing = existsSync(configPath)
|
|
1061
1278
|
? await readFile(configPath, "utf-8")
|
|
1062
1279
|
: "";
|
|
1280
|
+
if (options.decision.action === "preserve") {
|
|
1281
|
+
summary.skipped += 1;
|
|
1282
|
+
if (options.verbose) {
|
|
1283
|
+
console.log(` preserved plugin developer_instructions default: ${options.decision.reason}`);
|
|
1284
|
+
}
|
|
1285
|
+
return options.decision.state === "missing" ? "skipped" : "exists";
|
|
1286
|
+
}
|
|
1063
1287
|
const line = `developer_instructions = ${JSON.stringify(OMX_PLUGIN_DEVELOPER_INSTRUCTIONS)}`;
|
|
1064
1288
|
const hasExistingDeveloperInstructions = rootHasTomlKey(existing, "developer_instructions");
|
|
1065
|
-
if (hasExistingDeveloperInstructions) {
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
:
|
|
1069
|
-
if (!overwrite) {
|
|
1070
|
-
summary.skipped += 1;
|
|
1071
|
-
if (options.verbose) {
|
|
1072
|
-
console.log(" skipped plugin developer_instructions default: root developer_instructions already exists");
|
|
1073
|
-
}
|
|
1074
|
-
return "exists";
|
|
1289
|
+
if (hasExistingDeveloperInstructions && options.decision.action === "add") {
|
|
1290
|
+
summary.skipped += 1;
|
|
1291
|
+
if (options.verbose) {
|
|
1292
|
+
console.log(" skipped plugin developer_instructions default: root developer_instructions already exists");
|
|
1075
1293
|
}
|
|
1294
|
+
return "exists";
|
|
1076
1295
|
}
|
|
1077
1296
|
const nextConfig = hasExistingDeveloperInstructions
|
|
1078
1297
|
? replaceRootTomlKey(existing, "developer_instructions", line)
|
|
@@ -1102,7 +1321,7 @@ async function cleanupPluginModeLegacyConfig(configPath, backupContext, options)
|
|
|
1102
1321
|
config = stripFirstPartyOmxMcpSections(config);
|
|
1103
1322
|
config = stripExistingOmxBlocks(config).cleaned;
|
|
1104
1323
|
config = stripExistingSharedMcpRegistryBlock(config).cleaned;
|
|
1105
|
-
config = stripPluginModeLegacyRootDefaults(config);
|
|
1324
|
+
config = stripPluginModeLegacyRootDefaults(config, options.developerInstructionsDecision);
|
|
1106
1325
|
config = stripOmxSeededBehavioralDefaults(config);
|
|
1107
1326
|
config = stripOmxFeatureFlags(config);
|
|
1108
1327
|
config = stripManagedCodexHookTrustState(config);
|
|
@@ -1130,32 +1349,8 @@ async function cleanupPluginModeLegacyConfig(configPath, backupContext, options)
|
|
|
1130
1349
|
}
|
|
1131
1350
|
return true;
|
|
1132
1351
|
}
|
|
1133
|
-
async function cleanupPluginModeLegacyAgentsMd(agentsMdPath, backupContext, options) {
|
|
1134
|
-
if (!existsSync(agentsMdPath))
|
|
1135
|
-
return false;
|
|
1136
|
-
const fileInfo = await lstat(agentsMdPath);
|
|
1137
|
-
if (fileInfo.isSymbolicLink()) {
|
|
1138
|
-
if (options.verbose) {
|
|
1139
|
-
console.log(` preserved symlinked AGENTS.md at ${agentsMdPath}; plugin mode only removes direct legacy OMX-generated files`);
|
|
1140
|
-
}
|
|
1141
|
-
return false;
|
|
1142
|
-
}
|
|
1143
|
-
const content = await readFile(agentsMdPath, "utf-8");
|
|
1144
|
-
if (!isOmxGeneratedAgentsMd(content))
|
|
1145
|
-
return false;
|
|
1146
|
-
if (await ensureBackup(agentsMdPath, true, backupContext, options)) {
|
|
1147
|
-
// backup created for pre-existing AGENTS.md
|
|
1148
|
-
}
|
|
1149
|
-
if (!options.dryRun) {
|
|
1150
|
-
await rm(agentsMdPath, { force: true });
|
|
1151
|
-
}
|
|
1152
|
-
if (options.verbose) {
|
|
1153
|
-
console.log(` ${options.dryRun ? "would remove" : "removed"} legacy OMX-generated AGENTS.md`);
|
|
1154
|
-
}
|
|
1155
|
-
return true;
|
|
1156
|
-
}
|
|
1157
1352
|
export async function setup(options = {}) {
|
|
1158
|
-
const { force = false, dryRun = false, installMode: requestedInstallMode, mcpMode: requestedMcpMode, teamMode: requestedTeamMode, scope: requestedScope, verbose = false, setupScopePrompt, persistedSetupReviewPrompt, installModePrompt, modelUpgradePrompt, pluginAgentsMdPrompt, pluginDeveloperInstructionsPrompt,
|
|
1353
|
+
const { force = false, dryRun = false, installMode: requestedInstallMode, mcpMode: requestedMcpMode, teamMode: requestedTeamMode, scope: requestedScope, verbose = false, skipNativeAgentRefresh: requestedSkipNativeAgentRefresh = false, setupScopePrompt, persistedSetupReviewPrompt, installModePrompt, modelUpgradePrompt, pluginAgentsMdPrompt, pluginDeveloperInstructionsPrompt, firstPartyMcpRemovalPrompt, } = options;
|
|
1159
1354
|
const pkgRoot = getPackageRoot();
|
|
1160
1355
|
const projectRoot = process.cwd();
|
|
1161
1356
|
const persistedPreferences = await readPersistedSetupPreferences(projectRoot, { warnOnLegacyScope: true });
|
|
@@ -1194,6 +1389,8 @@ export async function setup(options = {}) {
|
|
|
1194
1389
|
: undefined)
|
|
1195
1390
|
?? "enabled";
|
|
1196
1391
|
const isTeamModeEnabled = teamModeEnabled(resolvedTeamMode);
|
|
1392
|
+
const skipNativeAgentRefresh = requestedSkipNativeAgentRefresh ||
|
|
1393
|
+
process.env[SKIP_NATIVE_AGENT_REFRESH_ENV] === "1";
|
|
1197
1394
|
const scopeDirs = resolveScopeDirectories(resolvedScope.scope, projectRoot);
|
|
1198
1395
|
const existingConfigForMcpMigration = existsSync(scopeDirs.codexConfigFile)
|
|
1199
1396
|
? await readFile(scopeDirs.codexConfigFile, "utf-8")
|
|
@@ -1224,15 +1421,32 @@ export async function setup(options = {}) {
|
|
|
1224
1421
|
const pluginAgentsMdDst = resolvedScope.scope === "project"
|
|
1225
1422
|
? join(projectRoot, "AGENTS.md")
|
|
1226
1423
|
: join(scopeDirs.codexHomeDir, "AGENTS.md");
|
|
1227
|
-
const
|
|
1228
|
-
? pluginDeveloperInstructionsPrompt
|
|
1229
|
-
|
|
1230
|
-
:
|
|
1231
|
-
|
|
1424
|
+
const pluginDeveloperInstructionsDecision = isPluginInstallMode
|
|
1425
|
+
? await resolvePluginDeveloperInstructionsDecision(scopeDirs.codexConfigFile, { pluginDeveloperInstructionsPrompt })
|
|
1426
|
+
: {
|
|
1427
|
+
action: "preserve",
|
|
1428
|
+
state: "custom",
|
|
1429
|
+
reason: "non-plugin setup mode",
|
|
1430
|
+
};
|
|
1431
|
+
let pluginAgentsMdPathExists = false;
|
|
1432
|
+
let pluginAgentsMdIsSymlink = false;
|
|
1433
|
+
try {
|
|
1434
|
+
const pluginAgentsMdStat = await lstat(pluginAgentsMdDst);
|
|
1435
|
+
pluginAgentsMdPathExists = true;
|
|
1436
|
+
pluginAgentsMdIsSymlink = pluginAgentsMdStat.isSymbolicLink();
|
|
1437
|
+
}
|
|
1438
|
+
catch {
|
|
1439
|
+
pluginAgentsMdPathExists = false;
|
|
1440
|
+
pluginAgentsMdIsSymlink = false;
|
|
1441
|
+
}
|
|
1232
1442
|
const usePluginAgentsMdDefault = isPluginInstallMode
|
|
1233
|
-
?
|
|
1234
|
-
?
|
|
1235
|
-
:
|
|
1443
|
+
? options.mergeAgents || pluginAgentsMdIsSymlink
|
|
1444
|
+
? false
|
|
1445
|
+
: force
|
|
1446
|
+
? true
|
|
1447
|
+
: pluginAgentsMdPrompt
|
|
1448
|
+
? await pluginAgentsMdPrompt(pluginAgentsMdDst)
|
|
1449
|
+
: await promptForPluginAgentsMdDefault(pluginAgentsMdDst)
|
|
1236
1450
|
: false;
|
|
1237
1451
|
console.log("oh-my-codex setup");
|
|
1238
1452
|
console.log("=================\n");
|
|
@@ -1382,7 +1596,11 @@ export async function setup(options = {}) {
|
|
|
1382
1596
|
}
|
|
1383
1597
|
// Step 4: Install native agent configs
|
|
1384
1598
|
console.log("[4/8] Installing native agent configs...");
|
|
1385
|
-
if (
|
|
1599
|
+
if (skipNativeAgentRefresh) {
|
|
1600
|
+
summary.nativeAgents = createEmptyCategorySummary();
|
|
1601
|
+
console.log(" Native agent refresh skipped for background update-check setup refresh.\n");
|
|
1602
|
+
}
|
|
1603
|
+
else if (isPluginInstallMode) {
|
|
1386
1604
|
summary.nativeAgents = await refreshNativeAgentConfigs(pkgRoot, scopeDirs.nativeAgentsDir, backupContext, {
|
|
1387
1605
|
force,
|
|
1388
1606
|
dryRun,
|
|
@@ -1443,6 +1661,7 @@ export async function setup(options = {}) {
|
|
|
1443
1661
|
verbose,
|
|
1444
1662
|
preserveFirstPartyMcp: shouldOfferFirstPartyMcpRemoval &&
|
|
1445
1663
|
!removeFirstPartyMcpRegistrations,
|
|
1664
|
+
developerInstructionsDecision: pluginDeveloperInstructionsDecision,
|
|
1446
1665
|
});
|
|
1447
1666
|
if (configCleaned)
|
|
1448
1667
|
summary.config.removed += 1;
|
|
@@ -1495,11 +1714,11 @@ export async function setup(options = {}) {
|
|
|
1495
1714
|
console.log(pluginScopedHooksSupported
|
|
1496
1715
|
? " Plugin-scoped Codex hooks and runtime feature flags refresh complete (plugin_hooks, goals).\n"
|
|
1497
1716
|
: ` Native Codex hooks fallback and runtime feature flags refresh complete (${scopeDirs.codexHooksFile}; hooks, goals).\n`);
|
|
1498
|
-
if (
|
|
1717
|
+
if (pluginDeveloperInstructionsDecision.action !== "preserve") {
|
|
1499
1718
|
const developerInstructionsResult = await applyPluginDeveloperInstructionsDefault(scopeDirs.codexConfigFile, backupContext, summary.config, {
|
|
1500
1719
|
dryRun,
|
|
1501
1720
|
verbose,
|
|
1502
|
-
|
|
1721
|
+
decision: pluginDeveloperInstructionsDecision,
|
|
1503
1722
|
});
|
|
1504
1723
|
if (developerInstructionsResult === "updated") {
|
|
1505
1724
|
resolvedConfig = existsSync(scopeDirs.codexConfigFile)
|
|
@@ -1512,7 +1731,7 @@ export async function setup(options = {}) {
|
|
|
1512
1731
|
}
|
|
1513
1732
|
}
|
|
1514
1733
|
else {
|
|
1515
|
-
console.log(
|
|
1734
|
+
console.log(` Plugin-mode developer_instructions default preserved (${pluginDeveloperInstructionsDecision.reason}).\n`);
|
|
1516
1735
|
}
|
|
1517
1736
|
}
|
|
1518
1737
|
else {
|
|
@@ -1537,6 +1756,7 @@ export async function setup(options = {}) {
|
|
|
1537
1756
|
const existingHooksContent = existsSync(scopeDirs.codexHooksFile)
|
|
1538
1757
|
? await readFile(scopeDirs.codexHooksFile, "utf-8")
|
|
1539
1758
|
: null;
|
|
1759
|
+
await migrateLegacyHooksJsonTrustStateToConfig(scopeDirs.codexConfigFile, existingHooksContent, backupContext, summary.config, { dryRun, verbose });
|
|
1540
1760
|
const hooksConfig = mergeManagedCodexHooksConfig(existingHooksContent, pkgRoot, scopeDirs.codexHooksFile, { platform: process.platform, codexHomeDir: scopeDirs.codexHomeDir });
|
|
1541
1761
|
await syncManagedContent(hooksConfig, scopeDirs.codexHooksFile, summary.config, backupContext, { dryRun, verbose }, `native hooks ${scopeDirs.codexHooksFile}`);
|
|
1542
1762
|
await syncManagedWindowsNativeHookShim(scopeDirs.codexHomeDir, pkgRoot, summary.config, backupContext, { dryRun, verbose });
|
|
@@ -1560,51 +1780,99 @@ export async function setup(options = {}) {
|
|
|
1560
1780
|
console.log();
|
|
1561
1781
|
// Step 6: Generate AGENTS.md
|
|
1562
1782
|
console.log("[6/8] Generating AGENTS.md...");
|
|
1783
|
+
const activeSession = resolvedScope.scope === "project"
|
|
1784
|
+
? await readSessionState(projectRoot)
|
|
1785
|
+
: null;
|
|
1786
|
+
const sessionIsActive = activeSession && !isSessionStale(activeSession);
|
|
1563
1787
|
if (isPluginInstallMode) {
|
|
1564
|
-
const
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
const rewritten = upsertAgentsModelTable(addGeneratedAgentsMarker(applyTeamModeToAgentsTemplate(applyPluginModeWordingToAgentsTemplate(content, resolvedScope.scope), resolvedTeamMode)), modelTableContext, modelTableDefinitions);
|
|
1578
|
-
const result = await syncManagedAgentsContent(rewritten, pluginAgentsMdDst, summary.agentsMd, backupContext, {
|
|
1579
|
-
agentsOverwritePrompt: options.agentsOverwritePrompt,
|
|
1580
|
-
dryRun,
|
|
1581
|
-
force,
|
|
1582
|
-
verbose,
|
|
1583
|
-
});
|
|
1584
|
-
if (result === "updated") {
|
|
1585
|
-
console.log(resolvedScope.scope === "project"
|
|
1586
|
-
? " Generated plugin-mode AGENTS.md defaults in project root."
|
|
1587
|
-
: ` Generated plugin-mode AGENTS.md defaults in ${scopeDirs.codexHomeDir}.`);
|
|
1788
|
+
const agentsMdSrc = join(pkgRoot, "templates", "AGENTS.md");
|
|
1789
|
+
const pluginAgentsMdExists = pluginAgentsMdPathExists;
|
|
1790
|
+
if (existsSync(agentsMdSrc)) {
|
|
1791
|
+
const content = await readFile(agentsMdSrc, "utf-8");
|
|
1792
|
+
const modelTableContext = resolveAgentsModelTableContext(resolvedConfig, {
|
|
1793
|
+
codexHomeOverride: scopeDirs.codexHomeDir,
|
|
1794
|
+
});
|
|
1795
|
+
const modelTableDefinitions = getAgentsModelTableDefinitionsForTeamMode(resolvedTeamMode);
|
|
1796
|
+
const rewritten = upsertAgentsModelTable(addGeneratedAgentsMarker(applyTeamModeToAgentsTemplate(applyPluginModeWordingToAgentsTemplate(content, resolvedScope.scope), resolvedTeamMode)), modelTableContext, modelTableDefinitions);
|
|
1797
|
+
if (options.mergeAgents && pluginAgentsMdExists) {
|
|
1798
|
+
if (pluginAgentsMdIsSymlink) {
|
|
1799
|
+
summary.agentsMd.skipped += 1;
|
|
1800
|
+
console.log(` Skipped plugin-mode AGENTS.md merge for symlinked ${pluginAgentsMdDst}; existing AGENTS.md left untouched.`);
|
|
1588
1801
|
}
|
|
1589
|
-
else
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1802
|
+
else {
|
|
1803
|
+
const existing = await readFile(pluginAgentsMdDst, "utf-8");
|
|
1804
|
+
const mergedAgentsContent = upsertManagedAgentsBlock(existing, rewritten);
|
|
1805
|
+
const canApplyManagedAgentsMerge = mergedAgentsContent !== existing;
|
|
1806
|
+
if (resolvedScope.scope === "project" &&
|
|
1807
|
+
sessionIsActive &&
|
|
1808
|
+
canApplyManagedAgentsMerge) {
|
|
1809
|
+
summary.agentsMd.skipped += 1;
|
|
1810
|
+
console.log(" WARNING: Active omx session detected (pid " +
|
|
1811
|
+
activeSession?.pid +
|
|
1812
|
+
").");
|
|
1813
|
+
console.log(" Skipping AGENTS.md overwrite to avoid corrupting runtime overlay.");
|
|
1814
|
+
console.log(" Stop the active session first, then re-run setup.");
|
|
1815
|
+
}
|
|
1816
|
+
else if (!canApplyManagedAgentsMerge) {
|
|
1817
|
+
summary.agentsMd.unchanged += 1;
|
|
1818
|
+
console.log(resolvedScope.scope === "project"
|
|
1819
|
+
? " Plugin-mode AGENTS.md already up to date in project root."
|
|
1820
|
+
: ` Plugin-mode AGENTS.md already up to date in ${scopeDirs.codexHomeDir}.`);
|
|
1821
|
+
}
|
|
1822
|
+
else {
|
|
1823
|
+
await syncManagedContent(mergedAgentsContent, pluginAgentsMdDst, summary.agentsMd, backupContext, { dryRun, verbose }, `plugin AGENTS merge ${pluginAgentsMdDst}`);
|
|
1824
|
+
console.log(resolvedScope.scope === "project"
|
|
1825
|
+
? " Merged plugin-mode OMX-managed AGENTS.md sections into project root."
|
|
1826
|
+
: ` Merged plugin-mode OMX-managed AGENTS.md sections into ${scopeDirs.codexHomeDir}.`);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
else if (usePluginAgentsMdDefault) {
|
|
1831
|
+
const defaultWouldChange = pluginAgentsMdExists
|
|
1832
|
+
? (await readFile(pluginAgentsMdDst, "utf-8")) !== rewritten
|
|
1833
|
+
: true;
|
|
1834
|
+
if (resolvedScope.scope === "project" &&
|
|
1835
|
+
sessionIsActive &&
|
|
1836
|
+
defaultWouldChange) {
|
|
1837
|
+
summary.agentsMd.skipped += 1;
|
|
1838
|
+
console.log(" WARNING: Active omx session detected (pid " +
|
|
1839
|
+
activeSession?.pid +
|
|
1840
|
+
").");
|
|
1841
|
+
console.log(" Skipping AGENTS.md overwrite to avoid corrupting runtime overlay.");
|
|
1842
|
+
console.log(" Stop the active session first, then re-run setup.");
|
|
1593
1843
|
}
|
|
1594
1844
|
else {
|
|
1595
|
-
|
|
1845
|
+
const result = await syncManagedAgentsContent(rewritten, pluginAgentsMdDst, summary.agentsMd, backupContext, {
|
|
1846
|
+
agentsOverwritePrompt: options.agentsOverwritePrompt,
|
|
1847
|
+
dryRun,
|
|
1848
|
+
force,
|
|
1849
|
+
verbose,
|
|
1850
|
+
});
|
|
1851
|
+
if (result === "updated") {
|
|
1852
|
+
console.log(resolvedScope.scope === "project"
|
|
1853
|
+
? " Generated plugin-mode AGENTS.md defaults in project root."
|
|
1854
|
+
: ` Generated plugin-mode AGENTS.md defaults in ${scopeDirs.codexHomeDir}.`);
|
|
1855
|
+
}
|
|
1856
|
+
else if (result === "unchanged") {
|
|
1857
|
+
console.log(resolvedScope.scope === "project"
|
|
1858
|
+
? " Plugin-mode AGENTS.md defaults already up to date in project root."
|
|
1859
|
+
: ` Plugin-mode AGENTS.md defaults already up to date in ${scopeDirs.codexHomeDir}.`);
|
|
1860
|
+
}
|
|
1861
|
+
else {
|
|
1862
|
+
console.log(` Skipped plugin-mode AGENTS.md defaults for ${pluginAgentsMdDst}.`);
|
|
1863
|
+
}
|
|
1596
1864
|
}
|
|
1597
1865
|
}
|
|
1598
1866
|
else {
|
|
1599
1867
|
summary.agentsMd.skipped += 1;
|
|
1600
|
-
console.log(
|
|
1868
|
+
console.log(pluginAgentsMdExists
|
|
1869
|
+
? " Plugin-mode AGENTS.md defaults not selected; existing AGENTS.md left untouched.\n"
|
|
1870
|
+
: " Plugin-mode AGENTS.md defaults not selected; no AGENTS.md was generated.\n");
|
|
1601
1871
|
}
|
|
1602
1872
|
}
|
|
1603
1873
|
else {
|
|
1604
1874
|
summary.agentsMd.skipped += 1;
|
|
1605
|
-
console.log(
|
|
1606
|
-
? " Plugin-mode AGENTS.md defaults not selected.\n"
|
|
1607
|
-
: " AGENTS.md generation skipped; no legacy OMX-generated AGENTS.md found and defaults not selected.\n");
|
|
1875
|
+
console.log(" AGENTS.md template not found, skipping.");
|
|
1608
1876
|
}
|
|
1609
1877
|
}
|
|
1610
1878
|
else {
|
|
@@ -1614,10 +1882,6 @@ export async function setup(options = {}) {
|
|
|
1614
1882
|
: join(scopeDirs.codexHomeDir, "AGENTS.md");
|
|
1615
1883
|
const agentsMdExists = existsSync(agentsMdDst);
|
|
1616
1884
|
// Guard: refuse to overwrite project-root AGENTS.md during active session
|
|
1617
|
-
const activeSession = resolvedScope.scope === "project"
|
|
1618
|
-
? await readSessionState(projectRoot)
|
|
1619
|
-
: null;
|
|
1620
|
-
const sessionIsActive = activeSession && !isSessionStale(activeSession);
|
|
1621
1885
|
if (existsSync(agentsMdSrc)) {
|
|
1622
1886
|
const content = await readFile(agentsMdSrc, "utf-8");
|
|
1623
1887
|
const modelTableContext = resolveAgentsModelTableContext(resolvedConfig, {
|
|
@@ -1772,7 +2036,7 @@ export async function setup(options = {}) {
|
|
|
1772
2036
|
if (isPluginInstallMode) {
|
|
1773
2037
|
console.log(` 2. Registered Codex marketplace ${OMX_LOCAL_MARKETPLACE_NAME} supplies OMX skills and workflow surfaces`);
|
|
1774
2038
|
console.log(" 3. Browse plugin-provided skills with /skills");
|
|
1775
|
-
console.log(" 4.
|
|
2039
|
+
console.log(" 4. Plugin-mode AGENTS.md defaults provide persistent orchestration guidance; developer_instructions is an optional bootstrap");
|
|
1776
2040
|
console.log(" 5. Native agent role TOML files written to .codex/agents/ for agent_type routing");
|
|
1777
2041
|
}
|
|
1778
2042
|
else {
|
|
@@ -1875,6 +2139,93 @@ async function syncManagedContent(content, dstPath, summary, backupContext, opti
|
|
|
1875
2139
|
console.log(` ${options.dryRun ? "would update" : "updated"} ${verboseLabel}`);
|
|
1876
2140
|
}
|
|
1877
2141
|
}
|
|
2142
|
+
function hashContent(content) {
|
|
2143
|
+
return createHash("sha256").update(content).digest("hex");
|
|
2144
|
+
}
|
|
2145
|
+
function nativeAgentInstallManifestPath(agentsDir) {
|
|
2146
|
+
return join(agentsDir, "..", ".omx", "native-agents.json");
|
|
2147
|
+
}
|
|
2148
|
+
async function readNativeAgentInstallManifest(agentsDir) {
|
|
2149
|
+
const manifestPath = nativeAgentInstallManifestPath(agentsDir);
|
|
2150
|
+
if (!existsSync(manifestPath))
|
|
2151
|
+
return { version: 1, files: {} };
|
|
2152
|
+
try {
|
|
2153
|
+
const parsed = JSON.parse(await readFile(manifestPath, "utf-8"));
|
|
2154
|
+
if (parsed.version !== 1 ||
|
|
2155
|
+
!parsed.files ||
|
|
2156
|
+
typeof parsed.files !== "object") {
|
|
2157
|
+
return { version: 1, files: {} };
|
|
2158
|
+
}
|
|
2159
|
+
const files = {};
|
|
2160
|
+
for (const [fileName, entry] of Object.entries(parsed.files)) {
|
|
2161
|
+
if (!fileName.endsWith(".toml"))
|
|
2162
|
+
continue;
|
|
2163
|
+
if (!entry || typeof entry !== "object")
|
|
2164
|
+
continue;
|
|
2165
|
+
const sha256 = entry.sha256;
|
|
2166
|
+
if (typeof sha256 === "string" && /^[0-9a-f]{64}$/i.test(sha256)) {
|
|
2167
|
+
files[fileName] = { sha256: sha256.toLowerCase() };
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return { version: 1, files };
|
|
2171
|
+
}
|
|
2172
|
+
catch {
|
|
2173
|
+
return { version: 1, files: {} };
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
async function writeNativeAgentInstallManifest(agentsDir, manifest) {
|
|
2177
|
+
const manifestPath = nativeAgentInstallManifestPath(agentsDir);
|
|
2178
|
+
await mkdir(dirname(manifestPath), { recursive: true });
|
|
2179
|
+
const sortedFiles = Object.fromEntries(Object.entries(manifest.files).sort(([left], [right]) => left.localeCompare(right)));
|
|
2180
|
+
await writeFile(manifestPath, JSON.stringify({ version: 1, files: sortedFiles }, null, 2) + "\n");
|
|
2181
|
+
}
|
|
2182
|
+
async function syncNativeAgentToml(content, dstPath, summary, backupContext, options, verboseLabel, manifest) {
|
|
2183
|
+
const fileName = basename(dstPath);
|
|
2184
|
+
const nextHash = hashContent(content);
|
|
2185
|
+
const destinationExists = existsSync(dstPath);
|
|
2186
|
+
if (!destinationExists) {
|
|
2187
|
+
if (!options.dryRun) {
|
|
2188
|
+
await mkdir(dirname(dstPath), { recursive: true });
|
|
2189
|
+
await writeFile(dstPath, content);
|
|
2190
|
+
manifest.files[fileName] = { sha256: nextHash };
|
|
2191
|
+
}
|
|
2192
|
+
summary.updated += 1;
|
|
2193
|
+
if (options.verbose) {
|
|
2194
|
+
console.log(` ${options.dryRun ? "would update" : "updated"} ${verboseLabel}`);
|
|
2195
|
+
}
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
const existing = await readFile(dstPath, "utf-8");
|
|
2199
|
+
const existingHash = hashContent(existing);
|
|
2200
|
+
if (existing === content) {
|
|
2201
|
+
if (!options.dryRun) {
|
|
2202
|
+
manifest.files[fileName] = { sha256: nextHash };
|
|
2203
|
+
}
|
|
2204
|
+
summary.unchanged += 1;
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
const priorHash = manifest.files[fileName]?.sha256;
|
|
2208
|
+
const safeToOverwrite = options.force || priorHash === existingHash;
|
|
2209
|
+
if (!safeToOverwrite) {
|
|
2210
|
+
summary.skipped += 1;
|
|
2211
|
+
if (options.verbose) {
|
|
2212
|
+
console.log(` skipped ${verboseLabel} (local modifications preserved; use --force to overwrite)`);
|
|
2213
|
+
}
|
|
2214
|
+
return;
|
|
2215
|
+
}
|
|
2216
|
+
if (await ensureBackup(dstPath, true, backupContext, options)) {
|
|
2217
|
+
summary.backedUp += 1;
|
|
2218
|
+
}
|
|
2219
|
+
if (!options.dryRun) {
|
|
2220
|
+
await mkdir(dirname(dstPath), { recursive: true });
|
|
2221
|
+
await writeFile(dstPath, content);
|
|
2222
|
+
manifest.files[fileName] = { sha256: nextHash };
|
|
2223
|
+
}
|
|
2224
|
+
summary.updated += 1;
|
|
2225
|
+
if (options.verbose) {
|
|
2226
|
+
console.log(` ${options.dryRun ? "would update" : "updated"} ${verboseLabel}`);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
1878
2229
|
async function syncManagedWindowsNativeHookShim(codexHomeDir, pkgRoot, summary, backupContext, options) {
|
|
1879
2230
|
if (process.platform !== "win32")
|
|
1880
2231
|
return;
|
|
@@ -2059,6 +2410,7 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
|
|
|
2059
2410
|
if (!options.dryRun) {
|
|
2060
2411
|
await mkdir(agentsDir, { recursive: true });
|
|
2061
2412
|
}
|
|
2413
|
+
const nativeAgentManifest = await readNativeAgentInstallManifest(agentsDir);
|
|
2062
2414
|
const manifest = tryReadCatalogManifest();
|
|
2063
2415
|
const agentStatusByName = manifest
|
|
2064
2416
|
? getCatalogAgentStatusByName(manifest)
|
|
@@ -2093,7 +2445,7 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
|
|
|
2093
2445
|
codexHomeOverride: join(agentsDir, ".."),
|
|
2094
2446
|
});
|
|
2095
2447
|
const dst = join(agentsDir, `${name}.toml`);
|
|
2096
|
-
await
|
|
2448
|
+
await syncNativeAgentToml(toml, dst, summary, backupContext, options, `native agent ${name}.toml`, nativeAgentManifest);
|
|
2097
2449
|
}
|
|
2098
2450
|
summary.removed += await cleanupObsoleteNativeAgents(agentsDir, backupContext, options);
|
|
2099
2451
|
if (manifest) {
|
|
@@ -2124,6 +2476,7 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
|
|
|
2124
2476
|
}
|
|
2125
2477
|
if (!options.dryRun) {
|
|
2126
2478
|
await rm(staleAgentPath, { force: true });
|
|
2479
|
+
delete nativeAgentManifest.files[file];
|
|
2127
2480
|
}
|
|
2128
2481
|
summary.removed += 1;
|
|
2129
2482
|
if (options.verbose) {
|
|
@@ -2136,6 +2489,9 @@ async function refreshNativeAgentConfigs(pkgRoot, agentsDir, backupContext, opti
|
|
|
2136
2489
|
}
|
|
2137
2490
|
}
|
|
2138
2491
|
}
|
|
2492
|
+
if (!options.dryRun) {
|
|
2493
|
+
await writeNativeAgentInstallManifest(agentsDir, nativeAgentManifest);
|
|
2494
|
+
}
|
|
2139
2495
|
return summary;
|
|
2140
2496
|
}
|
|
2141
2497
|
async function cleanupObsoleteNativeAgents(agentsDir, backupContext, options) {
|