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.
Files changed (149) hide show
  1. package/.agents/skills/codex-qa/scripts/lib/app-server-client.mjs +132 -82
  2. package/.agents/skills/codex-qa/scripts/lib/app-server-client.test.js +48 -0
  3. package/.agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh +361 -84
  4. package/.agents/skills/work-with-pr/SKILL.md +19 -5
  5. package/.opencode/skills/work-with-pr/SKILL.md +19 -5
  6. package/dist/agents/types.d.ts +7 -6
  7. package/dist/cli/index.js +438 -306
  8. package/dist/cli-node/index.js +438 -306
  9. package/dist/features/background-agent/parent-wake-flush-runner.d.ts +1 -0
  10. package/dist/features/background-agent/parent-wake-history-state.d.ts +1 -0
  11. package/dist/features/background-agent/parent-wake-session-history.d.ts +1 -47
  12. package/dist/features/background-agent/parent-wake-session-message.d.ts +47 -0
  13. package/dist/index.js +245 -69
  14. package/dist/plugin/tool-execute-before.d.ts +2 -0
  15. package/dist/plugin-handlers/plugin-components-loader.d.ts +1 -0
  16. package/dist/skills/ulw-plan/SKILL.md +2 -1
  17. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  18. package/dist/skills/ulw-plan/references/intent-clear.md +2 -2
  19. package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -2
  20. package/dist/tools/delegate-task/sync-session-turns.d.ts +3 -0
  21. package/dist/tui.js +16 -7
  22. package/package.json +13 -13
  23. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +22 -2
  24. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +25 -9
  25. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  26. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +10 -3
  28. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +9 -2
  29. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +7 -2
  31. package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +2 -1
  32. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +9 -9
  33. package/packages/omo-codex/plugin/components/codegraph/test/provisioned-node-guard.test.ts +94 -0
  34. package/packages/omo-codex/plugin/components/codegraph/test/serve-node-support.test.ts +33 -0
  35. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  37. package/packages/omo-codex/plugin/components/comment-checker/test/package-smoke.test.ts +2 -71
  38. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  39. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  43. package/packages/omo-codex/plugin/components/lsp/package.json +2 -2
  44. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +26 -104
  45. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  46. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +12 -92
  48. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +157 -234
  49. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  50. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -2
  51. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +227 -15
  52. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +62 -2
  53. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +34 -3
  54. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +139 -0
  55. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +111 -0
  56. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +250 -0
  57. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +220 -0
  58. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  59. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  60. package/packages/omo-codex/plugin/components/test-support/package-smoke-fixture.ts +158 -0
  61. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  62. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  63. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -1
  64. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/agents/openai.yaml +1 -1
  65. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  66. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -2
  67. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +76 -49
  68. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  69. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  70. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/agents/openai.yaml +1 -1
  71. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +1 -1
  72. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +17 -0
  73. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +17 -0
  74. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +17 -0
  75. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +17 -0
  76. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +17 -0
  77. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +17 -0
  78. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +17 -0
  79. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +17 -0
  80. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +17 -0
  81. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +17 -0
  82. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +16 -0
  83. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +16 -0
  84. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +16 -0
  85. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +16 -0
  86. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +16 -0
  87. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +17 -0
  88. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +16 -0
  89. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +16 -0
  90. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +16 -0
  91. package/packages/omo-codex/plugin/package-lock.json +12 -22
  92. package/packages/omo-codex/plugin/package.json +1 -1
  93. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +14 -11
  94. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +15 -4
  95. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +37 -0
  96. package/packages/omo-codex/plugin/skills/ast-grep/agents/openai.yaml +2 -0
  97. package/packages/omo-codex/plugin/skills/comment-checker/agents/openai.yaml +2 -0
  98. package/packages/omo-codex/plugin/skills/debugging/agents/openai.yaml +2 -0
  99. package/packages/omo-codex/plugin/skills/frontend/agents/openai.yaml +2 -0
  100. package/packages/omo-codex/plugin/skills/git-master/agents/openai.yaml +1 -1
  101. package/packages/omo-codex/plugin/skills/init-deep/agents/openai.yaml +2 -0
  102. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  103. package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
  104. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  105. package/packages/omo-codex/plugin/skills/lsp/agents/openai.yaml +2 -0
  106. package/packages/omo-codex/plugin/skills/lsp-setup/agents/openai.yaml +2 -0
  107. package/packages/omo-codex/plugin/skills/programming/agents/openai.yaml +2 -0
  108. package/packages/omo-codex/plugin/skills/refactor/agents/openai.yaml +2 -0
  109. package/packages/omo-codex/plugin/skills/remove-ai-slops/agents/openai.yaml +2 -0
  110. package/packages/omo-codex/plugin/skills/review-work/agents/openai.yaml +2 -0
  111. package/packages/omo-codex/plugin/skills/rules/agents/openai.yaml +2 -0
  112. package/packages/omo-codex/plugin/skills/start-work/agents/openai.yaml +2 -0
  113. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +139 -0
  114. package/packages/omo-codex/plugin/skills/teammode/agents/openai.yaml +2 -0
  115. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +111 -0
  116. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +250 -0
  117. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +220 -0
  118. package/packages/omo-codex/plugin/skills/ultraresearch/agents/openai.yaml +2 -0
  119. package/packages/omo-codex/plugin/skills/ulw-loop/agents/openai.yaml +1 -1
  120. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -1
  121. package/packages/omo-codex/plugin/skills/ulw-plan/agents/openai.yaml +1 -1
  122. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  123. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -2
  124. package/packages/omo-codex/plugin/skills/visual-qa/agents/openai.yaml +2 -0
  125. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +34 -33
  126. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +5 -2
  127. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +13 -0
  128. package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +1 -1
  129. package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +14 -6
  130. package/packages/omo-codex/plugin/test/bootstrap-ps-guard.test.mjs +7 -3
  131. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +3 -3
  132. package/packages/omo-codex/plugin/test/display-metadata.test.mjs +83 -0
  133. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +30 -19
  134. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +3 -3
  135. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
  136. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
  137. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +9 -4
  138. package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +88 -0
  139. package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +240 -0
  140. package/packages/omo-codex/scripts/install-dist/install-local.mjs +385 -206
  141. package/packages/omo-codex/scripts/install-hook-targets.test.mjs +93 -0
  142. package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
  143. package/packages/omo-codex/scripts/install-local-bun-global-update.test.mjs +63 -0
  144. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +109 -1
  145. package/packages/omo-codex/scripts/install-local.mjs +1 -0
  146. package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -1
  147. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  148. package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -2
  149. package/packages/omo-codex/plugin/hooks/hooks.json +0 -214
