oh-my-opencode 4.15.1 → 4.16.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 (142) hide show
  1. package/dist/cli/doctor/checks/tui-plugin-config.d.ts +4 -4
  2. package/dist/cli/index.js +673 -678
  3. package/dist/cli/install-senpi/index.d.ts +1 -0
  4. package/dist/cli/run/poll-for-completion.d.ts +4 -0
  5. package/dist/cli/run/session-resolver.d.ts +1 -0
  6. package/dist/cli/senpi-platform-flag.d.ts +4 -0
  7. package/dist/cli/types.d.ts +2 -1
  8. package/dist/cli-node/index.js +673 -678
  9. package/dist/features/background-agent/manager.d.ts +16 -0
  10. package/dist/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.d.ts +1 -0
  11. package/dist/index.js +1334 -1146
  12. package/dist/skills/debugging/references/methodology/06-fix.md +6 -0
  13. package/dist/skills/frontend/ATTRIBUTION.md +10 -1
  14. package/dist/skills/frontend/SKILL.md +8 -3
  15. package/dist/skills/frontend/references/design/README.md +1 -1
  16. package/dist/skills/frontend/references/design/_INDEX.md +1 -1
  17. package/dist/skills/frontend/references/design/lazyweb.md +77 -0
  18. package/dist/skills/frontend/references/design/stitch-design-example.md +121 -0
  19. package/dist/skills/programming/SKILL.md +10 -1
  20. package/dist/skills/programming/references/logging.md +91 -0
  21. package/dist/skills/ulw-plan/SKILL.md +5 -5
  22. package/dist/skills/ulw-plan/references/full-workflow.md +7 -7
  23. package/dist/skills/ulw-plan/references/intent-unclear.md +3 -3
  24. package/dist/skills/ulw-research/ATTRIBUTION.md +2 -2
  25. package/dist/skills/ulw-research/SKILL.md +28 -7
  26. package/dist/tools/skill/description-formatter.d.ts +1 -0
  27. package/dist/tools/skill/description-formatter.test-support.d.ts +9 -0
  28. package/dist/tools/skill/zauc-mocks-skill-tools/test-support.d.ts +25 -0
  29. package/dist/tui.js +121 -419
  30. package/package.json +32 -15
  31. package/packages/git-bash-mcp/dist/cli.js +2 -2
  32. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  33. package/packages/omo-codex/plugin/components/bootstrap/AGENTS.md +55 -0
  34. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +19 -16
  35. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  36. package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +51 -0
  37. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +936 -248
  38. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +233 -135
  39. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/codegraph/src/cache-gc.ts +29 -0
  41. package/packages/omo-codex/plugin/components/codegraph/src/cli.ts +13 -0
  42. package/packages/omo-codex/plugin/components/codegraph/src/hook-sweep.ts +25 -0
  43. package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
  44. package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +30 -1
  45. package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +16 -2
  46. package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +80 -0
  47. package/packages/omo-codex/plugin/components/codegraph/test/cache-gc.test.ts +34 -0
  48. package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +107 -0
  49. package/packages/omo-codex/plugin/components/codegraph/test/hook-registration.test.ts +26 -0
  50. package/packages/omo-codex/plugin/components/codegraph/test/hook-sweep.test.ts +29 -0
  51. package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +8 -21
  52. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +108 -0
  53. package/packages/omo-codex/plugin/components/codegraph/test/serve-built-wrapper.test.ts +76 -0
  54. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +17 -111
  55. package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +1 -1
  56. package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +45 -67
  57. package/packages/omo-codex/plugin/components/codegraph/test/sweep-cli.test.ts +56 -0
  58. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  59. package/packages/omo-codex/plugin/components/git-bash/AGENTS.md +31 -0
  60. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/AGENTS.md +45 -0
  62. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  63. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  64. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +37 -102
  65. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  66. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  67. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +46 -0
  68. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  69. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +6 -0
  70. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +6 -0
  71. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  72. package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +5 -2
  73. package/packages/omo-codex/plugin/components/ultrawork/CHANGELOG.md +2 -0
  74. package/packages/omo-codex/plugin/components/ultrawork/README.md +3 -3
  75. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +51 -2
  76. package/packages/omo-codex/plugin/components/ultrawork/package.json +2 -1
  77. package/packages/omo-codex/plugin/components/ultrawork/scripts/sync-directive.mjs +14 -1
  78. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +380 -0
  79. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +5 -2
  80. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +4 -4
  81. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-unclear.md +2 -2
  82. package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +5 -3
  83. package/packages/omo-codex/plugin/components/ultrawork/src/skill-pointer.ts +56 -0
  84. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +4 -4
  85. package/packages/omo-codex/plugin/components/ultrawork/test/directive-source.test.ts +12 -0
  86. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +1 -0
  87. package/packages/omo-codex/plugin/components/ultrawork/test/skill-pointer.test.ts +82 -0
  88. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +1 -0
  89. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +56 -5
  90. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-hook.d.ts +1 -0
  91. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-hook.js +5 -1
  92. package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-directive.d.ts +2 -1
  93. package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-directive.js +5 -3
  94. package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-skill-pointer.d.ts +7 -0
  95. package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-skill-pointer.js +46 -0
  96. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  97. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-hook.ts +5 -1
  98. package/packages/omo-codex/plugin/components/ulw-loop/src/ultrawork-directive.ts +9 -3
  99. package/packages/omo-codex/plugin/components/ulw-loop/src/ultrawork-skill-pointer.ts +55 -0
  100. package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +49 -0
  101. package/packages/omo-codex/plugin/package-lock.json +13 -13
  102. package/packages/omo-codex/plugin/package.json +1 -1
  103. package/packages/omo-codex/plugin/scripts/build-components.mjs +63 -17
  104. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +10 -3
  105. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +1 -0
  106. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +18 -0
  107. package/packages/omo-codex/plugin/skills/debugging/references/methodology/06-fix.md +6 -0
  108. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +10 -1
  109. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +8 -3
  110. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +1 -1
  111. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -1
  112. package/packages/omo-codex/plugin/skills/frontend/references/design/lazyweb.md +77 -0
  113. package/packages/omo-codex/plugin/skills/frontend/references/design/stitch-design-example.md +121 -0
  114. package/packages/omo-codex/plugin/skills/programming/SKILL.md +10 -1
  115. package/packages/omo-codex/plugin/skills/programming/references/logging.md +91 -0
  116. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +380 -0
  117. package/packages/omo-codex/plugin/skills/ultrawork/agents/openai.yaml +2 -0
  118. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +5 -2
  119. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +4 -4
  120. package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-unclear.md +2 -2
  121. package/packages/omo-codex/plugin/skills/ulw-research/ATTRIBUTION.md +2 -2
  122. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +28 -7
  123. package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +1 -1
  124. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
  125. package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +56 -0
  126. package/packages/omo-codex/plugin/test/ulw-research-epistemic-contract.test.mjs +98 -0
  127. package/packages/omo-codex/plugin/test/ulw-research-skill-contract.test.mjs +6 -6
  128. package/packages/omo-codex/scripts/install-dist/install-local.mjs +259 -3954
  129. package/packages/shared-skills/skills/debugging/references/methodology/06-fix.md +6 -0
  130. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +10 -1
  131. package/packages/shared-skills/skills/frontend/SKILL.md +8 -3
  132. package/packages/shared-skills/skills/frontend/references/design/README.md +1 -1
  133. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -1
  134. package/packages/shared-skills/skills/frontend/references/design/lazyweb.md +77 -0
  135. package/packages/shared-skills/skills/frontend/references/design/stitch-design-example.md +121 -0
  136. package/packages/shared-skills/skills/programming/SKILL.md +10 -1
  137. package/packages/shared-skills/skills/programming/references/logging.md +91 -0
  138. package/packages/shared-skills/skills/ulw-plan/SKILL.md +5 -5
  139. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +7 -7
  140. package/packages/shared-skills/skills/ulw-plan/references/intent-unclear.md +3 -3
  141. package/packages/shared-skills/skills/ulw-research/ATTRIBUTION.md +2 -2
  142. package/packages/shared-skills/skills/ulw-research/SKILL.md +28 -7
