oh-my-opencode 4.18.0 → 4.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +3677 -0
- package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +3154 -0
- package/.agents/skills/work-with-pr/SKILL.md +16 -37
- package/.agents/skills/work-with-pr-workspace/evals/evals.json +3 -3
- package/.opencode/skills/work-with-pr/SKILL.md +16 -37
- package/.opencode/skills/work-with-pr-workspace/evals/evals.json +3 -3
- package/bin/AGENTS.md +33 -0
- package/dist/cli/index.js +500 -158
- package/dist/cli-node/index.js +500 -158
- package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
- package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
- package/dist/hooks/comment-checker/hook.d.ts +8 -1
- package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
- package/dist/index.js +956 -702
- package/dist/plugin/messages-transform.d.ts +1 -0
- package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
- package/dist/tui.js +43 -4
- package/package.json +16 -16
- package/packages/git-bash-mcp/dist/cli.js +81 -19
- package/packages/lsp-core/package.json +4 -0
- package/packages/lsp-core/src/index.ts +1 -0
- package/packages/lsp-core/src/lsp/cleanup-errors.test.ts +18 -0
- package/packages/lsp-core/src/lsp/cleanup-errors.ts +12 -3
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +261 -0
- package/packages/lsp-core/src/lsp/client-wrapper.test.ts +63 -0
- package/packages/lsp-core/src/lsp/client-wrapper.ts +35 -5
- package/packages/lsp-core/src/lsp/client.ts +262 -80
- package/packages/lsp-core/src/lsp/config-loader.ts +5 -17
- package/packages/lsp-core/src/lsp/connection.ts +12 -6
- package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +221 -0
- package/packages/lsp-core/src/lsp/directory-diagnostics.ts +61 -28
- package/packages/lsp-core/src/lsp/errors.ts +11 -0
- package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +283 -0
- package/packages/lsp-core/src/lsp/fixtures/workspace-edit-contract-probe.ts +196 -0
- package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +215 -0
- package/packages/lsp-core/src/lsp/formatters.ts +3 -0
- package/packages/lsp-core/src/lsp/json-rpc-connection-cancellation.test.ts +97 -0
- package/packages/lsp-core/src/lsp/json-rpc-connection.ts +73 -5
- package/packages/lsp-core/src/lsp/server-install-state.ts +3 -6
- package/packages/lsp-core/src/lsp/transport-protocol.ts +52 -0
- package/packages/lsp-core/src/lsp/transport.ts +96 -70
- package/packages/lsp-core/src/lsp/workspace-apply-edit-failure.ts +19 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit-lease.integration.test.ts +214 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit-sync.integration.test.ts +113 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit-test-support.ts +163 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit.integration.test.ts +163 -0
- package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +67 -0
- package/packages/lsp-core/src/lsp/workspace-document-state.ts +368 -0
- package/packages/lsp-core/src/lsp/workspace-edit-adversarial.test.ts +113 -0
- package/packages/lsp-core/src/lsp/workspace-edit-commit.test.ts +140 -0
- package/packages/lsp-core/src/lsp/workspace-edit-commit.ts +220 -0
- package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.test.ts +56 -0
- package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.ts +30 -0
- package/packages/lsp-core/src/lsp/workspace-edit-fingerprint.ts +44 -0
- package/packages/lsp-core/src/lsp/workspace-edit-options.test.ts +147 -0
- package/packages/lsp-core/src/lsp/workspace-edit-parse-helpers.ts +59 -0
- package/packages/lsp-core/src/lsp/workspace-edit-parser.ts +130 -0
- package/packages/lsp-core/src/lsp/workspace-edit-path.ts +98 -0
- package/packages/lsp-core/src/lsp/workspace-edit-plan-types.ts +60 -0
- package/packages/lsp-core/src/lsp/workspace-edit-plan.ts +73 -0
- package/packages/lsp-core/src/lsp/workspace-edit-prevalidation.test.ts +174 -0
- package/packages/lsp-core/src/lsp/workspace-edit-resource-parser.ts +89 -0
- package/packages/lsp-core/src/lsp/workspace-edit-simulation.ts +183 -0
- package/packages/lsp-core/src/lsp/workspace-edit-snapshot.ts +53 -0
- package/packages/lsp-core/src/lsp/workspace-edit-text.ts +125 -0
- package/packages/lsp-core/src/lsp/workspace-edit-types.ts +121 -0
- package/packages/lsp-core/src/lsp/workspace-edit.characterization.test.ts +95 -0
- package/packages/lsp-core/src/lsp/workspace-edit.ts +49 -200
- package/packages/lsp-core/src/lsp/workspace-mutation-controller.ts +182 -0
- package/packages/lsp-core/src/mcp.ts +18 -7
- package/packages/lsp-core/src/missing-dependency-result.test.ts +105 -0
- package/packages/lsp-core/src/missing-dependency-result.ts +57 -0
- package/packages/lsp-core/src/post-edit/index.ts +1 -0
- package/packages/lsp-core/src/post-edit/orchestration.test.ts +157 -0
- package/packages/lsp-core/src/post-edit/orchestration.ts +178 -0
- package/packages/lsp-core/src/request-context.test.ts +171 -0
- package/packages/lsp-core/src/request-context.ts +222 -9
- package/packages/lsp-core/src/tool-surface.test.ts +4 -1
- package/packages/lsp-core/src/tools/diagnostics.ts +32 -13
- package/packages/lsp-core/src/tools/navigation.ts +12 -12
- package/packages/lsp-core/src/tools/rename.ts +10 -15
- package/packages/lsp-core/src/tools/symbols.ts +11 -11
- package/packages/lsp-core/src/tools/types.ts +2 -1
- package/packages/lsp-daemon/dist/cli.js +3330 -764
- package/packages/lsp-daemon/dist/client.d.ts +105 -0
- package/packages/lsp-daemon/dist/client.js +5995 -0
- package/packages/lsp-daemon/dist/daemon-client.d.ts +12 -7
- package/packages/lsp-daemon/dist/daemon-client.js +139 -32
- package/packages/lsp-daemon/dist/daemon-server.d.ts +1 -0
- package/packages/lsp-daemon/dist/daemon-server.js +40 -15
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +10 -8
- package/packages/lsp-daemon/dist/ensure-daemon.js +135 -51
- package/packages/lsp-daemon/dist/index.d.ts +2 -2
- package/packages/lsp-daemon/dist/index.js +3093 -786
- package/packages/lsp-daemon/dist/ipc-protocol.d.ts +46 -0
- package/packages/lsp-daemon/dist/ipc-protocol.js +187 -0
- package/packages/lsp-daemon/dist/lock.js +14 -4
- package/packages/lsp-daemon/dist/ownership.d.ts +49 -0
- package/packages/lsp-daemon/dist/ownership.js +168 -0
- package/packages/lsp-daemon/dist/paths.d.ts +33 -9
- package/packages/lsp-daemon/dist/paths.js +72 -33
- package/packages/lsp-daemon/dist/proxy.d.ts +5 -0
- package/packages/lsp-daemon/dist/proxy.js +123 -16
- package/packages/lsp-daemon/dist/request-routing.d.ts +7 -2
- package/packages/lsp-daemon/dist/request-routing.js +71 -22
- package/packages/lsp-daemon/dist/run-daemon.js +9 -2
- package/packages/lsp-daemon/dist/runtime-contract.d.ts +21 -0
- package/packages/lsp-daemon/dist/runtime-contract.js +58 -0
- package/packages/lsp-daemon/dist/socket-jsonrpc.js +6 -1
- package/packages/lsp-daemon/package.json +12 -3
- package/packages/lsp-tools-mcp/dist/cli.js +2189 -454
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +1741 -148
- package/packages/lsp-tools-mcp/dist/mcp.js +2206 -471
- package/packages/lsp-tools-mcp/dist/request-context.js +176 -6
- package/packages/lsp-tools-mcp/dist/tools.js +2119 -447
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +55 -0
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +3033 -936
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook-cli.js +0 -4
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.d.ts +5 -2
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +41 -62
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +24 -15
- package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.d.ts +3 -7
- package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.js +23 -49
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +3 -2
- package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.mjs +31 -1
- package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.test.mjs +76 -0
- package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +201 -0
- package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.test.mjs +55 -0
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +0 -4
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +49 -71
- package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +26 -15
- package/packages/omo-codex/plugin/components/lsp/src/lsp-session-state.ts +26 -64
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-unavailable.test.ts +16 -17
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook.test.ts +30 -4
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +20 -5
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +15 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +20 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +6 -5
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +26 -14
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +2 -3
- package/packages/omo-codex/plugin/scripts/build-components.mjs +13 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +9 -1
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -1
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +6 -5
- package/packages/omo-codex/plugin/test/aggregate-build.test.mjs +8 -0
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +128 -15
- package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +10 -0
- package/packages/omo-codex/plugin/test/lsp-prebuild-layouts.test.mjs +2 -0
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +34 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +328 -63
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { stdin as processStdin } from "node:process";
|
|
2
|
-
import { disposeDefaultLspManager } from "@code-yeongyu/lsp-daemon";
|
|
3
2
|
import { isRecord, runLspPostCompactHook, runLspPostToolUseHook } from "./codex-hook.js";
|
|
4
3
|
export async function runPostToolUseHookCli(stdin = processStdin) {
|
|
5
4
|
await runHookCli((input) => runLspPostToolUseHook(input), stdin);
|
|
@@ -22,9 +21,6 @@ async function runHookCli(runHook, stdin) {
|
|
|
22
21
|
// LSP feedback is best-effort: stderr or a non-zero exit here surfaces as harness noise on every edit.
|
|
23
22
|
return;
|
|
24
23
|
}
|
|
25
|
-
finally {
|
|
26
|
-
await disposeDefaultLspManager().catch(() => undefined);
|
|
27
|
-
}
|
|
28
24
|
}
|
|
29
25
|
async function readStdin(stdin) {
|
|
30
26
|
stdin.setEncoding("utf8");
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { type PostEditDiagnosticsOutcome } from "@oh-my-opencode/lsp-core/post-edit";
|
|
2
|
+
import { type LspRequestContext } from "@oh-my-opencode/lsp-core/request-context";
|
|
1
3
|
export { extractMutatedFilePaths } from "./mutated-file-paths.js";
|
|
2
|
-
export type DiagnosticsRunner = (filePath: string) => Promise<
|
|
4
|
+
export type DiagnosticsRunner = (filePath: string) => Promise<PostEditDiagnosticsOutcome>;
|
|
3
5
|
export interface CodexPostToolUseInput {
|
|
4
6
|
session_id?: unknown;
|
|
5
7
|
tool_name?: unknown;
|
|
@@ -10,7 +12,8 @@ export interface CodexPostToolUseInput {
|
|
|
10
12
|
export interface CodexPostCompactInput {
|
|
11
13
|
session_id?: unknown;
|
|
12
14
|
}
|
|
13
|
-
export declare function runLspDiagnosticsText(filePath: string): Promise<
|
|
15
|
+
export declare function runLspDiagnosticsText(filePath: string): Promise<PostEditDiagnosticsOutcome>;
|
|
16
|
+
export declare function codexLspRequestContext(env?: Record<string, string | undefined>, cwd?: string): LspRequestContext;
|
|
14
17
|
export declare function runLspPostToolUseHook(input: CodexPostToolUseInput, runDiagnostics?: DiagnosticsRunner): Promise<string>;
|
|
15
18
|
export declare function runLspPostCompactHook(input: CodexPostCompactInput): Promise<string>;
|
|
16
19
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import {
|
|
1
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { callDiagnosticsViaDaemon } from "@code-yeongyu/lsp-daemon/client";
|
|
5
|
+
import { collectPostEditDiagnostics } from "@oh-my-opencode/lsp-core/post-edit";
|
|
6
|
+
import { parseLspRequestContext } from "@oh-my-opencode/lsp-core/request-context";
|
|
3
7
|
import { ensureLspDaemonCliEnv } from "./daemon-cli-path.js";
|
|
4
|
-
import { isLspDaemonUnreachableDiagnostics,
|
|
8
|
+
import { isLspDaemonUnreachableDiagnostics, markLspSessionCompacted, readLspPostEditCache, sessionIdFrom, writeLspPostEditCache, } from "./lsp-session-state.js";
|
|
5
9
|
import { extractMutatedFilePaths } from "./mutated-file-paths.js";
|
|
6
10
|
export { extractMutatedFilePaths } from "./mutated-file-paths.js";
|
|
7
|
-
const CLEAN_DIAGNOSTICS_TEXT = "No diagnostics found";
|
|
8
|
-
const UNSUPPORTED_EXTENSION_TEXT = "No LSP server configured for extension:";
|
|
9
11
|
const DIAGNOSTIC_START_PATTERN = /(?:error|warning|information|hint)\[[^\]\r\n]+\] \(\d+\) at \d+:\d+:/g;
|
|
10
12
|
const DIAGNOSTIC_CHUNK_PATTERN = /^(?:error|warning|information|hint)\[[^\]\r\n]+\] \(\d+\) at \d+:\d+:/;
|
|
11
13
|
const DEFAULT_MAX_HOOK_FEEDBACK_CHARS = 8000;
|
|
12
14
|
const CONTEXT_PRESSURE_MAX_HOOK_FEEDBACK_CHARS = 1200;
|
|
13
|
-
const MAX_CONCURRENT_DIAGNOSTICS = 4;
|
|
14
15
|
const CONTEXT_PRESSURE_MARKERS = [
|
|
15
16
|
"context compacted",
|
|
16
17
|
"context_length_exceeded",
|
|
@@ -22,30 +23,46 @@ const CONTEXT_PRESSURE_MARKERS = [
|
|
|
22
23
|
];
|
|
23
24
|
export async function runLspDiagnosticsText(filePath) {
|
|
24
25
|
ensureLspDaemonCliEnv();
|
|
25
|
-
const result = await callDiagnosticsViaDaemon(filePath, { context:
|
|
26
|
+
const result = await callDiagnosticsViaDaemon(filePath, { context: codexLspRequestContext() });
|
|
27
|
+
return postEditOutcomeFromDaemonResult(result);
|
|
28
|
+
}
|
|
29
|
+
function postEditOutcomeFromDaemonResult(result) {
|
|
30
|
+
const availability = notConfiguredAvailability(result.details);
|
|
31
|
+
if (availability !== undefined)
|
|
32
|
+
return { kind: "not_configured", extension: availability.extension };
|
|
26
33
|
return result.content.map((block) => block.text).join("\n");
|
|
27
34
|
}
|
|
35
|
+
function notConfiguredAvailability(details) {
|
|
36
|
+
if (!isRecord(details))
|
|
37
|
+
return undefined;
|
|
38
|
+
const availability = details["availability"];
|
|
39
|
+
if (!isRecord(availability))
|
|
40
|
+
return undefined;
|
|
41
|
+
if (availability["kind"] !== "not_configured")
|
|
42
|
+
return undefined;
|
|
43
|
+
const extension = availability["extension"];
|
|
44
|
+
return typeof extension === "string" && extension.length > 0 ? { extension } : undefined;
|
|
45
|
+
}
|
|
46
|
+
export function codexLspRequestContext(env = process.env, cwd = process.cwd()) {
|
|
47
|
+
const canonicalCwd = realpathSync(resolve(cwd));
|
|
48
|
+
const codexHome = resolve(env["CODEX_HOME"]?.trim() || join(homedir(), ".codex"));
|
|
49
|
+
return parseLspRequestContext({
|
|
50
|
+
cwd: canonicalCwd,
|
|
51
|
+
projectConfigPaths: [join(canonicalCwd, ".codex", "lsp-client.json")],
|
|
52
|
+
userConfigPath: join(codexHome, "lsp-client.json"),
|
|
53
|
+
installDecisionsPath: join(codexHome, "lsp-install-decisions.json"),
|
|
54
|
+
capabilities: { installDecisionTool: true },
|
|
55
|
+
});
|
|
56
|
+
}
|
|
28
57
|
export async function runLspPostToolUseHook(input, runDiagnostics = runLspDiagnosticsText) {
|
|
29
58
|
const sessionId = sessionIdFrom(input);
|
|
30
|
-
const filePaths = extractMutatedFilePaths(input)
|
|
59
|
+
const filePaths = extractMutatedFilePaths(input);
|
|
31
60
|
if (filePaths.length === 0)
|
|
32
61
|
return "";
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// recording it would silence this extension for the rest of the session.
|
|
38
|
-
if (isLspDaemonUnreachableDiagnostics(diagnostics))
|
|
39
|
-
continue;
|
|
40
|
-
const unavailable = isUnavailableLspDiagnostics(diagnostics);
|
|
41
|
-
observations.push({ filePath, unavailable });
|
|
42
|
-
if (isCleanDiagnostics(diagnostics))
|
|
43
|
-
continue;
|
|
44
|
-
if (unavailable)
|
|
45
|
-
continue;
|
|
46
|
-
blocks.push({ filePath, diagnostics });
|
|
47
|
-
}
|
|
48
|
-
recordLspDiagnosticsObservations(sessionId, observations);
|
|
62
|
+
const cache = readLspPostEditCache(sessionId);
|
|
63
|
+
const result = await collectPostEditDiagnostics({ filePaths, runDiagnostics, cache });
|
|
64
|
+
writeLspPostEditCache(sessionId, cache);
|
|
65
|
+
const blocks = result.blocks.filter(({ diagnostics }) => !isLspDaemonUnreachableDiagnostics(diagnostics));
|
|
49
66
|
if (blocks.length === 0)
|
|
50
67
|
return "";
|
|
51
68
|
const rawReason = blocks.map(formatDiagnosticBlock).join("\n\n");
|
|
@@ -64,39 +81,6 @@ export async function runLspPostCompactHook(input) {
|
|
|
64
81
|
markLspSessionCompacted(sessionIdFrom(input));
|
|
65
82
|
return "";
|
|
66
83
|
}
|
|
67
|
-
async function collectDiagnostics(filePaths, runDiagnostics) {
|
|
68
|
-
const results = [];
|
|
69
|
-
let nextIndex = 0;
|
|
70
|
-
const workerCount = Math.min(MAX_CONCURRENT_DIAGNOSTICS, filePaths.length);
|
|
71
|
-
async function worker() {
|
|
72
|
-
for (;;) {
|
|
73
|
-
const index = nextIndex;
|
|
74
|
-
nextIndex += 1;
|
|
75
|
-
const filePath = filePaths[index];
|
|
76
|
-
if (filePath === undefined)
|
|
77
|
-
return;
|
|
78
|
-
results[index] = { filePath, diagnostics: await collectFileDiagnostics(filePath, runDiagnostics) };
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
82
|
-
return results;
|
|
83
|
-
}
|
|
84
|
-
async function collectFileDiagnostics(filePath, runDiagnostics) {
|
|
85
|
-
try {
|
|
86
|
-
return (await runDiagnostics(filePath)).trim();
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
return formatDiagnosticsError(error);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function formatDiagnosticsError(error) {
|
|
93
|
-
if (error instanceof Error) {
|
|
94
|
-
const message = error.message.trim();
|
|
95
|
-
if (message.length > 0)
|
|
96
|
-
return message;
|
|
97
|
-
}
|
|
98
|
-
return String(error).trim();
|
|
99
|
-
}
|
|
100
84
|
function formatDiagnosticBlock({ filePath, diagnostics }) {
|
|
101
85
|
return `LSP diagnostics after editing ${filePath}:\n\n${formatDiagnosticsForDisplay(diagnostics)}`;
|
|
102
86
|
}
|
|
@@ -172,11 +156,6 @@ function limitHookText(text, maxChars) {
|
|
|
172
156
|
const head = text.slice(0, maxChars - marker.length).replace(/[ \t\r\n]+$/, "");
|
|
173
157
|
return `${head}${marker}`;
|
|
174
158
|
}
|
|
175
|
-
function isCleanDiagnostics(diagnostics) {
|
|
176
|
-
return (diagnostics.length === 0 ||
|
|
177
|
-
diagnostics === CLEAN_DIAGNOSTICS_TEXT ||
|
|
178
|
-
diagnostics.startsWith(UNSUPPORTED_EXTENSION_TEXT));
|
|
179
|
-
}
|
|
180
159
|
export function isRecord(value) {
|
|
181
160
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
182
161
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare function ensureLspDaemonCliEnv(env?: NodeJS.ProcessEnv): void;
|
|
2
|
-
export declare function resolveLspDaemonCliPath(): string;
|
|
2
|
+
export declare function resolveLspDaemonCliPath(env?: NodeJS.ProcessEnv): string;
|
|
@@ -2,21 +2,21 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { OMO_LSP_DAEMON_CLI, OMO_LSP_DAEMON_VERSION, resolveDaemonRuntime, } from "@code-yeongyu/lsp-daemon/client";
|
|
5
6
|
const requireFromHere = createRequire(import.meta.url);
|
|
6
|
-
const PACKAGE_LSP_DAEMON_CLI = "@code-yeongyu/lsp-daemon/
|
|
7
|
-
const CODEX_LSP_DAEMON_CLI_ENV = "CODEX_LSP_DAEMON_CLI";
|
|
8
|
-
const CODEX_LSP_DAEMON_VERSION_ENV = "CODEX_LSP_DAEMON_VERSION";
|
|
7
|
+
const PACKAGE_LSP_DAEMON_CLI = "@code-yeongyu/lsp-daemon/cli";
|
|
9
8
|
export function ensureLspDaemonCliEnv(env = process.env) {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
env[CODEX_LSP_DAEMON_CLI_ENV] = resolution.cliPath;
|
|
14
|
-
if (!env[CODEX_LSP_DAEMON_VERSION_ENV]?.trim() && resolution.version !== null) {
|
|
15
|
-
env[CODEX_LSP_DAEMON_VERSION_ENV] = resolution.version;
|
|
16
|
-
}
|
|
9
|
+
const runtime = resolveDaemonRuntime(env, resolveLspDaemonCli());
|
|
10
|
+
env[OMO_LSP_DAEMON_CLI] = runtime.cliPath;
|
|
11
|
+
env[OMO_LSP_DAEMON_VERSION] = runtime.version;
|
|
17
12
|
}
|
|
18
|
-
export function resolveLspDaemonCliPath() {
|
|
19
|
-
|
|
13
|
+
export function resolveLspDaemonCliPath(env = process.env) {
|
|
14
|
+
const runtime = resolveDaemonRuntime(env, resolveLspDaemonCli());
|
|
15
|
+
if (env[OMO_LSP_DAEMON_CLI] === undefined)
|
|
16
|
+
env[OMO_LSP_DAEMON_CLI] = runtime.cliPath;
|
|
17
|
+
if (env[OMO_LSP_DAEMON_VERSION] === undefined)
|
|
18
|
+
env[OMO_LSP_DAEMON_VERSION] = runtime.version;
|
|
19
|
+
return runtime.cliPath;
|
|
20
20
|
}
|
|
21
21
|
function resolveLspDaemonCli() {
|
|
22
22
|
const packageCli = resolvePackageLspDaemonCliPath();
|
|
@@ -31,14 +31,20 @@ function resolvePackageLspDaemonCliPath() {
|
|
|
31
31
|
try {
|
|
32
32
|
return requireFromHere.resolve(PACKAGE_LSP_DAEMON_CLI);
|
|
33
33
|
}
|
|
34
|
-
catch {
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (!(error instanceof Error))
|
|
36
|
+
throw error;
|
|
35
37
|
return null;
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
function resolveConfiguredLspDaemonCli(cliPath) {
|
|
41
|
+
const version = readDaemonPackageVersion(cliPath);
|
|
42
|
+
if (version === null) {
|
|
43
|
+
throw new Error(`Unable to determine packaged LSP daemon version beside ${cliPath}`);
|
|
44
|
+
}
|
|
39
45
|
return {
|
|
40
46
|
cliPath,
|
|
41
|
-
version
|
|
47
|
+
version,
|
|
42
48
|
};
|
|
43
49
|
}
|
|
44
50
|
function readDaemonPackageVersion(cliPath) {
|
|
@@ -48,7 +54,10 @@ function readDaemonPackageVersion(cliPath) {
|
|
|
48
54
|
return parsed["version"];
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
|
-
catch {
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (!(error instanceof Error))
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
52
61
|
return null;
|
|
53
62
|
}
|
|
54
63
|
function isRecord(value) {
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
readonly filePath: string;
|
|
3
|
-
readonly unavailable: boolean;
|
|
4
|
-
}
|
|
1
|
+
import type { PostEditNotConfiguredCache } from "@oh-my-opencode/lsp-core/post-edit";
|
|
5
2
|
export declare function sessionIdFrom(input: {
|
|
6
3
|
readonly session_id?: unknown;
|
|
7
4
|
}): string | undefined;
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function
|
|
5
|
+
export declare function readLspPostEditCache(sessionId: string | undefined): PostEditNotConfiguredCache;
|
|
6
|
+
export declare function writeLspPostEditCache(sessionId: string | undefined, cache: PostEditNotConfiguredCache): void;
|
|
10
7
|
export declare function markLspSessionCompacted(sessionId: string | undefined): void;
|
|
11
|
-
export declare function isUnavailableLspDiagnostics(diagnostics: string): boolean;
|
|
12
8
|
export declare function isLspDaemonUnreachableDiagnostics(diagnostics: string): boolean;
|
|
@@ -1,53 +1,26 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname,
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
4
|
export function sessionIdFrom(input) {
|
|
5
5
|
return typeof input.session_id === "string" && input.session_id.length > 0 ? input.session_id : undefined;
|
|
6
6
|
}
|
|
7
|
-
export function
|
|
7
|
+
export function readLspPostEditCache(sessionId) {
|
|
8
8
|
if (sessionId === undefined)
|
|
9
|
-
return
|
|
9
|
+
return { notConfiguredExtensions: new Set() };
|
|
10
10
|
const state = readSessionState(sessionStatePath(sessionId));
|
|
11
|
-
|
|
12
|
-
return (extension !== undefined &&
|
|
13
|
-
state.postCompactProbePending !== true &&
|
|
14
|
-
state.unavailableExtensions.includes(extension));
|
|
11
|
+
return { notConfiguredExtensions: new Set(state.notConfiguredExtensions) };
|
|
15
12
|
}
|
|
16
|
-
export function
|
|
17
|
-
if (sessionId === undefined || observations.length === 0)
|
|
18
|
-
return;
|
|
19
|
-
const state = readSessionState(sessionStatePath(sessionId));
|
|
20
|
-
const unavailableExtensions = new Set(state.unavailableExtensions);
|
|
21
|
-
for (const observation of observations) {
|
|
22
|
-
const extension = extensionKey(observation.filePath);
|
|
23
|
-
if (extension === undefined)
|
|
24
|
-
continue;
|
|
25
|
-
if (observation.unavailable) {
|
|
26
|
-
unavailableExtensions.add(extension);
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
unavailableExtensions.delete(extension);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
writeSessionState(sessionStatePath(sessionId), { unavailableExtensions: [...unavailableExtensions].sort() });
|
|
33
|
-
}
|
|
34
|
-
export function markLspSessionCompacted(sessionId) {
|
|
13
|
+
export function writeLspPostEditCache(sessionId, cache) {
|
|
35
14
|
if (sessionId === undefined)
|
|
36
15
|
return;
|
|
37
|
-
const state = readSessionState(sessionStatePath(sessionId));
|
|
38
|
-
if (state.unavailableExtensions.length === 0)
|
|
39
|
-
return;
|
|
40
16
|
writeSessionState(sessionStatePath(sessionId), {
|
|
41
|
-
|
|
42
|
-
postCompactProbePending: true,
|
|
17
|
+
notConfiguredExtensions: [...cache.notConfiguredExtensions].sort(),
|
|
43
18
|
});
|
|
44
19
|
}
|
|
45
|
-
export function
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
normalized.includes("NOT INSTALLED") ||
|
|
50
|
-
normalized.includes("Command not found:"));
|
|
20
|
+
export function markLspSessionCompacted(sessionId) {
|
|
21
|
+
if (sessionId === undefined)
|
|
22
|
+
return;
|
|
23
|
+
writeSessionState(sessionStatePath(sessionId), emptyState());
|
|
51
24
|
}
|
|
52
25
|
export function isLspDaemonUnreachableDiagnostics(diagnostics) {
|
|
53
26
|
return diagnostics.includes("LSP daemon unreachable");
|
|
@@ -59,8 +32,8 @@ function sessionStatePath(sessionId) {
|
|
|
59
32
|
function readSessionState(path) {
|
|
60
33
|
try {
|
|
61
34
|
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
62
|
-
if (isLspSessionState(parsed))
|
|
63
|
-
return parsed;
|
|
35
|
+
if (isRecord(parsed) && isLspSessionState(parsed))
|
|
36
|
+
return normalizeSessionState(parsed);
|
|
64
37
|
return emptyState();
|
|
65
38
|
}
|
|
66
39
|
catch (error) {
|
|
@@ -74,21 +47,22 @@ function writeSessionState(path, state) {
|
|
|
74
47
|
writeFileSync(path, `${JSON.stringify(state)}\n`);
|
|
75
48
|
}
|
|
76
49
|
function emptyState() {
|
|
77
|
-
return {
|
|
78
|
-
}
|
|
79
|
-
function extensionKey(filePath) {
|
|
80
|
-
const extension = extname(filePath).toLowerCase();
|
|
81
|
-
return extension.length === 0 ? undefined : extension;
|
|
50
|
+
return { notConfiguredExtensions: [] };
|
|
82
51
|
}
|
|
83
52
|
function safePathSegment(value) {
|
|
84
53
|
return value.replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 120) || "unknown-session";
|
|
85
54
|
}
|
|
86
55
|
function isLspSessionState(value) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
56
|
+
const notConfiguredExtensions = value["notConfiguredExtensions"] ?? value["unavailableExtensions"];
|
|
57
|
+
return Array.isArray(notConfiguredExtensions) && notConfiguredExtensions.every((item) => typeof item === "string");
|
|
58
|
+
}
|
|
59
|
+
function normalizeSessionState(value) {
|
|
60
|
+
const notConfiguredExtensions = value["notConfiguredExtensions"] ?? value["unavailableExtensions"];
|
|
61
|
+
return {
|
|
62
|
+
notConfiguredExtensions: Array.isArray(notConfiguredExtensions)
|
|
63
|
+
? notConfiguredExtensions.filter((item) => typeof item === "string").sort()
|
|
64
|
+
: [],
|
|
65
|
+
};
|
|
92
66
|
}
|
|
93
67
|
function isRecord(value) {
|
|
94
68
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -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": "(OmO 4.18.
|
|
11
|
+
"statusMessage": "(OmO 4.18.2) 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": "(OmO 4.18.
|
|
24
|
+
"statusMessage": "(OmO 4.18.2) 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.18.
|
|
3
|
+
"version": "4.18.2",
|
|
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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"scripts": {
|
|
39
39
|
"bootstrap": "node scripts/build-lsp-tools.mjs && node scripts/build-lsp-daemon.mjs",
|
|
40
40
|
"prebuild": "node scripts/build-lsp-tools.mjs && node scripts/build-lsp-daemon.mjs",
|
|
41
|
-
"build": "node scripts/
|
|
41
|
+
"build": "node scripts/build-runtime.mjs",
|
|
42
42
|
"pretest": "npm run build --silent",
|
|
43
43
|
"test": "node scripts/test.mjs",
|
|
44
44
|
"test:watch": "vitest",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"check": "tsc --noEmit && biome check src test && tsc -p tsconfig.build.json"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
+
"@oh-my-opencode/lsp-core": "file:../../../../lsp-core",
|
|
53
54
|
"@code-yeongyu/lsp-daemon": "file:../../../../lsp-daemon"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync } from "node:child_process";
|
|
3
|
-
import { existsSync, statSync } from "node:fs";
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, realpathSync, rmSync, statSync, symlinkSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
|
|
7
7
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const componentRoot = join(here, "..");
|
|
8
9
|
const force = process.argv.includes("--force");
|
|
9
10
|
|
|
10
11
|
// Ordered candidate list:
|
|
@@ -18,6 +19,8 @@ const candidates = [
|
|
|
18
19
|
function requiredOutputs(dir) {
|
|
19
20
|
return [
|
|
20
21
|
join(dir, "dist", "cli.js"),
|
|
22
|
+
join(dir, "dist", "client.js"),
|
|
23
|
+
join(dir, "dist", "client.d.ts"),
|
|
21
24
|
join(dir, "dist", "index.js"),
|
|
22
25
|
join(dir, "dist", "index.d.ts"),
|
|
23
26
|
];
|
|
@@ -30,12 +33,14 @@ for (const dir of candidates) {
|
|
|
30
33
|
if (existsSync(packageJson)) {
|
|
31
34
|
const outputs = requiredOutputs(dir);
|
|
32
35
|
if (!force && isBuildFresh(packageJson, outputs)) {
|
|
36
|
+
ensureComponentPackageLink(dir);
|
|
33
37
|
process.exit(0);
|
|
34
38
|
}
|
|
35
39
|
console.log("Installing repository lsp-daemon dependencies...");
|
|
36
40
|
execSync("npm ci", { cwd: dir, stdio: "inherit" });
|
|
37
41
|
console.log("Building repository lsp-daemon...");
|
|
38
42
|
execSync("npm run build", { cwd: dir, stdio: "inherit" });
|
|
43
|
+
ensureComponentPackageLink(dir);
|
|
39
44
|
console.log("Done.");
|
|
40
45
|
process.exit(0);
|
|
41
46
|
}
|
|
@@ -46,6 +51,7 @@ for (const dir of candidates) {
|
|
|
46
51
|
for (const dir of candidates) {
|
|
47
52
|
const outputs = requiredOutputs(dir);
|
|
48
53
|
if (outputs.every((p) => existsSync(p))) {
|
|
54
|
+
ensureComponentPackageLink(dir);
|
|
49
55
|
console.log(`Using bundled lsp-daemon dist at ${dir}.`);
|
|
50
56
|
process.exit(0);
|
|
51
57
|
}
|
|
@@ -66,3 +72,27 @@ function isBuildFresh(inputPath, outputPaths) {
|
|
|
66
72
|
const inputMtime = statSync(inputPath).mtimeMs;
|
|
67
73
|
return outputPaths.every((path) => statSync(path).mtimeMs >= inputMtime);
|
|
68
74
|
}
|
|
75
|
+
|
|
76
|
+
function ensureComponentPackageLink(packageDir) {
|
|
77
|
+
const scopeDir = join(componentRoot, "node_modules", "@code-yeongyu");
|
|
78
|
+
const linkPath = join(scopeDir, "lsp-daemon");
|
|
79
|
+
mkdirSync(scopeDir, { recursive: true });
|
|
80
|
+
if (isSymlinkToUsablePackage(linkPath, packageDir)) return;
|
|
81
|
+
rmSync(linkPath, { recursive: true, force: true });
|
|
82
|
+
symlinkSync(packageDir, linkPath, "junction");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function isSymlinkToUsablePackage(linkPath, packageDir) {
|
|
86
|
+
try {
|
|
87
|
+
const stat = lstatSync(linkPath);
|
|
88
|
+
return (
|
|
89
|
+
stat.isSymbolicLink() &&
|
|
90
|
+
existsSync(packageDir) &&
|
|
91
|
+
realpathSync(linkPath) === realpathSync(packageDir) &&
|
|
92
|
+
requiredOutputs(linkPath).every((path) => existsSync(path))
|
|
93
|
+
);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return false;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { chmod, copyFile, lstat, mkdir, mkdtemp, readFile, realpath, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { delimiter, join } from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
|
|
8
|
+
async function makeFixture() {
|
|
9
|
+
const root = await mkdtemp(join(tmpdir(), "codex-lsp-daemon-build-"));
|
|
10
|
+
const componentRoot = join(root, "packages", "omo-codex", "plugin", "components", "lsp");
|
|
11
|
+
const packageDir = join(root, "packages", "lsp-daemon");
|
|
12
|
+
const script = join(componentRoot, "scripts", "build-lsp-daemon.mjs");
|
|
13
|
+
await mkdir(join(componentRoot, "scripts"), { recursive: true });
|
|
14
|
+
await mkdir(join(packageDir, "dist"), { recursive: true });
|
|
15
|
+
await copyFile(new URL("./build-lsp-daemon.mjs", import.meta.url), script);
|
|
16
|
+
await writeFile(join(packageDir, "package.json"), "{}\n");
|
|
17
|
+
await writeDaemonOutputs(packageDir);
|
|
18
|
+
return { root, componentRoot, packageDir, script };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function writeDaemonOutputs(packageDir) {
|
|
22
|
+
for (const output of ["cli.js", "client.js", "client.d.ts", "index.js", "index.d.ts"]) {
|
|
23
|
+
await writeFile(join(packageDir, "dist", output), `${output}\n`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function runScript(script, fakeBin, args = []) {
|
|
28
|
+
return spawnSync(process.execPath, [script, ...args], {
|
|
29
|
+
encoding: "utf8",
|
|
30
|
+
env: { ...process.env, PATH: fakeBin ? `${fakeBin}${delimiter}${process.env.PATH ?? ""}` : process.env.PATH },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test("#given fresh daemon dist #when bootstrapping component #then local package link is created", async () => {
|
|
35
|
+
// given
|
|
36
|
+
const fixture = await makeFixture();
|
|
37
|
+
const link = join(fixture.componentRoot, "node_modules", "@code-yeongyu", "lsp-daemon");
|
|
38
|
+
|
|
39
|
+
// when
|
|
40
|
+
const result = runScript(fixture.script);
|
|
41
|
+
|
|
42
|
+
// then
|
|
43
|
+
assert.equal(result.status, 0, result.stderr);
|
|
44
|
+
assert.equal((await lstat(link)).isSymbolicLink(), true);
|
|
45
|
+
assert.equal(await realpath(link), await realpath(fixture.packageDir));
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("#given client outputs are missing #when bootstrapping component #then daemon rebuild runs before linking", async () => {
|
|
49
|
+
// given
|
|
50
|
+
const fixture = await makeFixture();
|
|
51
|
+
await rm(join(fixture.packageDir, "dist", "client.d.ts"));
|
|
52
|
+
const fakeBin = join(fixture.root, "bin");
|
|
53
|
+
const npmLog = join(fixture.root, "npm.log");
|
|
54
|
+
await mkdir(fakeBin, { recursive: true });
|
|
55
|
+
await writeFile(
|
|
56
|
+
join(fakeBin, "npm.js"),
|
|
57
|
+
`const { appendFileSync, mkdirSync, writeFileSync } = require("node:fs");\n` +
|
|
58
|
+
`const { join } = require("node:path");\n` +
|
|
59
|
+
`appendFileSync(${JSON.stringify(npmLog)}, process.argv.slice(2).join(" ") + "\\n");\n` +
|
|
60
|
+
`if (process.argv.slice(2).join(" ") === "run build") {\n` +
|
|
61
|
+
` const dist = join(process.cwd(), "dist");\n` +
|
|
62
|
+
` mkdirSync(dist, { recursive: true });\n` +
|
|
63
|
+
` for (const name of ["cli.js", "client.js", "client.d.ts", "index.js", "index.d.ts"]) writeFileSync(join(dist, name), name + "\\n");\n` +
|
|
64
|
+
`}\n`,
|
|
65
|
+
);
|
|
66
|
+
await writeFile(join(fakeBin, "npm"), "#!/usr/bin/env node\nrequire('./npm.js');\n");
|
|
67
|
+
await chmod(join(fakeBin, "npm"), 0o755);
|
|
68
|
+
|
|
69
|
+
// when
|
|
70
|
+
const result = runScript(fixture.script, fakeBin);
|
|
71
|
+
|
|
72
|
+
// then
|
|
73
|
+
assert.equal(result.status, 0, result.stderr);
|
|
74
|
+
assert.match(await readFile(npmLog, "utf8"), /ci\nrun build\n/u);
|
|
75
|
+
assert.equal(await realpath(join(fixture.componentRoot, "node_modules", "@code-yeongyu", "lsp-daemon")), await realpath(fixture.packageDir));
|
|
76
|
+
});
|