diff --git a/packages/omo-codex/plugin/components/lsp/.gitattributes b/packages/omo-codex/plugin/components/lsp/.gitattributes new file mode 100644 index 000000000..9363fdb74 --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/.gitattributes @@ -0,0 +1,13 @@ +# Normalize line endings: store LF in git, check out LF on every platform. +# Required so biome's --check passes on Windows (default core.autocrlf=true). +* text=auto eol=lf + +# Explicit binary types +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.zip binary +*.tgz binary +*.gz binary diff --git a/packages/omo-codex/plugin/components/lsp/.gitignore b/packages/omo-codex/plugin/components/lsp/.gitignore new file mode 100644 index 000000000..1bf09f16c --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +*.log +.env +.DS_Store +coverage/ +.vitest/ diff --git a/packages/omo-codex/plugin/components/lsp/.mcp.json b/packages/omo-codex/plugin/components/lsp/.mcp.json new file mode 100644 index 000000000..46ecc491f --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/.mcp.json @@ -0,0 +1,9 @@ +{ + "mcpServers": { + "lsp": { + "command": "node", + "args": ["../../../../lsp-tools-mcp/dist/cli.js", "mcp"], + "cwd": "." + } + } +} diff --git a/packages/omo-codex/plugin/components/lsp/AGENTS.md b/packages/omo-codex/plugin/components/lsp/AGENTS.md new file mode 100644 index 000000000..69e85a30c --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/AGENTS.md @@ -0,0 +1,25 @@ +# Repository Conventions + +Conventions for humans and agents working on this repository. + +## Style + +- TypeScript strict mode. No `any`, `@ts-ignore`, `@ts-expect-error`, or enums. +- ESM modules with `.js` suffix in import paths. +- Tabs for indentation. Double quotes for strings. +- Runtime is Node only. +- Tests use vitest and should exercise Codex hook/MCP behavior before implementation changes. + +## Commands + +- `npm install` installs dependencies. +- `npm test` runs the test suite once. +- `npm run typecheck` runs strict TypeScript checking. +- `npm run check` runs typecheck, Biome, and build. + +## LSP Constraints + +- LSP server processes are owned by `LspManager`. +- Tool execution acquires clients through `withLspClient(...)` unless it only reports static status. +- `lsp.rename` mutates files by applying workspace edits; keep it sequential at the MCP caller level. +- Do not add pi-coding-agent or omo source dependencies. This package is standalone. diff --git a/packages/omo-codex/plugin/components/lsp/CHANGELOG.md b/packages/omo-codex/plugin/components/lsp/CHANGELOG.md new file mode 100644 index 000000000..319414453 --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog + +## Unreleased + +- Reuse the repository-level `packages/lsp-tools-mcp` package instead of carrying a second copy under `components/lsp/packages`. + +## 0.2.0 + +- Extracted the LSP runtime and MCP server into [`@code-yeongyu/lsp-tools-mcp`](https://github.com/code-yeongyu/lsp-tools-mcp). +- codex-lsp now consumes that runtime as a git submodule at `packages/lsp-tools-mcp`. +- Kept the Codex-specific PostToolUse hook in this package and routed MCP serving through the upstream CLI. + +- Extract LSP runtime to `lsp-tools-mcp` upstream and consume it via git submodule at `packages/lsp-tools-mcp`. +- Renamed the MCP server namespace to `lsp` and exposed shorter tool names such as `lsp.diagnostics`. +- Use portable Codex hook interpolation and add package smoke coverage for hook/MCP entrypoints. +- Spawn language servers without shell mode; Windows `.cmd` and `.bat` shims are routed through `cmd.exe` with explicit arguments. +- Cap directory diagnostics file traversal and run CI on Windows in addition to Ubuntu and macOS. +- Replace the external JSON-RPC runtime dependency with an internal LSP framing layer so clean Codex plugin installs run without `node_modules`. + +## 0.1.0 + +- Ported the standalone LSP client, server resolution, diagnostics aggregation, and workspace edit runtime from `pi-lsp-client`. +- Added Codex `PostToolUse` diagnostics for edit-style tools. +- Added MCP tools for status, diagnostics, definitions, references, symbols, prepare rename, and rename. +- Added Codex plugin metadata, skill docs, CI, and release automation. diff --git a/packages/omo-codex/plugin/components/lsp/LICENSE b/packages/omo-codex/plugin/components/lsp/LICENSE new file mode 100644 index 000000000..09aac3c3b --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Yeongyu Kim + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/omo-codex/plugin/components/lsp/NOTICE b/packages/omo-codex/plugin/components/lsp/NOTICE new file mode 100644 index 000000000..01916eda3 --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/NOTICE @@ -0,0 +1,3 @@ +codex-lsp ports the standalone LSP runtime from pi-lsp-client into a Codex plugin. + +The package includes adapted code originally developed for pi-lsp-client. diff --git a/packages/omo-codex/plugin/components/lsp/README.md b/packages/omo-codex/plugin/components/lsp/README.md new file mode 100644 index 000000000..90acc6545 --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/README.md @@ -0,0 +1,148 @@ +# codex-lsp + +[![ci](https://github.com/code-yeongyu/codex-lsp/actions/workflows/ci.yml/badge.svg)](https://github.com/code-yeongyu/codex-lsp/actions/workflows/ci.yml) [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) + +Codex plugin that ports the standalone LSP runtime from [`pi-lsp-client`](https://github.com/code-yeongyu/pi-lsp-client). It gives Codex post-edit diagnostics plus explicit MCP tools for language-aware code work. + +## Architecture + +The LSP runtime moved to [`lsp-tools-mcp`](https://github.com/code-yeongyu/lsp-tools-mcp) and is consumed from this repository's root `packages/lsp-tools-mcp/` package. + +- `codex-lsp` keeps Codex-specific integration (`hook post-tool-use`, plugin metadata, package wiring). +- `lsp-tools-mcp` owns MCP runtime, LSP manager, and tool implementations. +- `src/cli.ts` routes `mcp` to upstream runtime and keeps `hook post-tool-use` local. + +## Behavior + +| Case | Result | +|------|--------| +| `apply_patch` succeeds | parses `tool_input.command`, extracts added/updated/moved files, and checks each with LSP error diagnostics | +| `write` / `edit` / `multiedit` succeeds | checks `path`, `filePath`, or `file_path` aliases | +| diagnostics contain errors | returns Codex `PostToolUse` blocking feedback and injects the same diagnostics as additional context so Codex fixes the file | +| no diagnostics | emits no hook output | +| unsupported extension | emits no hook output | +| missing configured language server | surfaces the install/config message through hook or MCP output | + +Deletes are ignored because they cannot introduce new diagnostics. + +## MCP Tools + +- `lsp.status` +- `lsp.diagnostics` +- `lsp.goto_definition` +- `lsp.find_references` +- `lsp.symbols` +- `lsp.prepare_rename` +- `lsp.rename` + +`lsp.rename` applies the returned workspace edit to files. Use `lsp.prepare_rename` first when possible. + +## Configuration + +Project config: + +```text +.codex/lsp-client.json +``` + +User config: + +```text +~/.codex/lsp-client.json +``` + +Example: + +```json +{ + "lsp": { + "typescript": { + "command": ["typescript-language-server", "--stdio"], + "extensions": [".ts", ".tsx", ".js", ".jsx"] + } + } +} +``` + +Built-in server definitions are used when no custom config overrides them. `lsp.status` shows which configured servers are installed or missing. + +## Codex Plugin + +The plugin ships: + +- `.codex-plugin/plugin.json` for Codex plugin discovery. +- `.mcp.json` for the `lsp` MCP server. +- `hooks/hooks.json` for the `PostToolUse` diagnostics hook. +- `skills/lsp/SKILL.md` with MCP usage guidance. + +The runtime depends on `@code-yeongyu/lsp-tools-mcp` via `file:../../../../lsp-tools-mcp`, so marketplace builds reuse the root package instead of carrying a second copy under this component. + +The hook command is: + +```bash +node "${PLUGIN_ROOT}/dist/cli.js" hook post-tool-use +``` + +The MCP command is: + +```bash +node ../../../../lsp-tools-mcp/dist/cli.js mcp +``` + +## Local Development + +```bash +npm run bootstrap # installs + builds the root packages/lsp-tools-mcp package +npm install +npm test +npm run typecheck +npm run check +npm pack --dry-run +``` + +The `bootstrap` script installs and builds the root `lsp-tools-mcp` package so +`@code-yeongyu/lsp-tools-mcp/dist/*.js` is available for the codex-lsp build. + +Smoke-test the hook: + +```bash +node dist/cli.js hook post-tool-use < test/fixtures/post-tool-use.json +``` + +Smoke-test the MCP server: + +```bash +printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/cli.js mcp +``` + +## Local Codex Installation + +```bash +bunx lazycodex install +``` + +The installer builds and copies the plugin into `~/.codex/plugins/cache/sisyphuslabs/omo/0.1.0`, registers the `sisyphuslabs` marketplace from the `lazycodex` Git repository, and enables: + +```toml +[plugins."omo@sisyphuslabs"] +enabled = true +``` + +## Branch Rules and Releases + +- `main` is protected by `.github/branch-ruleset.json`. +- CI runs Node 20 and 22 on Ubuntu, macOS, and Windows. +- Releases are GitHub Releases tagged as `v`. +- Publishing runs from the `publish` workflow after a GitHub Release is published. + +## Privacy + +This plugin runs locally. It starts configured language-server commands on your machine and does not call a network service by itself. + +## License + +[MIT](LICENSE). + +## Related + +- [pi-lsp-client](https://github.com/code-yeongyu/pi-lsp-client) - source extension this Codex plugin ports. diff --git a/packages/omo-codex/plugin/components/lsp/biome.json b/packages/omo-codex/plugin/components/lsp/biome.json new file mode 100644 index 000000000..5aa1a0dcc --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/biome.json @@ -0,0 +1,48 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.15/schema.json", + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "style": { + "noDefaultExport": "error", + "noEnum": "error", + "noNonNullAssertion": "error", + "useImportType": "error", + "useConst": "error", + "useNodejsImportProtocol": "off" + }, + "complexity": { + "useLiteralKeys": "off" + }, + "suspicious": { + "noExplicitAny": "error", + "noTsIgnore": "error", + "noControlCharactersInRegex": "off", + "noEmptyInterface": "off" + } + } + }, + "formatter": { + "enabled": true, + "formatWithErrors": false, + "indentStyle": "tab", + "indentWidth": 3, + "lineWidth": 120 + }, + "files": { + "includes": ["src/**/*.ts", "test/**/*.ts", "vitest.config.ts", "!**/node_modules/**/*", "!**/dist/**/*"] + }, + "overrides": [ + { + "includes": ["vitest.config.ts"], + "linter": { + "rules": { + "style": { + "noDefaultExport": "off" + } + } + } + } + ] +} diff --git a/packages/omo-codex/plugin/components/lsp/package.json b/packages/omo-codex/plugin/components/lsp/package.json new file mode 100644 index 000000000..b6a9af598 --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/package.json @@ -0,0 +1,64 @@ +{ + "name": "@code-yeongyu/codex-lsp", + "version": "0.2.0", + "description": "Codex plugin that exposes Language Server Protocol tools and post-edit diagnostics.", + "type": "module", + "packageManager": "npm@11.12.1", + "license": "MIT", + "homepage": "https://github.com/code-yeongyu/codex-lsp", + "repository": { + "type": "git", + "url": "git+https://github.com/code-yeongyu/codex-lsp.git" + }, + "bugs": { + "url": "https://github.com/code-yeongyu/codex-lsp/issues" + }, + "keywords": [ + "codex", + "codex-plugin", + "lsp", + "language-server-protocol", + "mcp", + "diagnostics" + ], + "bin": { + "omo-lsp": "./dist/cli.js" + }, + "files": [ + "dist", + "hooks", + "skills", + ".codex-plugin", + ".mcp.json", + "LICENSE", + "NOTICE", + "README.md", + "CHANGELOG.md" + ], + "scripts": { + "bootstrap": "node scripts/build-lsp-tools.mjs", + "prebuild": "node scripts/build-lsp-tools.mjs", + "build": "node scripts/clean-dist.mjs && tsc -p tsconfig.build.json", + "pretest": "node scripts/build-lsp-tools.mjs", + "test": "node scripts/test.mjs", + "test:watch": "vitest", + "pretypecheck": "node scripts/build-lsp-tools.mjs", + "typecheck": "tsc --noEmit", + "lint": "biome check src test", + "lint:fix": "biome check --write src test", + "precheck": "node scripts/build-lsp-tools.mjs", + "check": "tsc --noEmit && biome check src test && tsc -p tsconfig.build.json" + }, + "dependencies": { + "@code-yeongyu/lsp-tools-mcp": "file:../../../../lsp-tools-mcp" + }, + "devDependencies": { + "@biomejs/biome": "2.4.15", + "@types/node": "^25.7.0", + "typescript": "^6.0.3", + "vitest": "^4.1.5" + }, + "engines": { + "node": ">=20.0.0" + } +} diff --git a/packages/omo-codex/plugin/components/lsp/tsconfig.build.json b/packages/omo-codex/plugin/components/lsp/tsconfig.build.json new file mode 100644 index 000000000..5b5bbcafd --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "allowImportingTsExtensions": false, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "noEmit": false + }, + "include": ["src/**/*"], + "exclude": ["test/**/*"] +} diff --git a/packages/omo-codex/plugin/components/lsp/tsconfig.json b/packages/omo-codex/plugin/components/lsp/tsconfig.json new file mode 100644 index 000000000..342229c02 --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "lib": ["ES2022"], + "strict": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "noPropertyAccessFromIndexSignature": true, + "verbatimModuleSyntax": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "useDefineForClassFields": false, + "types": ["node"], + "noEmit": true + }, + "include": ["src/**/*", "test/**/*"] +} diff --git a/packages/omo-codex/plugin/components/lsp/vitest.config.ts b/packages/omo-codex/plugin/components/lsp/vitest.config.ts new file mode 100644 index 000000000..57bd8f12b --- /dev/null +++ b/packages/omo-codex/plugin/components/lsp/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["test/**/*.test.ts"], + environment: "node", + pool: "threads", + }, +});