oh-my-opencode 4.11.1 → 4.12.0
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/scripts/lib/app-server-client.mjs +132 -82
- package/.agents/skills/codex-qa/scripts/lib/app-server-client.test.js +48 -0
- package/.agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh +361 -84
- package/.agents/skills/work-with-pr/SKILL.md +19 -5
- package/.opencode/skills/work-with-pr/SKILL.md +19 -5
- package/dist/agents/types.d.ts +7 -6
- package/dist/cli/index.js +438 -306
- package/dist/cli-node/index.js +438 -306
- package/dist/features/background-agent/parent-wake-flush-runner.d.ts +1 -0
- package/dist/features/background-agent/parent-wake-history-state.d.ts +1 -0
- package/dist/features/background-agent/parent-wake-session-history.d.ts +1 -47
- package/dist/features/background-agent/parent-wake-session-message.d.ts +47 -0
- package/dist/index.js +245 -69
- package/dist/plugin/tool-execute-before.d.ts +2 -0
- package/dist/plugin-handlers/plugin-components-loader.d.ts +1 -0
- package/dist/skills/ulw-plan/SKILL.md +2 -1
- package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
- package/dist/skills/ulw-plan/references/intent-clear.md +2 -2
- package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -2
- package/dist/tools/delegate-task/sync-session-turns.d.ts +3 -0
- package/dist/tui.js +16 -7
- package/package.json +13 -13
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +22 -2
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +25 -9
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +10 -3
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +9 -2
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +7 -2
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +2 -1
- package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +9 -9
- package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +94 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +33 -0
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/test/package-smoke.test.ts +2 -71
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +26 -104
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +12 -92
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +157 -234
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +227 -15
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +62 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +34 -3
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +139 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +111 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +250 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +220 -0
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/test-support/package-smoke-fixture.ts +158 -0
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +76 -49
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +17 -0
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +17 -0
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +17 -0
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +17 -0
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +17 -0
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +17 -0
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +16 -0
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +16 -0
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +16 -0
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +16 -0
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +16 -0
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +17 -0
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +16 -0
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +16 -0
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +16 -0
- package/packages/omo-codex/plugin/package-lock.json +12 -22
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +14 -11
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +15 -4
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +37 -0
- package/packages/omo-codex/plugin/skills/ast-grep/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/comment-checker/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/debugging/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/frontend/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/git-master/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/init-deep/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lsp/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/lsp-setup/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/programming/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/refactor/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/remove-ai-slops/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/review-work/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/rules/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/start-work/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +139 -0
- package/packages/omo-codex/plugin/skills/teammode/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +111 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +250 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +220 -0
- package/packages/omo-codex/plugin/skills/ultraresearch/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/ulw-loop/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -2
- package/packages/omo-codex/plugin/skills/visual-qa/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +34 -33
- package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +5 -2
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +13 -0
- package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +14 -6
- package/packages/omo-codex/plugin/test/bootstrap-ps-guard.test.mjs +7 -3
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/display-metadata.test.mjs +83 -0
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +30 -19
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +9 -4
- package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +88 -0
- package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +240 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +385 -206
- package/packages/omo-codex/scripts/install-hook-targets.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
- package/packages/omo-codex/scripts/install-local-bun-global-update.test.mjs +63 -0
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +109 -1
- package/packages/omo-codex/scripts/install-local.mjs +1 -0
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -1
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -2
- package/packages/omo-codex/plugin/hooks/hooks.json +0 -214
|
@@ -7,16 +7,24 @@ import {
|
|
|
7
7
|
collectCommandHooks,
|
|
8
8
|
exists,
|
|
9
9
|
hookLocation,
|
|
10
|
+
readAggregateHookManifests,
|
|
10
11
|
readComponentHookManifests,
|
|
11
|
-
readJson,
|
|
12
|
-
readPluginVersion,
|
|
13
12
|
root,
|
|
14
13
|
} from "./aggregate-plugin-fixture.mjs";
|
|
15
14
|
|
|
15
|
+
async function readAggregateCommandHooks() {
|
|
16
|
+
const manifests = await readAggregateHookManifests();
|
|
17
|
+
return manifests.flatMap(({ source, hooks }) => collectCommandHooks(hooks, source));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function readAggregateHooksText() {
|
|
21
|
+
const manifests = await readAggregateHookManifests();
|
|
22
|
+
return manifests.map(({ hooks }) => JSON.stringify(hooks)).join("\n");
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
test("#given isolated components #when hooks are inspected #then commands stay inside component roots", async () => {
|
|
17
26
|
// given
|
|
18
|
-
const
|
|
19
|
-
const text = JSON.stringify(hooks);
|
|
27
|
+
const text = await readAggregateHooksText();
|
|
20
28
|
|
|
21
29
|
// when
|
|
22
30
|
const componentMarkers = [
|
|
@@ -40,12 +48,11 @@ test("#given isolated components #when hooks are inspected #then commands stay i
|
|
|
40
48
|
|
|
41
49
|
test("#given aggregate SubagentStop hooks #when inspected #then start-work and LazyCodex executor verifier are separate groups", async () => {
|
|
42
50
|
// given
|
|
43
|
-
const
|
|
44
|
-
const aggregateVersion = await readPluginVersion();
|
|
51
|
+
const manifests = await readAggregateHookManifests();
|
|
45
52
|
|
|
46
53
|
// when
|
|
47
|
-
const subagentStopGroups = hooks.hooks.SubagentStop;
|
|
48
|
-
const verifierGroups = collectCommandHooks(hooks,
|
|
54
|
+
const subagentStopGroups = manifests.filter(({ hooks }) => hooks.hooks.SubagentStop).flatMap(({ hooks }) => hooks.hooks.SubagentStop);
|
|
55
|
+
const verifierGroups = manifests.flatMap(({ source, hooks }) => collectCommandHooks(hooks, source)).filter(
|
|
49
56
|
(hook) =>
|
|
50
57
|
hook.eventName === "SubagentStop" &&
|
|
51
58
|
hook.handler.command ===
|
|
@@ -57,21 +64,17 @@ test("#given aggregate SubagentStop hooks #when inspected #then start-work and L
|
|
|
57
64
|
assert.equal(subagentStopGroups[0]?.matcher, undefined);
|
|
58
65
|
assert.equal(subagentStopGroups[1]?.matcher, "^lazycodex-executor$");
|
|
59
66
|
assert.equal(verifierGroups.length, 1);
|
|
60
|
-
assert.equal(verifierGroups[0]?.groupIndex,
|
|
67
|
+
assert.equal(verifierGroups[0]?.groupIndex, 0);
|
|
61
68
|
assert.equal(verifierGroups[0]?.handler.timeout, 10);
|
|
62
|
-
assert.equal(
|
|
63
|
-
verifierGroups[0]?.handler.statusMessage,
|
|
64
|
-
`LazyCodex(${aggregateVersion}): Verifying LazyCodex Executor Evidence`,
|
|
65
|
-
);
|
|
69
|
+
assert.equal(verifierGroups[0]?.handler.statusMessage, "(OmO) Verifying LazyCodex Executor Evidence");
|
|
66
70
|
});
|
|
67
71
|
|
|
68
72
|
test("#given aggregate PostCompact hooks #when hooks are inspected #then LSP diagnostics cache reset is registered", async () => {
|
|
69
73
|
// given
|
|
70
|
-
const
|
|
71
|
-
const aggregateVersion = await readPluginVersion();
|
|
74
|
+
const commandHooks = await readAggregateCommandHooks();
|
|
72
75
|
|
|
73
76
|
// when
|
|
74
|
-
const lspPostCompactHooks =
|
|
77
|
+
const lspPostCompactHooks = commandHooks.filter(
|
|
75
78
|
(hook) =>
|
|
76
79
|
hook.eventName === "PostCompact" &&
|
|
77
80
|
hook.handler.command === 'node "${PLUGIN_ROOT}/components/lsp/dist/cli.js" hook post-compact',
|
|
@@ -79,15 +82,13 @@ test("#given aggregate PostCompact hooks #when hooks are inspected #then LSP dia
|
|
|
79
82
|
|
|
80
83
|
// then
|
|
81
84
|
assert.equal(lspPostCompactHooks.length, 1);
|
|
82
|
-
assert.equal(lspPostCompactHooks[0]?.handler.statusMessage,
|
|
85
|
+
assert.equal(lspPostCompactHooks[0]?.handler.statusMessage, "(OmO) Resetting LSP Diagnostics Cache");
|
|
83
86
|
});
|
|
84
87
|
|
|
85
88
|
test("#given aggregate hook commands #when inspected #then every command exposes a Codex status message", async () => {
|
|
86
89
|
// given
|
|
87
|
-
const hooks = await readJson("hooks/hooks.json");
|
|
88
|
-
|
|
89
90
|
// when
|
|
90
|
-
const commandHooks =
|
|
91
|
+
const commandHooks = await readAggregateCommandHooks();
|
|
91
92
|
const missingStatusMessages = commandHooks
|
|
92
93
|
.filter(({ handler }) => typeof handler.statusMessage !== "string" || handler.statusMessage.trim() === "")
|
|
93
94
|
.map(hookLocation);
|
|
@@ -98,10 +99,8 @@ test("#given aggregate hook commands #when inspected #then every command exposes
|
|
|
98
99
|
|
|
99
100
|
test("#given aggregate hook commands #when inspected #then commands stay Node-based and platform-neutral", async () => {
|
|
100
101
|
// given
|
|
101
|
-
const hooks = await readJson("hooks/hooks.json");
|
|
102
|
-
|
|
103
102
|
// when
|
|
104
|
-
const commands =
|
|
103
|
+
const commands = (await readAggregateCommandHooks()).map(({ handler }) => handler.command);
|
|
105
104
|
|
|
106
105
|
// then
|
|
107
106
|
assert(!commands.some((command) => /\bpython3?\b/i.test(command)));
|
|
@@ -126,12 +125,11 @@ test("#given component hook commands #when inspected #then standalone packages e
|
|
|
126
125
|
|
|
127
126
|
test("#given hook status messages #when inspected #then labels describe OMO responsibilities instead of the hook runner", async () => {
|
|
128
127
|
// given
|
|
129
|
-
const aggregateHooks = await readJson("hooks/hooks.json");
|
|
130
128
|
const componentHooks = await readComponentHookManifests();
|
|
131
129
|
|
|
132
130
|
// when
|
|
133
131
|
const commandHooks = [
|
|
134
|
-
...
|
|
132
|
+
...(await readAggregateCommandHooks()),
|
|
135
133
|
...componentHooks.flatMap(({ source, hooks }) => collectCommandHooks(hooks, source)),
|
|
136
134
|
];
|
|
137
135
|
const genericStatusMessages = commandHooks
|
|
@@ -144,11 +142,11 @@ test("#given hook status messages #when inspected #then labels describe OMO resp
|
|
|
144
142
|
|
|
145
143
|
test("#given aggregate OMO plugin is enabled #when hooks are inspected #then shell guidance and ulw-loop guard are registered", async () => {
|
|
146
144
|
// given
|
|
147
|
-
const
|
|
148
|
-
const text =
|
|
145
|
+
const manifests = await readAggregateHookManifests();
|
|
146
|
+
const text = await readAggregateHooksText();
|
|
149
147
|
|
|
150
148
|
// when
|
|
151
|
-
const preToolUseGroups = hooks.hooks.PreToolUse;
|
|
149
|
+
const preToolUseGroups = manifests.filter(({ hooks }) => hooks.hooks.PreToolUse).flatMap(({ hooks }) => hooks.hooks.PreToolUse);
|
|
152
150
|
|
|
153
151
|
// then
|
|
154
152
|
assert.match(text, /components\/git-bash\/dist\/cli\.js/);
|
|
@@ -162,14 +160,17 @@ test("#given aggregate OMO plugin is enabled #when hooks are inspected #then she
|
|
|
162
160
|
|
|
163
161
|
test("#given aggregate SessionStart hooks #when inspected #then LazyCodex auto-update is registered", async () => {
|
|
164
162
|
// given
|
|
165
|
-
const
|
|
166
|
-
const text =
|
|
163
|
+
const manifests = await readAggregateHookManifests();
|
|
164
|
+
const text = await readAggregateHooksText();
|
|
167
165
|
|
|
168
166
|
// when
|
|
169
|
-
const sessionStartCommands =
|
|
167
|
+
const sessionStartCommands = (await readAggregateCommandHooks())
|
|
170
168
|
.filter(({ eventName }) => eventName === "SessionStart")
|
|
171
169
|
.map(({ handler }) => handler.command);
|
|
172
|
-
const autoUpdateGroup =
|
|
170
|
+
const autoUpdateGroup = manifests
|
|
171
|
+
.filter(({ hooks }) => hooks.hooks.SessionStart)
|
|
172
|
+
.flatMap(({ hooks }) => hooks.hooks.SessionStart)
|
|
173
|
+
.find((group) => JSON.stringify(group).includes("scripts/auto-update.mjs"));
|
|
173
174
|
|
|
174
175
|
// then
|
|
175
176
|
assert.equal(autoUpdateGroup?.matcher, "^startup$");
|
|
@@ -180,7 +181,7 @@ test("#given aggregate SessionStart hooks #when inspected #then LazyCodex auto-u
|
|
|
180
181
|
|
|
181
182
|
test("#given aggregate plugin packaging #when inspected #then hooks and compatibility sentinels stay Python-free", async () => {
|
|
182
183
|
// given
|
|
183
|
-
const hooksText = await readFile(join(root,
|
|
184
|
+
const hooksText = (await Promise.all((await readAggregateHookManifests()).map(({ source }) => readFile(join(root, source), "utf8")))).join("\n");
|
|
184
185
|
const aggregateTestText = await readFile(join(root, "test/aggregate.test.mjs"), "utf8");
|
|
185
186
|
|
|
186
187
|
// when
|
|
@@ -10,13 +10,16 @@ test("#given aggregate plugin manifest #when inspected #then it owns the omo nam
|
|
|
10
10
|
const manifest = await readJson(".codex-plugin/plugin.json");
|
|
11
11
|
|
|
12
12
|
// when
|
|
13
|
-
const
|
|
13
|
+
const hookPaths = manifest.hooks;
|
|
14
14
|
const skillsPath = manifest.skills;
|
|
15
15
|
const mcpPath = manifest.mcpServers;
|
|
16
16
|
|
|
17
17
|
// then
|
|
18
18
|
assert.equal(manifest.name, "omo");
|
|
19
|
-
assert.
|
|
19
|
+
assert(Array.isArray(hookPaths));
|
|
20
|
+
assert.equal(hookPaths.length, 19);
|
|
21
|
+
assert(hookPaths.every((hookPath) => typeof hookPath === "string" && hookPath.startsWith("./hooks/")));
|
|
22
|
+
assert(!hookPaths.includes("./hooks/hooks.json"));
|
|
20
23
|
assert.equal(skillsPath, "./skills/");
|
|
21
24
|
assert.equal(mcpPath, "./.mcp.json");
|
|
22
25
|
});
|
|
@@ -17,6 +17,19 @@ export async function readPluginVersion() {
|
|
|
17
17
|
return (await readJson(".codex-plugin/plugin.json")).version;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export async function readAggregateHookManifests() {
|
|
21
|
+
const manifest = await readJson(".codex-plugin/plugin.json");
|
|
22
|
+
const hookPaths = Array.isArray(manifest.hooks) ? manifest.hooks : [manifest.hooks];
|
|
23
|
+
return Promise.all(
|
|
24
|
+
hookPaths
|
|
25
|
+
.filter((hookPath) => typeof hookPath === "string")
|
|
26
|
+
.map(async (hookPath) => {
|
|
27
|
+
const source = hookPath.replace(/^\.\//, "");
|
|
28
|
+
return { source, hooks: await readJson(source) };
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
20
33
|
export async function exists(relativePath) {
|
|
21
34
|
try {
|
|
22
35
|
await stat(join(root, relativePath));
|
|
@@ -50,7 +50,7 @@ async function writeVersionedRoot(root, version, { withRuntimeCli = false } = {}
|
|
|
50
50
|
hooks: [
|
|
51
51
|
{
|
|
52
52
|
command: 'node "${PLUGIN_ROOT}/components/bootstrap/dist/cli.js" hook session-start',
|
|
53
|
-
statusMessage:
|
|
53
|
+
statusMessage: "(OmO) Checking Bootstrap Provisioning",
|
|
54
54
|
timeout: 30,
|
|
55
55
|
type: "command",
|
|
56
56
|
},
|
|
@@ -3,7 +3,13 @@ import { readdir, readFile, stat } from "node:fs/promises";
|
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
collectCommandHooks,
|
|
8
|
+
readAggregateHookManifests,
|
|
9
|
+
readComponentHookManifests,
|
|
10
|
+
readJson,
|
|
11
|
+
root,
|
|
12
|
+
} from "./aggregate-plugin-fixture.mjs";
|
|
7
13
|
|
|
8
14
|
const PLUGIN_ROOT_TARGET_PATTERN = /\$\{PLUGIN_ROOT\}([^"']+)/g;
|
|
9
15
|
const SKIPPED_DIRECTORY_NAMES = new Set([".git", "node_modules"]);
|
|
@@ -58,13 +64,13 @@ function collectPluginRootTargets(handler) {
|
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
async function readHookManifestsWithRoots() {
|
|
61
|
-
const aggregate = { source
|
|
67
|
+
const aggregate = (await readAggregateHookManifests()).map(({ source, hooks }) => ({ source, hooks, roots: [root] }));
|
|
62
68
|
const components = (await readComponentHookManifests()).map(({ source, hooks }) => ({
|
|
63
69
|
source,
|
|
64
70
|
hooks,
|
|
65
71
|
roots: [dirname(dirname(join(root, source))), root],
|
|
66
72
|
}));
|
|
67
|
-
return [aggregate, ...components];
|
|
73
|
+
return [...aggregate, ...components];
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
function findBootstrapSessionStartHandlers(hooks) {
|
|
@@ -123,11 +129,13 @@ test("#given aggregate and component hook manifests #when command targets are re
|
|
|
123
129
|
|
|
124
130
|
test("#given the bootstrap component #when its SessionStart registration is inspected #then aggregate and component entries declare both platform commands", async () => {
|
|
125
131
|
// given
|
|
126
|
-
const aggregateHooks = await
|
|
132
|
+
const aggregateHooks = (await readAggregateHookManifests()).find(({ source }) =>
|
|
133
|
+
source.endsWith("session-start-checking-bootstrap-provisioning.json"),
|
|
134
|
+
)?.hooks;
|
|
127
135
|
const componentHooks = await readJson("components/bootstrap/hooks/hooks.json");
|
|
128
136
|
|
|
129
137
|
// when
|
|
130
|
-
const aggregateEntries = findBootstrapSessionStartHandlers(aggregateHooks);
|
|
138
|
+
const aggregateEntries = findBootstrapSessionStartHandlers(aggregateHooks ?? {});
|
|
131
139
|
const componentEntries = findBootstrapSessionStartHandlers(componentHooks);
|
|
132
140
|
|
|
133
141
|
// then
|
|
@@ -144,7 +152,7 @@ test("#given the bootstrap component #when its SessionStart registration is insp
|
|
|
144
152
|
assert.equal(typeof handler.timeout, "number");
|
|
145
153
|
assert(handler.timeout <= 60, `${label} bootstrap timeout must stay <= 60 seconds`);
|
|
146
154
|
assert.equal(typeof handler.statusMessage, "string");
|
|
147
|
-
assert.match(handler.statusMessage,
|
|
155
|
+
assert.match(handler.statusMessage, /^\(OmO\) .+$/);
|
|
148
156
|
}
|
|
149
157
|
});
|
|
150
158
|
|
|
@@ -3,10 +3,11 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
|
|
6
|
-
import { collectCommandHooks, readJson, root } from "./aggregate-plugin-fixture.mjs";
|
|
6
|
+
import { collectCommandHooks, readAggregateHookManifests, readJson, root } from "./aggregate-plugin-fixture.mjs";
|
|
7
7
|
|
|
8
8
|
const BOOTSTRAP_SCRIPT_RELATIVE_PATH = join("components", "bootstrap", "scripts", "bootstrap.ps1");
|
|
9
|
-
const
|
|
9
|
+
const BOOTSTRAP_AGGREGATE_HOOK_SOURCE = "hooks/session-start-checking-bootstrap-provisioning.json";
|
|
10
|
+
const HOOK_MANIFEST_SOURCES = [BOOTSTRAP_AGGREGATE_HOOK_SOURCE, "components/bootstrap/hooks/hooks.json"];
|
|
10
11
|
const BOOTSTRAP_PS1_COMMAND_WINDOWS_TARGET = "\\components\\bootstrap\\scripts\\bootstrap.ps1";
|
|
11
12
|
const TLS12_LINE_PATTERN =
|
|
12
13
|
/\[Net\.ServicePointManager\]::SecurityProtocol\s*=\s*\[Net\.ServicePointManager\]::SecurityProtocol\s+-bor\s+\[Net\.SecurityProtocolType\]::Tls12/;
|
|
@@ -114,9 +115,12 @@ test("#given bootstrap.ps1 #when every character is inspected #then the script i
|
|
|
114
115
|
});
|
|
115
116
|
|
|
116
117
|
test("#given the aggregate and bootstrap component hook manifests #when commandWindows entries are read #then the bootstrap SessionStart hook launches bootstrap.ps1", async () => {
|
|
118
|
+
const aggregateHooks = await readAggregateHookManifests();
|
|
117
119
|
for (const source of HOOK_MANIFEST_SOURCES) {
|
|
118
120
|
// given
|
|
119
|
-
const hooks =
|
|
121
|
+
const hooks =
|
|
122
|
+
aggregateHooks.find((manifest) => manifest.source === source)?.hooks ??
|
|
123
|
+
(await readJson(source));
|
|
120
124
|
|
|
121
125
|
// when
|
|
122
126
|
const launchers = collectCommandHooks(hooks, source)
|
|
@@ -6,7 +6,7 @@ import { join } from "node:path";
|
|
|
6
6
|
import { spawn, spawnSync } from "node:child_process";
|
|
7
7
|
import test from "node:test";
|
|
8
8
|
|
|
9
|
-
import { readJson, root } from "./aggregate-plugin-fixture.mjs";
|
|
9
|
+
import { readAggregateHookManifests, readJson, root } from "./aggregate-plugin-fixture.mjs";
|
|
10
10
|
import { componentHookContractCases } from "./component-hook-contract-cases.mjs";
|
|
11
11
|
|
|
12
12
|
const HOOK_EVENTS_BY_COMPONENT = {
|
|
@@ -142,8 +142,8 @@ test("#given malformed comment-checker stdin #when executed through dist CLI con
|
|
|
142
142
|
|
|
143
143
|
test("#given aggregate hook manifest #when command hooks are inspected #then component CLI invocation contract is unchanged", async () => {
|
|
144
144
|
// given
|
|
145
|
-
const
|
|
146
|
-
const commands = collectHookCommands(hooks.hooks);
|
|
145
|
+
const manifests = await readAggregateHookManifests();
|
|
146
|
+
const commands = manifests.flatMap(({ hooks }) => collectHookCommands(hooks.hooks));
|
|
147
147
|
const components = await workspaceComponents();
|
|
148
148
|
|
|
149
149
|
// when
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
|
|
6
|
+
import { readJson, root } from "./aggregate-plugin-fixture.mjs";
|
|
7
|
+
|
|
8
|
+
const hookSlugPattern = /^\.\/hooks\/[a-z0-9]+(?:-[a-z0-9]+)*\.json$/;
|
|
9
|
+
const displayNamePrefix = "(OmO) ";
|
|
10
|
+
|
|
11
|
+
async function exists(path) {
|
|
12
|
+
try {
|
|
13
|
+
await stat(path);
|
|
14
|
+
return true;
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function readSkillName(skillPath) {
|
|
22
|
+
const content = await readFile(skillPath, "utf8");
|
|
23
|
+
const frontmatter = content.match(/^---\n(?<body>[\s\S]*?)\n---\n/);
|
|
24
|
+
assert(frontmatter?.groups?.body, `${skillPath} must have YAML frontmatter`);
|
|
25
|
+
const name = frontmatter.groups.body.match(/^name:\s*"?([^"\n]+)"?\s*$/m)?.[1]?.trim();
|
|
26
|
+
assert(name, `${skillPath} must have a frontmatter name`);
|
|
27
|
+
return name;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function readOpenAiDisplayName(skillDir) {
|
|
31
|
+
const metadataPath = join(skillDir, "agents", "openai.yaml");
|
|
32
|
+
assert(await exists(metadataPath), `${basename(skillDir)} must include agents/openai.yaml`);
|
|
33
|
+
const content = await readFile(metadataPath, "utf8");
|
|
34
|
+
const displayName = content.match(/^\s*display_name:\s*"?([^"\n]+)"?\s*$/m)?.[1]?.trim();
|
|
35
|
+
assert(displayName, `${metadataPath} must define interface.display_name`);
|
|
36
|
+
return displayName;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test("#given Codex plugin hooks #when plugin metadata is inspected #then hook keys contain human-readable hook slugs", async () => {
|
|
40
|
+
// given
|
|
41
|
+
const manifest = await readJson(".codex-plugin/plugin.json");
|
|
42
|
+
|
|
43
|
+
// when
|
|
44
|
+
const hookPaths = manifest.hooks;
|
|
45
|
+
|
|
46
|
+
// then
|
|
47
|
+
assert(Array.isArray(hookPaths), "plugin manifest hooks must list per-hook metadata files");
|
|
48
|
+
assert(hookPaths.length > 0, "plugin manifest must expose at least one hook file");
|
|
49
|
+
for (const hookPath of hookPaths) {
|
|
50
|
+
assert.equal(typeof hookPath, "string");
|
|
51
|
+
assert.match(hookPath, hookSlugPattern);
|
|
52
|
+
assert.notEqual(hookPath, "./hooks/hooks.json");
|
|
53
|
+
const hooksJson = await readJson(hookPath.replace(/^\.\//, ""));
|
|
54
|
+
const commandHookCount = Object.values(hooksJson.hooks)
|
|
55
|
+
.flat()
|
|
56
|
+
.flatMap((group) => group.hooks)
|
|
57
|
+
.filter((hook) => hook.type === "command").length;
|
|
58
|
+
assert.equal(commandHookCount, 1, `${hookPath} should describe exactly one displayed hook`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("#given Codex plugin skills #when skill metadata is inspected #then every skill has an OmO display name", async () => {
|
|
63
|
+
// given
|
|
64
|
+
const skillsDir = join(root, "skills");
|
|
65
|
+
const skillEntries = await readdir(skillsDir, { withFileTypes: true });
|
|
66
|
+
const skillDirs = skillEntries.filter((entry) => entry.isDirectory()).map((entry) => join(skillsDir, entry.name));
|
|
67
|
+
|
|
68
|
+
// when
|
|
69
|
+
const invalidDisplayNames = [];
|
|
70
|
+
for (const skillDir of skillDirs) {
|
|
71
|
+
const skillPath = join(skillDir, "SKILL.md");
|
|
72
|
+
if (!(await exists(skillPath))) continue;
|
|
73
|
+
const skillName = await readSkillName(skillPath);
|
|
74
|
+
const displayName = await readOpenAiDisplayName(dirname(skillPath));
|
|
75
|
+
const expected = `${displayNamePrefix}${skillName}`;
|
|
76
|
+
if (displayName !== expected) {
|
|
77
|
+
invalidDisplayNames.push(`${basename(skillDir)}: expected ${expected}, got ${displayName}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// then
|
|
82
|
+
assert.deepEqual(invalidDisplayNames, []);
|
|
83
|
+
});
|
|
@@ -68,6 +68,19 @@ async function readPluginVersion() {
|
|
|
68
68
|
return (await readJson(".codex-plugin/plugin.json")).version;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
async function readAggregateHookManifests() {
|
|
72
|
+
const manifest = await readJson(".codex-plugin/plugin.json");
|
|
73
|
+
const hookPaths = Array.isArray(manifest.hooks) ? manifest.hooks : [manifest.hooks];
|
|
74
|
+
return Promise.all(
|
|
75
|
+
hookPaths
|
|
76
|
+
.filter((hookPath) => typeof hookPath === "string")
|
|
77
|
+
.map(async (hookPath) => {
|
|
78
|
+
const source = hookPath.replace(/^\.\//, "");
|
|
79
|
+
return { source, version: await readPluginVersion(), hooks: await readJson(source) };
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
async function readComponentVersion(componentName) {
|
|
72
85
|
return (await readJson(join("components", componentName, "package.json"))).version;
|
|
73
86
|
}
|
|
@@ -114,7 +127,7 @@ function collectCommandHooks(hooks, source, version) {
|
|
|
114
127
|
return commandHooks;
|
|
115
128
|
}
|
|
116
129
|
|
|
117
|
-
test("#given hook status label #when formatting #then prefixes
|
|
130
|
+
test("#given hook status label #when formatting #then prefixes OmO display namespace", async () => {
|
|
118
131
|
// given
|
|
119
132
|
const version = (await readRepoJson("package.json")).version;
|
|
120
133
|
const label = "Checking Comments";
|
|
@@ -123,10 +136,10 @@ test("#given hook status label #when formatting #then prefixes LazyCodex with cu
|
|
|
123
136
|
const message = formatLazyCodexHookStatusMessage(version, label);
|
|
124
137
|
|
|
125
138
|
// then
|
|
126
|
-
assert.equal(message,
|
|
139
|
+
assert.equal(message, "(OmO) Checking Comments");
|
|
127
140
|
});
|
|
128
141
|
|
|
129
|
-
test("#given hook status label with blank version #when formatting #then prefixes
|
|
142
|
+
test("#given hook status label with blank version #when formatting #then still prefixes OmO display namespace", () => {
|
|
130
143
|
// given
|
|
131
144
|
const version = " ";
|
|
132
145
|
const label = "Checking Comments";
|
|
@@ -135,7 +148,7 @@ test("#given hook status label with blank version #when formatting #then prefixe
|
|
|
135
148
|
const message = formatLazyCodexHookStatusMessage(version, label);
|
|
136
149
|
|
|
137
150
|
// then
|
|
138
|
-
assert.equal(message, "
|
|
151
|
+
assert.equal(message, "(OmO) Checking Comments");
|
|
139
152
|
});
|
|
140
153
|
|
|
141
154
|
test("#given loose legacy status label #when normalizing #then removes OMO wording and title-cases label", async () => {
|
|
@@ -149,7 +162,7 @@ test("#given loose legacy status label #when normalizing #then removes OMO wordi
|
|
|
149
162
|
|
|
150
163
|
// then
|
|
151
164
|
assert.equal(normalized, "Checking Comments");
|
|
152
|
-
assert.equal(message,
|
|
165
|
+
assert.equal(message, "(OmO) Checking Comments");
|
|
153
166
|
});
|
|
154
167
|
|
|
155
168
|
test("#given LazyCodex appears inside hook label #when normalizing #then product casing is preserved", async () => {
|
|
@@ -163,7 +176,7 @@ test("#given LazyCodex appears inside hook label #when normalizing #then product
|
|
|
163
176
|
|
|
164
177
|
// then
|
|
165
178
|
assert.equal(normalized, "Verifying LazyCodex Executor Evidence");
|
|
166
|
-
assert.equal(message,
|
|
179
|
+
assert.equal(message, "(OmO) Verifying LazyCodex Executor Evidence");
|
|
167
180
|
});
|
|
168
181
|
|
|
169
182
|
test("#given MCP appears inside hook label #when normalizing #then protocol casing is preserved", () => {
|
|
@@ -176,31 +189,29 @@ test("#given MCP appears inside hook label #when normalizing #then protocol casi
|
|
|
176
189
|
|
|
177
190
|
// then
|
|
178
191
|
assert.equal(normalized, "Recommending Git Bash MCP");
|
|
179
|
-
assert.equal(message, "
|
|
192
|
+
assert.equal(message, "(OmO) Recommending Git Bash MCP");
|
|
180
193
|
});
|
|
181
|
-
test("#given aggregate comment-checker hook #when status is inspected #then it uses
|
|
194
|
+
test("#given aggregate comment-checker hook #when status is inspected #then it uses OmO comments label", async () => {
|
|
182
195
|
// given
|
|
183
|
-
const
|
|
184
|
-
const aggregateHooks = await readJson("hooks/hooks.json");
|
|
196
|
+
const aggregateManifests = await readAggregateHookManifests();
|
|
185
197
|
|
|
186
198
|
// when
|
|
187
|
-
const hooks = collectCommandHooks(
|
|
188
|
-
const commentCheckerHook = hooks.find((hook) => hook.
|
|
199
|
+
const hooks = aggregateManifests.flatMap((manifest) => collectCommandHooks(manifest.hooks, manifest.source, manifest.version));
|
|
200
|
+
const commentCheckerHook = hooks.find((hook) => hook.command.includes("components/comment-checker/dist/cli.js"));
|
|
189
201
|
|
|
190
202
|
// then
|
|
191
|
-
assert.equal(commentCheckerHook?.statusMessage, formatLazyCodexHookStatusMessage(
|
|
192
|
-
assert.doesNotMatch(JSON.stringify(
|
|
203
|
+
assert.equal(commentCheckerHook?.statusMessage, formatLazyCodexHookStatusMessage("", "Checking Comments"));
|
|
204
|
+
assert.doesNotMatch(JSON.stringify(aggregateManifests), /checking\s+OMO\s+comments/i);
|
|
193
205
|
});
|
|
194
206
|
|
|
195
|
-
test("#given aggregate and component hooks #when status messages are inspected #then all use the
|
|
207
|
+
test("#given aggregate and component hooks #when status messages are inspected #then all use the OmO formatter", async () => {
|
|
196
208
|
// given
|
|
197
|
-
const
|
|
198
|
-
const aggregateHooks = await readJson("hooks/hooks.json");
|
|
209
|
+
const aggregateManifests = await readAggregateHookManifests();
|
|
199
210
|
const componentManifests = await readComponentHookManifests();
|
|
200
211
|
|
|
201
212
|
// when
|
|
202
213
|
const commandHooks = [
|
|
203
|
-
...collectCommandHooks(
|
|
214
|
+
...aggregateManifests.flatMap((manifest) => collectCommandHooks(manifest.hooks, manifest.source, manifest.version)),
|
|
204
215
|
...componentManifests.flatMap((manifest) => collectCommandHooks(manifest.hooks, manifest.source, manifest.version)),
|
|
205
216
|
];
|
|
206
217
|
const expectedLabels = new Map([...AGGREGATE_EXPECTED_LABELS, ...COMPONENT_EXPECTED_LABELS]);
|
|
@@ -219,6 +230,6 @@ test("#given aggregate and component hooks #when status messages are inspected #
|
|
|
219
230
|
const actualLabels = new Set(commandHooks.map((hook) => parseLazyCodexHookStatusMessage(hook.statusMessage)?.label));
|
|
220
231
|
assert.deepEqual([...expectedLabels.values()].filter((label) => !actualLabels.has(label)), []);
|
|
221
232
|
for (const hook of commandHooks) {
|
|
222
|
-
assert.
|
|
233
|
+
assert.match(hook.statusMessage, /^\(OmO\) /);
|
|
223
234
|
}
|
|
224
235
|
});
|
|
@@ -20,7 +20,7 @@ test("#given synced lcx-report-bug skill #when inspected #then it files LazyCode
|
|
|
20
20
|
assert.match(skill, /gh pr create --repo openai\/codex/);
|
|
21
21
|
assert.doesNotMatch(skill, /gh pr create --repo "\$TARGET_REPO"/);
|
|
22
22
|
assert.doesNotMatch(skill, /gh pr create --repo code-yeongyu\/lazycodex/);
|
|
23
|
-
assert.match(interfaceMetadata, /display_name: "lcx-report-bug
|
|
23
|
+
assert.match(interfaceMetadata, /display_name: "\(OmO\) lcx-report-bug"/);
|
|
24
24
|
assert.match(interfaceMetadata, /- "lazycodex bug"/);
|
|
25
25
|
assert.match(interfaceMetadata, /- "openai codex bug"/);
|
|
26
26
|
});
|
|
@@ -40,7 +40,7 @@ test("#given synced lcx-contribute-bug-fix skill #when inspected #then it delive
|
|
|
40
40
|
assert.match(skill, /gh pr create --repo openai\/codex/);
|
|
41
41
|
assert.doesNotMatch(skill, /gh pr create --repo "\$TARGET_REPO"/);
|
|
42
42
|
assert.doesNotMatch(skill, /gh pr create --repo code-yeongyu\/lazycodex/);
|
|
43
|
-
assert.match(interfaceMetadata, /display_name: "lcx-contribute-bug-fix
|
|
43
|
+
assert.match(interfaceMetadata, /display_name: "\(OmO\) lcx-contribute-bug-fix"/);
|
|
44
44
|
assert.match(interfaceMetadata, /- "contribute a bug fix"/);
|
|
45
45
|
assert.match(interfaceMetadata, /- "fix bug pr"/);
|
|
46
46
|
});
|
|
@@ -55,7 +55,7 @@ test("#given synced lcx-doctor skill #when inspected #then it diagnoses installs
|
|
|
55
55
|
|
|
56
56
|
// then
|
|
57
57
|
assert.match(skill, /^---\r?\nname: lcx-doctor\r?\n/m);
|
|
58
|
-
assert.match(interfaceMetadata, /display_name: "
|
|
58
|
+
assert.match(interfaceMetadata, /display_name: "\(OmO\) lcx-doctor"/);
|
|
59
59
|
assert.match(interfaceMetadata, /- "lazycodex doctor"/);
|
|
60
60
|
assert.match(interfaceMetadata, /- "lazycodex health check"/);
|
|
61
61
|
});
|
|
@@ -88,10 +88,10 @@ test("#given source package versions and component without hooks #when hook stat
|
|
|
88
88
|
const aggregateHooks = await readJson(join(root, "hooks", "hooks.json"));
|
|
89
89
|
const componentHooks = await readJson(join(root, "components", "comment-checker", "hooks", "hooks.json"));
|
|
90
90
|
const lspHooks = await readJson(join(root, "components", "lsp", "hooks", "hooks.json"));
|
|
91
|
-
assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "
|
|
92
|
-
assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[1].statusMessage, "
|
|
93
|
-
assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "
|
|
94
|
-
assert.equal(lspHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "
|
|
91
|
+
assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
|
|
92
|
+
assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[1].statusMessage, "(OmO) Checking LSP Diagnostics");
|
|
93
|
+
assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
|
|
94
|
+
assert.equal(lspHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking LSP Diagnostics");
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
test("#given release version override #when hook status messages sync #then aggregate hooks use release version", async () => {
|
|
@@ -139,6 +139,6 @@ test("#given release version override #when hook status messages sync #then aggr
|
|
|
139
139
|
// then
|
|
140
140
|
const aggregateHooks = await readJson(join(root, "hooks", "hooks.json"));
|
|
141
141
|
const componentHooks = await readJson(join(root, "components", "comment-checker", "hooks", "hooks.json"));
|
|
142
|
-
assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "
|
|
143
|
-
assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "
|
|
142
|
+
assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
|
|
143
|
+
assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
|
|
144
144
|
});
|
|
@@ -22,6 +22,7 @@ export const expectedSkills = [
|
|
|
22
22
|
"review-work",
|
|
23
23
|
"rules",
|
|
24
24
|
"start-work",
|
|
25
|
+
"teammode",
|
|
25
26
|
"ultraresearch",
|
|
26
27
|
"ulw-loop",
|
|
27
28
|
"ulw-plan",
|
|
@@ -32,6 +33,7 @@ export const componentSkillSources = [
|
|
|
32
33
|
["comment-checker", "components/comment-checker/skills/comment-checker"],
|
|
33
34
|
["lsp", "components/lsp/skills/lsp"],
|
|
34
35
|
["rules", "components/rules/skills/rules"],
|
|
36
|
+
["teammode", "components/teammode/skills/teammode"],
|
|
35
37
|
["ulw-loop", "components/ulw-loop/skills/ulw-loop"],
|
|
36
38
|
["ulw-plan", "components/ultrawork/skills/ulw-plan"],
|
|
37
39
|
];
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
18
18
|
const repoRoot = join(root, "..", "..", "..");
|
|
19
19
|
const opencodeOnlyToolPattern = /\b(?:call_omo_agent|background_output|team_[a-z_]+|task)\s*\(/;
|
|
20
|
+
const generatedSkillMetadataFiles = new Set(["agents/openai.yaml"]);
|
|
20
21
|
|
|
21
22
|
async function readPackagedSkillFile(...segments) {
|
|
22
23
|
const path = join(root, "skills", ...segments);
|
|
@@ -24,6 +25,10 @@ async function readPackagedSkillFile(...segments) {
|
|
|
24
25
|
return { path, content };
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
function excludeGeneratedSkillMetadata(files) {
|
|
29
|
+
return files.filter((file) => !generatedSkillMetadataFiles.has(file.replaceAll("\\", "/")));
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
test("#given synced aggregate Codex skills #when inspected #then component and shared skills are present", async () => {
|
|
28
33
|
// given
|
|
29
34
|
const skillsRoot = join(root, "skills");
|
|
@@ -121,8 +126,8 @@ test("#given component skill sources #when aggregate Codex component skills are
|
|
|
121
126
|
for (const [skillName, sourcePath] of componentSkillSources) {
|
|
122
127
|
const sourceDir = join(root, sourcePath);
|
|
123
128
|
const aggregateDir = join(aggregateSkillsRoot, skillName);
|
|
124
|
-
const sourceFiles = await listSkillFiles(sourceDir);
|
|
125
|
-
const aggregateFiles = await listSkillFiles(aggregateDir);
|
|
129
|
+
const sourceFiles = excludeGeneratedSkillMetadata(await listSkillFiles(sourceDir));
|
|
130
|
+
const aggregateFiles = excludeGeneratedSkillMetadata(await listSkillFiles(aggregateDir));
|
|
126
131
|
assert.deepEqual(aggregateFiles, sourceFiles, `${skillName} resource set drifted from its component skill source`);
|
|
127
132
|
for (const relativePath of sourceFiles) {
|
|
128
133
|
const sourceContent = await readFile(join(sourceDir, relativePath), "utf8");
|
|
@@ -146,7 +151,7 @@ test("#given synced ulw-loop skill #when Codex hint metadata is inspected #then
|
|
|
146
151
|
|
|
147
152
|
// then
|
|
148
153
|
assert.match(skill, /^---\r?\nname: ulw-loop\r?\n/m);
|
|
149
|
-
assert.match(interfaceMetadata, /display_name: "
|
|
154
|
+
assert.match(interfaceMetadata, /display_name: "\(OmO\) ulw-loop"/);
|
|
150
155
|
assert.doesNotMatch(interfaceMetadata, /ulw-loop \/ ulw-loop/);
|
|
151
156
|
assert.match(interfaceMetadata, /short_description: "Goal-like ultrawork loop for systematic decomposition"/);
|
|
152
157
|
assert.match(interfaceMetadata, /default_prompt: "Use \$ulw-loop/);
|
|
@@ -179,7 +184,7 @@ test("#given synced git-master skill #when inspected #then commits and git histo
|
|
|
179
184
|
assert.match(skill, /Choose the Git tool by the question/);
|
|
180
185
|
assert.match(skill, /git log -S "text"/);
|
|
181
186
|
assert.match(skill, /git blame -L start,end -- file/);
|
|
182
|
-
assert.match(interfaceMetadata, /display_name: "
|
|
187
|
+
assert.match(interfaceMetadata, /display_name: "\(OmO\) git-master"/);
|
|
183
188
|
assert.match(interfaceMetadata, /- "git commit"/);
|
|
184
189
|
assert.match(interfaceMetadata, /- "history search"/);
|
|
185
190
|
});
|