@@ -1,15 +1,28 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { chmodSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
- import { tmpdir } from "node:os";
2
+ import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
4
3
  import { join } from "node:path";
5
4
  import { PassThrough } from "node:stream";
5
+ import { fileURLToPath } from "node:url";
6
6
 
7
7
  import { runCodegraphServe } from "../src/serve.ts";
8
+ import {
9
+ arrayProperty,
10
+ findTool,
11
+ frameMcpRequest,
12
+ parseMcpBodies,
13
+ recordProperty,
14
+ recordValue,
15
+ stringProperty,
16
+ writeFakeContractCodegraph,
17
+ writeFakeNewlineCodegraph,
18
+ } from "./mcp-bridge-fixtures.ts";
19
+
20
+ const componentRoot = realpathSync(fileURLToPath(new URL("..", import.meta.url)));
8
21
 
9
22
  describe("runCodegraphServe MCP protocol bridge", () => {
10
23
  it("#given Codex framed stdio and a newline-json CodeGraph child #when listing tools #then it bridges frames and serves from the project cwd", async () => {
11
24
  // given
12
- const tempRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-bridge-"));
25
+ const tempRoot = mkdtempSync(join(componentRoot, ".tmp-codegraph-bridge-"));
13
26
  const projectRoot = join(tempRoot, "project");
14
27
  const pluginCacheRoot = join(tempRoot, "plugin-cache");
15
28
  const fakeCodegraph = join(tempRoot, "codegraph-fake.cjs");
@@ -96,7 +109,7 @@ describe("runCodegraphServe MCP protocol bridge", () => {
96
109
 
97
110
  it("#given old upstream codegraph_node metadata and container outline guidance #when bridging #then it exposes the corrected LazyCodex contract", async () => {
98
111
  // given
99
- const tempRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-contract-"));
112
+ const tempRoot = mkdtempSync(join(componentRoot, ".tmp-codegraph-contract-"));
100
113
  const projectRoot = join(tempRoot, "project");
101
114
  const fakeCodegraph = join(tempRoot, "codegraph-contract-fake.cjs");
102
115
  const childLog = join(tempRoot, "child.log");
@@ -178,110 +191,3 @@ describe("runCodegraphServe MCP protocol bridge", () => {
178
191
  }
179
192
  });
180
193
  });
181
-
182
- function writeFakeNewlineCodegraph(filePath: string): void {
183
- writeFileSync(
184
- filePath,
185
- [
186
- "#!/usr/bin/env node",
187
- "const fs = require('node:fs');",
188
- "const readline = require('node:readline');",
189
- "fs.writeFileSync(process.env.CODEGRAPH_FAKE_LOG, `cwd=${process.cwd()}\\n`);",
190
- "const rl = readline.createInterface({ input: process.stdin });",
191
- "rl.on('line', (line) => {",
192
- " const request = JSON.parse(line);",
193
- " if (request.method === 'initialize') {",
194
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { capabilities: { tools: { listChanged: false } }, protocolVersion: request.params.protocolVersion, serverInfo: { name: 'codegraph', version: '1.0.1' } } }) + '\\n');",
195
- " }",
196
- " if (request.method === 'tools/list') {",
197
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { tools: [{ name: 'codegraph_search' }, { name: 'codegraph_node' }, { name: 'codegraph_explore' }, { name: 'codegraph_callers' }] } }) + '\\n');",
198
- " }",
199
- "});",
200
- ].join("\n"),
201
- );
202
- chmodSync(filePath, 0o755);
203
- }
204
-
205
- function writeFakeContractCodegraph(filePath: string): void {
206
- writeFileSync(
207
- filePath,
208
- [
209
- "#!/usr/bin/env node",
210
- "const fs = require('node:fs');",
211
- "const readline = require('node:readline');",
212
- "fs.writeFileSync(process.env.CODEGRAPH_FAKE_LOG, `cwd=${process.cwd()}\\n`);",
213
- "const rl = readline.createInterface({ input: process.stdin });",
214
- "rl.on('line', (line) => {",
215
- " const request = JSON.parse(line);",
216
- " if (request.method === 'tools/list') {",
217
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { tools: [",
218
- " { name: 'codegraph_search', description: 'search stays unchanged' },",
219
- " { name: 'codegraph_node', description: 'ONE SYMBOL you can name - its location, signature, verbatim source (includeCode=true) and caller/callee trail in one call', inputSchema: { type: 'object', properties: { includeCode: { type: 'boolean', description: \"Symbol mode: include the symbol's full body (default: false).\" }, symbol: { type: 'string' } } } }",
220
- " ] } }) + '\\n');",
221
- " }",
222
- " if (request.method === 'tools/call') {",
223
- " process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { content: [{ type: 'text', text: 'JsonlMCP\\nMembers (7)\\nStructural outline only. For full source, use the Read tool on this file or request a member.' }] } }) + '\\n');",
224
- " }",
225
- "});",
226
- ].join("\n"),
227
- );
228
- chmodSync(filePath, 0o755);
229
- }
230
-
231
- function frameMcpRequest(request: {
232
- readonly id: number;
233
- readonly method: string;
234
- readonly params: Record<string, unknown>;
235
- }): string {
236
- const body = JSON.stringify({ jsonrpc: "2.0", ...request });
237
- return `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`;
238
- }
239
-
240
- function parseMcpBodies(transcript: string): readonly unknown[] {
241
- const bodies: unknown[] = [];
242
- let cursor = 0;
243
- while (cursor < transcript.length) {
244
- const headerEnd = transcript.indexOf("\r\n\r\n", cursor);
245
- if (headerEnd === -1) break;
246
- const header = transcript.slice(cursor, headerEnd);
247
- const match = /^Content-Length: (?<length>\d+)$/m.exec(header);
248
- if (match?.groups?.["length"] === undefined) break;
249
- const length = Number.parseInt(match.groups["length"], 10);
250
- const bodyStart = headerEnd + 4;
251
- const body = transcript.slice(bodyStart, bodyStart + length);
252
- bodies.push(JSON.parse(body));
253
- cursor = bodyStart + length;
254
- }
255
- return bodies;
256
- }
257
-
258
- function findTool(tools: readonly unknown[], name: string): Record<string, unknown> {
259
- for (const tool of tools) {
260
- const record = recordValue(tool);
261
- if (record["name"] === name) return record;
262
- }
263
- throw new Error(`Missing tool ${name}`);
264
- }
265
-
266
- function arrayProperty(record: Record<string, unknown>, key: string): readonly unknown[] {
267
- const value = record[key];
268
- if (!Array.isArray(value)) throw new Error(`Expected ${key} to be an array`);
269
- return value;
270
- }
271
-
272
- function recordProperty(record: Record<string, unknown>, key: string): Record<string, unknown> {
273
- return recordValue(record[key]);
274
- }
275
-
276
- function stringProperty(record: Record<string, unknown>, key: string): string {
277
- const value = record[key];
278
- if (typeof value !== "string") throw new Error(`Expected ${key} to be a string`);
279
- return value;
280
- }
281
-
282
- function recordValue(value: unknown): Record<string, unknown> {
283
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
284
- throw new Error("Expected record value");
285
- }
286
- return value as Record<string, unknown>;
287
- }
@@ -41,7 +41,7 @@ describe("runCodegraphServe provisioning", () => {
41
41
  {
42
42
  args: ["serve", "--mcp"],
43
43
  command: binPath,
44
- env: { PATH: "/bin" },
44
+ env: { CODEGRAPH_NO_DAEMON: "1", PATH: "/bin" },
45
45
  },
46
46
  ]);
47
47
  });