@@ -1,44 +1,12 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { describe, expect, it } from "vitest";
3
-
4
- type PackageJson = {
5
- readonly type: string;
6
- readonly packageManager: string;
7
- readonly bin: Record<string, string>;
8
- readonly dependencies?: Record<string, unknown>;
9
- readonly optionalDependencies: Record<string, string>;
10
- };
11
-
12
- type HookCommand = {
13
- readonly command: string;
14
- };
15
-
16
- type HookEntry = {
17
- readonly hooks: readonly HookCommand[];
18
- };
19
-
20
- type HooksJson = {
21
- readonly hooks: Record<string, readonly HookEntry[]>;
22
- };
23
-
24
- function readPackageJson(path: string): PackageJson {
25
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
26
- if (!isPackageJson(parsed)) throw new TypeError(`Invalid package metadata: ${path}`);
27
- return parsed;
28
- }
29
-
30
- function readHooksJson(path: string): HooksJson {
31
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
32
- if (!isHooksJson(parsed)) throw new TypeError(`Invalid hooks metadata: ${path}`);
33
- return parsed;
34
- }
2
+ import { readHooksJson, readPackageJson, readTextFile } from "../../test-support/package-smoke-fixture.js";
35
3
 
36
4
  describe("plugin package metadata", () => {
37
5
  it("#given packaged plugin files #when validating entrypoints #then hook command uses portable plugin root interpolation", () => {
38
6
  // given
39
7
  const packageJson = readPackageJson("package.json");
40
8
  const hooksJson = readHooksJson("hooks/hooks.json");
41
- const cliSource = readFileSync("src/cli.ts", "utf8");
9
+ const cliSource = readTextFile("src/cli.ts");
42
10
 
43
11
  // when
44
12
  const command = hooksJson.hooks["PostToolUse"]?.[0]?.hooks[0]?.command;
@@ -54,40 +22,3 @@ describe("plugin package metadata", () => {
54
22
  expect(command).toBe(`node "${pluginRoot}/dist/cli.js" hook post-tool-use`);
55
23
  });
56
24
  });
57
-
58
- function isPackageJson(value: unknown): value is PackageJson {
59
- if (!isRecord(value)) return false;
60
- const dependencies = value["dependencies"];
61
- return (
62
- value["type"] === "module" &&
63
- value["packageManager"] === "npm@11.12.1" &&
64
- isStringRecord(value["bin"]) &&
65
- isStringRecord(value["optionalDependencies"]) &&
66
- (dependencies === undefined || isRecord(dependencies))
67
- );
68
- }
69
-
70
- function isHooksJson(value: unknown): value is HooksJson {
71
- if (!isRecord(value) || !isRecord(value["hooks"])) return false;
72
- return Object.values(value["hooks"]).every(isHookEntries);
73
- }
74
-
75
- function isHookEntries(value: unknown): value is readonly HookEntry[] {
76
- return Array.isArray(value) && value.every(isHookEntry);
77
- }
78
-
79
- function isHookEntry(value: unknown): value is HookEntry {
80
- return isRecord(value) && Array.isArray(value["hooks"]) && value["hooks"].every(isHookCommand);
81
- }
82
-
83
- function isHookCommand(value: unknown): value is HookCommand {
84
- return isRecord(value) && typeof value["command"] === "string";
85
- }
86
-
87
- function isStringRecord(value: unknown): value is Record<string, string> {
88
- return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
89
- }
90
-
91
- function isRecord(value: unknown): value is Record<string, unknown> {
92
- return typeof value === "object" && value !== null && !Array.isArray(value);
93
- }
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook pre-tool-use",
10
10
  "timeout": 5,
11
- "statusMessage": "LazyCodex(4.11.1): Recommending Git Bash MCP"
11
+ "statusMessage": "(OmO) Recommending Git Bash MCP"
12
12
  }
13
13
  ]
