oh-my-opencode 4.5.12 → 4.7.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 (189) hide show
  1. package/.agents/skills/opencode-qa/SKILL.md +194 -0
  2. package/.agents/skills/opencode-qa/references/cli-commands.md +188 -0
  3. package/.agents/skills/opencode-qa/references/db-investigation.md +197 -0
  4. package/.agents/skills/opencode-qa/references/events-hooks.md +110 -0
  5. package/.agents/skills/opencode-qa/references/sdk.md +96 -0
  6. package/.agents/skills/opencode-qa/references/server-api.md +200 -0
  7. package/.agents/skills/opencode-qa/references/testing-harness.md +218 -0
  8. package/.agents/skills/opencode-qa/references/tui-tmux.md +52 -0
  9. package/.agents/skills/opencode-qa/scripts/db-session-by-id.sh +53 -0
  10. package/.agents/skills/opencode-qa/scripts/db-session-by-name.sh +57 -0
  11. package/.agents/skills/opencode-qa/scripts/db-session-by-text.sh +158 -0
  12. package/.agents/skills/opencode-qa/scripts/export-roundtrip.sh +57 -0
  13. package/.agents/skills/opencode-qa/scripts/lib/common.sh +216 -0
  14. package/.agents/skills/opencode-qa/scripts/server-smoke.sh +64 -0
  15. package/.agents/skills/opencode-qa/scripts/sse-hook-probe.sh +106 -0
  16. package/.agents/skills/opencode-qa/scripts/tui-smoke.sh +89 -0
  17. package/README.ja.md +13 -3
  18. package/README.ko.md +13 -3
  19. package/README.md +24 -14
  20. package/README.ru.md +13 -3
  21. package/README.zh-cn.md +13 -3
  22. package/bin/oh-my-opencode.js +4 -3
  23. package/bin/oh-my-opencode.test.ts +35 -7
  24. package/bin/platform.d.ts +1 -1
  25. package/bin/platform.js +4 -4
  26. package/bin/platform.test.ts +31 -9
  27. package/bin/version-mismatch.js +47 -0
  28. package/bin/version-mismatch.test.ts +120 -0
  29. package/dist/cli/cleanup-command.d.ts +4 -0
  30. package/dist/cli/cleanup.d.ts +11 -0
  31. package/dist/cli/cli-program.d.ts +2 -1
  32. package/dist/cli/codex-ulw-loop.d.ts +12 -0
  33. package/dist/cli/doctor/checks/tui-plugin-config.d.ts +2 -0
  34. package/dist/cli/index.js +2189 -529
  35. package/dist/cli/install-codex/codex-cache.d.ts +1 -0
  36. package/dist/cli/install-codex/codex-cleanup-config.d.ts +6 -0
  37. package/dist/cli/install-codex/codex-cleanup.d.ts +21 -0
  38. package/dist/cli/install-codex/codex-config-permissions.d.ts +1 -0
  39. package/dist/cli/install-codex/codex-config-reasoning.d.ts +2 -0
  40. package/dist/cli/install-codex/codex-config-toml.d.ts +2 -1
  41. package/dist/cli/install-codex/codex-installation-detection.d.ts +36 -0
  42. package/dist/cli/install-codex/codex-model-catalog.d.ts +13 -0
  43. package/dist/cli/install-codex/codex-package-layout.d.ts +1 -0
  44. package/dist/cli/install-codex/codex-project-local-cleanup-best-effort.d.ts +7 -0
  45. package/dist/cli/install-codex/codex-project-local-cleanup.d.ts +35 -0
  46. package/dist/cli/install-codex/git-bash.d.ts +35 -0
  47. package/dist/cli/install-codex/index.d.ts +4 -0
  48. package/dist/cli/install-codex/toml-section-editor.d.ts +2 -0
  49. package/dist/cli/install-codex/types.d.ts +20 -0
  50. package/dist/cli/run/event-state.d.ts +1 -0
  51. package/dist/cli/run/poll-for-completion.d.ts +1 -0
  52. package/dist/cli/run/prompt-start.d.ts +7 -0
  53. package/dist/cli/star-request.d.ts +9 -0
  54. package/dist/config/schema/hooks.d.ts +0 -1
  55. package/dist/create-hooks.d.ts +0 -1
  56. package/dist/features/background-agent/concurrency.d.ts +1 -0
  57. package/dist/features/background-agent/process-cleanup.d.ts +6 -0
  58. package/dist/features/builtin-skills/skills/debugging.d.ts +2 -0
  59. package/dist/features/builtin-skills/skills/index.d.ts +1 -0
  60. package/dist/features/claude-code-session-state/state.d.ts +1 -0
  61. package/dist/features/opencode-skill-loader/index.d.ts +1 -0
  62. package/dist/features/opencode-skill-loader/opencode-config-skills-reader.d.ts +5 -0
  63. package/dist/features/tmux-subagent/attachable-session-status.d.ts +1 -1
  64. package/dist/features/tmux-subagent/session-status-parser.d.ts +1 -0
  65. package/dist/hooks/comment-checker/cli.d.ts +1 -0
  66. package/dist/hooks/index.d.ts +0 -1
  67. package/dist/hooks/tasks-todowrite-disabler/constants.d.ts +1 -1
  68. package/dist/index.js +1077 -563
  69. package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
  70. package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
  71. package/dist/plugin/messages-transform.d.ts +8 -1
  72. package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +6 -0
  73. package/dist/shared/command-executor/execute-hook-command.d.ts +2 -0
  74. package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +14 -0
  75. package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +7 -0
  76. package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +1 -0
  77. package/dist/shared/prompt-async-gate/timing.d.ts +1 -0
  78. package/dist/shared/prompt-async-gate/types.d.ts +2 -0
  79. package/dist/shared/prompt-async-gate.d.ts +1 -1
  80. package/dist/tools/skill/description-formatter.d.ts +5 -1
  81. package/dist/tools/skill/types.d.ts +1 -0
  82. package/package.json +22 -18
  83. package/packages/ast-grep-mcp/dist/cli.js +53 -9
  84. package/packages/git-bash-mcp/dist/cli.js +367 -0
  85. package/packages/lsp-tools-mcp/dist/lsp/process.js +1 -1
  86. package/packages/omo-codex/plugin/.mcp.json +11 -0
  87. package/packages/omo-codex/plugin/components/comment-checker/README.md +1 -1
  88. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +29 -0
  89. package/packages/omo-codex/plugin/components/git-bash/package.json +23 -0
  90. package/packages/omo-codex/plugin/components/git-bash/src/cli.ts +33 -0
  91. package/packages/omo-codex/plugin/components/git-bash/src/codex-hook.ts +180 -0
  92. package/packages/omo-codex/plugin/components/git-bash/src/index.ts +10 -0
  93. package/packages/omo-codex/plugin/components/git-bash/test/codex-hook.test.ts +195 -0
  94. package/packages/omo-codex/plugin/components/git-bash/tsconfig.build.json +13 -0
  95. package/packages/omo-codex/plugin/components/git-bash/tsconfig.json +25 -0
  96. package/packages/omo-codex/plugin/components/lsp/README.md +1 -1
  97. package/packages/omo-codex/plugin/components/lsp/src/cli.ts +5 -5
  98. package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +33 -0
  99. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +19 -27
  100. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +28 -0
  101. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-errors.test.ts +55 -0
  102. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +7 -5
  103. package/packages/omo-codex/plugin/components/rules/README.md +1 -1
  104. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +6 -4
  105. package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +10 -0
  106. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +0 -2
  107. package/packages/omo-codex/plugin/components/rules/test/package-smoke.test.ts +3 -1
  108. package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +97 -0
  109. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +6 -5
  110. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +22 -0
  111. package/packages/omo-codex/plugin/components/ultrawork/CHANGELOG.md +1 -1
  112. package/packages/omo-codex/plugin/components/ultrawork/README.md +3 -3
  113. package/packages/omo-codex/plugin/components/ultrawork/agents/codex-ultrawork-reviewer.toml +4 -1
  114. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +8 -7
  115. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +9 -8
  116. package/packages/omo-codex/plugin/components/ultrawork/directive.md +32 -6
  117. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +27 -4
  118. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +25 -0
  119. package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -1
  120. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +28 -205
  121. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +231 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +12 -1
  123. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint.test.ts +19 -1
  124. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +102 -5
  125. package/packages/omo-codex/plugin/hooks/hooks.json +35 -2
  126. package/packages/omo-codex/plugin/model-catalog.json +49 -0
  127. package/packages/omo-codex/plugin/package-lock.json +19 -0
  128. package/packages/omo-codex/plugin/package.json +3 -1
  129. package/packages/omo-codex/plugin/scripts/auto-update.mjs +159 -0
  130. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +16 -1
  131. package/packages/omo-codex/plugin/scripts/build-components.mjs +2 -1
  132. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +269 -0
  133. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +89 -0
  134. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +6 -6
  135. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +6 -6
  136. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +127 -0
  137. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +9 -0
  138. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +6 -6
  139. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +6 -6
  140. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +33 -8
  141. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +25 -5
  142. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +28 -205
  143. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +231 -0
  144. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +17 -17
  145. package/packages/omo-codex/plugin/test/aggregate.test.mjs +188 -20
  146. package/packages/omo-codex/plugin/test/auto-update.test.mjs +129 -0
  147. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +58 -11
  148. package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +34 -0
  149. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +21 -0
  150. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +146 -0
  151. package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +48 -0
  152. package/packages/omo-codex/plugin/test/subagent-guidance.test.mjs +76 -0
  153. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +67 -0
  154. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +54 -2
  155. package/packages/omo-codex/scripts/install/cache.mjs +5 -3
  156. package/packages/omo-codex/scripts/install/cli-args.mjs +112 -0
  157. package/packages/omo-codex/scripts/install/config.mjs +23 -1
  158. package/packages/omo-codex/scripts/install/delegated-command.mjs +25 -0
  159. package/packages/omo-codex/scripts/install/git-bash.mjs +99 -0
  160. package/packages/omo-codex/scripts/install/git-bash.test.mjs +174 -0
  161. package/packages/omo-codex/scripts/install/legacy-bins.mjs +1 -0
  162. package/packages/omo-codex/scripts/install/mcp-runtime-cache.mjs +5 -1
  163. package/packages/omo-codex/scripts/install/model-catalog.mjs +66 -0
  164. package/packages/omo-codex/scripts/install/multi-agent-v2-config.mjs +7 -1
  165. package/packages/omo-codex/scripts/install/permissions.d.mts +1 -0
  166. package/packages/omo-codex/scripts/install/permissions.mjs +26 -0
  167. package/packages/omo-codex/scripts/install/project-local-cleanup.mjs +229 -0
  168. package/packages/omo-codex/scripts/install/reasoning-config.mjs +72 -0
  169. package/packages/omo-codex/scripts/install/source-package-build.mjs +20 -0
  170. package/packages/omo-codex/scripts/install/toml-editor.mjs +19 -2
  171. package/packages/omo-codex/scripts/install-bin-links.test.mjs +23 -0
  172. package/packages/omo-codex/scripts/install-cli-args.test.mjs +146 -0
  173. package/packages/omo-codex/scripts/install-config-autonomous.test.mjs +48 -0
  174. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +141 -0
  175. package/packages/omo-codex/scripts/install-config.test.mjs +205 -0
  176. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +157 -0
  177. package/packages/omo-codex/scripts/install-local-git-bash-preflight.test.mjs +145 -0
  178. package/packages/omo-codex/scripts/install-local.mjs +91 -8
  179. package/packages/omo-codex/scripts/install-local.test.mjs +15 -0
  180. package/packages/omo-codex/scripts/install-mcp-runtime.test.mjs +60 -0
  181. package/packages/omo-codex/scripts/install-packaged-local.test.mjs +67 -0
  182. package/packages/omo-codex/scripts/install-project-local-cleanup.test.mjs +277 -0
  183. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +127 -0
  184. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +9 -0
  185. package/packages/shared-skills/skills/review-work/SKILL.md +33 -8
  186. package/packages/shared-skills/skills/start-work/SKILL.md +25 -5
  187. package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -11
  188. package/postinstall.mjs +36 -3
  189. package/dist/hooks/context-window-monitor.d.ts +0 -19
