oh-my-opencode 4.14.0 → 4.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/codex-qa/references/isolation.md +1 -1
- package/README.md +1 -1
- package/dist/cli/doctor/framework/types.d.ts +2 -0
- package/dist/cli/index.js +149 -1426
- package/dist/cli-node/index.js +149 -1426
- package/dist/index.js +4255 -714
- package/dist/plugin/native-skills.d.ts +9 -0
- package/dist/plugin/system-transform.d.ts +1 -1
- package/dist/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/dist/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/dist/skills/lcx-doctor/SKILL.md +48 -13
- package/dist/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/dist/skills/lcx-report-bug/SKILL.md +44 -13
- package/dist/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/dist/skills/visual-qa/SKILL.md +5 -5
- package/dist/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/dist/skills/visual-qa/scripts/cli.test.ts +7 -2
- package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
- package/dist/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/dist/tools/delegate-task/skill-resolver.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +2 -0
- package/dist/tools/skill/types.d.ts +1 -0
- package/package.json +26 -13
- package/packages/git-bash-mcp/dist/cli.js +2 -0
- package/packages/lsp-daemon/dist/cli.js +15 -4
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +1 -0
- package/packages/lsp-daemon/dist/ensure-daemon.js +9 -3
- package/packages/lsp-daemon/dist/index.js +15 -4
- package/packages/lsp-daemon/dist/paths.d.ts +1 -0
- package/packages/lsp-daemon/dist/paths.js +6 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -7
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +123 -10
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +123 -10
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +126 -9
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +141 -0
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +90 -25
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +2 -0
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +2 -0
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +56 -0
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/src/cli.ts +2 -5
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +2 -0
- package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +65 -0
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +6 -1
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +8 -106
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +4 -17
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +17 -5
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +63 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +80 -50
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +4 -12
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +7 -33
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +4 -12
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/sample-quality-gate.json +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +3 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-doc.test.ts +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +7 -6
- package/packages/omo-codex/plugin/package-lock.json +16 -56
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +48 -13
- package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +44 -13
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +17 -5
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +63 -1
- package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +80 -50
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +5 -5
- package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/packages/omo-codex/plugin/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +38 -2
- package/packages/omo-codex/plugin/test/component-bin-names.test.mjs +12 -11
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +99 -1
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +62 -8
- package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +102 -0
- package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +41 -1
- package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-bin-links.test.mjs +6 -0
- package/packages/omo-codex/scripts/install-cache-copy.test.mjs +28 -0
- package/packages/omo-codex/scripts/install-config-autonomous-features.test.mjs +43 -3
- package/packages/omo-codex/scripts/install-delegated-command.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +103 -28
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +21 -2
- package/packages/omo-codex/scripts/install-local.test.mjs +0 -1
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +48 -13
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +44 -13
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/visual-qa/SKILL.md +5 -5
- package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/packages/shared-skills/skills/visual-qa/scripts/cli.test.ts +7 -2
- package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
- package/packages/shared-skills/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/dist/cli/sparkshell-appserver-websocket.d.ts +0 -4
- package/dist/cli/sparkshell-appserver.d.ts +0 -16
- package/dist/cli/sparkshell-condense.d.ts +0 -10
- package/dist/cli/sparkshell-parse.d.ts +0 -24
- package/dist/cli/sparkshell-session-context.d.ts +0 -20
- package/dist/cli/sparkshell-spark.d.ts +0 -23
- package/dist/cli/sparkshell.d.ts +0 -33
- package/dist/shared/sparkshell-awareness.d.ts +0 -5
- package/packages/omo-codex/plugin/components/rules/src/sparkshell-awareness.ts +0 -106
- package/packages/omo-codex/plugin/components/rules/test/sparkshell-awareness.test.ts +0 -355
|
@@ -75,11 +75,104 @@ test("#given a dry-run doctor #when delegating #then routes to the Codex LazyCod
|
|
|
75
75
|
// then
|
|
76
76
|
assert.equal(ran, false);
|
|
77
77
|
assert.match(logged, /^codex exec /);
|
|
78
|
+
assert.match(logged, /--sandbox danger-full-access/);
|
|
79
|
+
assert.doesNotMatch(logged, /--model/);
|
|
80
|
+
assert.doesNotMatch(logged, /gpt-5\.5-codex-mini/);
|
|
81
|
+
assert.doesNotMatch(logged, /--sandbox read-only/);
|
|
78
82
|
assert.match(logged, /Use \$omo:lcx-doctor/);
|
|
83
|
+
assert.match(logged, /LAZYCODEX_SOURCE_ROOT/);
|
|
84
|
+
assert.match(logged, /\$\{TMPDIR:-\/tmp\}\/lazycodex-sources/);
|
|
79
85
|
assert.match(logged, /Requested doctor arguments: --json/);
|
|
80
86
|
assert.doesNotMatch(logged, /oh-my-openagent omo doctor/);
|
|
81
87
|
});
|
|
82
88
|
|
|
89
|
+
test("#given doctor without model override #when delegating #then uses the configured Codex default model", async () => {
|
|
90
|
+
// given
|
|
91
|
+
const parsed = { kind: "command", command: "doctor", args: ["--json"] };
|
|
92
|
+
|
|
93
|
+
// when
|
|
94
|
+
const invocation = buildDelegatedOmoInvocation(parsed);
|
|
95
|
+
|
|
96
|
+
// then
|
|
97
|
+
assert.equal(invocation.command, "codex");
|
|
98
|
+
assert.equal(invocation.args.includes("--model"), false);
|
|
99
|
+
assert.equal(invocation.args.some((arg) => arg.includes("gpt-5.5-codex-mini")), false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("#given explicit doctor model override #when delegating #then passes only that model to Codex exec", async () => {
|
|
103
|
+
// given
|
|
104
|
+
const parsed = { kind: "command", command: "doctor", args: ["--model", "gpt-5.5", "--json"] };
|
|
105
|
+
|
|
106
|
+
// when
|
|
107
|
+
const invocation = buildDelegatedOmoInvocation(parsed);
|
|
108
|
+
|
|
109
|
+
// then
|
|
110
|
+
assert.equal(invocation.command, "codex");
|
|
111
|
+
assert.deepEqual(invocation.args.slice(0, 9), [
|
|
112
|
+
"exec",
|
|
113
|
+
"--ephemeral",
|
|
114
|
+
"--sandbox",
|
|
115
|
+
"danger-full-access",
|
|
116
|
+
"--skip-git-repo-check",
|
|
117
|
+
"--cd",
|
|
118
|
+
".",
|
|
119
|
+
"--model",
|
|
120
|
+
"gpt-5.5",
|
|
121
|
+
]);
|
|
122
|
+
assert.match(invocation.args.at(-1), /Requested doctor arguments: --json/);
|
|
123
|
+
assert.doesNotMatch(invocation.args.at(-1), /--model/);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("#given doctor source-root override #when delegating #then passes it to the Codex workflow environment", async () => {
|
|
127
|
+
// given
|
|
128
|
+
const parsed = { kind: "command", command: "doctor", args: ["--source-root", "/var/tmp/lcx-sources", "--json"] };
|
|
129
|
+
|
|
130
|
+
// when
|
|
131
|
+
const invocation = buildDelegatedOmoInvocation(parsed);
|
|
132
|
+
|
|
133
|
+
// then
|
|
134
|
+
assert.equal(invocation.env?.LAZYCODEX_SOURCE_ROOT, "/var/tmp/lcx-sources");
|
|
135
|
+
assert.deepEqual(invocation.args.slice(0, 7), [
|
|
136
|
+
"exec",
|
|
137
|
+
"--ephemeral",
|
|
138
|
+
"--sandbox",
|
|
139
|
+
"danger-full-access",
|
|
140
|
+
"--skip-git-repo-check",
|
|
141
|
+
"--cd",
|
|
142
|
+
".",
|
|
143
|
+
]);
|
|
144
|
+
assert.match(invocation.args.at(-1), /Requested doctor arguments: --json/);
|
|
145
|
+
assert.doesNotMatch(invocation.args.at(-1), /--source-root/);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("#given dry-run install without explicit platform #when delegating #then logs one Windows-safe Codex install command", async () => {
|
|
149
|
+
// given
|
|
150
|
+
const parsed = {
|
|
151
|
+
kind: "command",
|
|
152
|
+
command: "install",
|
|
153
|
+
dryRun: true,
|
|
154
|
+
noTui: true,
|
|
155
|
+
skipAuth: false,
|
|
156
|
+
autonomousPermissions: true,
|
|
157
|
+
repoRoot: undefined,
|
|
158
|
+
args: [],
|
|
159
|
+
};
|
|
160
|
+
const logged = [];
|
|
161
|
+
|
|
162
|
+
// when
|
|
163
|
+
await runDelegatedOmoCommand(parsed, {
|
|
164
|
+
cwd: "/tmp/project",
|
|
165
|
+
log: (line) => {
|
|
166
|
+
logged.push(line);
|
|
167
|
+
},
|
|
168
|
+
runCommand: async () => {},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// then
|
|
172
|
+
assert.deepEqual(logged, [
|
|
173
|
+
"npx --yes oh-my-openagent@latest install --platform=codex --no-tui --codex-autonomous",
|
|
174
|
+
]);
|
|
175
|
+
});
|
|
83
176
|
test("#given doctor recursion guard is active #when lazycodex doctor delegates #then rejects before launching Codex", async () => {
|
|
84
177
|
// given
|
|
85
178
|
const parsed = { kind: "command", command: "doctor", args: [] };
|
|
@@ -5907,7 +5907,7 @@ var package_default;
|
|
|
5907
5907
|
var init_package = __esm(() => {
|
|
5908
5908
|
package_default = {
|
|
5909
5909
|
name: "@oh-my-opencode/omo-codex",
|
|
5910
|
-
version: "4.14.
|
|
5910
|
+
version: "4.14.1",
|
|
5911
5911
|
type: "module",
|
|
5912
5912
|
private: true,
|
|
5913
5913
|
description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
|
|
@@ -6603,10 +6603,9 @@ function posixRuntimeWrapper(cliPath, codexHome, binDir, nodeCliPath) {
|
|
|
6603
6603
|
"#!/bin/sh",
|
|
6604
6604
|
`# ${RUNTIME_WRAPPER_MARKER}`,
|
|
6605
6605
|
`export CODEX_HOME="\${CODEX_HOME:-${escapedCodexHome}}"`,
|
|
6606
|
-
'export OMO_SPARKSHELL_APP_SERVER_SOCKET="${OMO_SPARKSHELL_APP_SERVER_SOCKET:-$CODEX_HOME/app-server-control/app-server-control.sock}"',
|
|
6607
6606
|
'if [ "$1" = "ulw-loop" ] && [ -x "' + escapedUlwLoopBin + '" ]; then',
|
|
6608
6607
|
" shift",
|
|
6609
|
-
' exec "' + escapedUlwLoopBin + '" "$@"',
|
|
6608
|
+
' exec "' + escapedUlwLoopBin + '" ulw-loop "$@"',
|
|
6610
6609
|
"fi",
|
|
6611
6610
|
`if [ "\${OMO_RUNTIME:-}" = "node" ] && [ -f "${nodeCli}" ]; then`,
|
|
6612
6611
|
` exec node "${nodeCli}" "$@"`,
|
|
@@ -6645,11 +6644,10 @@ function windowsRuntimeWrapper(cliPath, codexHome, binDir, nodeCliPath) {
|
|
|
6645
6644
|
"@echo off",
|
|
6646
6645
|
`rem ${RUNTIME_WRAPPER_MARKER}`,
|
|
6647
6646
|
`if not defined CODEX_HOME set "CODEX_HOME=${codexHome}"`,
|
|
6648
|
-
'if not defined OMO_SPARKSHELL_APP_SERVER_SOCKET set "OMO_SPARKSHELL_APP_SERVER_SOCKET=%CODEX_HOME%\\app-server-control\\app-server-control.sock"',
|
|
6649
6647
|
...windowsNodeDiscoveryLines(),
|
|
6650
6648
|
`if "%~1"=="ulw-loop" if exist "${ulwLoopBin}" (`,
|
|
6651
6649
|
" shift /1",
|
|
6652
|
-
` "${ulwLoopBin}" %*`,
|
|
6650
|
+
` "${ulwLoopBin}" ulw-loop %*`,
|
|
6653
6651
|
" exit /b %ERRORLEVEL%",
|
|
6654
6652
|
")",
|
|
6655
6653
|
`if "%OMO_RUNTIME%"=="node" if defined OMO_NODE_BINARY if exist "${nodeCliPath}" (`,
|
|
@@ -7442,6 +7440,7 @@ async function installCachedPlugin(input) {
|
|
|
7442
7440
|
await copyDirectory(input.sourcePath, tempPath);
|
|
7443
7441
|
await rewriteCachedPackageLocalFileDependencies(tempPath, input.sourcePath);
|
|
7444
7442
|
await copyBundledMcpRuntimeDists({ pluginRoot: tempPath, sourceRoot: input.sourcePath });
|
|
7443
|
+
await copyRootRuntimeDists({ pluginRoot: tempPath, sourcePath: input.sourcePath });
|
|
7445
7444
|
await maybeRunNpmInstall(tempPath, input.runCommand, ["ci", "--omit=dev"]);
|
|
7446
7445
|
await removeCachedManagedNpmBinShims(tempPath);
|
|
7447
7446
|
if (input.buildSource === false)
|
|
@@ -7524,6 +7523,29 @@ function shouldCopyPluginPath(path, root) {
|
|
|
7524
7523
|
const parts = relative4.split(sep5);
|
|
7525
7524
|
return !parts.some((part) => part === ".git" || part === "node_modules");
|
|
7526
7525
|
}
|
|
7526
|
+
async function copyRootRuntimeDists(input) {
|
|
7527
|
+
const repoRoot = repoRootForCodexPluginSource(input.sourcePath);
|
|
7528
|
+
if (repoRoot === null)
|
|
7529
|
+
return;
|
|
7530
|
+
for (const runtimePath of ["dist/cli", "dist/cli-node"]) {
|
|
7531
|
+
const sourcePath = join11(repoRoot, runtimePath);
|
|
7532
|
+
if (!await fileExistsStrict(join11(sourcePath, "index.js")))
|
|
7533
|
+
continue;
|
|
7534
|
+
await mkdir3(dirname4(join11(input.pluginRoot, runtimePath)), { recursive: true });
|
|
7535
|
+
await cp2(sourcePath, join11(input.pluginRoot, runtimePath), { recursive: true });
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
function repoRootForCodexPluginSource(sourcePath) {
|
|
7539
|
+
const codexPackageRoot = dirname4(sourcePath);
|
|
7540
|
+
const packagesRoot = dirname4(codexPackageRoot);
|
|
7541
|
+
if (basename3(sourcePath) !== "plugin")
|
|
7542
|
+
return null;
|
|
7543
|
+
if (basename3(codexPackageRoot) !== "omo-codex")
|
|
7544
|
+
return null;
|
|
7545
|
+
if (basename3(packagesRoot) !== "packages")
|
|
7546
|
+
return null;
|
|
7547
|
+
return dirname4(packagesRoot);
|
|
7548
|
+
}
|
|
7527
7549
|
// packages/omo-codex/src/install/codex-cache-prune.ts
|
|
7528
7550
|
import { lstat as lstat4, readdir as readdir3, rm as rm4, stat as stat3 } from "node:fs/promises";
|
|
7529
7551
|
import { join as join12 } from "node:path";
|
|
@@ -8117,7 +8139,7 @@ function ensureMarketplaceBlock(config, marketplaceName, source) {
|
|
|
8117
8139
|
}
|
|
8118
8140
|
|
|
8119
8141
|
// packages/omo-codex/src/install/codex-config-permissions.ts
|
|
8120
|
-
var AUTONOMOUS_FEATURES = ["multi_agent", "
|
|
8142
|
+
var AUTONOMOUS_FEATURES = ["multi_agent", "unified_exec", "goals"];
|
|
8121
8143
|
function ensureAutonomousPermissions(config) {
|
|
8122
8144
|
let next = replaceOrInsertRootSetting(config, "approval_policy", JSON.stringify("never"));
|
|
8123
8145
|
next = replaceOrInsertRootSetting(next, "sandbox_mode", JSON.stringify("danger-full-access"));
|
|
@@ -8537,7 +8559,6 @@ async function updateCodexConfig(input) {
|
|
|
8537
8559
|
config = ensureFeatureEnabled(config, "plugins");
|
|
8538
8560
|
config = ensureFeatureEnabled(config, "plugin_hooks");
|
|
8539
8561
|
config = ensureFeatureEnabled(config, "multi_agent");
|
|
8540
|
-
config = ensureFeatureEnabled(config, "child_agents_md");
|
|
8541
8562
|
config = removeUnsupportedCodexMultiAgentModeConfig(config);
|
|
8542
8563
|
config = ensureCodexReasoningConfig(config, await readCodexModelCatalog(input.repoRoot));
|
|
8543
8564
|
config = ensureCodexMultiAgentV2Config(config);
|
|
@@ -13554,7 +13575,7 @@ async function runCodexInstaller(options = {}) {
|
|
|
13554
13575
|
if (runtimeLink !== null)
|
|
13555
13576
|
log2(`Linked ${runtimeLink.name} -> ${runtimeLink.target}`);
|
|
13556
13577
|
else
|
|
13557
|
-
log2(`Warning: skipped the omo runtime wrapper because ${join31(repoRoot, "dist", "cli", "index.js")} is missing; omo
|
|
13578
|
+
log2(`Warning: skipped the omo runtime wrapper because ${join31(repoRoot, "dist", "cli", "index.js")} is missing; omo ulw-loop commands will be unavailable until a package shipping dist/cli is installed`);
|
|
13558
13579
|
}
|
|
13559
13580
|
pluginSources.push({ name: entry.name, sourcePath });
|
|
13560
13581
|
installed.push(plugin);
|
|
@@ -13855,6 +13876,7 @@ function formatLazyCodexInstallHelp() {
|
|
|
13855
13876
|
"Usage: lazycodex-ai install [--no-tui] [--codex-autonomous|--no-codex-autonomous] [--repo-root <path>]",
|
|
13856
13877
|
" lazycodex-ai uninstall [--project <path>]",
|
|
13857
13878
|
" lazycodex-ai update [--dry-run] [--repo-root <path>]",
|
|
13879
|
+
" lazycodex-ai doctor [--source-root <path>] [--model <model>] [--json|--status|--verbose]",
|
|
13858
13880
|
" lazycodex-ai version",
|
|
13859
13881
|
" lazycodex-ai <command> [args...]",
|
|
13860
13882
|
"",
|
|
@@ -13884,20 +13906,22 @@ async function runDelegatedOmoCommand(parsed, options) {
|
|
|
13884
13906
|
function buildDelegatedOmoInvocation(parsed) {
|
|
13885
13907
|
if (parsed.command === "doctor")
|
|
13886
13908
|
return buildLazyCodexDoctorInvocation(parsed.args);
|
|
13887
|
-
const args = ["--yes", "--package", "oh-my-openagent", "omo", parsed.command];
|
|
13888
13909
|
if (parsed.command === "install") {
|
|
13889
|
-
|
|
13910
|
+
const args2 = ["--yes", "oh-my-openagent@latest", parsed.command, "--platform=codex"];
|
|
13890
13911
|
if (parsed.noTui)
|
|
13891
|
-
|
|
13912
|
+
args2.push("--no-tui");
|
|
13892
13913
|
if (parsed.skipAuth)
|
|
13893
|
-
|
|
13914
|
+
args2.push("--skip-auth");
|
|
13894
13915
|
if (parsed.autonomousPermissions !== false)
|
|
13895
|
-
|
|
13916
|
+
args2.push("--codex-autonomous");
|
|
13896
13917
|
if (parsed.autonomousPermissions === false)
|
|
13897
|
-
|
|
13918
|
+
args2.push("--no-codex-autonomous");
|
|
13898
13919
|
if (parsed.repoRoot)
|
|
13899
|
-
|
|
13900
|
-
|
|
13920
|
+
args2.push(`--repo-root=${parsed.repoRoot}`);
|
|
13921
|
+
return { command: "npx", args: args2, delegatesToOmo: true };
|
|
13922
|
+
}
|
|
13923
|
+
const args = ["--yes", "--package", "oh-my-openagent", "omo", parsed.command];
|
|
13924
|
+
if (parsed.command === "cleanup") {
|
|
13901
13925
|
args.push("--platform=codex", ...parsed.args);
|
|
13902
13926
|
} else {
|
|
13903
13927
|
args.push(...parsed.args);
|
|
@@ -13905,21 +13929,26 @@ function buildDelegatedOmoInvocation(parsed) {
|
|
|
13905
13929
|
return { command: "npx", args, delegatesToOmo: true };
|
|
13906
13930
|
}
|
|
13907
13931
|
function buildLazyCodexDoctorInvocation(doctorArgs) {
|
|
13932
|
+
const doctorOptions = parseLazyCodexDoctorOptions(doctorArgs);
|
|
13933
|
+
const codexArgs = [
|
|
13934
|
+
"exec",
|
|
13935
|
+
"--ephemeral",
|
|
13936
|
+
"--sandbox",
|
|
13937
|
+
"danger-full-access",
|
|
13938
|
+
"--skip-git-repo-check",
|
|
13939
|
+
"--cd",
|
|
13940
|
+
"."
|
|
13941
|
+
];
|
|
13942
|
+
if (doctorOptions.model !== undefined)
|
|
13943
|
+
codexArgs.push("--model", doctorOptions.model);
|
|
13944
|
+
codexArgs.push(buildLazyCodexDoctorPrompt(doctorOptions.args));
|
|
13908
13945
|
return {
|
|
13909
13946
|
command: "codex",
|
|
13910
|
-
args:
|
|
13911
|
-
"exec",
|
|
13912
|
-
"--ephemeral",
|
|
13913
|
-
"--sandbox",
|
|
13914
|
-
"read-only",
|
|
13915
|
-
"--skip-git-repo-check",
|
|
13916
|
-
"--cd",
|
|
13917
|
-
".",
|
|
13918
|
-
buildLazyCodexDoctorPrompt(doctorArgs)
|
|
13919
|
-
],
|
|
13947
|
+
args: codexArgs,
|
|
13920
13948
|
delegatesToOmo: false,
|
|
13921
13949
|
env: {
|
|
13922
|
-
LAZYCODEX_DOCTOR_LCX_ACTIVE: "1"
|
|
13950
|
+
LAZYCODEX_DOCTOR_LCX_ACTIVE: "1",
|
|
13951
|
+
...doctorOptions.sourceRoot === undefined ? {} : { LAZYCODEX_SOURCE_ROOT: doctorOptions.sourceRoot }
|
|
13923
13952
|
}
|
|
13924
13953
|
};
|
|
13925
13954
|
}
|
|
@@ -13927,12 +13956,58 @@ function buildLazyCodexDoctorPrompt(doctorArgs) {
|
|
|
13927
13956
|
return [
|
|
13928
13957
|
"Use $omo:lcx-doctor to diagnose this LazyCodex/Codex installation.",
|
|
13929
13958
|
"This command is already the lazycodex doctor surface; never invoke lazycodex doctor from inside the doctor workflow.",
|
|
13930
|
-
"
|
|
13959
|
+
"Use the resolved source root from LAZYCODEX_SOURCE_ROOT when set; otherwise use ${TMPDIR:-/tmp}/lazycodex-sources.",
|
|
13960
|
+
"Validate cached source checkouts before reuse, quarantine corrupt caches, and do not rely on /tmp/lazycodex-source.",
|
|
13961
|
+
"Sync the latest LazyCodex and OpenAI Codex sources there, inventory the local installation,",
|
|
13931
13962
|
"probe the Codex plugin/cache/hooks/MCP state, and report PASS/WARN/FAIL findings with evidence and remediations.",
|
|
13932
13963
|
buildDoctorOutputInstruction(doctorArgs),
|
|
13933
13964
|
doctorArgs.length > 0 ? `Requested doctor arguments: ${doctorArgs.join(" ")}` : "Requested doctor arguments: none"
|
|
13934
13965
|
].join(" ");
|
|
13935
13966
|
}
|
|
13967
|
+
function parseLazyCodexDoctorOptions(doctorArgs) {
|
|
13968
|
+
const args = [];
|
|
13969
|
+
let model;
|
|
13970
|
+
let sourceRoot;
|
|
13971
|
+
let index = 0;
|
|
13972
|
+
while (index < doctorArgs.length) {
|
|
13973
|
+
const arg = doctorArgs[index];
|
|
13974
|
+
if (arg === "--model") {
|
|
13975
|
+
const value = doctorArgs[index + 1];
|
|
13976
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
13977
|
+
throw new Error("--model requires a value");
|
|
13978
|
+
model = value;
|
|
13979
|
+
index += 2;
|
|
13980
|
+
continue;
|
|
13981
|
+
}
|
|
13982
|
+
if (typeof arg === "string" && arg.startsWith("--model=")) {
|
|
13983
|
+
const value = arg.slice("--model=".length);
|
|
13984
|
+
if (value.trim().length === 0)
|
|
13985
|
+
throw new Error("--model requires a value");
|
|
13986
|
+
model = value;
|
|
13987
|
+
index += 1;
|
|
13988
|
+
continue;
|
|
13989
|
+
}
|
|
13990
|
+
if (arg === "--source-root") {
|
|
13991
|
+
const value = doctorArgs[index + 1];
|
|
13992
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
13993
|
+
throw new Error("--source-root requires a path");
|
|
13994
|
+
sourceRoot = value;
|
|
13995
|
+
index += 2;
|
|
13996
|
+
continue;
|
|
13997
|
+
}
|
|
13998
|
+
if (typeof arg === "string" && arg.startsWith("--source-root=")) {
|
|
13999
|
+
const value = arg.slice("--source-root=".length);
|
|
14000
|
+
if (value.trim().length === 0)
|
|
14001
|
+
throw new Error("--source-root requires a path");
|
|
14002
|
+
sourceRoot = value;
|
|
14003
|
+
index += 1;
|
|
14004
|
+
continue;
|
|
14005
|
+
}
|
|
14006
|
+
args.push(arg);
|
|
14007
|
+
index += 1;
|
|
14008
|
+
}
|
|
14009
|
+
return { args, ...model === undefined ? {} : { model }, ...sourceRoot === undefined ? {} : { sourceRoot } };
|
|
14010
|
+
}
|
|
13936
14011
|
function buildDoctorOutputInstruction(doctorArgs) {
|
|
13937
14012
|
if (doctorArgs.includes("--json")) {
|
|
13938
14013
|
return "Return exactly one JSON object with summary, environment, checks, remediations, and knownIssues fields; do not wrap it in Markdown.";
|
|
@@ -69,7 +69,7 @@ test("#given dry-run install flags #when running the Node installer entrypoint #
|
|
|
69
69
|
).trim();
|
|
70
70
|
|
|
71
71
|
// then
|
|
72
|
-
assert.equal(output, "npx --yes
|
|
72
|
+
assert.equal(output, "npx --yes oh-my-openagent@latest install --platform=codex --no-tui --codex-autonomous");
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
test("#given dry-run install opt-out #when running the Node installer entrypoint #then preserves existing Codex permission settings", () => {
|
|
@@ -84,7 +84,21 @@ test("#given dry-run install opt-out #when running the Node installer entrypoint
|
|
|
84
84
|
).trim();
|
|
85
85
|
|
|
86
86
|
// then
|
|
87
|
-
assert.equal(output, "npx --yes
|
|
87
|
+
assert.equal(output, "npx --yes oh-my-openagent@latest install --platform=codex --no-tui --no-codex-autonomous");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("#given explicit non-Codex dry-run platform #when running the Node installer entrypoint #then rejects it", () => {
|
|
91
|
+
// given
|
|
92
|
+
const scriptPath = fileURLToPath(new URL("./install-local.mjs", import.meta.url));
|
|
93
|
+
|
|
94
|
+
// when/then
|
|
95
|
+
assert.throws(
|
|
96
|
+
() =>
|
|
97
|
+
execFileSync(process.execPath, [scriptPath, "--dry-run", "install", "--platform=gemini", "--no-tui"], {
|
|
98
|
+
encoding: "utf8",
|
|
99
|
+
}),
|
|
100
|
+
/lazycodex-ai installs the Codex Light edition only/,
|
|
101
|
+
);
|
|
88
102
|
});
|
|
89
103
|
|
|
90
104
|
test("#given dry-run doctor #when running the Node installer entrypoint #then prints Codex LazyCodex doctor workflow command", () => {
|
|
@@ -98,7 +112,12 @@ test("#given dry-run doctor #when running the Node installer entrypoint #then pr
|
|
|
98
112
|
|
|
99
113
|
// then
|
|
100
114
|
assert.match(output, /^codex exec /);
|
|
115
|
+
assert.match(output, /--sandbox danger-full-access/);
|
|
116
|
+
assert.doesNotMatch(output, /--model/);
|
|
117
|
+
assert.doesNotMatch(output, /gpt-5\.5-codex-mini/);
|
|
118
|
+
assert.doesNotMatch(output, /--sandbox read-only/);
|
|
101
119
|
assert.match(output, /Use \$omo:lcx-doctor/);
|
|
120
|
+
assert.match(output, /\$\{TMPDIR:-\/tmp\}\/lazycodex-sources/);
|
|
102
121
|
assert.match(output, /Requested doctor arguments: --json/);
|
|
103
122
|
assert.match(output, /Return exactly one JSON object/);
|
|
104
123
|
assert.doesNotMatch(output, /oh-my-openagent omo doctor/);
|
|
@@ -69,7 +69,6 @@ test("#given custom CODEX_HOME and PATH without omo #when installing locally wit
|
|
|
69
69
|
new RegExp(escapeRegExp(escapePosixDoubleQuoted(toPosixPath(join(repoRoot, "dist", "cli", "index.js"))))),
|
|
70
70
|
);
|
|
71
71
|
assert.match(wrapper, /CODEX_HOME/);
|
|
72
|
-
assert.match(wrapper, /OMO_SPARKSHELL_APP_SERVER_SOCKET/);
|
|
73
72
|
assert.match(wrapper, /omo-ulw-loop/);
|
|
74
73
|
});
|
|
75
74
|
|
|
@@ -18,7 +18,7 @@ Route ownership the same way as `$lcx-report-bug`, but the deliverable differs b
|
|
|
18
18
|
|
|
19
19
|
For `openai/codex`, create a fork PR that includes:
|
|
20
20
|
|
|
21
|
-
- a focused branch from a fresh
|
|
21
|
+
- a focused branch from a fresh `${TMPDIR:-/tmp}` clone/worktree
|
|
22
22
|
- reproduction logs from before the fix
|
|
23
23
|
- the smallest implementation that fixes the defect
|
|
24
24
|
- verification logs from after the fix
|
|
@@ -30,7 +30,7 @@ For `code-yeongyu/lazycodex`, create an issue (never a PR) that includes:
|
|
|
30
30
|
|
|
31
31
|
- reproduction logs from before the fix
|
|
32
32
|
- the root cause with source evidence
|
|
33
|
-
- the verified patch as a unified diff, produced and tested in a fresh
|
|
33
|
+
- the verified patch as a unified diff, produced and tested in a fresh `${TMPDIR:-/tmp}` clone/worktree
|
|
34
34
|
- verification logs from after the fix
|
|
35
35
|
- the `lazycodex-generated` label and the footer tag `Tag: lazycodex-generated`
|
|
36
36
|
- cleanup of temporary worktrees and clones
|
|
@@ -39,27 +39,58 @@ For `code-yeongyu/lazycodex`, create an issue (never a PR) that includes:
|
|
|
39
39
|
|
|
40
40
|
1. Read the user's bug report and identify the affected surface.
|
|
41
41
|
2. Invoke `$omo:debugging` for the investigation. If only unqualified skill names are exposed, invoke `$debugging` and state that it is the OMO debugging skill.
|
|
42
|
-
3. Materialize the latest sources
|
|
42
|
+
3. Materialize the latest sources under `LAZYCODEX_SOURCE_ROOT="${LAZYCODEX_SOURCE_ROOT:-${TMPDIR:-/tmp}/lazycodex-sources}"`, then decide the target repository. Sync both checkouts on every run and compare them before choosing. Validate cached checkouts before reuse so an incomplete `.git` directory cannot route the fix to the wrong repo:
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
|
+
LAZYCODEX_SOURCE_ROOT="${LAZYCODEX_SOURCE_ROOT:-${TMPDIR:-/tmp}/lazycodex-sources}"
|
|
46
|
+
mkdir -p "$LAZYCODEX_SOURCE_ROOT"
|
|
47
|
+
|
|
48
|
+
valid_source_checkout() {
|
|
49
|
+
DEST="$1"
|
|
50
|
+
git -C "$DEST" rev-parse --is-inside-work-tree >/dev/null 2>&1 &&
|
|
51
|
+
git -C "$DEST" config --get remote.origin.url >/dev/null 2>&1
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
recover_corrupt_source_checkout() {
|
|
55
|
+
DEST="$1"
|
|
56
|
+
if [ -e "$DEST" ] && ! valid_source_checkout "$DEST"; then
|
|
57
|
+
QUARANTINED="$DEST.corrupt.$(date +%Y%m%d%H%M%S)"
|
|
58
|
+
mv "$DEST" "$QUARANTINED"
|
|
59
|
+
echo "Moved corrupt source cache $DEST to $QUARANTINED" >&2
|
|
60
|
+
fi
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
sync_latest_source() {
|
|
46
64
|
REPO="$1"; DEST="$2"
|
|
47
|
-
|
|
65
|
+
recover_corrupt_source_checkout "$DEST"
|
|
66
|
+
if [ ! -d "$DEST" ]; then
|
|
48
67
|
gh repo clone "$REPO" "$DEST" -- --depth=1 \
|
|
49
68
|
|| git clone --depth=1 "https://github.com/$REPO" "$DEST"
|
|
50
69
|
fi
|
|
70
|
+
if ! valid_source_checkout "$DEST"; then
|
|
71
|
+
echo "Source cache $DEST is not a usable git checkout after clone" >&2
|
|
72
|
+
return 1
|
|
73
|
+
fi
|
|
74
|
+
git -C "$DEST" remote set-url origin "https://github.com/$REPO.git" >/dev/null 2>&1 || true
|
|
51
75
|
DEFAULT_BRANCH="$(git -C "$DEST" remote show origin | sed -n '/HEAD branch/s/.*: //p')"
|
|
76
|
+
if [ -z "$DEFAULT_BRANCH" ]; then
|
|
77
|
+
DEFAULT_BRANCH="$(git -C "$DEST" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')"
|
|
78
|
+
fi
|
|
79
|
+
if [ -z "$DEFAULT_BRANCH" ]; then
|
|
80
|
+
echo "Could not determine default branch for $REPO in $DEST" >&2
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
52
83
|
git -C "$DEST" fetch --depth=1 origin "$DEFAULT_BRANCH"
|
|
53
84
|
git -C "$DEST" checkout -B "$DEFAULT_BRANCH" FETCH_HEAD
|
|
54
85
|
}
|
|
55
|
-
sync_latest_source code-yeongyu/lazycodex /
|
|
56
|
-
sync_latest_source openai/codex /
|
|
86
|
+
sync_latest_source code-yeongyu/lazycodex "$LAZYCODEX_SOURCE_ROOT/lazycodex-source"
|
|
87
|
+
sync_latest_source openai/codex "$LAZYCODEX_SOURCE_ROOT/openai-codex-source"
|
|
57
88
|
```
|
|
58
|
-
4. Create a fresh temporary clone and branch
|
|
89
|
+
4. Create a fresh temporary clone and branch under `${TMPDIR:-/tmp}`. Do not modify the user's current repository for the target fix unless the current repository is itself the requested target and the user explicitly asked for local edits.
|
|
59
90
|
|
|
60
91
|
```bash
|
|
61
92
|
TARGET_REPO="code-yeongyu/lazycodex" # or openai/codex
|
|
62
|
-
WORK_ROOT="$(mktemp -d
|
|
93
|
+
WORK_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/lazycodex-fix-XXXXXX")"
|
|
63
94
|
gh repo clone "$TARGET_REPO" "$WORK_ROOT/repo" -- --depth=1
|
|
64
95
|
cd "$WORK_ROOT/repo"
|
|
65
96
|
BASE_BRANCH="$(git remote show origin | sed -n '/HEAD branch/s/.*: //p')"
|
|
@@ -71,7 +102,7 @@ cd "$WORK_ROOT/worktree"
|
|
|
71
102
|
|
|
72
103
|
If `gh` cannot clone, use `git clone --depth=1 "https://github.com/$TARGET_REPO" "$WORK_ROOT/repo"` and continue with the same worktree flow.
|
|
73
104
|
|
|
74
|
-
5. Reproduce the bug in the worktree through the real surface. Save exact command output to
|
|
105
|
+
5. Reproduce the bug in the worktree through the real surface. Save exact command output to `${TMPDIR:-/tmp}/lazycodex-fix-<short-slug>-repro.log`.
|
|
75
106
|
6. Write or update a failing regression test before production changes. Confirm it fails for the bug, not for a missing fixture or typo.
|
|
76
107
|
7. Implement the smallest correct fix. Avoid refactors unless the fix cannot be made safely without one.
|
|
77
108
|
8. Run the regression test, adjacent tests, and the smallest real-surface QA command that proves the user-visible behavior changed.
|
|
@@ -89,7 +120,7 @@ git log --oneline "origin/$BASE_BRANCH..HEAD"
|
|
|
89
120
|
- `code-yeongyu/lazycodex`: export the verified patch and write the issue body from the Verified-Fix Issue Template below:
|
|
90
121
|
|
|
91
122
|
```bash
|
|
92
|
-
PATCH_FILE="
|
|
123
|
+
PATCH_FILE="${TMPDIR:-/tmp}/lazycodex-fix-<short-slug>.patch"
|
|
93
124
|
git diff "origin/$BASE_BRANCH"..HEAD > "$PATCH_FILE"
|
|
94
125
|
```
|
|
95
126
|
|
|
@@ -108,7 +139,7 @@ fi
|
|
|
108
139
|
- `code-yeongyu/lazycodex`: create the verified-fix issue. Never push a branch to this repo and never run `gh pr create` against it:
|
|
109
140
|
|
|
110
141
|
```bash
|
|
111
|
-
ISSUE_BODY="
|
|
142
|
+
ISSUE_BODY="${TMPDIR:-/tmp}/lazycodex-fix-<short-slug>-issue.md"
|
|
112
143
|
gh issue create --repo code-yeongyu/lazycodex --title "<short fix title>" "${LABEL_ARGS[@]}" --body-file "$ISSUE_BODY"
|
|
113
144
|
```
|
|
114
145
|
|
|
@@ -184,8 +215,8 @@ Use the bundled script to generate the PR body. Create a JSON file with this sha
|
|
|
184
215
|
Run:
|
|
185
216
|
|
|
186
217
|
```bash
|
|
187
|
-
PR_INPUT="
|
|
188
|
-
PR_BODY="
|
|
218
|
+
PR_INPUT="${TMPDIR:-/tmp}/lazycodex-fix-<short-slug>-pr.json"
|
|
219
|
+
PR_BODY="${TMPDIR:-/tmp}/lazycodex-fix-<short-slug>-pr.md"
|
|
189
220
|
node "<skill-root>/scripts/create-pr-body.mjs" "$PR_INPUT" "$PR_BODY"
|
|
190
221
|
```
|
|
191
222
|
|
|
@@ -9,4 +9,4 @@ interface:
|
|
|
9
9
|
- "lazycodex fix"
|
|
10
10
|
- "omo-codex bug fix"
|
|
11
11
|
- "codex bug fix pr"
|
|
12
|
-
default_prompt: "Use $lcx-contribute-bug-fix to debug this LazyCodex or Codex bug, work from a fresh
|
|
12
|
+
default_prompt: "Use $lcx-contribute-bug-fix to debug this LazyCodex or Codex bug, work from a fresh ${TMPDIR:-/tmp} clone and worktree, implement the smallest verified fix, apply the lazycodex-generated label, deliver it (verified-fix issue on code-yeongyu/lazycodex — never a PR there; fork PR only for openai/codex), and clean up."
|
|
@@ -7,40 +7,73 @@ metadata:
|
|
|
7
7
|
|
|
8
8
|
# lcx-doctor
|
|
9
9
|
|
|
10
|
-
You are a LazyCodex install doctor. Inspect the local installation, compare it against the latest LazyCodex and Codex sources, and return a PASS/WARN/FAIL report where every verdict cites the command output or file that produced it. Diagnose only: the only writes you make are under
|
|
10
|
+
You are a LazyCodex install doctor. Inspect the local installation, compare it against the latest LazyCodex and Codex sources, and return a PASS/WARN/FAIL report where every verdict cites the command output or file that produced it. Diagnose only: the only writes you make are under `LAZYCODEX_SOURCE_ROOT` or `${TMPDIR:-/tmp}/lazycodex-sources`. Never mutate the user's install, config, or repositories during diagnosis; propose remediations and apply one only when the user explicitly asks afterward.
|
|
11
11
|
|
|
12
12
|
Use GPT-5.5 style: outcome first, concise, evidence-bound.
|
|
13
13
|
|
|
14
14
|
## Required Workflow
|
|
15
15
|
|
|
16
|
-
1. Materialize the latest sources under
|
|
16
|
+
1. Materialize the latest sources under `LAZYCODEX_SOURCE_ROOT="${LAZYCODEX_SOURCE_ROOT:-${TMPDIR:-/tmp}/lazycodex-sources}"` first. Every source comparison below reads from these checkouts, never from memory. Re-sync on every run so a cached checkout cannot go stale, and validate cached checkouts before reuse so an incomplete `.git` directory cannot poison diagnosis:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
+
LAZYCODEX_SOURCE_ROOT="${LAZYCODEX_SOURCE_ROOT:-${TMPDIR:-/tmp}/lazycodex-sources}"
|
|
20
|
+
mkdir -p "$LAZYCODEX_SOURCE_ROOT"
|
|
21
|
+
|
|
22
|
+
valid_source_checkout() {
|
|
23
|
+
DEST="$1"
|
|
24
|
+
git -C "$DEST" rev-parse --is-inside-work-tree >/dev/null 2>&1 &&
|
|
25
|
+
git -C "$DEST" config --get remote.origin.url >/dev/null 2>&1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
recover_corrupt_source_checkout() {
|
|
29
|
+
DEST="$1"
|
|
30
|
+
if [ -e "$DEST" ] && ! valid_source_checkout "$DEST"; then
|
|
31
|
+
QUARANTINED="$DEST.corrupt.$(date +%Y%m%d%H%M%S)"
|
|
32
|
+
mv "$DEST" "$QUARANTINED"
|
|
33
|
+
echo "Moved corrupt source cache $DEST to $QUARANTINED" >&2
|
|
34
|
+
fi
|
|
35
|
+
}
|
|
36
|
+
|
|
19
37
|
sync_latest_source() {
|
|
20
38
|
REPO="$1"; DEST="$2"
|
|
21
|
-
|
|
39
|
+
recover_corrupt_source_checkout "$DEST"
|
|
40
|
+
if [ ! -d "$DEST" ]; then
|
|
22
41
|
gh repo clone "$REPO" "$DEST" -- --depth=1 \
|
|
23
42
|
|| git clone --depth=1 "https://github.com/$REPO" "$DEST"
|
|
24
43
|
fi
|
|
44
|
+
if ! valid_source_checkout "$DEST"; then
|
|
45
|
+
echo "Source cache $DEST is not a usable git checkout after clone" >&2
|
|
46
|
+
return 1
|
|
47
|
+
fi
|
|
48
|
+
git -C "$DEST" remote set-url origin "https://github.com/$REPO.git" >/dev/null 2>&1 || true
|
|
25
49
|
DEFAULT_BRANCH="$(git -C "$DEST" remote show origin | sed -n '/HEAD branch/s/.*: //p')"
|
|
50
|
+
if [ -z "$DEFAULT_BRANCH" ]; then
|
|
51
|
+
DEFAULT_BRANCH="$(git -C "$DEST" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')"
|
|
52
|
+
fi
|
|
53
|
+
if [ -z "$DEFAULT_BRANCH" ]; then
|
|
54
|
+
echo "Could not determine default branch for $REPO in $DEST" >&2
|
|
55
|
+
return 1
|
|
56
|
+
fi
|
|
26
57
|
git -C "$DEST" fetch --depth=1 origin "$DEFAULT_BRANCH"
|
|
27
58
|
git -C "$DEST" checkout -B "$DEFAULT_BRANCH" FETCH_HEAD
|
|
28
59
|
}
|
|
29
|
-
sync_latest_source code-yeongyu/lazycodex /
|
|
30
|
-
sync_latest_source openai/codex /
|
|
60
|
+
sync_latest_source code-yeongyu/lazycodex "$LAZYCODEX_SOURCE_ROOT/lazycodex-source"
|
|
61
|
+
sync_latest_source openai/codex "$LAZYCODEX_SOURCE_ROOT/openai-codex-source"
|
|
31
62
|
```
|
|
32
63
|
|
|
33
64
|
2. Inventory the installed surface. Resolve `CODEX_HOME` (default `~/.codex`), then collect:
|
|
34
65
|
- `codex --version` and how `codex` resolves (`command -v codex`).
|
|
35
66
|
- Installed LazyCodex version: the `version` in the installed plugin manifest, discoverable with `find "${CODEX_HOME:-$HOME/.codex}/plugins" -path '*/.codex-plugin/plugin.json'`. Installed plugins live under `$CODEX_HOME/plugins/cache/<marketplace>/<name>/<version>/`.
|
|
36
|
-
- Latest LazyCodex version from
|
|
67
|
+
- Latest LazyCodex version from `$LAZYCODEX_SOURCE_ROOT/lazycodex-source` (release tags or the version stamped in the repo) and latest Codex release (`gh release view --repo openai/codex`).
|
|
37
68
|
- OS, install method, and `lazycodex` / `lazycodex-ai` bin links resolving (`command -v`).
|
|
38
|
-
3. Check config and wiring against the latest installer, not against assumptions. Read what the current installer under
|
|
69
|
+
3. Check config and wiring against the latest installer, not against assumptions. Read what the current installer under `$LAZYCODEX_SOURCE_ROOT/lazycodex-source` writes (installer sources live in the omo-codex package, e.g. `scripts/install/`), then verify the local equivalents:
|
|
39
70
|
- `$CODEX_HOME/config.toml` exists and parses; LazyCodex-managed entries match what the latest installer would write.
|
|
40
|
-
- Plugin payload present and non-empty: `hooks/hooks.json
|
|
71
|
+
- Plugin payload present and non-empty: read `.codex-plugin/plugin.json`; when that manifest declares a `hooks` array, validate every direct hook path declared by the manifest; require `hooks/hooks.json` only when the manifest declares it; do not require retired paths such as `components/workflow-selector` or `hooks/user-prompt-submit-selecting-lazycodex-workflow.json` unless the current manifest declares them.
|
|
72
|
+
- Verify the manifest-declared runtime payload, not a remembered source tree. Current payload includes `skills/`, `.mcp.json`, root CLI runtimes such as `dist/cli/index.js` and `dist/cli-node/index.js`, and every hook/MCP `components/*/dist/*.js` target referenced by installed manifests.
|
|
73
|
+
- Treat install-time materialization rewrites as expected when the rewritten target exists and is non-empty. For example, `.mcp.json` may use plugin-local or absolute installed paths for CodeGraph/MCP runtimes; that is PASS/WARN context, not payload drift. Missing or zero-byte rewritten targets are FAIL.
|
|
41
74
|
- Stale project-local leftovers the installer now removes (e.g. `.codex/hooks.json`, `.codex/skills` in the project) are flagged, not deleted.
|
|
42
|
-
4. Probe the real surface. Do not invoke `lazycodex doctor`; this skill is already running inside that doctor workflow, so calling it would recurse. Instead run non-recursive probes directly: `codex --version`, `command -v codex`, the bin-link checks above, config/plugin payload inspections, and a trivial non-interactive Codex invocation that loads the plugin. Capture stderr verbatim; a clean exit with warnings is WARN, not PASS.
|
|
43
|
-
5. Compare for drift. Where installed bundled files differ from the same files at the installed version, or the latest source
|
|
75
|
+
4. Probe the real surface. Do not invoke `lazycodex doctor`; this skill is already running inside that doctor workflow, so calling it would recurse. Instead run non-recursive probes directly: `codex --version`, `command -v codex`, the bin-link checks above, config/plugin payload inspections, and a trivial non-interactive Codex invocation that loads the plugin. Use the configured Codex default model for the runtime probe unless the user explicitly passed a model override to the doctor surface; never force a guessed/rejected model such as `gpt-5.5-codex-mini`. Capture stderr verbatim; a clean exit with warnings is WARN, not PASS.
|
|
76
|
+
5. Compare for drift. Where installed manifest-declared bundled files differ from the same files at the installed version, or the latest source removed or renamed something the local config still references, record it with both paths. Do not report expected materialization differences, such as absolute `.mcp.json` runtime paths, as drift when their targets exist and are non-empty.
|
|
44
77
|
6. Check whether each FAIL is already known: `gh issue list --repo code-yeongyu/lazycodex --search "<short symptom>" --state open` (and `openai/codex` when the failure points upstream). Link matches in the report instead of re-diagnosing from scratch.
|
|
45
78
|
7. If a probe fails and the cause is not explained by config or source comparison, invoke `$omo:debugging` for the investigation. If Codex exposes only unqualified skill names in the current session, invoke `$debugging` and state that it is the OMO debugging skill.
|
|
46
79
|
8. Emit the report.
|
|
@@ -67,7 +100,7 @@ sync_latest_source openai/codex /tmp/openai-codex-source
|
|
|
67
100
|
| Plugin payload wiring | PASS/WARN/FAIL | [evidence] |
|
|
68
101
|
| Bin links / aliases | PASS/WARN/FAIL | [evidence] |
|
|
69
102
|
| Runtime probe | PASS/WARN/FAIL | [evidence] |
|
|
70
|
-
| Drift vs latest source | PASS/WARN/FAIL | [evidence, citing /
|
|
103
|
+
| Drift vs latest source | PASS/WARN/FAIL | [evidence, citing `$LAZYCODEX_SOURCE_ROOT/lazycodex-source` or `$LAZYCODEX_SOURCE_ROOT/openai-codex-source` paths] |
|
|
71
104
|
|
|
72
105
|
### Remediations
|
|
73
106
|
1. [Most important fix first: exact command or config edit, and what it resolves.]
|
|
@@ -79,7 +112,7 @@ sync_latest_source openai/codex /tmp/openai-codex-source
|
|
|
79
112
|
## Follow-up Routing
|
|
80
113
|
|
|
81
114
|
- Local misconfiguration or stale install: give the remediation; reinstalling via the standard LazyCodex install command is the default fix for payload drift.
|
|
82
|
-
- Defect in LazyCodex or Codex product code: recommend `$lcx-report-bug` to file it, or `$lcx-contribute-bug-fix` when the user wants a fix PR. Both reuse the
|
|
115
|
+
- Defect in LazyCodex or Codex product code: recommend `$lcx-report-bug` to file it, or `$lcx-contribute-bug-fix` when the user wants a fix PR. Both reuse the source-root checkouts you already synced.
|
|
83
116
|
|
|
84
117
|
## Stop Conditions
|
|
85
118
|
|
|
@@ -89,5 +122,7 @@ Do not:
|
|
|
89
122
|
|
|
90
123
|
- mutate config, installs, or repositories during diagnosis
|
|
91
124
|
- report a verdict without captured evidence
|
|
92
|
-
- compare against remembered source layout instead of
|
|
125
|
+
- compare against remembered source layout instead of `$LAZYCODEX_SOURCE_ROOT/lazycodex-source` and `$LAZYCODEX_SOURCE_ROOT/openai-codex-source`
|
|
126
|
+
- require retired payload paths that the current `.codex-plugin/plugin.json` does not declare
|
|
127
|
+
- force a runtime-probe model unless the user explicitly passed one
|
|
93
128
|
- declare healthy while any probe output was never captured
|
|
@@ -8,4 +8,4 @@ interface:
|
|
|
8
8
|
- "codex doctor"
|
|
9
9
|
- "lazycodex install broken"
|
|
10
10
|
- "omo-codex diagnose"
|
|
11
|
-
default_prompt: "Use $lcx-doctor to sync the latest LazyCodex and Codex sources
|
|
11
|
+
default_prompt: "Use $lcx-doctor to sync the latest LazyCodex and Codex sources under LAZYCODEX_SOURCE_ROOT or ${TMPDIR:-/tmp}/lazycodex-sources, inventory the local installation, compare versions, config, and wiring against the latest sources, probe the real surface, and report PASS/WARN/FAIL findings with evidence and remediations."
|