14
14
  }
@@ -20,7 +20,7 @@
20
20
  "type": "command",
21
21
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
22
22
  "timeout": 5,
23
- "statusMessage": "LazyCodex(4.11.1): Resetting Git Bash MCP Reminder"
23
+ "statusMessage": "(OmO) Resetting Git Bash MCP Reminder"
24
24
  }
25
25
  ]
26
26
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-git-bash-hook",
3
- "version": "4.11.1",
3
+ "version": "4.12.0",
4
4
  "description": "Codex hook component that reminds Windows sessions to prefer the OMO git_bash MCP.",
5
5
  "type": "module",
6
6
  "private": true,
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/components/lazycodex-executor-verify/dist/cli.js\" hook subagent-stop",
10
10
  "timeout": 10,
11
- "statusMessage": "LazyCodex(4.11.1): Verifying LazyCodex Executor Evidence"
11
+ "statusMessage": "(OmO) Verifying LazyCodex Executor Evidence"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-lazycodex-executor-verify",
3
- "version": "4.11.1",
3
+ "version": "4.12.0",
4
4
  "description": "Codex SubagentStop evidence verifier for LazyCodex executor completions.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 60,
11
- "statusMessage": "LazyCodex(4.11.1): Checking LSP Diagnostics"
11
+ "statusMessage": "(OmO) Checking LSP Diagnostics"
12
12
  }