@@ -1,5 +1,7 @@
1
1
  // biome-ignore-all format: smoke test pulls verbatim JSON for structural assertion.
2
- import { readFile, stat } from "node:fs/promises";
2
+ import { spawn } from "node:child_process";
3
+ import { chmod, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
3
5
  import { dirname, join, resolve } from "node:path";
4
6
  import { fileURLToPath } from "node:url";
5
7
  import { describe, expect, it } from "vitest";
@@ -14,6 +16,41 @@ async function readJson(relative: string): Promise<unknown> {
14
16
  return JSON.parse(await readText(relative));
15
17
  }
16
18
 
19
+ type ShellResult = {
20
+ readonly code: number | null;
21
+ readonly stdout: string;
22
+ readonly stderr: string;
23
+ };
24
+
25
+ function bootstrapScriptFrom(text: string): string {
26
+ const heading = text.indexOf("### 1. Create goals from the brief");
27
+ expect(heading).toBeGreaterThanOrEqual(0);
28
+ const blockStart = text.indexOf("```sh\n", heading);
29
+ expect(blockStart).toBeGreaterThanOrEqual(0);
30
+ const codeStart = blockStart + "```sh\n".length;
31
+ const blockEnd = text.indexOf("\n```", codeStart);
32
+ expect(blockEnd).toBeGreaterThan(codeStart);
33
+ return text.slice(codeStart, blockEnd);
34
+ }
35
+
36
+ async function runShell(script: string, env: NodeJS.ProcessEnv): Promise<ShellResult> {
37
+ return new Promise((resolvePromise, reject) => {
38
+ const child = spawn("/bin/sh", ["-c", script], { env });
39
+ const stdout: Buffer[] = [];
40
+ const stderr: Buffer[] = [];
41
+ child.stdout.on("data", (chunk: Buffer) => stdout.push(chunk));
42
+ child.stderr.on("data", (chunk: Buffer) => stderr.push(chunk));
43
+ child.on("error", reject);
44
+ child.on("close", (code) => {
45
+ resolvePromise({
46
+ code,
47
+ stdout: Buffer.concat(stdout).toString("utf8"),
48
+ stderr: Buffer.concat(stderr).toString("utf8"),
49
+ });
50
+ });
51
+ });
52
+ }
53
+
17
54
  describe("package.json", () => {
18
55
  it("declares ESM + npm + Node >=20", async () => {
19
56
  const pkg = await readJson("package.json") as Record<string, unknown>;
@@ -102,13 +139,13 @@ describe("skills/ulw-loop/SKILL.md", () => {
102
139
  });
103
140
 
104
141
  it("references the success criteria and record-evidence vocabulary", async () => {
105
- const text = await readText("skills/ulw-loop/SKILL.md");
142
+ const text = await readText("skills/ulw-loop/references/full-workflow.md");
106
143
  expect(text.toLowerCase()).toMatch(/success criteria|successcriteria/);
107
144
  expect(text.toLowerCase()).toContain("record-evidence");
108
145
  });
109
146
 
110
147
  it("#given omo is absent from PATH #when bootstrap instructions are read #then local cached CLI fallback is documented", async () => {
111
- const text = await readText("skills/ulw-loop/SKILL.md");
148
+ const text = await readText("skills/ulw-loop/references/full-workflow.md");
112
149
 
113
150
  expect(text).toContain("If `omo` is absent from PATH");
114
151
  expect(text).toContain("ULW_LOOP_CLI");
@@ -116,7 +153,7 @@ describe("skills/ulw-loop/SKILL.md", () => {
116
153
  });
117
154
 
118
155
  it("#given empty PATH #when bootstrap instructions are read #then handles empty PATH without losing notepad bootstrap", async () => {
119
- const text = await readText("skills/ulw-loop/SKILL.md");
156
+ const text = await readText("skills/ulw-loop/references/full-workflow.md");
120
157
 
121
158
  expect(text).toContain("If PATH is empty");
122
159
  expect(text).toContain("ULW_LOOP_NODE");
@@ -124,13 +161,57 @@ describe("skills/ulw-loop/SKILL.md", () => {
124
161
  expect(text).not.toContain("ls -1");
125
162
  });
126
163
 
164
+ it("#given PATH omo lacks ulw-loop #when bootstrap runs #then falls back to cached ulw-loop CLI", async () => {
165
+ const text = await readText("skills/ulw-loop/references/full-workflow.md");
166
+ const bootstrap = bootstrapScriptFrom(text);
167
+ const root = await mkdtemp(join(tmpdir(), "omo-ulw-loop-bootstrap-"));
168
+ try {
169
+ const badBin = join(root, "bad-bin");
170
+ const home = join(root, "home");
171
+ const codexHome = join(home, ".codex");
172
+ const cachedCli = join(codexHome, "plugins", "cache", "sisyphuslabs", "omo", "0.1.0", "components", "ulw-loop", "dist", "cli.js");
173
+ await mkdir(badBin, { recursive: true });
174
+ await mkdir(dirname(cachedCli), { recursive: true });
175
+ await writeFile(join(badBin, "omo"), "#!/bin/sh\nprintf '%s\\n' \"error: unknown command 'ulw-loop'\" >&2\nexit 1\n");
176
+ await chmod(join(badBin, "omo"), 0o755);
177
+ await writeFile(
178
+ cachedCli,
179
+ [
180
+ "#!/usr/bin/env node",
181
+ "const args = process.argv.slice(2);",
182
+ "if (args[0] === 'ulw-loop' && args[1] === 'help') process.exit(0);",
183
+ "if (args[0] === 'ulw-loop' && args[1] === 'status' && args.includes('--json')) {",
184
+ " console.log(JSON.stringify({ ok: true, source: 'cached-ulw-loop' }));",
185
+ " process.exit(0);",
186
+ "}",
187
+ "console.error('unexpected args: ' + args.join(' '));",
188
+ "process.exit(1);",
189
+ "",
190
+ ].join("\n"),
191
+ );
192
+
193
+ const result = await runShell(`${bootstrap}\nomo ulw-loop status --json`, {
194
+ ...process.env,
195
+ CODEX_HOME: codexHome,
196
+ HOME: home,
197
+ PATH: `${badBin}:${process.env["PATH"] ?? ""}`,
198
+ });
199
+
200
+ expect(result.code).toBe(0);
201
+ expect(result.stdout).toContain('"source":"cached-ulw-loop"');
202
+ expect(result.stderr).not.toContain("unknown command");
203
+ } finally {
204
+ await rm(root, { recursive: true, force: true });
205
+ }
206
+ });
207
+
127
208
  it("uses the .omo workspace path", async () => {
128
209
  const text = await readText("skills/ulw-loop/SKILL.md");
129
210
  expect(text).toContain(".omo/ulw-loop");
130
211
  });
131
212
 
132
213
  it("#given long Codex runs #when worker guidance is inspected #then avoids context-expensive agent polling", async () => {
133
- const text = await readText("skills/ulw-loop/SKILL.md");
214
+ const text = await readText("skills/ulw-loop/references/full-workflow.md");
134
215
 
135
216
  expect(text).toMatch(/list_agents/);
136
217
  expect(text).toMatch(/polling or status tool/);
@@ -139,9 +220,25 @@ describe("skills/ulw-loop/SKILL.md", () => {
139
220
  expect(text).toMatch(/wait_agent.*completion/);
140
221
  expect(text).toMatch(/targeted followups only when needed/);
141
222
  expect(text).toMatch(/close_agent.*after integrating each result/);
223
+ expect(text).toMatch(/Plan and reviewer agents may run for a long time/);
224
+ expect(text).toMatch(/short wait_agent cycles/);
225
+ expect(text).toMatch(/single long blocking wait/);
142
226
  expect(text).toContain("Every worker message MUST carry");
143
227
  expect(text).toContain("Each worker does strict TDD");
144
228
  });
229
+
230
+ it("#given Codex subagent delegation #when worker guidance is inspected #then assignment ambiguity is hardened", async () => {
231
+ const text = await readText("skills/ulw-loop/SKILL.md");
232
+
233
+ expect(text).toMatch(/TASK:/);
234
+ expect(text).toMatch(/fork_turns:\s*"none"/);
235
+ expect(text).toMatch(/wait_agent.*signal, not proof/);
236
+ expect(text).toMatch(/one targeted followup/);
237
+ expect(text).toMatch(/respawn.*smaller/);
238
+ expect(text).toMatch(/Plan and reviewer agents may run for a long time/);
239
+ expect(text).toMatch(/short wait_agent cycles/);
240
+ expect(text).toMatch(/single long blocking wait/);
241
+ });
145
242
  });
146
243
 
147
244
  describe("source LOC budget", () => {
@@ -20,6 +20,17 @@
20
20
  "statusMessage": "LazyCodex(0.1.0): Recording Session Telemetry"
21
21
  }
22
22
  ]
23
+ },
24
+ {
25
+ "matcher": "^startup$",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "node \"${PLUGIN_ROOT}/scripts/auto-update.mjs\" hook session-start",
30
+ "timeout": 5,
31
+ "statusMessage": "LazyCodex(0.1.0): Checking Auto Update"
32
+ }
33
+ ]
23
34
  }
24
35
  ],
25
36
  "UserPromptSubmit": [
@@ -55,6 +66,17 @@
55
66
  }
56
67
  ],
57
68
  "PreToolUse": [
69
+ {
70
+ "matcher": "^Bash$",
71
+ "hooks": [
72
+ {
73
+ "type": "command",
74
+ "command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook pre-tool-use",
75
+ "timeout": 5,
76
+ "statusMessage": "LazyCodex(0.1.0): Recommending Git Bash Mcp"
77
+ }
78
+ ]
79
+ },
58
80
  {
59
81
  "matcher": "^create_goal$",
60
82
  "hooks": [
@@ -75,13 +97,13 @@
75
97
  "type": "command",
76
98
  "command": "node \"${PLUGIN_ROOT}/components/comment-checker/dist/cli.js\" hook post-tool-use",
77
99
  "timeout": 30,
78
- "statusMessage": "LazyCodex(0.1.0): Checking Comments"
100
+ "statusMessage": "LazyCodex(0.1.1): Checking Comments"
79
101
  },
80
102
  {
81
103
  "type": "command",
82
104
  "command": "node \"${PLUGIN_ROOT}/components/lsp/dist/cli.js\" hook post-tool-use",
83
105
  "timeout": 60,
84
- "statusMessage": "LazyCodex(0.1.0): Checking LSP Diagnostics"
106
+ "statusMessage": "LazyCodex(0.2.0): Checking LSP Diagnostics"
85
107
  }
86
108
  ]
87
109
  },
@@ -98,6 +120,17 @@
98
120
  }
99
121
  ],
100
122
  "PostCompact": [
123
+ {
124
+ "matcher": "manual|auto",
125
+ "hooks": [
126
+ {
127
+ "type": "command",
128
+ "command": "node \"${PLUGIN_ROOT}/components/git-bash/dist/cli.js\" hook post-compact",
129
+ "timeout": 5,
130
+ "statusMessage": "LazyCodex(0.1.0): Resetting Git Bash Mcp Reminder"
131
+ }
132
+ ]
133
+ },
101
134
  {
102
135
  "matcher": "manual|auto",
103
136
  "hooks": [
@@ -0,0 +1,49 @@
1
+ {
2
+ "version": "2026-06-03.gpt-5.5-400k",
3
+ "current": {
4
+ "model": "gpt-5.5",
5
+ "model_context_window": 400000,
6
+ "model_reasoning_effort": "high",
7
+ "plan_mode_reasoning_effort": "xhigh"
8
+ },
9
+ "roles": {
10
+ "default": {
11
+ "model": "gpt-5.5",
12
+ "model_context_window": 400000,
13
+ "model_reasoning_effort": "high",
14
+ "plan_mode_reasoning_effort": "xhigh"
15
+ },
16
+ "verifier": {
17
+ "model": "gpt-5.5",
18
+ "model_reasoning_effort": "xhigh"
19
+ },
20
+ "worker": {
21
+ "model": "gpt-5.4",
22
+ "model_reasoning_effort": "high"
23
+ }
24
+ },
25
+ "managedProfiles": [
26
+ {
27
+ "version": "legacy.gpt-5.2",
28
+ "match": {
29
+ "model": "gpt-5.2"
30
+ }
31
+ },
32
+ {
33
+ "version": "legacy.gpt-5.4-1m",
34
+ "match": {
35
+ "model": "gpt-5.4",
36
+ "model_context_window": 1000000,
37
+ "model_reasoning_effort": "high",
38
+ "plan_mode_reasoning_effort": "xhigh"
39
+ }
40
+ },
41
+ {
42
+ "version": "legacy.gpt-5.5-272k",
43
+ "match": {
44
+ "model": "gpt-5.5",
45
+ "model_context_window": 272000
46
+ }
47
+ }
48
+ ]
49
+ }
@@ -9,6 +9,7 @@
9
9
  "version": "0.1.0",
10
10
  "workspaces": [
11
11
  "components/comment-checker",
12
+ "components/git-bash",
12
13
  "components/rules",
13
14
  "components/lsp",
14
15
  "components/telemetry",
@@ -61,6 +62,20 @@
61
62
  "@code-yeongyu/comment-checker": "^0.8.0"
62
63
  }
63
64
  },
65
+ "components/git-bash": {
66
+ "name": "@sisyphuslabs/codex-git-bash-hook",
67
+ "version": "0.1.0",
68
+ "bin": {
69
+ "omo-git-bash-hook": "dist/cli.js"
70
+ },
71
+ "devDependencies": {
72
+ "@types/node": "^25.7.0",
73
+ "typescript": "^6.0.3"
74
+ },
75
+ "engines": {
76
+ "node": ">=20.0.0"
77
+ }
78
+ },
64
79
  "components/lsp": {
65
80
  "name": "@code-yeongyu/codex-lsp",
66
81
  "version": "0.2.0",
@@ -765,6 +780,10 @@
765
780
  "dev": true,
766
781
  "license": "MIT"
767
782
  },
783
+ "node_modules/@sisyphuslabs/codex-git-bash-hook": {
784
+ "resolved": "components/git-bash",
785
+ "link": true
786
+ },
768
787
  "node_modules/@standard-schema/spec": {
769
788
  "version": "1.1.0",
770
789
  "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
@@ -7,6 +7,7 @@
7
7
  "private": true,
8
8
  "workspaces": [
9
9
  "components/comment-checker",
10
+ "components/git-bash",
10
11
  "components/rules",
11
12
  "components/lsp",
12
13
  "components/telemetry",
@@ -18,8 +19,9 @@
18
19
  "@oh-my-opencode/shared-skills": "file:../../shared-skills"
19
20
  },
20
21
  "scripts": {
21
- "build": "node scripts/build-bundled-mcp-runtimes.mjs && node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && node scripts/build-components.mjs",
22
+ "build": "node scripts/sync-hook-status-messages.mjs && node scripts/build-bundled-mcp-runtimes.mjs && node scripts/sync-skills.mjs && node ../scripts/sync-telemetry-component.mjs && node scripts/build-components.mjs",
22
23
  "check": "npm run build && npm test",
24
+ "sync:hooks": "node scripts/sync-hook-status-messages.mjs",
23
25
  "sync:skills": "node scripts/sync-skills.mjs",
24
26
  "test": "node --test test/*.test.mjs"
25
27
  }
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn, spawnSync } from "node:child_process";
4
+ import { mkdir, open, readFile, rm, stat, writeFile } from "node:fs/promises";
5
+ import { homedir } from "node:os";
6
+ import { dirname, join } from "node:path";
7
+ import { pathToFileURL } from "node:url";
8
+ import { migrateCodexConfig } from "./migrate-codex-config.mjs";
9
+
10
+ const DEFAULT_INTERVAL_MS = 24 * 60 * 60 * 1_000;
11
+ const DEFAULT_LOCK_STALE_MS = 10 * 60 * 1_000;
12
+
13
+ export function resolveAutoUpdatePlan({ env = process.env, now = Date.now(), lastCheckedAt } = {}) {
14
+ if (env.LAZYCODEX_AUTO_UPDATE_DISABLED === "1" || env.OMO_CODEX_AUTO_UPDATE_DISABLED === "1") {
15
+ return { shouldRun: false, reason: "disabled" };
16
+ }
17
+
18
+ const intervalMs = parsePositiveInteger(env.LAZYCODEX_AUTO_UPDATE_INTERVAL_MS, DEFAULT_INTERVAL_MS);
19
+ if (typeof lastCheckedAt === "number" && intervalMs > 0 && now - lastCheckedAt < intervalMs) {
20
+ return { shouldRun: false, reason: "throttled" };
21
+ }
22
+
23
+ return {
24
+ shouldRun: true,
25
+ command: resolveCommand(env),
26
+ args: resolveArgs(env),
27
+ env: {
28
+ ...env,
29
+ LAZYCODEX_AUTO_UPDATE_DISABLED: "1",
30
+ OMO_CODEX_AUTO_UPDATE_DISABLED: "1",
31
+ },
32
+ };
33
+ }
34
+
35
+ export async function runAutoUpdateCheck({ env = process.env, now = Date.now() } = {}) {
36
+ await runConfigMigration({ env });
37
+ const statePath = resolveStatePath(env);
38
+ const state = await readState(statePath);
39
+ const plan = resolveAutoUpdatePlan({ env, now, lastCheckedAt: state.lastCheckedAt });
40
+ if (!plan.shouldRun) return { started: false, reason: plan.reason };
41
+
42
+ const lock = await acquireLock(resolveLockPath(env, statePath), now, env);
43
+ if (lock === null) return { started: false, reason: "locked" };
44
+ try {
45
+ await writeState(statePath, { lastCheckedAt: now });
46
+ if (env.LAZYCODEX_AUTO_UPDATE_WAIT === "1") {
47
+ const result = spawnSync(plan.command, plan.args, {
48
+ env: plan.env,
49
+ stdio: "ignore",
50
+ });
51
+ return { started: true, status: result.status ?? 0 };
52
+ }
53
+
54
+ const child = spawn(plan.command, plan.args, {
55
+ env: plan.env,
56
+ stdio: "ignore",
57
+ detached: true,
58
+ });
59
+ child.unref();
60
+ return { started: true };
61
+ } finally {
62
+ await lock.release();
63
+ }
64
+ }
65
+
66
+ async function runConfigMigration({ env }) {
67
+ if (env.LAZYCODEX_CONFIG_MIGRATION_DISABLED === "1" || env.OMO_CODEX_CONFIG_MIGRATION_DISABLED === "1") return;
68
+ try {
69
+ await migrateCodexConfig({ env });
70
+ } catch (error) {
71
+ if (!(error instanceof Error)) throw error;
72
+ return;
73
+ }
74
+ }
75
+
76
+ function resolveCommand(env) {
77
+ return env.LAZYCODEX_AUTO_UPDATE_COMMAND?.trim() || "npx";
78
+ }
79
+
80
+ function resolveArgs(env) {
81
+ if (env.LAZYCODEX_AUTO_UPDATE_ARGS_JSON) {
82
+ const parsed = JSON.parse(env.LAZYCODEX_AUTO_UPDATE_ARGS_JSON);
83
+ if (!Array.isArray(parsed) || parsed.some((value) => typeof value !== "string")) {
84
+ throw new TypeError("LAZYCODEX_AUTO_UPDATE_ARGS_JSON must be a JSON string array");
85
+ }
86
+ return parsed;
87
+ }
88
+ return ["--yes", "lazycodex-ai@latest", "install", "--no-tui", "--skip-auth"];
89
+ }
90
+
91
+ function resolveStatePath(env) {
92
+ if (env.LAZYCODEX_AUTO_UPDATE_STATE_PATH?.trim()) return env.LAZYCODEX_AUTO_UPDATE_STATE_PATH;
93
+ const dataRoot = env.PLUGIN_DATA?.trim() || join(homedir(), ".local", "share", "lazycodex");
94
+ return join(dataRoot, "auto-update.json");
95
+ }
96
+
97
+ function resolveLockPath(env, statePath) {
98
+ if (env.LAZYCODEX_AUTO_UPDATE_LOCK_PATH?.trim()) return env.LAZYCODEX_AUTO_UPDATE_LOCK_PATH;
99
+ return `${statePath}.lock`;
100
+ }
101
+
102
+ async function acquireLock(lockPath, now, env) {
103
+ await mkdir(dirname(lockPath), { recursive: true });
104
+ const staleMs = parsePositiveInteger(env.LAZYCODEX_AUTO_UPDATE_LOCK_STALE_MS, DEFAULT_LOCK_STALE_MS);
105
+ try {
106
+ const handle = await open(lockPath, "wx");
107
+ await handle.writeFile(`${now}\n`);
108
+ await handle.close();
109
+ return {
110
+ release: () => rm(lockPath, { force: true }),
111
+ };
112
+ } catch (error) {
113
+ if (!(error instanceof Error && "code" in error && error.code === "EEXIST")) throw error;
114
+ if (!(await removeStaleLock(lockPath, now, staleMs))) return null;
115
+ return acquireLock(lockPath, now, { ...env, LAZYCODEX_AUTO_UPDATE_LOCK_STALE_MS: "0" });
116
+ }
117
+ }
118
+
119
+ async function removeStaleLock(lockPath, now, staleMs) {
120
+ if (staleMs <= 0) return false;
121
+ try {
122
+ const lockStat = await stat(lockPath);
123
+ if (now - lockStat.mtimeMs < staleMs) return false;
124
+ await rm(lockPath, { force: true });
125
+ return true;
126
+ } catch (error) {
127
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return true;
128
+ throw error;
129
+ }
130
+ }
131
+
132
+ async function readState(statePath) {
133
+ try {
134
+ const raw = await readFile(statePath, "utf8");
135
+ const parsed = JSON.parse(raw);
136
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
137
+ } catch (error) {
138
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return {};
139
+ return {};
140
+ }
141
+ }
142
+
143
+ async function writeState(statePath, state) {
144
+ await mkdir(dirname(statePath), { recursive: true });
145
+ await writeFile(statePath, `${JSON.stringify(state, null, 2)}\n`);
146
+ }
147
+
148
+ function parsePositiveInteger(value, fallback) {
149
+ if (value === undefined || value === "") return fallback;
150
+ const parsed = Number.parseInt(value, 10);
151
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback;
152
+ }
153
+
154
+ if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
155
+ runAutoUpdateCheck().catch((error) => {
156
+ console.error(error instanceof Error ? error.message : String(error));
157
+ process.exit(0);
158
+ });
159
+ }
@@ -18,6 +18,11 @@ const runtimes = [
18
18
  packageRoot: join(repoPackagesRoot, "ast-grep-mcp"),
19
19
  requiredOutputs: ["dist/cli.js"],
20
20
  },
21
+ {
22
+ label: "git-bash-mcp",
23
+ packageRoot: join(repoPackagesRoot, "git-bash-mcp"),
24
+ requiredOutputs: ["dist/cli.js"],
25
+ },
21
26
  ];
22
27
 
23
28
  for (const runtime of runtimes) {
@@ -25,20 +30,30 @@ for (const runtime of runtimes) {
25
30
  }
26
31
 
27
32
  function buildRuntime(runtime) {
33
+ if (hasBundledDist(runtime)) {
34
+ console.log(`Using bundled ${runtime.label} dist`);
35
+ return;
36
+ }
37
+
28
38
  if (!existsSync(join(runtime.packageRoot, "package.json"))) {
29
39
  assertBundledDist(runtime);
30
40
  console.log(`Using bundled ${runtime.label} dist`);
31
41
  return;
32
42
  }
33
43
 
34
- const result = spawnSync("bun", ["run", "build"], {
44
+ const result = spawnSync("npm", ["run", "build"], {
35
45
  cwd: runtime.packageRoot,
46
+ shell: process.platform === "win32",
36
47
  stdio: "inherit",
37
48
  });
38
49
  if (result.error !== undefined) throw result.error;
39
50
  if (result.status !== 0) process.exit(result.status ?? 1);
40
51
  }
41
52
 
53
+ function hasBundledDist(runtime) {
54
+ return runtime.requiredOutputs.every((output) => existsSync(join(runtime.packageRoot, output)));
55
+ }
56
+
42
57
  function assertBundledDist(runtime) {
43
58
  const missingOutputs = runtime.requiredOutputs.filter((output) => !existsSync(join(runtime.packageRoot, output)));
44
59
  if (missingOutputs.length === 0) return;
@@ -14,8 +14,9 @@ for (const workspace of workspaces) {
14
14
  if (typeof workspacePackageJson.scripts?.build !== "string") continue;
15
15
 
16
16
  console.log(`Building ${workspace}`);
17
- const result = spawnSync("bun", ["run", "--cwd", workspace, "build"], {
17
+ const result = spawnSync("npm", ["run", "--workspace", workspace, "build"], {
18
18
  cwd: root,
19
+ shell: process.platform === "win32",
19
20
  stdio: "inherit",
20
21
  });
21
22
  if (result.error !== undefined) throw result.error;