litclaude-ai 0.3.3 → 0.3.7
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/CHANGELOG.md +71 -0
- package/README.md +5 -5
- package/README_ko-KR.md +5 -5
- package/RELEASE_CHECKLIST.md +8 -6
- package/cover.png +0 -0
- package/generate_cover.py +1 -1
- package/package.json +1 -1
- package/plugins/litclaude/.claude-plugin/plugin.json +1 -1
- package/plugins/litclaude/bin/litclaude-hook.js +42 -6
- package/plugins/litclaude/commands/dynamic-workflow.md +6 -6
- package/plugins/litclaude/commands/litresearch.md +37 -0
- package/plugins/litclaude/commands/review-work.md +5 -5
- package/plugins/litclaude/hooks/hooks.json +12 -0
- package/plugins/litclaude/lib/litgoal/autoloop.mjs +78 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +8 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +12 -10
- package/plugins/litclaude/skills/lit-plan/SKILL.md +9 -10
- package/plugins/litclaude/skills/litgoal/SKILL.md +41 -5
- package/plugins/litclaude/skills/litresearch/SKILL.md +148 -0
- package/plugins/litclaude/skills/lsp-setup/SKILL.md +151 -0
- package/plugins/litclaude/skills/lsp-setup/references/bash/README.md +63 -0
- package/plugins/litclaude/skills/lsp-setup/references/c-cpp/README.md +72 -0
- package/plugins/litclaude/skills/lsp-setup/references/csharp/README.md +81 -0
- package/plugins/litclaude/skills/lsp-setup/references/dart/README.md +53 -0
- package/plugins/litclaude/skills/lsp-setup/references/elixir/README.md +57 -0
- package/plugins/litclaude/skills/lsp-setup/references/go/README.md +59 -0
- package/plugins/litclaude/skills/lsp-setup/references/haskell/README.md +64 -0
- package/plugins/litclaude/skills/lsp-setup/references/java/README.md +66 -0
- package/plugins/litclaude/skills/lsp-setup/references/julia/README.md +62 -0
- package/plugins/litclaude/skills/lsp-setup/references/kotlin/README.md +68 -0
- package/plugins/litclaude/skills/lsp-setup/references/lua/README.md +64 -0
- package/plugins/litclaude/skills/lsp-setup/references/php/README.md +56 -0
- package/plugins/litclaude/skills/lsp-setup/references/python/README.md +68 -0
- package/plugins/litclaude/skills/lsp-setup/references/ruby/README.md +62 -0
- package/plugins/litclaude/skills/lsp-setup/references/rust/README.md +61 -0
- package/plugins/litclaude/skills/lsp-setup/references/swift/README.md +60 -0
- package/plugins/litclaude/skills/lsp-setup/references/terraform/README.md +61 -0
- package/plugins/litclaude/skills/lsp-setup/references/typescript/README.md +77 -0
- package/plugins/litclaude/skills/lsp-setup/references/yaml/README.md +60 -0
- package/plugins/litclaude/skills/lsp-setup/references/zig/README.md +55 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/detect-lsp.ts +220 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/lsp-server-table.ts +146 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/tsconfig.json +18 -0
- package/plugins/litclaude/skills/lsp-setup/scripts/verify-lsp.ts +242 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +12 -13
- package/plugins/litclaude/skills/visual-qa/SKILL.md +259 -0
- package/plugins/litclaude/skills/visual-qa/scripts/ansi.ts +17 -0
- package/plugins/litclaude/skills/visual-qa/scripts/cli.ts +96 -0
- package/plugins/litclaude/skills/visual-qa/scripts/east-asian-width.ts +72 -0
- package/plugins/litclaude/skills/visual-qa/scripts/image-diff.ts +109 -0
- package/plugins/litclaude/skills/visual-qa/scripts/png-crc.ts +27 -0
- package/plugins/litclaude/skills/visual-qa/scripts/png-decode.ts +206 -0
- package/plugins/litclaude/skills/visual-qa/scripts/png-synth.ts +57 -0
- package/plugins/litclaude/skills/visual-qa/scripts/tui-grid.ts +88 -0
- package/plugins/litclaude/skills/visual-qa/scripts/types.ts +54 -0
- package/scripts/qa-portable-install.sh +1 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Standalone snapshot of the primary language server per supported language,
|
|
2
|
+
// embedded so detect-lsp.ts runs without any external dependency in any project.
|
|
3
|
+
// Each entry mirrors the per-language references/<language>/README.md and the
|
|
4
|
+
// LitClaude .lsp.json shape, which is keyed by language name:
|
|
5
|
+
// { "<language>": { "command": [...], "extensionToLanguage": { ".ext": "<lang>" } } }
|
|
6
|
+
// Edit this table when a reference README changes.
|
|
7
|
+
|
|
8
|
+
export interface LanguageServer {
|
|
9
|
+
readonly language: string
|
|
10
|
+
readonly command: readonly string[]
|
|
11
|
+
readonly extensions: readonly string[]
|
|
12
|
+
readonly installHint: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const LANGUAGES: readonly LanguageServer[] = [
|
|
16
|
+
{
|
|
17
|
+
language: "typescript",
|
|
18
|
+
command: ["typescript-language-server", "--stdio"],
|
|
19
|
+
extensions: [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".mts", ".cts"],
|
|
20
|
+
installHint: "npm install -g typescript-language-server typescript",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
language: "python",
|
|
24
|
+
command: ["basedpyright-langserver", "--stdio"],
|
|
25
|
+
extensions: [".py", ".pyi"],
|
|
26
|
+
installHint: "pip install basedpyright (or: uv tool install basedpyright)",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
language: "go",
|
|
30
|
+
command: ["gopls"],
|
|
31
|
+
extensions: [".go"],
|
|
32
|
+
installHint: "go install golang.org/x/tools/gopls@latest",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
language: "rust",
|
|
36
|
+
command: ["rust-analyzer"],
|
|
37
|
+
extensions: [".rs"],
|
|
38
|
+
installHint: "rustup component add rust-analyzer",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
language: "c-cpp",
|
|
42
|
+
command: ["clangd", "--background-index", "--clang-tidy"],
|
|
43
|
+
extensions: [".c", ".cpp", ".cc", ".cxx", ".c++", ".h", ".hpp", ".hh", ".hxx", ".h++"],
|
|
44
|
+
installHint: "macOS: brew install llvm | Linux: apt install clangd | https://clangd.llvm.org/installation",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
language: "java",
|
|
48
|
+
command: ["jdtls"],
|
|
49
|
+
extensions: [".java"],
|
|
50
|
+
installHint: "macOS: brew install jdtls | https://github.com/eclipse-jdtls/eclipse.jdt.ls",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
language: "kotlin",
|
|
54
|
+
command: ["kotlin-lsp"],
|
|
55
|
+
extensions: [".kt", ".kts"],
|
|
56
|
+
installHint: "https://github.com/Kotlin/kotlin-lsp",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
language: "csharp",
|
|
60
|
+
command: ["csharp-ls"],
|
|
61
|
+
extensions: [".cs"],
|
|
62
|
+
installHint: "dotnet tool install -g csharp-ls",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
language: "razor",
|
|
66
|
+
command: ["roslyn-language-server", "--stdio"],
|
|
67
|
+
extensions: [".razor", ".cshtml"],
|
|
68
|
+
installHint: "dotnet tool install -g roslyn-language-server --prerelease (see references/csharp/README.md)",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
language: "swift",
|
|
72
|
+
command: ["sourcekit-lsp"],
|
|
73
|
+
extensions: [".swift", ".objc", ".objcpp"],
|
|
74
|
+
installHint: "Included with Xcode (xcode-select --install) or the Swift toolchain",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
language: "ruby",
|
|
78
|
+
command: ["rubocop", "--lsp"],
|
|
79
|
+
extensions: [".rb", ".rake", ".gemspec", ".ru"],
|
|
80
|
+
installHint: "gem install rubocop (the builtin runs `rubocop --lsp`)",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
language: "php",
|
|
84
|
+
command: ["intelephense", "--stdio"],
|
|
85
|
+
extensions: [".php"],
|
|
86
|
+
installHint: "npm install -g intelephense",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
language: "dart",
|
|
90
|
+
command: ["dart", "language-server", "--lsp"],
|
|
91
|
+
extensions: [".dart"],
|
|
92
|
+
installHint: "Included with the Dart/Flutter SDK",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
language: "elixir",
|
|
96
|
+
command: ["elixir-ls"],
|
|
97
|
+
extensions: [".ex", ".exs"],
|
|
98
|
+
installHint: "https://github.com/elixir-lsp/elixir-ls",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
language: "zig",
|
|
102
|
+
command: ["zls"],
|
|
103
|
+
extensions: [".zig", ".zon"],
|
|
104
|
+
installHint: "https://github.com/zigtools/zls (match zls version to your zig version)",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
language: "lua",
|
|
108
|
+
command: ["lua-language-server"],
|
|
109
|
+
extensions: [".lua"],
|
|
110
|
+
installHint: "macOS: brew install lua-language-server | https://github.com/LuaLS/lua-language-server",
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
language: "bash",
|
|
114
|
+
command: ["bash-language-server", "start"],
|
|
115
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh"],
|
|
116
|
+
installHint: "npm install -g bash-language-server",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
language: "yaml",
|
|
120
|
+
command: ["yaml-language-server", "--stdio"],
|
|
121
|
+
extensions: [".yaml", ".yml"],
|
|
122
|
+
installHint: "npm install -g yaml-language-server",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
language: "terraform",
|
|
126
|
+
command: ["terraform-ls", "serve"],
|
|
127
|
+
extensions: [".tf", ".tfvars"],
|
|
128
|
+
installHint: "macOS: brew install hashicorp/tap/terraform-ls | https://github.com/hashicorp/terraform-ls",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
language: "haskell",
|
|
132
|
+
command: ["haskell-language-server-wrapper", "--lsp"],
|
|
133
|
+
extensions: [".hs", ".lhs"],
|
|
134
|
+
installHint: "ghcup install hls",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
language: "julia",
|
|
138
|
+
command: ["julia", "--startup-file=no", "--history-file=no", "-e", "using LanguageServer; runserver()"],
|
|
139
|
+
extensions: [".jl"],
|
|
140
|
+
installHint: "julia -e 'using Pkg; Pkg.add(\"LanguageServer\")' (see references/julia/README.md)",
|
|
141
|
+
},
|
|
142
|
+
] as const
|
|
143
|
+
|
|
144
|
+
// The LitClaude LSP config file, relative to the plugin root. detect-lsp.ts also
|
|
145
|
+
// accepts an explicit path so it can be pointed at a project copy.
|
|
146
|
+
export const LSP_CONFIG_FILE = "plugins/litclaude/.lsp.json"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noUncheckedIndexedAccess": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"noEmit": true,
|
|
13
|
+
"lib": ["ESNext"],
|
|
14
|
+
"types": ["node"],
|
|
15
|
+
"allowImportingTsExtensions": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["*.ts"]
|
|
18
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// verify-lsp.ts <file> [--timeout=ms] [--config=<path>] — perform a real LSP
|
|
3
|
+
// diagnostics roundtrip for <file>: resolve the server for the file extension
|
|
4
|
+
// (from plugins/litclaude/.lsp.json when present, else the embedded table),
|
|
5
|
+
// spawn it, run the JSON-RPC initialize -> initialized -> didOpen handshake over
|
|
6
|
+
// stdio, wait for textDocument/publishDiagnostics, and report OK / FAIL / SKIP.
|
|
7
|
+
//
|
|
8
|
+
// Dependency-free: uses only node:child_process and node:fs. No monorepo imports.
|
|
9
|
+
//
|
|
10
|
+
// Runtime: Node 22.6+ with `node --experimental-strip-types`, or `bun`.
|
|
11
|
+
// node --experimental-strip-types scripts/verify-lsp.ts <file>
|
|
12
|
+
// bun scripts/verify-lsp.ts <file>
|
|
13
|
+
//
|
|
14
|
+
// Exit codes: 0 OK, 1 FAIL (server error / not installed), 2 usage, 3 SKIP
|
|
15
|
+
// (no server known for this extension).
|
|
16
|
+
|
|
17
|
+
import { spawn } from "node:child_process"
|
|
18
|
+
import { existsSync, readFileSync, statSync } from "node:fs"
|
|
19
|
+
import { extname, isAbsolute, resolve } from "node:path"
|
|
20
|
+
import { pathToFileURL } from "node:url"
|
|
21
|
+
import process from "node:process"
|
|
22
|
+
|
|
23
|
+
import { LANGUAGES, LSP_CONFIG_FILE } from "./lsp-server-table.ts"
|
|
24
|
+
|
|
25
|
+
const DEFAULT_TIMEOUT_MS = 60_000
|
|
26
|
+
|
|
27
|
+
interface ResolvedServer {
|
|
28
|
+
readonly language: string
|
|
29
|
+
readonly command: readonly string[]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
33
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isStringArray(value: unknown): value is string[] {
|
|
37
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Resolve the server command for `ext`, preferring a LitClaude .lsp.json entry
|
|
41
|
+
// whose `extensionToLanguage` maps the extension, then the embedded table.
|
|
42
|
+
function resolveServer(ext: string, configPath: string): ResolvedServer | null {
|
|
43
|
+
if (existsSync(configPath)) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed: unknown = JSON.parse(readFileSync(configPath, "utf-8"))
|
|
46
|
+
if (isRecord(parsed)) {
|
|
47
|
+
for (const [language, entry] of Object.entries(parsed)) {
|
|
48
|
+
if (!isRecord(entry)) continue
|
|
49
|
+
const map = entry["extensionToLanguage"]
|
|
50
|
+
const command = entry["command"]
|
|
51
|
+
if (isRecord(map) && Object.keys(map).includes(ext) && isStringArray(command)) {
|
|
52
|
+
return { language, command }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// fall through to embedded table
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const server of LANGUAGES) {
|
|
62
|
+
if (server.extensions.includes(ext)) {
|
|
63
|
+
return { language: server.language, command: [...server.command] }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface JsonRpcMessage {
|
|
70
|
+
readonly method?: string
|
|
71
|
+
readonly id?: number
|
|
72
|
+
readonly params?: unknown
|
|
73
|
+
readonly result?: unknown
|
|
74
|
+
readonly error?: unknown
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function encode(message: unknown): Buffer {
|
|
78
|
+
const body = Buffer.from(JSON.stringify(message), "utf-8")
|
|
79
|
+
return Buffer.concat([Buffer.from(`Content-Length: ${body.length}\r\n\r\n`, "ascii"), body])
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Incremental LSP framing parser: pulls complete Content-Length messages off a
|
|
83
|
+
// growing buffer.
|
|
84
|
+
function makeReader(onMessage: (message: JsonRpcMessage) => void): (chunk: Buffer) => void {
|
|
85
|
+
let buffer = Buffer.alloc(0)
|
|
86
|
+
return (chunk: Buffer): void => {
|
|
87
|
+
buffer = Buffer.concat([buffer, chunk])
|
|
88
|
+
while (true) {
|
|
89
|
+
const headerEnd = buffer.indexOf("\r\n\r\n")
|
|
90
|
+
if (headerEnd === -1) return
|
|
91
|
+
const header = buffer.subarray(0, headerEnd).toString("ascii")
|
|
92
|
+
const match = /content-length:\s*(\d+)/i.exec(header)
|
|
93
|
+
if (!match) {
|
|
94
|
+
buffer = buffer.subarray(headerEnd + 4)
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
const length = Number.parseInt(match[1] ?? "0", 10)
|
|
98
|
+
const start = headerEnd + 4
|
|
99
|
+
if (buffer.length < start + length) return
|
|
100
|
+
const body = buffer.subarray(start, start + length).toString("utf-8")
|
|
101
|
+
buffer = buffer.subarray(start + length)
|
|
102
|
+
try {
|
|
103
|
+
onMessage(JSON.parse(body) as JsonRpcMessage)
|
|
104
|
+
} catch {
|
|
105
|
+
// ignore malformed frame
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function languageId(ext: string): string {
|
|
112
|
+
const server = LANGUAGES.find((entry) => entry.extensions.includes(ext))
|
|
113
|
+
return server?.language ?? ext.replace(/^\./, "")
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function run(filePath: string, timeoutMs: number, configPath: string): Promise<number> {
|
|
117
|
+
const absolute = isAbsolute(filePath) ? filePath : resolve(process.cwd(), filePath)
|
|
118
|
+
const ext = extname(absolute).toLowerCase()
|
|
119
|
+
const server = resolveServer(ext, configPath)
|
|
120
|
+
|
|
121
|
+
if (server === null) {
|
|
122
|
+
process.stderr.write(`SKIP ${absolute}: no language server known for "${ext}". See references/.\n`)
|
|
123
|
+
return 3
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const [bin, ...binArgs] = server.command
|
|
127
|
+
if (bin === undefined) {
|
|
128
|
+
process.stderr.write(`SKIP ${absolute}: empty command for ${server.language}.\n`)
|
|
129
|
+
return 3
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const text = readFileSync(absolute, "utf-8")
|
|
133
|
+
const uri = pathToFileURL(absolute).href
|
|
134
|
+
|
|
135
|
+
return await new Promise<number>((resolveResult) => {
|
|
136
|
+
let child
|
|
137
|
+
try {
|
|
138
|
+
child = spawn(bin, binArgs, { stdio: ["pipe", "pipe", "pipe"] })
|
|
139
|
+
} catch {
|
|
140
|
+
process.stdout.write(`FAIL ${absolute}: language server not installed (${bin})\n`)
|
|
141
|
+
resolveResult(1)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let settled = false
|
|
146
|
+
let stderr = ""
|
|
147
|
+
const finish = (code: number, line: string): void => {
|
|
148
|
+
if (settled) return
|
|
149
|
+
settled = true
|
|
150
|
+
clearTimeout(timer)
|
|
151
|
+
try {
|
|
152
|
+
child.kill("SIGKILL")
|
|
153
|
+
} catch {
|
|
154
|
+
/* already gone */
|
|
155
|
+
}
|
|
156
|
+
process.stdout.write(`${line}\n`)
|
|
157
|
+
resolveResult(code)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const timer = setTimeout(() => {
|
|
161
|
+
finish(1, `FAIL ${absolute}: timed out after ${timeoutMs}ms waiting for diagnostics`)
|
|
162
|
+
}, timeoutMs)
|
|
163
|
+
|
|
164
|
+
child.on("error", (error: NodeJS.ErrnoException) => {
|
|
165
|
+
if (error.code === "ENOENT") {
|
|
166
|
+
finish(1, `FAIL ${absolute}: language server not installed (${bin})`)
|
|
167
|
+
} else {
|
|
168
|
+
finish(1, `FAIL ${absolute}: ${error.message}`)
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
child.on("exit", (code) => {
|
|
172
|
+
if (!settled) {
|
|
173
|
+
finish(1, `FAIL ${absolute}: server exited (code ${code ?? "?"})\n${stderr.trim()}`)
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
child.stderr.on("data", (chunk: Buffer) => {
|
|
177
|
+
stderr += chunk.toString("utf-8")
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
const send = (message: unknown): void => {
|
|
181
|
+
child.stdin.write(encode(message))
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const read = makeReader((message) => {
|
|
185
|
+
if (message.id === 1 && message.result !== undefined) {
|
|
186
|
+
send({ jsonrpc: "2.0", method: "initialized", params: {} })
|
|
187
|
+
send({
|
|
188
|
+
jsonrpc: "2.0",
|
|
189
|
+
method: "textDocument/didOpen",
|
|
190
|
+
params: { textDocument: { uri, languageId: languageId(ext), version: 1, text } },
|
|
191
|
+
})
|
|
192
|
+
}
|
|
193
|
+
if (message.method === "textDocument/publishDiagnostics" && isRecord(message.params)) {
|
|
194
|
+
const diagnostics = message.params["diagnostics"]
|
|
195
|
+
const count = Array.isArray(diagnostics) ? diagnostics.length : 0
|
|
196
|
+
if (message.params["uri"] === uri) {
|
|
197
|
+
finish(0, `OK ${absolute}: LSP roundtrip succeeded (${count} diagnostic(s)) via ${server.language}`)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
child.stdout.on("data", read)
|
|
202
|
+
|
|
203
|
+
send({
|
|
204
|
+
jsonrpc: "2.0",
|
|
205
|
+
id: 1,
|
|
206
|
+
method: "initialize",
|
|
207
|
+
params: {
|
|
208
|
+
processId: process.pid,
|
|
209
|
+
rootUri: pathToFileURL(process.cwd()).href,
|
|
210
|
+
capabilities: { textDocument: { publishDiagnostics: {} } },
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function parseTimeout(args: readonly string[]): number {
|
|
217
|
+
const flag = args.find((arg) => arg.startsWith("--timeout="))
|
|
218
|
+
if (flag === undefined) return DEFAULT_TIMEOUT_MS
|
|
219
|
+
const parsed = Number.parseInt(flag.slice("--timeout=".length), 10)
|
|
220
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TIMEOUT_MS
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function main(): Promise<void> {
|
|
224
|
+
const args = process.argv.slice(2)
|
|
225
|
+
const configFlag = args.find((arg) => arg.startsWith("--config="))
|
|
226
|
+
const configPath = configFlag ? configFlag.slice("--config=".length) : LSP_CONFIG_FILE
|
|
227
|
+
const filePath = args.find((arg) => !arg.startsWith("--"))
|
|
228
|
+
|
|
229
|
+
if (filePath === undefined) {
|
|
230
|
+
process.stderr.write("Usage: verify-lsp.ts <file> [--timeout=ms] [--config=<path>]\n")
|
|
231
|
+
process.exit(2)
|
|
232
|
+
}
|
|
233
|
+
if (!existsSync(filePath) || !statSync(filePath).isFile()) {
|
|
234
|
+
process.stderr.write(`verify-lsp: not a file: ${filePath}\n`)
|
|
235
|
+
process.exit(2)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const code = await run(filePath, parseTimeout(args), configPath)
|
|
239
|
+
process.exit(code)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
await main()
|
|
@@ -25,19 +25,18 @@ Before the first edit:
|
|
|
25
25
|
|
|
26
26
|
## Native Goal + Dynamic Workflow
|
|
27
27
|
|
|
28
|
-
Before the first checkbox,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
help the user keep the session bounded.
|
|
28
|
+
Before the first checkbox, bind the native goal. Claude Code's `/goal` (v2.1.139+) is a
|
|
29
|
+
**user-typed slash command**, not a model-facing tool, and a hook/skill **cannot invoke it**:
|
|
30
|
+
|
|
31
|
+
- For a long plan, offer the user one ready-to-paste `/goal <plan completion condition>` line —
|
|
32
|
+
this is the real way to bind the native session goal — and keep the Boulder/ledger discipline.
|
|
33
|
+
- Forward-compat only: if a future Claude Code build exposes model-facing goal tools, call
|
|
34
|
+
`get_goal` if exposed, `create_goal` with the plan objective only when no matching active goal
|
|
35
|
+
exists, and `update_goal` only after all top-level checkboxes and final gates are complete (or
|
|
36
|
+
when the plan is genuinely blocked). These are not exposed today.
|
|
37
|
+
- Do not auto-type or send `/goal` text from a skill; if the user invokes `/goal`, respect it.
|
|
38
|
+
|
|
39
|
+
If model-facing goal tools are absent (the case today), continue with Boulder/ledger discipline.
|
|
41
40
|
If goal tools are not exposed, do not print repeated fallback status; just keep
|
|
42
41
|
the plan ledger accurate and continue with verified evidence.
|
|
43
42
|
|