13
13
  ]
14
14
  }
@@ -21,7 +21,7 @@
21
21
  "type": "command",
22
22
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
23
23
  "timeout": 5,
24
- "statusMessage": "LazyCodex(4.11.1): Resetting LSP Diagnostics Cache"
24
+ "statusMessage": "(OmO) Resetting LSP Diagnostics Cache"
25
25
  }
26
26
  ]
27
27
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-lsp",
3
- "version": "4.11.1",
3
+ "version": "4.12.0",
4
4
  "description": "Codex plugin that exposes Language Server Protocol tools and post-edit diagnostics.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -39,7 +39,7 @@
39
39
  "bootstrap": "node scripts/build-lsp-tools.mjs && node scripts/build-lsp-daemon.mjs",
40
40
  "prebuild": "node scripts/build-lsp-tools.mjs && node scripts/build-lsp-daemon.mjs",
41
41
  "build": "node scripts/clean-dist.mjs && tsc -p tsconfig.build.json",
42
- "pretest": "node scripts/build-lsp-tools.mjs && node scripts/build-lsp-daemon.mjs",
42
+ "pretest": "npm run build --silent",
43
43
  "test": "node scripts/test.mjs",
44
44
  "test:watch": "vitest",
45
45
  "pretypecheck": "node scripts/build-lsp-tools.mjs && node scripts/build-lsp-daemon.mjs",
@@ -1,53 +1,12 @@
1
- import { readdirSync, readFileSync } from "node:fs";
2
1
  import { describe, expect, it } from "vitest";
3
-
4
- type PackageJson = {
5
- readonly version: string;
6
- readonly type: string;
7
- readonly packageManager: string;
8
- readonly bin: Record<string, string>;
9
- readonly dependencies: Record<string, string>;
10
- readonly scripts: Record<string, string>;
11
- };
12
-
13
- type HookCommand = {
14
- readonly command: string;
15
- };
16
-
17
- type HookEntry = {
18
- readonly hooks: readonly HookCommand[];
19
- };
20
-
21
- type HooksJson = {
22
- readonly hooks: Record<string, readonly HookEntry[]>;
23
- };
24
-
25
- type McpServer = {
26
- readonly command: string;
27
- readonly args: readonly string[];
28
- };
29
-
30
- type McpJson = {
31
- readonly mcpServers: Record<string, McpServer>;
32
- };
33
-
34
- function readPackageJson(path: string): PackageJson {
35
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
36
- if (!isPackageJson(parsed)) throw new TypeError(`Invalid package metadata: ${path}`);
37
- return parsed;
38
- }
39
-
40
- function readHooksJson(path: string): HooksJson {
41
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
42
- if (!isHooksJson(parsed)) throw new TypeError(`Invalid hooks metadata: ${path}`);
43
- return parsed;
44
- }
45
-
46
- function readMcpJson(path: string): McpJson {
47
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
48
- if (!isMcpJson(parsed)) throw new TypeError(`Invalid MCP metadata: ${path}`);
49
- return parsed;
50
- }
2
+ import {
3
+ listDirectoryEntries,
4
+ readHooksJson,
5
+ readMcpJson,
6
+ readPackageJson,
7
+ readTextFile,
8
+ requireScripts,
9
+ } from "../../test-support/package-smoke-fixture.js";
51
10
 