@@ -1,8 +1,8 @@
1
1
  import { describe, expect, it } from "bun:test";
2
- import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
5
- import { spawnSync } from "node:child_process";
5
+ import { Readable, Writable } from "node:stream";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
8
  import { resolveServeProcessInvocation, runCodegraphServe } from "../src/serve.ts";
@@ -54,6 +54,7 @@ describe("runCodegraphServe", () => {
54
54
  cwd: resolve(runCwd),
55
55
  env: {
56
56
  CODEGRAPH_INSTALL_DIR: "/tmp/home/.omo/codegraph",
57
+ CODEGRAPH_NO_DAEMON: "1",
57
58
  CODEGRAPH_NO_DOWNLOAD: "1",
58
59
  CODEGRAPH_TELEMETRY: "0",
59
60
  DO_NOT_TRACK: "1",
@@ -176,6 +177,7 @@ describe("runCodegraphServe", () => {
176
177
  command: binPath,
177
178
  env: {
178
179
  CODEGRAPH_INSTALL_DIR: installDir,
180
+ CODEGRAPH_NO_DAEMON: "1",
179
181
  CODEGRAPH_NO_DOWNLOAD: "1",
180
182
  CODEGRAPH_TELEMETRY: "0",
181
183
  DO_NOT_TRACK: "1",
@@ -189,6 +191,47 @@ describe("runCodegraphServe", () => {
189
191
  });
190
192
  });
191
193
 
194
+ it("#given project cwd is under a configured excluded root #when serving MCP #then it exposes an unavailable stub without spawning CodeGraph", async () => {
195
+ // given
196
+ const excludedRoot = mkdtempSync(join(componentRoot, ".tmp-codegraph-excluded-"));
197
+ const projectRoot = join(excludedRoot, "repo");
198
+ const stderr: string[] = [];
199
+ const stdout: string[] = [];
200
+ const calls: string[] = [];
201
+ mkdirSync(projectRoot, { recursive: true });
202
+
203
+ try {
204
+ // when
205
+ const exitCode = await runCodegraphServe({
206
+ config: { codegraph: { enabled: true, excluded_roots: [excludedRoot] }, sources: [], warnings: [] },
207
+ cwd: projectRoot,
208
+ env: { HOME: "/tmp/home" },
209
+ homeDir: "/tmp/home",
210
+ nodeVersion: "22.14.0",
211
+ resolve: () => ({ argsPrefix: [], command: "/tmp/codegraph", exists: true, source: "path" }),
212
+ runProcess: () => {
213
+ calls.push("spawned");
214
+ return Promise.resolve(0);
215
+ },
216
+ stderr: { write: (chunk) => stderr.push(chunk) },
217
+ stdin: Readable.from([]),
218
+ stdout: new Writable({
219
+ write: (chunk, _encoding, callback) => {
220
+ stdout.push(String(chunk));
221
+ callback();
222
+ },
223
+ }),
224
+ });
225
+
226
+ // then
227
+ expect(exitCode).toBe(0);
228
+ expect(calls).toEqual([]);
229
+ expect(stderr.join("")).toContain("CodeGraph MCP skipped: project excluded");
230
+ } finally {
231
+ rmSync(excludedRoot, { recursive: true, force: true });
232
+ }
233
+ });
234
+
192
235
  it("#given Windows OMO_CODEGRAPH_BIN is a Node script #when resolving serve invocation #then Node executes the script path", () => {
193
236
  // given
194
237
  const scriptPath = "C:\\Users\\runner\\codegraph-fake.cjs";
@@ -217,73 +260,8 @@ describe("runCodegraphServe", () => {
217
260
  });
218
261
  });
219
262
 
220
- it("#given built serve entry #when invoked with a fake CodeGraph binary #then it runs serve mcp exactly once", () => {
221
- // given
222
- const tempRoot = createFakeCodegraphRoot();
223
- try {
224
- // when
225
- const result = runBuiltWrapper("dist/serve.js", tempRoot);
226
-
227
- // then
228
- expect(result.status).toBe(0);
229
- expect(result.stderr).toBe("");
230
- expect(readInvocations(tempRoot)).toEqual(['["serve","--mcp"]']);
231
- } finally {
232
- rmSync(tempRoot, { recursive: true, force: true });
233
- }
234
- });
235
-
236
- it("#given built cli entry #when invoked with a fake CodeGraph binary #then it runs serve mcp exactly once", () => {
237
- // given
238
- const tempRoot = createFakeCodegraphRoot();
239
- try {
240
- // when
241
- const result = runBuiltWrapper("dist/cli.js", tempRoot);
242
-
243
- // then
244
- expect(result.status).toBe(0);
245
- expect(result.stderr).toBe("");
246
- expect(readInvocations(tempRoot)).toEqual(['["serve","--mcp"]']);
247
- } finally {
248
- rmSync(tempRoot, { recursive: true, force: true });
249
- }
250
- });
251
263
  });
252
264
 
253
- function createFakeCodegraphRoot(): string {
254
- const tempRoot = mkdtempSync(join(tmpdir(), "omo-codegraph-wrapper-"));
255
- const fakeBinaryPath = join(tempRoot, "codegraph-fake.cjs");
256
- writeFileSync(
257
- fakeBinaryPath,
258
- [
259
- "#!/usr/bin/env node",
260
- "const fs = require('node:fs');",
261
- "fs.appendFileSync(process.env.CODEGRAPH_FAKE_LOG, JSON.stringify(process.argv.slice(2)) + '\\n');",
262
- "",
263
- ].join("\n"),
264
- );
265
- chmodSync(fakeBinaryPath, 0o755);
266
- return tempRoot;
267
- }
268
-
269
- function runBuiltWrapper(entryPath: string, tempRoot: string): ReturnType<typeof spawnSync> {
270
- return spawnSync(process.execPath, [join(componentRoot, entryPath)], {
271
- cwd: componentRoot,
272
- encoding: "utf8",
273
- env: {
274
- ...process.env,
275
- CODEGRAPH_ALLOW_UNSAFE_NODE: "1",
276
- CODEGRAPH_FAKE_LOG: join(tempRoot, "invocations.log"),
277
- OMO_CODEGRAPH_BIN: join(tempRoot, "codegraph-fake.cjs"),
278
- },
279
- timeout: 5000,
280
- });
281
- }
282
-
283
- function readInvocations(tempRoot: string): readonly string[] {
284
- return readFileSync(join(tempRoot, "invocations.log"), "utf8").trim().split("\n");
285
- }
286
-
287
265
  async function withProcessPlatform(platform: NodeJS.Platform, run: () => Promise<void>): Promise<void> {
288
266
  const descriptor = Object.getOwnPropertyDescriptor(process, "platform");
289
267
  Object.defineProperty(process, "platform", { configurable: true, enumerable: true, value: platform });
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ import { runCodegraphCli } from "../src/cli.ts";
7
+
8
+ describe("CodeGraph sweep CLI", () => {
9
+ it("#given sweep dry-run args with an injected root #when invoked #then it reports candidates as JSON and does not kill", async () => {
10
+ // given
11
+ const homeDir = mkdtempSync(join(tmpdir(), "omo-codegraph-sweep-cli-home-"));
12
+ const root = join(homeDir, ".codex", "plugins", "cache", "sisyphuslabs", "omo", "4.15.1");
13
+ const stdout: string[] = [];
14
+ const killed: string[] = [];
15
+ try {
16
+ // when
17
+ const exitCode = await runCodegraphCli({
18
+ argv: ["node", "cli.js", "sweep", "--dry-run", "--force", "--root", root],
19
+ env: { HOME: homeDir },
20
+ stdout: { write: (chunk) => stdout.push(chunk) },
21
+ sweepOptions: {
22
+ killer: {
23
+ isAlive: () => true,
24
+ kill: (pid) => {
25
+ killed.push(`kill:${pid}`);
26
+ return Promise.resolve();
27
+ },
28
+ terminate: (pid) => {
29
+ killed.push(`term:${pid}`);
30
+ return Promise.resolve();
31
+ },
32
+ },
33
+ processProvider: () => Promise.resolve([
34
+ {
35
+ command: `${process.execPath} ${root}/components/codegraph/dist/serve.js`,
36
+ pid: 601,
37
+ ppid: 1,
38
+ },
39
+ ]),
40
+ },
41
+ });
42
+
43
+ // then
44
+ expect(exitCode).toBe(0);
45
+ expect(killed).toEqual([]);
46
+ expect(JSON.parse(stdout.join(""))).toMatchObject({
47
+ action: "swept",
48
+ candidates: [{ pid: 601 }],
49
+ dryRun: true,
50
+ killed: [],
51
+ });
52
+ } finally {
53
+ rmSync(homeDir, { force: true, recursive: true });
54
+ }
55
+ });
56
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-comment-checker",
3
- "version": "4.15.1",
3
+ "version": "4.16.0",
4
4
  "description": "Codex plugin that runs comment-checker after edit-like PostToolUse hooks.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -0,0 +1,31 @@
1
+ # git-bash — Codex plugin component: Windows git_bash MCP reminder hook
2
+
3
+ **Generated:** 2026-07-03
4
+
5
+ ## OVERVIEW
6
+
7
+ Component wrapper (`@sisyphuslabs/codex-git-bash-hook`, private, bin `omo-git-bash-hook`) that steers Windows Codex sessions toward the OMO `git_bash` MCP: a `PreToolUse` hook on `^Bash$` injects one `additionalContext` reminder per session to prefer `git_bash` over built-in `exec_command`; a `PostCompact` hook clears the once-per-session marker so the reminder re-fires after compaction. This component is NOT the MCP — the `git_bash` stdio server is declared plugin-wide in [`.mcp.json`](../../.mcp.json) (`node ../../git-bash-mcp/dist/cli.js mcp`) and implemented by [`packages/git-bash-mcp`](../../../../git-bash-mcp/AGENTS.md). Emits nothing on non-Windows hosts.
8
+
9
+ ## KEY FILES
10
+
11
+ | File | Role |
12
+ |------|------|
13
+ | `src/codex-hook.ts` | All logic: payload parsing + type guards, Windows detection, reminder marker lifecycle, hook JSON output |
14
+ | `src/cli.ts` | Bin entry; subcommands `hook pre-tool-use` / `hook post-compact` (stdin JSON in, hook JSON on stdout) |
15
+ | `src/index.ts` | Barrel export of the `codex-hook` API |
16
+ | `hooks/hooks.json` | Codex wiring: `PreToolUse` matcher `^Bash$` + `PostCompact`, both `node "${PLUGIN_ROOT}/dist/cli.js"`, 5s timeout |
17
+ | `test/codex-hook.test.ts` | 7 `bun:test` cases (given/when/then): once-per-session reminder, non-Bash / non-Windows skip, PostCompact reset, CLI stream round-trip |
18
+
19
+ ## WHERE TO LOOK
20
+
21
+ - MCP internals (tools `run` / `which_bash` / `diagnose`, `bash.exe` resolution, timeout env chain) → [`packages/git-bash-mcp/AGENTS.md`](../../../../git-bash-mcp/AGENTS.md). Do not document them here.
22
+ - Install-time handling → `packages/omo-codex/src/install/`: `codex-cache-bundled-mcps.ts` copies the MCP dist into the plugin cache and rewrites the `.mcp.json` arg to `./components/git-bash-mcp/dist/cli.js`; `codex-config-plugins.ts` sets `[plugins."omo@sisyphuslabs".mcp_servers.git_bash]` `enabled = true` only on win32 with Git Bash resolved (`enabled = false` elsewhere); `codex-git-bash-mcp-env.ts` `stampGitBashMcpEnv()` stamps `OMO_CODEX_GIT_BASH_PATH` into the server `env` on win32 when the override env var is set.
23
+ - Component roster + skills sync pipeline → [`packages/omo-codex/AGENTS.md`](../../../AGENTS.md).
24
+
25
+ ## NOTES
26
+
27
+ - **Windows detection is env-tolerant** (`isWindowsHost`): `platform === "win32"` OR any of `OS=Windows_NT` / `ComSpec` / `SystemRoot` present, so bash-hosted shells on Windows still count.
28
+ - **Marker path:** `${PLUGIN_DATA:-~/.codex/omo-git-bash}/git-bash-reminder/<session_id>.seen`; session id sanitized to `[A-Za-z0-9._-]`. `PostCompact` removes it with `rmSync(force)`.
29
+ - **Hook must never block the user's Bash call:** the CLI swallows parse/fs errors and exits 0 on both hook paths; malformed or empty stdin yields empty output.
30
+ - **Runtime dep-free Node >=20 ESM.** `@oh-my-opencode/utils` is a devDependency only (`file:` link). Build `tsc -p tsconfig.build.json`; tests run via `bun test test/*.test.ts` (unlike the vitest-based `lsp` component).
31
+ - **Windows absolute MCP targets are valid:** `isPluginRuntimePathArg` in `packages/omo-opencode/src/cli/doctor/checks/codex-components.ts` and `script/lazycodex-marketplace-validation.ts` gained `isAbsolute(arg)` so a rewritten `C:\...` git_bash/lsp entrypoint passes validation (commit 3b1681ae8).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-git-bash-hook",
3
- "version": "4.15.1",
3
+ "version": "4.16.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,
@@ -0,0 +1,45 @@
1
+ # lazycodex-executor-verify
2
+
3
+ **Generated:** 2026-07-03
4
+
5
+ ## OVERVIEW
6
+
7
+ Codex `SubagentStop` hook component: the evidence gate for the `lazycodex-executor` subagent (ultrawork implementation executor). When the executor stops without a valid evidence receipt, the hook emits `{"decision": "block", "reason": <directive>}` and Codex sends it back to work. Matcher is `^lazycodex-executor$` only; `lazycodex-qa-executor` and `lazycodex-gate-reviewer` (same `ultrawork/agents/` family) are NOT gated by this hook.
8
+
9
+ Valid receipt: `last_assistant_message` contains `EVIDENCE_RECORDED: <path>` where `<path>` resolves to a non-empty regular file strictly inside `<cwd>/.omo/evidence/`. Symlinks and directories rejected; containment checked on realpaths (file inside evidence root inside cwd, traversal-safe). A valid receipt clears attempt state and exits silently.
10
+
11
+ Escape hatches: after 3 blocked attempts (`MAX_ATTEMPTS`) the stop passes and state clears; a transcript containing a context-pressure marker ("context compacted", "context_length_exceeded", ...) passes immediately. Malformed stdin, unknown events, unrelated agents: silent exit 0 (fail-open).
12
+
13
+ ## KEY FILES
14
+
15
+ | File | Role |
16
+ |------|------|
17
+ | `src/codex-hook.ts` | Core `runSubagentStopHook()`: input guard, receipt validation, attempt escalation, block decision |
18
+ | `src/state.ts` | Attempt counter at `<cwd>/.omo/lazycodex-executor-verify/<session>-<agent>.json`; atomic tmp+rename write; `MAX_ATTEMPTS = 3` |
19
+ | `src/directive.ts` | Loads `directive.md` at import time; `renderDirective()` fills `{{ATTEMPT_COUNT}}` + `{{LAST_ASSISTANT_MESSAGE}}` |
20
+ | `directive.md` | Korean block message ("your completion claim is not trusted until evidence is recorded"); RUNTIME-READ via `../directive.md` relative to `dist/` |
21
+ | `src/cli.ts` | Node bin `lazycodex-executor-verify hook subagent-stop`: stdin JSON in, block JSON (or nothing) on stdout |
22
+ | `src/types.ts` | `SubagentStopInput` field guard, `StopHookOutput` (block-only shape), injectable `HookFileSystem` |
23
+ | `hooks/hooks.json` | Component-local wiring (Unix `node .../dist/cli.js` command only) |
24
+ | `test/codex-hook.test.ts`, `test/cli.test.ts` | Vitest given/when/then: symlink/traversal/zero-byte receipts, attempt escalation and cap, context pressure, malformed stdin |
25
+
26
+ ## WHERE TO LOOK
27
+
28
+ | Task | Location |
29
+ |------|----------|
30
+ | Change the block message | `directive.md`; keep both `{{...}}` placeholders and the literal `EVIDENCE_RECORDED: <path>` final-line contract |
31
+ | Change receipt validation | `src/codex-hook.ts` (`hasValidEvidenceReceipt`, `extractEvidencePath`, `isNonEmptyFileInsideEvidenceRoot`) |
32
+ | Change the retry budget | `src/state.ts` `MAX_ATTEMPTS` |
33
+ | Executor-side contract | `../ultrawork/agents/lazycodex-executor.toml` instructs the final `EVIDENCE_RECORDED: <path>` line this hook parses |
34
+ | Plugin-level wiring | `../../hooks/subagent-stop-verifying-lazycodex-executor-evidence.json` (adds `commandWindows` via `../bootstrap/scripts/node-dispatch.ps1`) |
35
+ | Wiring + contract tests | `../../test/aggregate-hooks.test.mjs`, `../../test/component-hook-contract-cases.mjs`, `../../test/hook-status-message.test.mjs`, `../../test/component-bundled-cli.test.mjs` |
36
+
37
+ ## NOTES
38
+
39
+ - Node >=20, npm + vitest + biome, TypeScript 6 strict. No Bun APIs: Codex launches hooks with Node. `npm test` builds first, then runs vitest.
40
+ - `hooks/hooks.json` (component) and `../../hooks/subagent-stop-verifying-lazycodex-executor-evidence.json` (aggregate) are hand-maintained twins. Edit both or aggregate tests fail. Only the aggregate copy carries the Windows dispatch.
41
+ - `dist/` is gitignored, but root `package.json` `files` ships `.../lazycodex-executor-verify/dist/cli.js` and the hook command targets `dist/cli.js`. Nothing works from a fresh clone until a build runs.
42
+ - `directive.md` is read at runtime, not bundled: `dist/directive.js` resolves `../directive.md`, so the file must stay at component root (it is in `package.json` `files`).
43
+ - `stop_hook_active: true` does NOT bypass the check; the directive says so explicitly. Only the attempt cap and context-pressure markers let a receipt-less stop through.
44
+ - Blocking output is the stable Codex stop-hook contract: `decision: "block"` + `reason`, nothing else. A passing stop is empty stdout, exit 0. The only nonzero exit is a CLI usage error.
45
+ - The receipt regex takes the first `EVIDENCE_RECORDED:` match and a single `\S+` token: no spaces in evidence paths.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-lazycodex-executor-verify",
3
- "version": "4.15.1",
3
+ "version": "4.16.0",
4
4
  "description": "Codex SubagentStop evidence verifier for LazyCodex executor completions.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-lsp",
3
- "version": "4.15.1",
3
+ "version": "4.16.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",