oh-my-opencode 4.14.0 → 4.14.2
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 +223 -1447
- package/dist/cli-node/index.js +223 -1447
- 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/frontend/ATTRIBUTION.md +7 -1
- package/dist/skills/frontend/SKILL.md +4 -3
- package/dist/skills/frontend/references/design/README.md +4 -3
- package/dist/skills/frontend/references/design/_INDEX.md +5 -3
- package/dist/skills/frontend/references/design/aside.md +209 -0
- 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/frontend/ATTRIBUTION.md +7 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +4 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +5 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +209 -0
- 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/aggregate-skills.test.mjs +23 -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 +169 -41
- 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/frontend/ATTRIBUTION.md +7 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +4 -3
- package/packages/shared-skills/skills/frontend/references/design/README.md +4 -3
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +5 -3
- package/packages/shared-skills/skills/frontend/references/design/aside.md +209 -0
- 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.2",
|
|
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}" (`,
|
|
@@ -6882,7 +6880,7 @@ async function existingNonSymlink(path) {
|
|
|
6882
6880
|
}
|
|
6883
6881
|
}
|
|
6884
6882
|
// packages/omo-codex/src/install/codex-cache-install.ts
|
|
6885
|
-
import { cp as cp2, mkdir as mkdir3, readFile as readFile7, rename, rm as rm3 } from "node:fs/promises";
|
|
6883
|
+
import { cp as cp2, mkdir as mkdir3, readFile as readFile7, readdir as readdir3, rename, rm as rm3 } from "node:fs/promises";
|
|
6886
6884
|
import { basename as basename3, dirname as dirname4, join as join11, sep as sep5 } from "node:path";
|
|
6887
6885
|
|
|
6888
6886
|
// packages/omo-codex/src/install/codex-cache-bundled-mcps.ts
|
|
@@ -7442,10 +7440,12 @@ 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)
|
|
7448
7447
|
await maybeRunNpmSyncSkills(tempPath, input.runCommand);
|
|
7448
|
+
await assertNoRemovedSparkshellPromptReferences(tempPath);
|
|
7449
7449
|
await rewriteCachedMcpManifest(tempPath, input.sourcePath);
|
|
7450
7450
|
await rewriteCachedManifestRoot(tempPath, tempPath, targetPath);
|
|
7451
7451
|
await assertHookCommandTargets(tempPath);
|
|
@@ -7524,8 +7524,83 @@ function shouldCopyPluginPath(path, root) {
|
|
|
7524
7524
|
const parts = relative4.split(sep5);
|
|
7525
7525
|
return !parts.some((part) => part === ".git" || part === "node_modules");
|
|
7526
7526
|
}
|
|
7527
|
+
var removedSparkshellReferencePattern = /\b(?:sparkshell|spark[-_\s]+shell)\b/i;
|
|
7528
|
+
var removedSparkshellPromptSurfaceDirs = new Set([".codex-plugin", "agents", "bundled-rules", "hooks", "skills"]);
|
|
7529
|
+
var removedSparkshellPromptSurfaceFiles = new Set(["directive.md", "plugin.json"]);
|
|
7530
|
+
var removedSparkshellTextFilePattern = /\.(?:json|md|toml|ya?ml)$/i;
|
|
7531
|
+
async function assertNoRemovedSparkshellPromptReferences(pluginRoot) {
|
|
7532
|
+
for (const filePath of await listRemovedSparkshellPromptSurfaceFiles(pluginRoot, "")) {
|
|
7533
|
+
const content = await readFile7(join11(pluginRoot, filePath), "utf8");
|
|
7534
|
+
if (!removedSparkshellReferencePattern.test(content))
|
|
7535
|
+
continue;
|
|
7536
|
+
throw new Error(`removed sparkshell reference found in Codex plugin prompt surface: ${filePath}`);
|
|
7537
|
+
}
|
|
7538
|
+
}
|
|
7539
|
+
async function listRemovedSparkshellPromptSurfaceFiles(pluginRoot, relativeDirectory) {
|
|
7540
|
+
const directory = relativeDirectory === "" ? pluginRoot : join11(pluginRoot, relativeDirectory);
|
|
7541
|
+
const entries = await readdir3(directory, { withFileTypes: true });
|
|
7542
|
+
const files = [];
|
|
7543
|
+
for (const entry of entries) {
|
|
7544
|
+
const relativePath = relativeDirectory === "" ? entry.name : join11(relativeDirectory, entry.name);
|
|
7545
|
+
if (entry.isDirectory()) {
|
|
7546
|
+
if (shouldDescendIntoRemovedSparkshellPromptSurface(relativePath)) {
|
|
7547
|
+
files.push(...await listRemovedSparkshellPromptSurfaceFiles(pluginRoot, relativePath));
|
|
7548
|
+
}
|
|
7549
|
+
continue;
|
|
7550
|
+
}
|
|
7551
|
+
if (shouldCheckRemovedSparkshellPromptFile(relativePath))
|
|
7552
|
+
files.push(relativePath);
|
|
7553
|
+
}
|
|
7554
|
+
return files.sort();
|
|
7555
|
+
}
|
|
7556
|
+
function shouldDescendIntoRemovedSparkshellPromptSurface(relativePath) {
|
|
7557
|
+
const parts = relativePath.split(sep5);
|
|
7558
|
+
if (parts.some((part) => part === ".git" || part === "dist" || part === "node_modules"))
|
|
7559
|
+
return false;
|
|
7560
|
+
if (parts[0] === "components") {
|
|
7561
|
+
if (parts.length <= 2)
|
|
7562
|
+
return true;
|
|
7563
|
+
return removedSparkshellPromptSurfaceDirs.has(parts[2]);
|
|
7564
|
+
}
|
|
7565
|
+
return removedSparkshellPromptSurfaceDirs.has(parts[0]);
|
|
7566
|
+
}
|
|
7567
|
+
function shouldCheckRemovedSparkshellPromptFile(relativePath) {
|
|
7568
|
+
if (!removedSparkshellTextFilePattern.test(relativePath))
|
|
7569
|
+
return false;
|
|
7570
|
+
const parts = relativePath.split(sep5);
|
|
7571
|
+
const fileName = parts.at(-1) ?? "";
|
|
7572
|
+
if (parts[0] === "components") {
|
|
7573
|
+
if (parts.length === 3)
|
|
7574
|
+
return removedSparkshellPromptSurfaceFiles.has(fileName);
|
|
7575
|
+
return parts.length > 3 && removedSparkshellPromptSurfaceDirs.has(parts[2]);
|
|
7576
|
+
}
|
|
7577
|
+
return removedSparkshellPromptSurfaceDirs.has(parts[0]);
|
|
7578
|
+
}
|
|
7579
|
+
async function copyRootRuntimeDists(input) {
|
|
7580
|
+
const repoRoot = repoRootForCodexPluginSource(input.sourcePath);
|
|
7581
|
+
if (repoRoot === null)
|
|
7582
|
+
return;
|
|
7583
|
+
for (const runtimePath of ["dist/cli", "dist/cli-node"]) {
|
|
7584
|
+
const sourcePath = join11(repoRoot, runtimePath);
|
|
7585
|
+
if (!await fileExistsStrict(join11(sourcePath, "index.js")))
|
|
7586
|
+
continue;
|
|
7587
|
+
await mkdir3(dirname4(join11(input.pluginRoot, runtimePath)), { recursive: true });
|
|
7588
|
+
await cp2(sourcePath, join11(input.pluginRoot, runtimePath), { recursive: true });
|
|
7589
|
+
}
|
|
7590
|
+
}
|
|
7591
|
+
function repoRootForCodexPluginSource(sourcePath) {
|
|
7592
|
+
const codexPackageRoot = dirname4(sourcePath);
|
|
7593
|
+
const packagesRoot = dirname4(codexPackageRoot);
|
|
7594
|
+
if (basename3(sourcePath) !== "plugin")
|
|
7595
|
+
return null;
|
|
7596
|
+
if (basename3(codexPackageRoot) !== "omo-codex")
|
|
7597
|
+
return null;
|
|
7598
|
+
if (basename3(packagesRoot) !== "packages")
|
|
7599
|
+
return null;
|
|
7600
|
+
return dirname4(packagesRoot);
|
|
7601
|
+
}
|
|
7527
7602
|
// packages/omo-codex/src/install/codex-cache-prune.ts
|
|
7528
|
-
import { lstat as lstat4, readdir as
|
|
7603
|
+
import { lstat as lstat4, readdir as readdir4, rm as rm4, stat as stat3 } from "node:fs/promises";
|
|
7529
7604
|
import { join as join12 } from "node:path";
|
|
7530
7605
|
async function pruneMarketplaceCache(input) {
|
|
7531
7606
|
const cacheRoot = join12(input.codexHome, "plugins", "cache", input.marketplaceName);
|
|
@@ -7553,11 +7628,11 @@ async function pruneMarketplacePluginCaches(input) {
|
|
|
7553
7628
|
}
|
|
7554
7629
|
async function readCacheEntries(path) {
|
|
7555
7630
|
const emptyEntries = [];
|
|
7556
|
-
return readCacheRoot(path, () =>
|
|
7631
|
+
return readCacheRoot(path, () => readdir4(path, { withFileTypes: true }), emptyEntries);
|
|
7557
7632
|
}
|
|
7558
7633
|
async function readCacheEntryNames(path) {
|
|
7559
7634
|
const emptyNames = [];
|
|
7560
|
-
return readCacheRoot(path, () =>
|
|
7635
|
+
return readCacheRoot(path, () => readdir4(path), emptyNames);
|
|
7561
7636
|
}
|
|
7562
7637
|
async function readCacheRoot(path, readEntries, fallback) {
|
|
7563
7638
|
try {
|
|
@@ -8117,7 +8192,7 @@ function ensureMarketplaceBlock(config, marketplaceName, source) {
|
|
|
8117
8192
|
}
|
|
8118
8193
|
|
|
8119
8194
|
// packages/omo-codex/src/install/codex-config-permissions.ts
|
|
8120
|
-
var AUTONOMOUS_FEATURES = ["multi_agent", "
|
|
8195
|
+
var AUTONOMOUS_FEATURES = ["multi_agent", "unified_exec", "goals"];
|
|
8121
8196
|
function ensureAutonomousPermissions(config) {
|
|
8122
8197
|
let next = replaceOrInsertRootSetting(config, "approval_policy", JSON.stringify("never"));
|
|
8123
8198
|
next = replaceOrInsertRootSetting(next, "sandbox_mode", JSON.stringify("danger-full-access"));
|
|
@@ -8537,7 +8612,6 @@ async function updateCodexConfig(input) {
|
|
|
8537
8612
|
config = ensureFeatureEnabled(config, "plugins");
|
|
8538
8613
|
config = ensureFeatureEnabled(config, "plugin_hooks");
|
|
8539
8614
|
config = ensureFeatureEnabled(config, "multi_agent");
|
|
8540
|
-
config = ensureFeatureEnabled(config, "child_agents_md");
|
|
8541
8615
|
config = removeUnsupportedCodexMultiAgentModeConfig(config);
|
|
8542
8616
|
config = ensureCodexReasoningConfig(config, await readCodexModelCatalog(input.repoRoot));
|
|
8543
8617
|
config = ensureCodexMultiAgentV2Config(config);
|
|
@@ -8720,7 +8794,7 @@ function toCodexResolution(resolution) {
|
|
|
8720
8794
|
}
|
|
8721
8795
|
|
|
8722
8796
|
// packages/omo-codex/src/install/link-cached-plugin-agents.ts
|
|
8723
|
-
import { copyFile, lstat as lstat7, mkdir as mkdir6, readFile as readFile13, readdir as
|
|
8797
|
+
import { copyFile, lstat as lstat7, mkdir as mkdir6, readFile as readFile13, readdir as readdir5, rm as rm6, writeFile as writeFile6 } from "node:fs/promises";
|
|
8724
8798
|
import { basename as basename5, join as join19 } from "node:path";
|
|
8725
8799
|
|
|
8726
8800
|
// packages/omo-codex/src/install/retired-managed-agent-purge.ts
|
|
@@ -8788,7 +8862,7 @@ async function capturePreservedAgentReasoning(input) {
|
|
|
8788
8862
|
if (!await exists4(agentsDir))
|
|
8789
8863
|
return new Map;
|
|
8790
8864
|
const preserved = new Map;
|
|
8791
|
-
const agentEntries = await
|
|
8865
|
+
const agentEntries = await readdir5(agentsDir, { withFileTypes: true });
|
|
8792
8866
|
for (const entry of agentEntries) {
|
|
8793
8867
|
if (!entry.name.endsWith(".toml"))
|
|
8794
8868
|
continue;
|
|
@@ -8806,7 +8880,7 @@ async function capturePreservedAgentServiceTier(input) {
|
|
|
8806
8880
|
if (!await exists4(agentsDir))
|
|
8807
8881
|
return new Map;
|
|
8808
8882
|
const preserved = new Map;
|
|
8809
|
-
const agentEntries = await
|
|
8883
|
+
const agentEntries = await readdir5(agentsDir, { withFileTypes: true });
|
|
8810
8884
|
for (const entry of agentEntries) {
|
|
8811
8885
|
if (!entry.name.endsWith(".toml"))
|
|
8812
8886
|
continue;
|
|
@@ -8863,7 +8937,7 @@ async function discoverBundledAgents(pluginRoot) {
|
|
|
8863
8937
|
const componentsRoot = join19(pluginRoot, "components");
|
|
8864
8938
|
if (!await exists4(componentsRoot))
|
|
8865
8939
|
return [];
|
|
8866
|
-
const componentEntries = await
|
|
8940
|
+
const componentEntries = await readdir5(componentsRoot, { withFileTypes: true });
|
|
8867
8941
|
const agents = [];
|
|
8868
8942
|
for (const entry of componentEntries) {
|
|
8869
8943
|
if (!entry.isDirectory())
|
|
@@ -8871,7 +8945,7 @@ async function discoverBundledAgents(pluginRoot) {
|
|
|
8871
8945
|
const agentsRoot = join19(componentsRoot, entry.name, "agents");
|
|
8872
8946
|
if (!await exists4(agentsRoot))
|
|
8873
8947
|
continue;
|
|
8874
|
-
const agentEntries = await
|
|
8948
|
+
const agentEntries = await readdir5(agentsRoot, { withFileTypes: true });
|
|
8875
8949
|
for (const file2 of agentEntries) {
|
|
8876
8950
|
if (!file2.isFile() || !file2.name.endsWith(".toml"))
|
|
8877
8951
|
continue;
|
|
@@ -9178,7 +9252,7 @@ function shouldCopyMarketplaceSourcePath(path, root) {
|
|
|
9178
9252
|
}
|
|
9179
9253
|
|
|
9180
9254
|
// packages/omo-codex/src/install/lazycodex-version-stamp.ts
|
|
9181
|
-
import { readdir as
|
|
9255
|
+
import { readdir as readdir6, readFile as readFile15, writeFile as writeFile8 } from "node:fs/promises";
|
|
9182
9256
|
import { join as join22 } from "node:path";
|
|
9183
9257
|
async function readDistributionManifest(repoRoot) {
|
|
9184
9258
|
try {
|
|
@@ -9271,7 +9345,7 @@ async function stampHookStatusMessages(path, version) {
|
|
|
9271
9345
|
async function stampComponentVersions(input) {
|
|
9272
9346
|
let entries;
|
|
9273
9347
|
try {
|
|
9274
|
-
entries = await
|
|
9348
|
+
entries = await readdir6(join22(input.pluginRoot, "components"));
|
|
9275
9349
|
} catch (error) {
|
|
9276
9350
|
if (error instanceof Error)
|
|
9277
9351
|
return;
|
|
@@ -9534,7 +9608,7 @@ function formatUnknownError(error) {
|
|
|
9534
9608
|
}
|
|
9535
9609
|
|
|
9536
9610
|
// packages/omo-codex/src/install/lsp-daemon-reaper.ts
|
|
9537
|
-
import { readFile as readFile17, readdir as
|
|
9611
|
+
import { readFile as readFile17, readdir as readdir7, rm as rm8 } from "node:fs/promises";
|
|
9538
9612
|
import { connect } from "node:net";
|
|
9539
9613
|
import { join as join24 } from "node:path";
|
|
9540
9614
|
async function reapLspDaemons(codexHome, deps = {}) {
|
|
@@ -9544,7 +9618,7 @@ async function reapLspDaemons(codexHome, deps = {}) {
|
|
|
9544
9618
|
const reaped = [];
|
|
9545
9619
|
let entries;
|
|
9546
9620
|
try {
|
|
9547
|
-
entries = await
|
|
9621
|
+
entries = await readdir7(daemonRoot);
|
|
9548
9622
|
} catch {
|
|
9549
9623
|
return reaped;
|
|
9550
9624
|
}
|
|
@@ -13554,7 +13628,7 @@ async function runCodexInstaller(options = {}) {
|
|
|
13554
13628
|
if (runtimeLink !== null)
|
|
13555
13629
|
log2(`Linked ${runtimeLink.name} -> ${runtimeLink.target}`);
|
|
13556
13630
|
else
|
|
13557
|
-
log2(`Warning: skipped the omo runtime wrapper because ${join31(repoRoot, "dist", "cli", "index.js")} is missing; omo
|
|
13631
|
+
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
13632
|
}
|
|
13559
13633
|
pluginSources.push({ name: entry.name, sourcePath });
|
|
13560
13634
|
installed.push(plugin);
|
|
@@ -13855,6 +13929,7 @@ function formatLazyCodexInstallHelp() {
|
|
|
13855
13929
|
"Usage: lazycodex-ai install [--no-tui] [--codex-autonomous|--no-codex-autonomous] [--repo-root <path>]",
|
|
13856
13930
|
" lazycodex-ai uninstall [--project <path>]",
|
|
13857
13931
|
" lazycodex-ai update [--dry-run] [--repo-root <path>]",
|
|
13932
|
+
" lazycodex-ai doctor [--source-root <path>] [--model <model>] [--json|--status|--verbose]",
|
|
13858
13933
|
" lazycodex-ai version",
|
|
13859
13934
|
" lazycodex-ai <command> [args...]",
|
|
13860
13935
|
"",
|
|
@@ -13884,20 +13959,22 @@ async function runDelegatedOmoCommand(parsed, options) {
|
|
|
13884
13959
|
function buildDelegatedOmoInvocation(parsed) {
|
|
13885
13960
|
if (parsed.command === "doctor")
|
|
13886
13961
|
return buildLazyCodexDoctorInvocation(parsed.args);
|
|
13887
|
-
const args = ["--yes", "--package", "oh-my-openagent", "omo", parsed.command];
|
|
13888
13962
|
if (parsed.command === "install") {
|
|
13889
|
-
|
|
13963
|
+
const args2 = ["--yes", "oh-my-openagent@latest", parsed.command, "--platform=codex"];
|
|
13890
13964
|
if (parsed.noTui)
|
|
13891
|
-
|
|
13965
|
+
args2.push("--no-tui");
|
|
13892
13966
|
if (parsed.skipAuth)
|
|
13893
|
-
|
|
13967
|
+
args2.push("--skip-auth");
|
|
13894
13968
|
if (parsed.autonomousPermissions !== false)
|
|
13895
|
-
|
|
13969
|
+
args2.push("--codex-autonomous");
|
|
13896
13970
|
if (parsed.autonomousPermissions === false)
|
|
13897
|
-
|
|
13971
|
+
args2.push("--no-codex-autonomous");
|
|
13898
13972
|
if (parsed.repoRoot)
|
|
13899
|
-
|
|
13900
|
-
|
|
13973
|
+
args2.push(`--repo-root=${parsed.repoRoot}`);
|
|
13974
|
+
return { command: "npx", args: args2, delegatesToOmo: true };
|
|
13975
|
+
}
|
|
13976
|
+
const args = ["--yes", "--package", "oh-my-openagent", "omo", parsed.command];
|
|
13977
|
+
if (parsed.command === "cleanup") {
|
|
13901
13978
|
args.push("--platform=codex", ...parsed.args);
|
|
13902
13979
|
} else {
|
|
13903
13980
|
args.push(...parsed.args);
|
|
@@ -13905,21 +13982,26 @@ function buildDelegatedOmoInvocation(parsed) {
|
|
|
13905
13982
|
return { command: "npx", args, delegatesToOmo: true };
|
|
13906
13983
|
}
|
|
13907
13984
|
function buildLazyCodexDoctorInvocation(doctorArgs) {
|
|
13985
|
+
const doctorOptions = parseLazyCodexDoctorOptions(doctorArgs);
|
|
13986
|
+
const codexArgs = [
|
|
13987
|
+
"exec",
|
|
13988
|
+
"--ephemeral",
|
|
13989
|
+
"--sandbox",
|
|
13990
|
+
"danger-full-access",
|
|
13991
|
+
"--skip-git-repo-check",
|
|
13992
|
+
"--cd",
|
|
13993
|
+
"."
|
|
13994
|
+
];
|
|
13995
|
+
if (doctorOptions.model !== undefined)
|
|
13996
|
+
codexArgs.push("--model", doctorOptions.model);
|
|
13997
|
+
codexArgs.push(buildLazyCodexDoctorPrompt(doctorOptions.args));
|
|
13908
13998
|
return {
|
|
13909
13999
|
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
|
-
],
|
|
14000
|
+
args: codexArgs,
|
|
13920
14001
|
delegatesToOmo: false,
|
|
13921
14002
|
env: {
|
|
13922
|
-
LAZYCODEX_DOCTOR_LCX_ACTIVE: "1"
|
|
14003
|
+
LAZYCODEX_DOCTOR_LCX_ACTIVE: "1",
|
|
14004
|
+
...doctorOptions.sourceRoot === undefined ? {} : { LAZYCODEX_SOURCE_ROOT: doctorOptions.sourceRoot }
|
|
13923
14005
|
}
|
|
13924
14006
|
};
|
|
13925
14007
|
}
|
|
@@ -13927,12 +14009,58 @@ function buildLazyCodexDoctorPrompt(doctorArgs) {
|
|
|
13927
14009
|
return [
|
|
13928
14010
|
"Use $omo:lcx-doctor to diagnose this LazyCodex/Codex installation.",
|
|
13929
14011
|
"This command is already the lazycodex doctor surface; never invoke lazycodex doctor from inside the doctor workflow.",
|
|
13930
|
-
"
|
|
14012
|
+
"Use the resolved source root from LAZYCODEX_SOURCE_ROOT when set; otherwise use ${TMPDIR:-/tmp}/lazycodex-sources.",
|
|
14013
|
+
"Validate cached source checkouts before reuse, quarantine corrupt caches, and do not rely on /tmp/lazycodex-source.",
|
|
14014
|
+
"Sync the latest LazyCodex and OpenAI Codex sources there, inventory the local installation,",
|
|
13931
14015
|
"probe the Codex plugin/cache/hooks/MCP state, and report PASS/WARN/FAIL findings with evidence and remediations.",
|
|
13932
14016
|
buildDoctorOutputInstruction(doctorArgs),
|
|
13933
14017
|
doctorArgs.length > 0 ? `Requested doctor arguments: ${doctorArgs.join(" ")}` : "Requested doctor arguments: none"
|
|
13934
14018
|
].join(" ");
|
|
13935
14019
|
}
|
|
14020
|
+
function parseLazyCodexDoctorOptions(doctorArgs) {
|
|
14021
|
+
const args = [];
|
|
14022
|
+
let model;
|
|
14023
|
+
let sourceRoot;
|
|
14024
|
+
let index = 0;
|
|
14025
|
+
while (index < doctorArgs.length) {
|
|
14026
|
+
const arg = doctorArgs[index];
|
|
14027
|
+
if (arg === "--model") {
|
|
14028
|
+
const value = doctorArgs[index + 1];
|
|
14029
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
14030
|
+
throw new Error("--model requires a value");
|
|
14031
|
+
model = value;
|
|
14032
|
+
index += 2;
|
|
14033
|
+
continue;
|
|
14034
|
+
}
|
|
14035
|
+
if (typeof arg === "string" && arg.startsWith("--model=")) {
|
|
14036
|
+
const value = arg.slice("--model=".length);
|
|
14037
|
+
if (value.trim().length === 0)
|
|
14038
|
+
throw new Error("--model requires a value");
|
|
14039
|
+
model = value;
|
|
14040
|
+
index += 1;
|
|
14041
|
+
continue;
|
|
14042
|
+
}
|
|
14043
|
+
if (arg === "--source-root") {
|
|
14044
|
+
const value = doctorArgs[index + 1];
|
|
14045
|
+
if (typeof value !== "string" || value.trim().length === 0)
|
|
14046
|
+
throw new Error("--source-root requires a path");
|
|
14047
|
+
sourceRoot = value;
|
|
14048
|
+
index += 2;
|
|
14049
|
+
continue;
|
|
14050
|
+
}
|
|
14051
|
+
if (typeof arg === "string" && arg.startsWith("--source-root=")) {
|
|
14052
|
+
const value = arg.slice("--source-root=".length);
|
|
14053
|
+
if (value.trim().length === 0)
|
|
14054
|
+
throw new Error("--source-root requires a path");
|
|
14055
|
+
sourceRoot = value;
|
|
14056
|
+
index += 1;
|
|
14057
|
+
continue;
|
|
14058
|
+
}
|
|
14059
|
+
args.push(arg);
|
|
14060
|
+
index += 1;
|
|
14061
|
+
}
|
|
14062
|
+
return { args, ...model === undefined ? {} : { model }, ...sourceRoot === undefined ? {} : { sourceRoot } };
|
|
14063
|
+
}
|
|
13936
14064
|
function buildDoctorOutputInstruction(doctorArgs) {
|
|
13937
14065
|
if (doctorArgs.includes("--json")) {
|
|
13938
14066
|
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
|
|
|
@@ -186,10 +186,16 @@ SOFTWARE.
|
|
|
186
186
|
|
|
187
187
|
## 5. Project-original files
|
|
188
188
|
|
|
189
|
-
`frontend/SKILL.md`, `frontend/references/design/README.md`, `_INDEX.md`,
|
|
189
|
+
`frontend/SKILL.md`, `frontend/references/design/README.md`, `_INDEX.md`, `aside.md`,
|
|
190
190
|
`design-system-architecture.md`, `react-dev-tooling-skill.md`,
|
|
191
191
|
`frontend/references/perfection/README.md`, `react-perf-tooling.md`, and
|
|
192
192
|
`frontend/scripts/perfection/lighthouse-audit.py` are original to this project and require
|
|
193
193
|
no third-party attribution. The perfection docs and script only invoke third-party tools
|
|
194
194
|
(react-scan, react-doctor, react-grab, playwright-lighthouse, lighthouse, chrome-launcher)
|
|
195
195
|
at runtime; no source from those tools is vendored, so their licenses are not carried here.
|
|
196
|
+
|
|
197
|
+
`frontend/references/design/aside.md` is a project-original synthesis from live browser
|
|
198
|
+
capture evidence and a local reconnaissance run following the MIT-licensed
|
|
199
|
+
`JCodesMore/ai-website-cloner-template` workflow; it is not copied from Aside or from the
|
|
200
|
+
template. Aside names, trademarks, product text, and visual assets remain the property of
|
|
201
|
+
their respective owners and are referenced only for descriptive design-analysis purposes.
|
|
@@ -33,7 +33,7 @@ When `references/designpowers/README.md` is loaded for implementation, redesign,
|
|
|
33
33
|
|
|
34
34
|
## Ruleset 1 — design (`references/design/`)
|
|
35
35
|
|
|
36
|
-
The reference library has one architecture file, 12 taste skills (Layer A — *how to execute*), and
|
|
36
|
+
The reference library has one architecture file, 12 taste skills (Layer A — *how to execute*), and 70 brand design systems (Layer B — *what it should look like*). Most non-trivial tasks load **one Layer A + one Layer B**. `README.md` carries the full routing flow, stacking rules, anti-patterns, and the mandatory browser-based Design QA phase; `_INDEX.md` catalogs all 83 files with mood-to-brand mappings — read it whenever routing is not obvious from the tables below.
|
|
37
37
|
|
|
38
38
|
### Layer 0 — architecture
|
|
39
39
|
|
|
@@ -58,7 +58,7 @@ The reference library has one architecture file, 12 taste skills (Layer A — *h
|
|
|
58
58
|
|
|
59
59
|
### Layer B — brand design systems (orthogonal to Layer A; stack freely)
|
|
60
60
|
|
|
61
|
-
When the user names a brand or site — "Linear-style", "like Stripe's landing" — load `references/design/<brand>.md` as the token source of truth (palette, type scale, components, do/don'ts). Coverage includes `apple` `stripe` `linear.app` `notion` `vercel` `claude` `figma` `airbnb` `nike` `tesla` `spotify` `raycast` `revolut` and ~56 more; the full list with mood shortcuts is in `_INDEX.md`. Extract the tokens and apply them to the project's own content — never copy logos or trademarked imagery. If the named brand is missing, fall back to a Layer A mood match or the `open-design` skill.
|
|
61
|
+
When the user names a brand or site — "Linear-style", "like Stripe's landing", "Aside-style browser agent" — load `references/design/<brand>.md` as the token source of truth (palette, type scale, components, do/don'ts). Coverage includes `aside` `apple` `stripe` `linear.app` `notion` `vercel` `claude` `figma` `airbnb` `nike` `tesla` `spotify` `raycast` `revolut` and ~56 more; the full list with mood shortcuts is in `_INDEX.md`. Extract the tokens and apply them to the project's own content — never copy logos or trademarked imagery. If the named brand is missing, fall back to a Layer A mood match or the `open-design` skill.
|
|
62
62
|
|
|
63
63
|
### React dev tooling
|
|
64
64
|
|
|
@@ -102,6 +102,7 @@ Domains: `product` `style` `typography` `color` `landing` `chart` `ux` `react` `
|
|
|
102
102
|
| Request | Load |
|
|
103
103
|
|---|---|
|
|
104
104
|
| "Build a landing page" (no direction given) | `design/README.md` + `design/_INDEX.md` shortlist → exactly one Layer B reference + `design/taste-skill.md` + `perfection/README.md` |
|
|
105
|
+
| "Aside-style AI browser / browser agent page" | `design/README.md` + `design/aside.md` + `design/taste-skill.md` + `perfection/README.md` |
|
|
105
106
|
| "Linear-style landing page" | `design/README.md` + `design/linear.app.md` + `design/taste-skill.md` + `perfection/README.md` |
|
|
106
107
|
| "Premium SaaS hero like Stripe" | `design/README.md` + `design/stripe.md` + `design/soft-skill.md` + `perfection/README.md` |
|
|
107
108
|
| "Improve this existing dashboard" | `design/README.md` + `design/redesign-skill.md` + `perfection/README.md` |
|
|
@@ -125,7 +126,7 @@ Domains: `product` `style` `typography` `color` `landing` `chart` `ux` `react` `
|
|
|
125
126
|
|
|
126
127
|
| Situation | Load |
|
|
127
128
|
|---|---|
|
|
128
|
-
| Brand/style not among the
|
|
129
|
+
| Brand/style not among the 70 in `references/design/`, or the user says "Open Design" | `open-design` skill — the local nexu-io/open-design library (137+ design skills, 150+ design systems) |
|
|
129
130
|
| Driving a browser for the Design QA phase | `agent-browser` skill |
|
|
130
131
|
| Pure TypeScript/logic work with zero visual surface | `programming` skill alone — this skill adds nothing there |
|
|
131
132
|
|
|
@@ -16,9 +16,9 @@ Two things ship flat most often, and both read as "clean but generic": the **her
|
|
|
16
16
|
The library lives flat in this directory (`references/design/`, max depth 1) and has two conceptual layers, and **most non-trivial tasks load one from each layer**:
|
|
17
17
|
|
|
18
18
|
- **Layer A — taste skills (12 files):** how to execute. Discipline, motion physics, spacing rules, anti-slop guardrails, output completeness. Filenames end in `-skill.md` or start with `imagegen-`.
|
|
19
|
-
- **Layer B — design systems (
|
|
19
|
+
- **Layer B — design systems (70 files):** what it should look like. Concrete color/type/component tokens for one specific brand aesthetic. Filenames are brand names (`aside.md`, `claude.md`, `notion.md`, `stripe.md`, ...).
|
|
20
20
|
|
|
21
|
-
A combined directory of all
|
|
21
|
+
A combined directory of all 83 reference files is at `_INDEX.md`. **Read that index before loading anything** unless the routing is obvious — it has the full mood-mapping and stacking rules in one place.
|
|
22
22
|
|
|
23
23
|
## Open Design Library
|
|
24
24
|
|
|
@@ -94,7 +94,7 @@ Run through this in order and stop at the first match. Do not skip — earlier r
|
|
|
94
94
|
|
|
95
95
|
### Step 1 — Did the user name a specific brand or site?
|
|
96
96
|
|
|
97
|
-
Phrasings: "make it look like Linear", "Stripe-style buttons", "Notion-feel sidebar", "like {brand}'s landing page", or pasting a screenshot of a known brand site.
|
|
97
|
+
Phrasings: "make it look like Linear", "Stripe-style buttons", "Notion-feel sidebar", "Aside-style browser agent", "like {brand}'s landing page", or pasting a screenshot of a known brand site.
|
|
98
98
|
|
|
99
99
|
**Action:** Open `_INDEX.md`, find the brand under "Layer B — Design Systems", then load `<brand>.md`. Use it as the project's design system source of truth (color hex values, type scale, component specs, do/don'ts).
|
|
100
100
|
|
|
@@ -201,6 +201,7 @@ Once references are loaded, before writing any UI code:
|
|
|
201
201
|
| User asks for... | Load these |
|
|
202
202
|
|---|---|
|
|
203
203
|
| "Build me a landing page" (no other info) | `_INDEX.md` shortlist → exactly one Layer B reference + `taste-skill.md` |
|
|
204
|
+
| "Build me an Aside-style AI browser / agent page" | `aside.md` + `taste-skill.md` |
|
|
204
205
|
| "Build me a Linear-style landing page" | `linear.app.md` + `taste-skill.md` |
|
|
205
206
|
| "Make it Notion-like and minimal" | `notion.md` + `minimalist-skill.md` |
|
|
206
207
|
| "Premium SaaS hero, like Stripe" | `stripe.md` + `soft-skill.md` |
|