52
11
  describe("plugin package metadata", () => {
53
12
  it("#given packaged component files #when validating entrypoints #then hook command stays local and MCP command references the package", () => {
@@ -55,10 +14,11 @@ describe("plugin package metadata", () => {
55
14
  const packageJson = readPackageJson("package.json");
56
15
  const hooksJson = readHooksJson("hooks/hooks.json");
57
16
  const mcpJson = readMcpJson(".mcp.json");
58
- const cliSource = readFileSync("src/cli.ts", "utf8");
59
- const codexHookCliSource = readFileSync("src/codex-hook-cli.ts", "utf8");
60
- const codexHookSource = readFileSync("src/codex-hook.ts", "utf8");
61
- const sourceFiles = readdirSync("src");
17
+ const cliSource = readTextFile("src/cli.ts");
18
+ const codexHookCliSource = readTextFile("src/codex-hook-cli.ts");
19
+ const codexHookSource = readTextFile("src/codex-hook.ts");
20
+ const sourceFiles = listDirectoryEntries("src");
21
+ const scripts = requireScripts(packageJson, "package.json");
62
22
 
63
23
  // when
64
24
  const postToolUseCommand = hooksJson.hooks["PostToolUse"]?.[0]?.hooks[0]?.command;
@@ -74,7 +34,8 @@ describe("plugin package metadata", () => {
74
34
  });
75
35
  expect(packageJson.bin["omo-lsp"]).toBe("./dist/cli.js");
76
36
  expect(packageJson.bin["codex-lsp"]).toBeUndefined();
77
- expect(packageJson.scripts["build"]).toBe("node scripts/clean-dist.mjs && tsc -p tsconfig.build.json");
37
+ expect(scripts["build"]).toBe("node scripts/clean-dist.mjs && tsc -p tsconfig.build.json");
38
+ expect(scripts["pretest"]).toBe("npm run build --silent");
78
39
  expect(cliSource.startsWith("#!/usr/bin/env node")).toBe(true);
79
40
  expect(cliSource).toContain("Usage: omo-lsp [mcp | hook post-tool-use | hook post-compact]");
80
41
  expect(postToolUseCommand).toBe(`node "${pluginRoot}/dist/cli.js" hook post-tool-use`);
@@ -91,55 +52,16 @@ describe("plugin package metadata", () => {
91
52
  expect(sourceFiles.filter((name) => name.startsWith("lazy-mcp") || name === "lazy-lsp-mcp.ts")).toEqual([]);
92
53
  });
93
54
 
94
- });
95
-
96
- function isPackageJson(value: unknown): value is PackageJson {
97
- return (
98
- isRecord(value) &&
99
- typeof value["version"] === "string" &&
100
- value["type"] === "module" &&
101
- value["packageManager"] === "npm@11.12.1" &&
102
- isStringRecord(value["bin"]) &&
103
- isStringRecord(value["dependencies"]) &&
104
- isStringRecord(value["scripts"])
105
- );
106
- }
107
-
108
- function isHooksJson(value: unknown): value is HooksJson {
109
- if (!isRecord(value) || !isRecord(value["hooks"])) return false;
110
- return Object.values(value["hooks"]).every(isHookEntries);
111
- }
112
-
113
- function isHookEntries(value: unknown): value is readonly HookEntry[] {
114
- return Array.isArray(value) && value.every(isHookEntry);
115
- }
116
-
117
- function isHookEntry(value: unknown): value is HookEntry {
118
- return isRecord(value) && Array.isArray(value["hooks"]) && value["hooks"].every(isHookCommand);
119
- }
120
-
121
- function isHookCommand(value: unknown): value is HookCommand {
122
- return isRecord(value) && typeof value["command"] === "string";
123
- }
124
-
125
- function isMcpJson(value: unknown): value is McpJson {
126
- if (!isRecord(value) || !isRecord(value["mcpServers"])) return false;
127
- return Object.values(value["mcpServers"]).every(isMcpServer);
128
- }
129
-
130
- function isMcpServer(value: unknown): value is McpServer {
131
- return (
132
- isRecord(value) &&
133
- typeof value["command"] === "string" &&
134
- Array.isArray(value["args"]) &&
135
- value["args"].every((item) => typeof item === "string")
136
- );
137
- }
55
+ it("#given LSP skill guidance #when validating MCP tool instructions #then tool names are not framed as shell commands", () => {
56
+ // given
57
+ const skill = readTextFile("skills/lsp/SKILL.md");
138
58
 
139
- function isStringRecord(value: unknown): value is Record<string, string> {
140
- return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
141
- }
59
+ // when
60
+ const mentionsToolInterface = skill.includes("through the tool interface");
61
+ const rejectsShellExecution = skill.includes("not shell commands");
142
62
 
143
- function isRecord(value: unknown): value is Record<string, unknown> {
144
- return typeof value === "object" && value !== null && !Array.isArray(value);
145
- }
63
+ // then
64
+ expect(mentionsToolInterface).toBe(true);
65
+ expect(rejectsShellExecution).toBe(true);
66
+ });
67
+ });
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook session-start",
9
9
  "timeout": 10,
10
- "statusMessage": "LazyCodex(4.11.1): Loading Project Rules"
10
+ "statusMessage": "(OmO) Loading Project Rules"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook user-prompt-submit",
21
21
  "timeout": 10,
22
- "statusMessage": "LazyCodex(4.11.1): Loading Project Rules"
22
+ "statusMessage": "(OmO) Loading Project Rules"
23
23
  }
24
24
  ]
