oh-my-opencode 4.9.1 → 4.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/opencode-qa/SKILL.md +1 -0
- package/.agents/skills/opencode-qa/scripts/lib/common.sh +39 -1
- package/.agents/skills/opencode-qa/scripts/lib/fake-openai-branches.mjs +39 -0
- package/.agents/skills/opencode-qa/scripts/lib/fake-openai-events.mjs +106 -0
- package/.agents/skills/opencode-qa/scripts/lib/fake-openai-server.mjs +117 -0
- package/.agents/skills/opencode-qa/scripts/serve-wake-split-probe.sh +716 -0
- package/.agents/skills/tech-debt-audit/SKILL.md +277 -0
- package/.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/execution-plan.md +1 -1
- package/.opencode/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/execution-plan.md +1 -1
- package/bin/platform.js +5 -0
- package/bin/platform.test.ts +56 -0
- package/dist/agents/atlas/agent.d.ts +4 -3
- package/dist/agents/gpt-apply-patch-guard.d.ts +2 -2
- package/dist/agents/hephaestus/agent.d.ts +5 -0
- package/dist/agents/hephaestus/index.d.ts +1 -1
- package/dist/agents/metis.d.ts +1 -0
- package/dist/agents/prometheus/system-prompt.d.ts +1 -1
- package/dist/agents/sisyphus/kimi-k2-7.d.ts +17 -0
- package/dist/agents/sisyphus-junior/agent.d.ts +1 -1
- package/dist/agents/sisyphus-junior/kimi-k2-7.d.ts +11 -0
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/doctor/checks/codex-components.d.ts +13 -0
- package/dist/cli/doctor/checks/tui-plugin-config.d.ts +1 -0
- package/dist/cli/doctor/constants.d.ts +1 -1
- package/dist/cli/index.js +32329 -31437
- package/dist/cli/install-codex/codex-cleanup.d.ts +4 -0
- package/dist/cli/install-codex/install-codex-test-fixtures.d.ts +34 -0
- package/dist/cli/install-codex/link-cached-plugin-agents.d.ts +4 -0
- package/dist/cli/model-fallback.d.ts +1 -0
- package/dist/cli/provider-availability.d.ts +2 -0
- package/dist/cli-node/index.js +32329 -31437
- package/dist/config/schema/agent-overrides.d.ts +80 -16
- package/dist/config/schema/experimental.d.ts +1 -1
- package/dist/config/schema/hooks.d.ts +0 -1
- package/dist/config/schema/internal/permission.d.ts +5 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +76 -16
- package/dist/create-hooks.d.ts +0 -1
- package/dist/features/background-agent/index.d.ts +1 -1
- package/dist/features/background-agent/manager.d.ts +6 -0
- package/dist/features/background-agent/types.d.ts +2 -0
- package/dist/features/claude-code-plugin-loader/types.d.ts +3 -0
- package/dist/features/claude-code-session-state/state.d.ts +1 -0
- package/dist/features/skill-mcp-manager/manager.d.ts +11 -7
- package/dist/features/team-mode/team-mailbox/pending-delivery-recovery.d.ts +31 -0
- package/dist/features/team-mode/team-runtime/delete-team.d.ts +2 -1
- package/dist/features/team-mode/tools/lifecycle-inline-spec.d.ts +2 -2
- package/dist/features/tmux-subagent/stale-tmux-resource-sweeper.d.ts +12 -0
- package/dist/features/tool-metadata-store/store.d.ts +5 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/storage/constants.d.ts +3 -0
- package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/messages-reader.d.ts +1 -1
- package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/part-content.d.ts +1 -1
- package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/parts-reader.d.ts +1 -1
- package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery/storage}/types.d.ts +0 -13
- package/dist/hooks/auto-update-checker/checker/bundled-version.d.ts +1 -0
- package/dist/hooks/auto-update-checker/checker.d.ts +1 -0
- package/dist/hooks/auto-update-checker/constants.d.ts +3 -3
- package/dist/hooks/auto-update-checker/hook.d.ts +2 -1
- package/dist/hooks/claude-code-hooks/types.d.ts +4 -0
- package/dist/hooks/index.d.ts +0 -1
- package/dist/hooks/team-session-events/team-idle-wake-hint.d.ts +5 -0
- package/dist/index.js +6061 -3714
- package/dist/oh-my-opencode.schema.json +123 -18
- package/dist/plugin/build-team-idle-wake-hint-client.d.ts +2 -0
- package/dist/plugin/event-session-lifecycle.d.ts +0 -3
- package/dist/plugin/hooks/create-continuation-hooks.d.ts +0 -6
- package/dist/plugin/hooks/create-core-hooks.d.ts +0 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +1 -2
- package/dist/shared/command-executor/execute-hook-command.d.ts +7 -0
- package/dist/shared/internal-initiator-marker.d.ts +7 -0
- package/dist/shared/live-server-route.d.ts +24 -0
- package/dist/shared/plugin-identity.d.ts +2 -2
- package/dist/shared/prompt-async-gate/prompt-message-state.d.ts +1 -0
- package/dist/shared/tmux/tmux-utils/server-health.d.ts +2 -1
- package/dist/shared/tmux/tmux-utils/stale-attach-pane-sweep.d.ts +16 -0
- package/dist/shared/tmux/tmux-utils.d.ts +1 -0
- package/dist/testing/create-plugin-module.d.ts +4 -0
- package/dist/tools/background-task/clients.d.ts +2 -0
- package/dist/tools/background-task/full-session-format.d.ts +1 -0
- package/dist/tools/background-task/types.d.ts +1 -0
- package/dist/tools/delegate-task/sync-prompt-sender.d.ts +1 -1
- package/dist/tools/delegate-task/sync-session-lifecycle.d.ts +2 -1
- package/dist/tools/look-at/look-at-input-preparer.d.ts +6 -2
- package/dist/tools/look-at/look-at-prompt.d.ts +2 -1
- package/dist/tools/look-at/look-at-session-runner.d.ts +3 -4
- package/dist/tools/look-at/types.d.ts +2 -0
- package/dist/tools/session-manager/types.d.ts +1 -0
- package/dist/tools/skill-mcp/types.d.ts +1 -0
- package/package.json +14 -13
- package/packages/ast-grep-mcp/dist/cli.js +50 -17
- package/packages/lsp-daemon/dist/cli.js +8 -5
- package/packages/lsp-daemon/dist/index.js +8 -5
- package/packages/lsp-tools-mcp/dist/lsp/connection.js +1 -1
- package/packages/lsp-tools-mcp/dist/lsp/server-definitions.js +2 -2
- package/packages/lsp-tools-mcp/dist/lsp/transport.d.ts +10 -1
- package/packages/lsp-tools-mcp/dist/lsp/transport.js +6 -3
- package/packages/omo-codex/lazycodex-repository/.github/workflows/pr-source-guidance.yml +11 -12
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2583 -0
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +17 -0
- package/packages/omo-codex/plugin/components/bootstrap/manifests/ast-grep.json +22 -0
- package/packages/omo-codex/plugin/components/bootstrap/manifests/node.json +10 -0
- package/packages/omo-codex/plugin/components/bootstrap/package.json +20 -0
- package/packages/omo-codex/plugin/components/bootstrap/scripts/bootstrap.ps1 +310 -0
- package/packages/omo-codex/plugin/components/bootstrap/scripts/build.mjs +35 -0
- package/packages/omo-codex/plugin/components/bootstrap/scripts/generate-manifests.mjs +115 -0
- package/packages/omo-codex/plugin/components/bootstrap/src/cli.ts +153 -0
- package/packages/omo-codex/plugin/components/bootstrap/src/download.ts +212 -0
- package/packages/omo-codex/plugin/components/bootstrap/src/environment.ts +286 -0
- package/packages/omo-codex/plugin/components/bootstrap/src/hook.ts +108 -0
- package/packages/omo-codex/plugin/components/bootstrap/src/provision.ts +243 -0
- package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +294 -0
- package/packages/omo-codex/plugin/components/bootstrap/src/worker.ts +279 -0
- package/packages/omo-codex/plugin/components/bootstrap/test/download.test.ts +295 -0
- package/packages/omo-codex/plugin/components/bootstrap/test/environment.test.ts +375 -0
- package/packages/omo-codex/plugin/components/bootstrap/test/provision.test.ts +464 -0
- package/packages/omo-codex/plugin/components/bootstrap/tsconfig.json +25 -0
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +4 -4
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook-cli.js +6 -10
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +4 -4
- package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-tools.test.mjs +8 -3
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +5 -8
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-cli.test.ts +24 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/windows-git-bash.md +3 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +4 -4
- package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +35 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +4 -4
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +4 -4
- package/packages/omo-codex/plugin/components/ultrawork/biome.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +155 -99
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +4 -4
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +19 -51
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +46 -51
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +19 -0
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +0 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +9 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +18 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +1 -3
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +6 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +19 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-commands.test.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-complete-goals.test.ts +26 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-json-errors.test.ts +89 -0
- package/packages/omo-codex/plugin/hooks/hooks.json +27 -16
- package/packages/omo-codex/plugin/package-lock.json +193 -193
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/auto-update-state.d.mts +20 -0
- package/packages/omo-codex/plugin/scripts/auto-update.mjs +28 -8
- package/packages/omo-codex/plugin/scripts/build-components.mjs +36 -5
- package/packages/omo-codex/plugin/scripts/install-flow.mjs +43 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +79 -28
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +2 -2
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -6
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +19 -51
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +46 -51
- package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +145 -0
- package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +250 -0
- package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +166 -0
- package/packages/omo-codex/plugin/test/bootstrap-orchestration.test.mjs +371 -0
- package/packages/omo-codex/plugin/test/bootstrap-ps-guard.test.mjs +134 -0
- package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +249 -0
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +10 -1
- package/packages/omo-codex/plugin/test/ulw-plan-skill.test.mjs +46 -0
- package/packages/omo-codex/scripts/atomic-write.test.mjs +82 -0
- package/packages/omo-codex/scripts/install/agents.d.mts +18 -0
- package/packages/omo-codex/scripts/install/agents.mjs +78 -5
- package/packages/omo-codex/scripts/install/atomic-write.mjs +59 -0
- package/packages/omo-codex/scripts/install/bin-dir.d.mts +7 -0
- package/packages/omo-codex/scripts/install/bin-links.d.mts +18 -0
- package/packages/omo-codex/scripts/install/config.d.mts +35 -0
- package/packages/omo-codex/scripts/install/config.mjs +13 -3
- package/packages/omo-codex/scripts/install/git-bash-mcp-env.d.mts +5 -0
- package/packages/omo-codex/scripts/install/git-bash.d.mts +23 -0
- package/packages/omo-codex/scripts/install/hook-trust.d.mts +10 -0
- package/packages/omo-codex/scripts/install-agent-links.test.mjs +41 -0
- package/packages/omo-codex/scripts/install-local.mjs +3 -2
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +79 -28
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +2 -2
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +7 -6
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/dist/hooks/session-recovery/constants.d.ts +0 -4
- package/dist/hooks/session-recovery/detect-error-type.d.ts +0 -4
- package/dist/hooks/session-recovery/error-recovery.d.ts +0 -4
- package/dist/hooks/session-recovery/hook-types.d.ts +0 -22
- package/dist/hooks/session-recovery/hook.d.ts +0 -4
- package/dist/hooks/session-recovery/index.d.ts +0 -5
- package/dist/hooks/session-recovery/interrupted-idle-message-fetch-timeout.d.ts +0 -7
- package/dist/hooks/session-recovery/interrupted-tool-results.d.ts +0 -3
- package/dist/hooks/session-recovery/message-state.d.ts +0 -4
- package/dist/hooks/session-recovery/recover-thinking-block-order.d.ts +0 -5
- package/dist/hooks/session-recovery/recover-thinking-disabled-violation.d.ts +0 -5
- package/dist/hooks/session-recovery/recover-tool-result-missing.d.ts +0 -10
- package/dist/hooks/session-recovery/recover-unavailable-tool.d.ts +0 -5
- package/dist/hooks/session-recovery/resume.d.ts +0 -7
- package/dist/hooks/session-recovery/storage/latest-assistant-message.d.ts +0 -5
- package/dist/hooks/session-recovery/storage/orphan-thinking-search.d.ts +0 -2
- package/dist/hooks/session-recovery/storage/thinking-block-search.d.ts +0 -2
- package/dist/hooks/session-recovery/storage/thinking-prepend.d.ts +0 -33
- package/dist/hooks/session-recovery/storage/thinking-strip.d.ts +0 -11
- package/dist/hooks/session-recovery/storage.d.ts +0 -20
- package/dist/plugin/event-session-recovery.d.ts +0 -9
- package/dist/plugin/user-abort-interrupted-recovery-guard.d.ts +0 -6
- /package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/empty-messages.d.ts +0 -0
- /package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/empty-text.d.ts +0 -0
- /package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/message-dir.d.ts +0 -0
- /package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/part-id.d.ts +0 -0
- /package/dist/hooks/{session-recovery → anthropic-context-window-limit-recovery}/storage/text-part-injector.d.ts +0 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { deflateRawSync } from "node:zlib";
|
|
8
|
+
|
|
9
|
+
import type { FetchLike } from "../src/download.ts";
|
|
10
|
+
import {
|
|
11
|
+
runSgProvision,
|
|
12
|
+
SG_FORCE_PROVISION_ENV_KEY,
|
|
13
|
+
SG_PROVISION_COMPONENT,
|
|
14
|
+
sgProvisionDestination,
|
|
15
|
+
} from "../src/provision.ts";
|
|
16
|
+
import {
|
|
17
|
+
BOOTSTRAP_DOCTOR_HINT,
|
|
18
|
+
defaultWorkerSteps,
|
|
19
|
+
readBootstrapState,
|
|
20
|
+
runBootstrapWorker,
|
|
21
|
+
type BootstrapWorkerContext,
|
|
22
|
+
} from "../src/worker.ts";
|
|
23
|
+
|
|
24
|
+
const temporaryDirectories: string[] = [];
|
|
25
|
+
|
|
26
|
+
function createTemporaryDirectory(prefix: string): string {
|
|
27
|
+
const directory = mkdtempSync(join(tmpdir(), prefix));
|
|
28
|
+
temporaryDirectories.push(directory);
|
|
29
|
+
return directory;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
for (const directory of temporaryDirectories.splice(0)) {
|
|
34
|
+
rmSync(directory, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function sha256Hex(bytes: Uint8Array): string {
|
|
39
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const CRC_TABLE = ((): Uint32Array => {
|
|
43
|
+
const table = new Uint32Array(256);
|
|
44
|
+
for (let index = 0; index < 256; index += 1) {
|
|
45
|
+
let value = index;
|
|
46
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
47
|
+
value = (value & 1) === 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
|
|
48
|
+
}
|
|
49
|
+
table[index] = value >>> 0;
|
|
50
|
+
}
|
|
51
|
+
return table;
|
|
52
|
+
})();
|
|
53
|
+
|
|
54
|
+
function crc32(bytes: Uint8Array): number {
|
|
55
|
+
let crc = 0xffffffff;
|
|
56
|
+
for (const byte of bytes) {
|
|
57
|
+
crc = (CRC_TABLE[(crc ^ byte) & 0xff] as number) ^ (crc >>> 8);
|
|
58
|
+
}
|
|
59
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Builds a real (deflate-compressed) zip archive, mirroring the ast-grep release asset layout. */
|
|
63
|
+
function makeZip(entries: ReadonlyArray<{ name: string; data: Uint8Array }>): Buffer {
|
|
64
|
+
const locals: Buffer[] = [];
|
|
65
|
+
const centrals: Buffer[] = [];
|
|
66
|
+
let localOffset = 0;
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
const name = Buffer.from(entry.name, "utf8");
|
|
69
|
+
const compressed = deflateRawSync(entry.data);
|
|
70
|
+
const checksum = crc32(entry.data);
|
|
71
|
+
const local = Buffer.alloc(30 + name.length + compressed.length);
|
|
72
|
+
local.writeUInt32LE(0x04034b50, 0);
|
|
73
|
+
local.writeUInt16LE(20, 4);
|
|
74
|
+
local.writeUInt16LE(8, 8);
|
|
75
|
+
local.writeUInt32LE(checksum, 14);
|
|
76
|
+
local.writeUInt32LE(compressed.length, 18);
|
|
77
|
+
local.writeUInt32LE(entry.data.length, 22);
|
|
78
|
+
local.writeUInt16LE(name.length, 26);
|
|
79
|
+
name.copy(local, 30);
|
|
80
|
+
compressed.copy(local, 30 + name.length);
|
|
81
|
+
locals.push(local);
|
|
82
|
+
|
|
83
|
+
const central = Buffer.alloc(46 + name.length);
|
|
84
|
+
central.writeUInt32LE(0x02014b50, 0);
|
|
85
|
+
central.writeUInt16LE(0x031e, 4);
|
|
86
|
+
central.writeUInt16LE(20, 6);
|
|
87
|
+
central.writeUInt16LE(8, 10);
|
|
88
|
+
central.writeUInt32LE(checksum, 16);
|
|
89
|
+
central.writeUInt32LE(compressed.length, 20);
|
|
90
|
+
central.writeUInt32LE(entry.data.length, 24);
|
|
91
|
+
central.writeUInt16LE(name.length, 28);
|
|
92
|
+
central.writeUInt32LE((0o100755 << 16) >>> 0, 38);
|
|
93
|
+
central.writeUInt32LE(localOffset, 42);
|
|
94
|
+
name.copy(central, 46);
|
|
95
|
+
centrals.push(central);
|
|
96
|
+
localOffset += local.length;
|
|
97
|
+
}
|
|
98
|
+
const centralDirectory = Buffer.concat(centrals);
|
|
99
|
+
const endRecord = Buffer.alloc(22);
|
|
100
|
+
endRecord.writeUInt32LE(0x06054b50, 0);
|
|
101
|
+
endRecord.writeUInt16LE(entries.length, 8);
|
|
102
|
+
endRecord.writeUInt16LE(entries.length, 10);
|
|
103
|
+
endRecord.writeUInt32LE(centralDirectory.length, 12);
|
|
104
|
+
endRecord.writeUInt32LE(localOffset, 16);
|
|
105
|
+
return Buffer.concat([...locals, centralDirectory, endRecord]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Real release zips ship a tiny PATH-searching `sg` alias shim plus the
|
|
109
|
+
// standalone `ast-grep` binary; provisioning must install the standalone one.
|
|
110
|
+
const STANDALONE_BYTES = new Uint8Array(Buffer.alloc(16_384, "standalone-ast-grep-binary "));
|
|
111
|
+
const SHIM_BYTES = new Uint8Array(Buffer.from("path-searching-sg-shim"));
|
|
112
|
+
const FIXTURE_ZIP = makeZip([
|
|
113
|
+
{ data: SHIM_BYTES, name: "sg" },
|
|
114
|
+
{ data: STANDALONE_BYTES, name: "ast-grep" },
|
|
115
|
+
]);
|
|
116
|
+
const FIXTURE_VERSION = "9.9.9";
|
|
117
|
+
|
|
118
|
+
async function writeAstGrepManifest(
|
|
119
|
+
manifestsDir: string,
|
|
120
|
+
options: { sha256?: string; platforms?: Record<string, { url: string; sha256: string }> } = {},
|
|
121
|
+
): Promise<void> {
|
|
122
|
+
await mkdir(manifestsDir, { recursive: true });
|
|
123
|
+
const platforms = options.platforms ?? {
|
|
124
|
+
"linux-x64": {
|
|
125
|
+
sha256: options.sha256 ?? sha256Hex(FIXTURE_ZIP),
|
|
126
|
+
url: "https://example.invalid/releases/9.9.9/app-x86_64-unknown-linux-gnu.zip",
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
const manifest = { name: "ast-grep", platforms, version: FIXTURE_VERSION };
|
|
130
|
+
await writeFile(join(manifestsDir, "ast-grep.json"), JSON.stringify(manifest, null, "\t"), "utf8");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function fetchReturning(bytes: Uint8Array): { fetchImpl: FetchLike; calls: string[] } {
|
|
134
|
+
const calls: string[] = [];
|
|
135
|
+
const fetchImpl: FetchLike = async (url) => {
|
|
136
|
+
calls.push(url);
|
|
137
|
+
return new Response(bytes, { status: 200 });
|
|
138
|
+
};
|
|
139
|
+
return { calls, fetchImpl };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function makeContext(options: {
|
|
143
|
+
codexHome: string;
|
|
144
|
+
manifestDir: string;
|
|
145
|
+
pluginData: string;
|
|
146
|
+
env?: Record<string, string | undefined>;
|
|
147
|
+
}): BootstrapWorkerContext {
|
|
148
|
+
return {
|
|
149
|
+
codexHome: options.codexHome,
|
|
150
|
+
env: options.env ?? {},
|
|
151
|
+
flags: { manifestDir: options.manifestDir, once: false, only: "sg" },
|
|
152
|
+
now: 1_700_000_000_000,
|
|
153
|
+
platform: "linux",
|
|
154
|
+
pluginData: options.pluginData,
|
|
155
|
+
pluginRoot: join(options.pluginData, "plugin-root"),
|
|
156
|
+
pluginVersion: "1.0.0",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function listFilesRecursively(root: string): Promise<string[]> {
|
|
161
|
+
try {
|
|
162
|
+
const entries = await readdir(root, { recursive: true, withFileTypes: true });
|
|
163
|
+
return entries.filter((entry) => entry.isFile()).map((entry) => join(entry.parentPath, entry.name));
|
|
164
|
+
} catch {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function readBootstrapLog(pluginData: string): Promise<string> {
|
|
170
|
+
try {
|
|
171
|
+
return await readFile(join(pluginData, "bootstrap", "bootstrap.log"), "utf8");
|
|
172
|
+
} catch {
|
|
173
|
+
return "";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
describe("runSgProvision", () => {
|
|
178
|
+
it("#given resolution misses #when provisioning from a pinned zip #then the standalone binary lands at the resolver probe path with mode 755 and no staging leftovers", async () => {
|
|
179
|
+
// given
|
|
180
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-");
|
|
181
|
+
const codexHome = join(root, "codex-home");
|
|
182
|
+
const manifestDir = join(root, "manifests");
|
|
183
|
+
await writeAstGrepManifest(manifestDir);
|
|
184
|
+
const { calls, fetchImpl } = fetchReturning(FIXTURE_ZIP);
|
|
185
|
+
const probed: string[] = [];
|
|
186
|
+
const context = makeContext({ codexHome, manifestDir, pluginData: join(root, "data") });
|
|
187
|
+
|
|
188
|
+
// when
|
|
189
|
+
const outcome = await runSgProvision(context, {
|
|
190
|
+
arch: "x64",
|
|
191
|
+
fetchImpl,
|
|
192
|
+
resolvePreexistingSg: () => null,
|
|
193
|
+
runVersionProbe: async (binaryPath: string) => {
|
|
194
|
+
probed.push(binaryPath);
|
|
195
|
+
return `ast-grep ${FIXTURE_VERSION}\n`;
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// then
|
|
200
|
+
const destination = join(codexHome, "runtime", "ast-grep", "linux-x64", "sg");
|
|
201
|
+
expect(sgProvisionDestination(context, "x64")).toBe(destination);
|
|
202
|
+
expect(outcome.degraded).toEqual([]);
|
|
203
|
+
expect(calls).toEqual(["https://example.invalid/releases/9.9.9/app-x86_64-unknown-linux-gnu.zip"]);
|
|
204
|
+
expect(probed).toEqual([destination]);
|
|
205
|
+
expect(new Uint8Array(await readFile(destination))).toEqual(STANDALONE_BYTES);
|
|
206
|
+
expect((await stat(destination)).mode & 0o777).toBe(0o755);
|
|
207
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([destination]);
|
|
208
|
+
expect(await readBootstrapLog(context.pluginData)).toContain(`"sg":"provisioned:${destination}"`);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("#given a preexisting sg resolution #when provisioning #then it short-circuits without fetching and records a preexisting note in the log", async () => {
|
|
212
|
+
// given
|
|
213
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-");
|
|
214
|
+
const codexHome = join(root, "codex-home");
|
|
215
|
+
const manifestDir = join(root, "manifests");
|
|
216
|
+
await writeAstGrepManifest(manifestDir);
|
|
217
|
+
const { calls, fetchImpl } = fetchReturning(FIXTURE_ZIP);
|
|
218
|
+
const context = makeContext({ codexHome, manifestDir, pluginData: join(root, "data") });
|
|
219
|
+
|
|
220
|
+
// when
|
|
221
|
+
const outcome = await runSgProvision(context, {
|
|
222
|
+
arch: "x64",
|
|
223
|
+
fetchImpl,
|
|
224
|
+
resolvePreexistingSg: () => "/opt/homebrew/bin/sg",
|
|
225
|
+
runVersionProbe: async () => {
|
|
226
|
+
throw new Error("must not probe on short-circuit");
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// then
|
|
231
|
+
expect(outcome.degraded).toEqual([]);
|
|
232
|
+
expect(calls).toEqual([]);
|
|
233
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([]);
|
|
234
|
+
expect(await readBootstrapLog(context.pluginData)).toContain('"sg":"preexisting:/opt/homebrew/bin/sg"');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("#given OMO_BOOTSTRAP_FORCE_PROVISION=1 #when a preexisting sg would resolve #then resolution is bypassed and provisioning still runs", async () => {
|
|
238
|
+
// given
|
|
239
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-");
|
|
240
|
+
const codexHome = join(root, "codex-home");
|
|
241
|
+
const manifestDir = join(root, "manifests");
|
|
242
|
+
await writeAstGrepManifest(manifestDir);
|
|
243
|
+
const { calls, fetchImpl } = fetchReturning(FIXTURE_ZIP);
|
|
244
|
+
const resolverCalls: string[] = [];
|
|
245
|
+
const context = makeContext({
|
|
246
|
+
codexHome,
|
|
247
|
+
env: { [SG_FORCE_PROVISION_ENV_KEY]: "1" },
|
|
248
|
+
manifestDir,
|
|
249
|
+
pluginData: join(root, "data"),
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// when
|
|
253
|
+
const outcome = await runSgProvision(context, {
|
|
254
|
+
arch: "x64",
|
|
255
|
+
fetchImpl,
|
|
256
|
+
resolvePreexistingSg: () => {
|
|
257
|
+
resolverCalls.push("resolved");
|
|
258
|
+
return "/opt/homebrew/bin/sg";
|
|
259
|
+
},
|
|
260
|
+
runVersionProbe: async () => `ast-grep ${FIXTURE_VERSION}`,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// then
|
|
264
|
+
expect(outcome.degraded).toEqual([]);
|
|
265
|
+
expect(resolverCalls).toEqual([]);
|
|
266
|
+
expect(calls).toHaveLength(1);
|
|
267
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([
|
|
268
|
+
join(codexHome, "runtime", "ast-grep", "linux-x64", "sg"),
|
|
269
|
+
]);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("#given a tampered checksum #when provisioning #then a degraded ast_grep entry names the mismatch and no file is left under runtime", async () => {
|
|
273
|
+
// given
|
|
274
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-");
|
|
275
|
+
const codexHome = join(root, "codex-home");
|
|
276
|
+
const manifestDir = join(root, "manifests");
|
|
277
|
+
await writeAstGrepManifest(manifestDir, { sha256: sha256Hex(new TextEncoder().encode("tampered")) });
|
|
278
|
+
const context = makeContext({ codexHome, manifestDir, pluginData: join(root, "data") });
|
|
279
|
+
|
|
280
|
+
// when
|
|
281
|
+
const outcome = await runSgProvision(context, {
|
|
282
|
+
arch: "x64",
|
|
283
|
+
fetchImpl: fetchReturning(FIXTURE_ZIP).fetchImpl,
|
|
284
|
+
resolvePreexistingSg: () => null,
|
|
285
|
+
runVersionProbe: async () => `ast-grep ${FIXTURE_VERSION}`,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// then
|
|
289
|
+
expect(outcome.degraded).toHaveLength(1);
|
|
290
|
+
const entry = outcome.degraded[0];
|
|
291
|
+
expect(entry?.component).toBe(SG_PROVISION_COMPONENT);
|
|
292
|
+
expect(entry?.reason).toMatch(/checksum mismatch/i);
|
|
293
|
+
expect(entry?.hint).toBe(BOOTSTRAP_DOCTOR_HINT);
|
|
294
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([]);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("#given a binary that reports the wrong version #when provisioning #then the provisioned file is removed and a degraded entry names both versions", async () => {
|
|
298
|
+
// given
|
|
299
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-");
|
|
300
|
+
const codexHome = join(root, "codex-home");
|
|
301
|
+
const manifestDir = join(root, "manifests");
|
|
302
|
+
await writeAstGrepManifest(manifestDir);
|
|
303
|
+
const context = makeContext({ codexHome, manifestDir, pluginData: join(root, "data") });
|
|
304
|
+
|
|
305
|
+
// when
|
|
306
|
+
const outcome = await runSgProvision(context, {
|
|
307
|
+
arch: "x64",
|
|
308
|
+
fetchImpl: fetchReturning(FIXTURE_ZIP).fetchImpl,
|
|
309
|
+
resolvePreexistingSg: () => null,
|
|
310
|
+
runVersionProbe: async () => "ast-grep 0.0.1",
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
// then
|
|
314
|
+
expect(outcome.degraded).toHaveLength(1);
|
|
315
|
+
const entry = outcome.degraded[0];
|
|
316
|
+
expect(entry?.component).toBe(SG_PROVISION_COMPONENT);
|
|
317
|
+
expect(entry?.reason).toContain("0.0.1");
|
|
318
|
+
expect(entry?.reason).toContain(FIXTURE_VERSION);
|
|
319
|
+
expect(entry?.hint).toBe(BOOTSTRAP_DOCTOR_HINT);
|
|
320
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([]);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("#given a platform absent from the manifest #when provisioning #then a degraded entry names the unsupported platform without fetching", async () => {
|
|
324
|
+
// given
|
|
325
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-");
|
|
326
|
+
const codexHome = join(root, "codex-home");
|
|
327
|
+
const manifestDir = join(root, "manifests");
|
|
328
|
+
await writeAstGrepManifest(manifestDir);
|
|
329
|
+
const { calls, fetchImpl } = fetchReturning(FIXTURE_ZIP);
|
|
330
|
+
const context = makeContext({ codexHome, manifestDir, pluginData: join(root, "data") });
|
|
331
|
+
|
|
332
|
+
// when
|
|
333
|
+
const outcome = await runSgProvision(context, {
|
|
334
|
+
arch: "riscv64",
|
|
335
|
+
fetchImpl,
|
|
336
|
+
resolvePreexistingSg: () => null,
|
|
337
|
+
runVersionProbe: async () => `ast-grep ${FIXTURE_VERSION}`,
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// then
|
|
341
|
+
expect(outcome.degraded).toHaveLength(1);
|
|
342
|
+
const entry = outcome.degraded[0];
|
|
343
|
+
expect(entry?.component).toBe(SG_PROVISION_COMPONENT);
|
|
344
|
+
expect(entry?.reason).toMatch(/unsupported platform/i);
|
|
345
|
+
expect(entry?.reason).toContain("linux-riscv64");
|
|
346
|
+
expect(calls).toEqual([]);
|
|
347
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([]);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it("#given win32 #when provisioning #then the destination binary is sg.exe extracted from the .exe entry", async () => {
|
|
351
|
+
// given
|
|
352
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-");
|
|
353
|
+
const codexHome = join(root, "codex-home");
|
|
354
|
+
const manifestDir = join(root, "manifests");
|
|
355
|
+
const windowsZip = makeZip([
|
|
356
|
+
{ data: SHIM_BYTES, name: "sg.exe" },
|
|
357
|
+
{ data: STANDALONE_BYTES, name: "ast-grep.exe" },
|
|
358
|
+
]);
|
|
359
|
+
await writeAstGrepManifest(manifestDir, {
|
|
360
|
+
platforms: {
|
|
361
|
+
"win32-x64": {
|
|
362
|
+
sha256: sha256Hex(windowsZip),
|
|
363
|
+
url: "https://example.invalid/releases/9.9.9/app-x86_64-pc-windows-msvc.zip",
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
const context: BootstrapWorkerContext = {
|
|
368
|
+
...makeContext({ codexHome, manifestDir, pluginData: join(root, "data") }),
|
|
369
|
+
platform: "win32",
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// when
|
|
373
|
+
const outcome = await runSgProvision(context, {
|
|
374
|
+
arch: "x64",
|
|
375
|
+
fetchImpl: fetchReturning(windowsZip).fetchImpl,
|
|
376
|
+
resolvePreexistingSg: () => null,
|
|
377
|
+
runVersionProbe: async () => `ast-grep ${FIXTURE_VERSION}`,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
// then
|
|
381
|
+
const destination = join(codexHome, "runtime", "ast-grep", "win32-x64", "sg.exe");
|
|
382
|
+
expect(outcome.degraded).toEqual([]);
|
|
383
|
+
expect(new Uint8Array(await readFile(destination))).toEqual(STANDALONE_BYTES);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
describe("worker sg step wiring", () => {
|
|
388
|
+
it("#given --only sg with injected seams #when the worker runs #then the default sg step provisions through the manifest-dir flag and writes a success state", async () => {
|
|
389
|
+
// given
|
|
390
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-worker-");
|
|
391
|
+
const codexHome = join(root, "codex-home");
|
|
392
|
+
const manifestDir = join(root, "manifests");
|
|
393
|
+
const pluginData = join(root, "data");
|
|
394
|
+
const pluginRoot = join(root, "plugin-root");
|
|
395
|
+
await writeAstGrepManifest(manifestDir);
|
|
396
|
+
await mkdir(join(pluginRoot, ".codex-plugin"), { recursive: true });
|
|
397
|
+
await writeFile(join(pluginRoot, ".codex-plugin", "plugin.json"), JSON.stringify({ version: "1.2.3" }), "utf8");
|
|
398
|
+
const { calls, fetchImpl } = fetchReturning(FIXTURE_ZIP);
|
|
399
|
+
|
|
400
|
+
// when
|
|
401
|
+
const result = await runBootstrapWorker({
|
|
402
|
+
argv: ["--only", "sg", "--codex-home", codexHome, "--manifest-dir", manifestDir],
|
|
403
|
+
env: { PLUGIN_DATA: pluginData, PLUGIN_ROOT: pluginRoot },
|
|
404
|
+
platform: "linux",
|
|
405
|
+
steps: defaultWorkerSteps({
|
|
406
|
+
sg: {
|
|
407
|
+
arch: "x64",
|
|
408
|
+
fetchImpl,
|
|
409
|
+
resolvePreexistingSg: () => null,
|
|
410
|
+
runVersionProbe: async () => `ast-grep ${FIXTURE_VERSION}`,
|
|
411
|
+
},
|
|
412
|
+
}),
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
// then
|
|
416
|
+
expect(result.ran).toBe(true);
|
|
417
|
+
if (!result.ran) throw new Error("expected the worker to run");
|
|
418
|
+
expect(result.status).toBe("success");
|
|
419
|
+
expect(calls).toHaveLength(1);
|
|
420
|
+
const state = await readBootstrapState(join(pluginData, "bootstrap", "state.json"));
|
|
421
|
+
expect(state.lastStatus).toBe("success");
|
|
422
|
+
expect(state.degraded).toEqual([]);
|
|
423
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([
|
|
424
|
+
join(codexHome, "runtime", "ast-grep", "linux-x64", "sg"),
|
|
425
|
+
]);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it("#given --only sg with a tampered manifest #when the worker runs #then it finishes degraded with an ast_grep checksum entry and exit-0 semantics", async () => {
|
|
429
|
+
// given
|
|
430
|
+
const root = createTemporaryDirectory("omo-bootstrap-provision-worker-");
|
|
431
|
+
const codexHome = join(root, "codex-home");
|
|
432
|
+
const manifestDir = join(root, "manifests");
|
|
433
|
+
const pluginData = join(root, "data");
|
|
434
|
+
const pluginRoot = join(root, "plugin-root");
|
|
435
|
+
await writeAstGrepManifest(manifestDir, { sha256: sha256Hex(new TextEncoder().encode("tampered")) });
|
|
436
|
+
await mkdir(join(pluginRoot, ".codex-plugin"), { recursive: true });
|
|
437
|
+
await writeFile(join(pluginRoot, ".codex-plugin", "plugin.json"), JSON.stringify({ version: "1.2.3" }), "utf8");
|
|
438
|
+
|
|
439
|
+
// when
|
|
440
|
+
const result = await runBootstrapWorker({
|
|
441
|
+
argv: ["--only", "sg", "--codex-home", codexHome, "--manifest-dir", manifestDir],
|
|
442
|
+
env: { PLUGIN_DATA: pluginData, PLUGIN_ROOT: pluginRoot },
|
|
443
|
+
platform: "linux",
|
|
444
|
+
steps: defaultWorkerSteps({
|
|
445
|
+
sg: {
|
|
446
|
+
arch: "x64",
|
|
447
|
+
fetchImpl: fetchReturning(FIXTURE_ZIP).fetchImpl,
|
|
448
|
+
resolvePreexistingSg: () => null,
|
|
449
|
+
runVersionProbe: async () => `ast-grep ${FIXTURE_VERSION}`,
|
|
450
|
+
},
|
|
451
|
+
}),
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// then
|
|
455
|
+
expect(result.ran).toBe(true);
|
|
456
|
+
if (!result.ran) throw new Error("expected the worker to run");
|
|
457
|
+
expect(result.status).toBe("degraded");
|
|
458
|
+
const state = await readBootstrapState(join(pluginData, "bootstrap", "state.json"));
|
|
459
|
+
expect(state.lastStatus).toBe("degraded");
|
|
460
|
+
expect(state.degraded?.[0]?.component).toBe(SG_PROVISION_COMPONENT);
|
|
461
|
+
expect(state.degraded?.[0]?.reason).toMatch(/checksum mismatch/i);
|
|
462
|
+
expect(await listFilesRecursively(join(codexHome, "runtime"))).toEqual([]);
|
|
463
|
+
});
|
|
464
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"exactOptionalPropertyTypes": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"noPropertyAccessFromIndexSignature": true,
|
|
11
|
+
"verbatimModuleSyntax": true,
|
|
12
|
+
"noImplicitOverride": true,
|
|
13
|
+
"noImplicitReturns": true,
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
"allowImportingTsExtensions": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"forceConsistentCasingInFileNames": true,
|
|
21
|
+
"types": ["node", "bun-types"],
|
|
22
|
+
"noEmit": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src/**/*", "test/**/*"]
|
|
25
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/codex-comment-checker",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.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",
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
"@code-yeongyu/comment-checker": "^0.8.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@biomejs/biome": "2.4.
|
|
50
|
-
"@types/node": "^25.
|
|
49
|
+
"@biomejs/biome": "2.4.16",
|
|
50
|
+
"@types/node": "^25.9.3",
|
|
51
51
|
"typescript": "^6.0.3",
|
|
52
|
-
"vitest": "^4.1.
|
|
52
|
+
"vitest": "^4.1.8"
|
|
53
53
|
},
|
|
54
54
|
"engines": {
|
|
55
55
|
"node": ">=20.0.0"
|
|
@@ -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
|
+
"statusMessage": "LazyCodex(4.10.0): 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.
|
|
23
|
+
"statusMessage": "LazyCodex(4.10.0): 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.
|
|
3
|
+
"version": "4.10.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,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"typecheck": "tsc --noEmit"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@types/node": "^25.
|
|
20
|
+
"@types/node": "^25.9.3",
|
|
21
21
|
"typescript": "^6.0.3"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
@@ -12,22 +12,18 @@ async function runHookCli(runHook, stdin) {
|
|
|
12
12
|
const raw = await readStdin(stdin);
|
|
13
13
|
if (!raw.trim())
|
|
14
14
|
return;
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
parsed = JSON.parse(raw);
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
if (error instanceof SyntaxError)
|
|
21
|
-
return;
|
|
22
|
-
throw error;
|
|
23
|
-
}
|
|
15
|
+
const parsed = JSON.parse(raw);
|
|
24
16
|
const input = isRecord(parsed) ? parsed : {};
|
|
25
17
|
const output = await runHook(input);
|
|
26
18
|
if (output)
|
|
27
19
|
process.stdout.write(output);
|
|
28
20
|
}
|
|
21
|
+
catch {
|
|
22
|
+
// LSP feedback is best-effort: stderr or a non-zero exit here surfaces as harness noise on every edit.
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
29
25
|
finally {
|
|
30
|
-
await disposeDefaultLspManager();
|
|
26
|
+
await disposeDefaultLspManager().catch(() => undefined);
|
|
31
27
|
}
|
|
32
28
|
}
|
|
33
29
|
async function readStdin(stdin) {
|
|
@@ -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
|
+
"statusMessage": "LazyCodex(4.10.0): 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.
|
|
24
|
+
"statusMessage": "LazyCodex(4.10.0): 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.
|
|
3
|
+
"version": "4.10.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",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"@code-yeongyu/lsp-daemon": "file:../../../../lsp-daemon"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@biomejs/biome": "2.4.
|
|
57
|
-
"@types/node": "^25.
|
|
56
|
+
"@biomejs/biome": "2.4.16",
|
|
57
|
+
"@types/node": "^25.9.3",
|
|
58
58
|
"typescript": "^6.0.3",
|
|
59
|
-
"vitest": "^4.1.
|
|
59
|
+
"vitest": "^4.1.8"
|
|
60
60
|
},
|
|
61
61
|
"engines": {
|
|
62
62
|
"node": ">=20.0.0"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { chmod, copyFile, mkdir, mkdtemp, readFile, rm, utimes, writeFile } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { delimiter, join } from "node:path";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
6
|
import test from "node:test";
|
|
7
7
|
|
|
@@ -18,10 +18,15 @@ async function makeFixture() {
|
|
|
18
18
|
const fakeBin = join(root, "bin");
|
|
19
19
|
await mkdir(fakeBin, { recursive: true });
|
|
20
20
|
const npmLog = join(root, "npm.log");
|
|
21
|
+
await writeFile(
|
|
22
|
+
join(fakeBin, "npm.js"),
|
|
23
|
+
`const { appendFileSync } = require("node:fs");\nappendFileSync(${JSON.stringify(npmLog)}, process.argv.slice(2).join(" ") + "\\n");\n`,
|
|
24
|
+
);
|
|
21
25
|
await writeFile(
|
|
22
26
|
join(fakeBin, "npm"),
|
|
23
|
-
`#!/usr/bin/env node\
|
|
27
|
+
`#!/usr/bin/env node\nrequire("./npm.js");\n`,
|
|
24
28
|
);
|
|
29
|
+
await writeFile(join(fakeBin, "npm.cmd"), `@echo off\r\nnode "%~dp0\\npm.js" %*\r\n`);
|
|
25
30
|
await chmod(join(fakeBin, "npm"), 0o755);
|
|
26
31
|
return { root, npmLog, script: join(root, "packages", "omo-codex", "plugin", "components", "lsp", "scripts", "build-lsp-tools.mjs"), fakeBin };
|
|
27
32
|
}
|
|
@@ -29,7 +34,7 @@ async function makeFixture() {
|
|
|
29
34
|
function runScript(script, fakeBin, args = []) {
|
|
30
35
|
return spawnSync(process.execPath, [script, ...args], {
|
|
31
36
|
encoding: "utf8",
|
|
32
|
-
env: { ...process.env, PATH: `${fakeBin}
|
|
37
|
+
env: { ...process.env, PATH: `${fakeBin}${delimiter}${process.env.PATH ?? ""}` },
|
|
33
38
|
});
|
|
34
39
|
}
|
|
35
40
|
|