25
25
  }
@@ -32,7 +32,7 @@
32
32
  "type": "command",
33
33
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
34
34
  "timeout": 10,
35
- "statusMessage": "LazyCodex(4.11.1): Matching Project Rules"
35
+ "statusMessage": "(OmO) Matching Project Rules"
36
36
  }
37
37
  ]
38
38
  }
@@ -45,7 +45,7 @@
45
45
  "type": "command",
46
46
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-compact",
47
47
  "timeout": 10,
48
- "statusMessage": "LazyCodex(4.11.1): Resetting Project Rule Cache"
48
+ "statusMessage": "(OmO) Resetting Project Rule Cache"
49
49
  }
50
50
  ]
51
51
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-rules",
3
- "version": "4.11.1",
3
+ "version": "4.12.0",
4
4
  "description": "Codex plugin that injects project rule files into model context through lifecycle hooks.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -1,48 +1,12 @@
1
- import { readdirSync, readFileSync } from "node:fs";
2
1
  import { describe, expect, it } from "vitest";
3
-
4
- type PackageJson = {
5
- readonly type: string;
6
- readonly packageManager: string;
7
- readonly bin: Record<string, string>;
8
- readonly files: readonly string[];
9
- readonly dependencies?: Record<string, unknown>;
10
- };
11
-
12
- type PluginJson = {
13
- readonly hooks: string;
14
- };
15
-
16
- type HookCommand = {
17
- readonly command: string;
18
- };
19
-
20
- type HookEntry = {
21
- readonly matcher?: string;
22
- readonly hooks: readonly HookCommand[];
23
- };
24
-
25
- type HooksJson = {
26
- readonly hooks: Record<string, readonly HookEntry[]>;
27
- };
28
-
29
- function readPackageJson(path: string): PackageJson {
30
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
31
- if (!isPackageJson(parsed)) throw new TypeError(`Invalid package metadata: ${path}`);
32
- return parsed;
33
- }
34
-
35
- function readPluginJson(path: string): PluginJson {
36
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
37
- if (!isPluginJson(parsed)) throw new TypeError(`Invalid plugin metadata: ${path}`);
38
- return parsed;
39
- }
40
-
41
- function readHooksJson(path: string): HooksJson {
42
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
43
- if (!isHooksJson(parsed)) throw new TypeError(`Invalid hooks metadata: ${path}`);
44
- return parsed;
45
- }
2
+ import {
3
+ listDirectoryEntries,
4
+ readHooksJson,
5
+ readPackageJson,
6
+ readPluginJson,
7
+ readTextFile,
8
+ requireFiles,
9
+ } from "../../test-support/package-smoke-fixture.js";
46
10
 
47
11
  describe("plugin package metadata", () => {
48
12
  it("#given packaged plugin files #when validating entrypoints #then hook commands use portable plugin root interpolation", () => {
@@ -50,8 +14,9 @@ describe("plugin package metadata", () => {
50
14
  const packageJson = readPackageJson("package.json");
51
15
  const pluginJson = readPluginJson(".codex-plugin/plugin.json");
52
16
  const hooksJson = readHooksJson("hooks/hooks.json");
53
- const cliSource = readFileSync("src/cli.ts", "utf8");
54
- const bundledRules = readdirSync("bundled-rules").sort();
17
+ const cliSource = readTextFile("src/cli.ts");
18
+ const packageFiles = requireFiles(packageJson, "package.json");
19
+ const bundledRules = [...listDirectoryEntries("bundled-rules")].sort();
55
20
 
56
21
  // when
57
22
  const hookConfig = hooksJson.hooks;
@@ -69,7 +34,7 @@ describe("plugin package metadata", () => {
69
34
  expect(packageJson.packageManager).toBe("npm@11.12.1");
70
35
  expect(packageJson.dependencies ?? {}).toEqual({ picomatch: "^4.0.3" });
71
36
  expect(packageJson.bin["omo-rules"]).toBe("./dist/cli.js");
72
- expect(packageJson.files).toContain("bundled-rules");
37
+ expect(packageFiles).toContain("bundled-rules");
73
38
  expect(bundledRules).toContain("windows-git-bash.md");
74
39
  expect(pluginJson.hooks).toBe("./hooks/hooks.json");
75
40
  expect(cliSource.startsWith("#!/usr/bin/env node")).toBe(true);
@@ -106,48 +71,3 @@ describe("plugin package metadata", () => {
106
71
  ).toBe(false);
107
72
  });
108
73
  });
109
-
110
- function isPackageJson(value: unknown): value is PackageJson {
111
- if (!isRecord(value)) return false;
112
- const dependencies = value["dependencies"];
113
- return (
114
- value["type"] === "module" &&
115
- value["packageManager"] === "npm@11.12.1" &&
116
- isStringRecord(value["bin"]) &&
117
- isStringArray(value["files"]) &&
118
- (dependencies === undefined || isRecord(dependencies))
119
- );
120
- }
121
-
122
- function isStringArray(value: unknown): value is readonly string[] {
123
- return Array.isArray(value) && value.every((item) => typeof item === "string");
124
- }
125
-
126
- function isPluginJson(value: unknown): value is PluginJson {
127
- return isRecord(value) && typeof value["hooks"] === "string";
128
- }
129
-
130
- function isHooksJson(value: unknown): value is HooksJson {
131
- if (!isRecord(value) || !isRecord(value["hooks"])) return false;
132
- return Object.values(value["hooks"]).every(isHookEntries);
133
- }
134
-
135
- function isHookEntries(value: unknown): value is readonly HookEntry[] {
136
- return Array.isArray(value) && value.every(isHookEntry);
137
- }
138
-
139
- function isHookEntry(value: unknown): value is HookEntry {
140
- return isRecord(value) && Array.isArray(value["hooks"]) && value["hooks"].every(isHookCommand);
141
- }
142
-
143
- function isHookCommand(value: unknown): value is HookCommand {
144
- return isRecord(value) && typeof value["command"] === "string";
145
- }
146
-
147
- function isStringRecord(value: unknown): value is Record<string, string> {
148
- return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
149
- }
150
-
151
- function isRecord(value: unknown): value is Record<string, unknown> {
152
- return typeof value === "object" && value !== null && !Array.isArray(value);
153
- }