@zoisythe/codex-codeintel 0.10.0 → 0.10.1
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/.codex-plugin/plugin.json +3 -3
- package/CHANGELOG.md +17 -0
- package/README.md +19 -9
- package/dist/cli.js +1116 -253
- package/docs/testing/hooks-host.md +9 -9
- package/docs/testing/local-lsp.md +81 -0
- package/docs/usage.md +145 -99
- package/package.json +15 -10
- package/skills/code-intelligence/SKILL.md +14 -3
- package/skills/setup-lsp/references/configuration.md +5 -3
package/dist/cli.js
CHANGED
|
@@ -28,6 +28,13 @@ var init_budgets = __esm({
|
|
|
28
28
|
merge: 200,
|
|
29
29
|
mergeMax: 1e3,
|
|
30
30
|
warmupDelay: 2e3,
|
|
31
|
+
warmup: 2e4,
|
|
32
|
+
planTtl: 30 * 60 * 1e3,
|
|
33
|
+
validation: 3e4,
|
|
34
|
+
validationServer: 1e4,
|
|
35
|
+
validationExtraFiles: 20,
|
|
36
|
+
validationFiles: 20,
|
|
37
|
+
validationBytes: 2 * 1024 * 1024,
|
|
31
38
|
idle: 3e5,
|
|
32
39
|
cleanup: 1e3,
|
|
33
40
|
postWait: 3500,
|
|
@@ -535,9 +542,10 @@ var init_results = __esm({
|
|
|
535
542
|
"src/results.ts"() {
|
|
536
543
|
"use strict";
|
|
537
544
|
WriteFailure = class extends Error {
|
|
538
|
-
constructor(reason, modifiedPaths) {
|
|
545
|
+
constructor(reason, modifiedPaths, planFailure) {
|
|
539
546
|
super(reason);
|
|
540
547
|
this.modifiedPaths = modifiedPaths;
|
|
548
|
+
this.planFailure = planFailure;
|
|
541
549
|
}
|
|
542
550
|
};
|
|
543
551
|
}
|
|
@@ -618,7 +626,7 @@ function server(value, language, source) {
|
|
|
618
626
|
const item = builtin ?? value;
|
|
619
627
|
if (!record(item)) throw new Error(`lsp.${language} must be a built-in name, custom server or false`);
|
|
620
628
|
for (const key of Object.keys(item))
|
|
621
|
-
if (!["command", "extensions", "env", "initialization"].includes(key))
|
|
629
|
+
if (!["command", "extensions", "env", "initialization", "settings", "languageId"].includes(key))
|
|
622
630
|
throw new Error(`Invalid lsp.${language}.${key}; priority/disabled were removed; use false to disable`);
|
|
623
631
|
const command = item["command"];
|
|
624
632
|
const extensions = item["extensions"];
|
|
@@ -628,10 +636,15 @@ function server(value, language, source) {
|
|
|
628
636
|
throw new Error(`lsp.${language}.extensions requires dot-prefixed extensions`);
|
|
629
637
|
const env = item["env"];
|
|
630
638
|
const initialization = item["initialization"];
|
|
639
|
+
const settings = item["settings"];
|
|
640
|
+
const languageId = item["languageId"];
|
|
631
641
|
if (env !== void 0 && (!record(env) || !Object.values(env).every((v) => typeof v === "string")))
|
|
632
642
|
throw new Error(`Invalid lsp.${language}.env`);
|
|
633
643
|
if (initialization !== void 0 && !record(initialization))
|
|
634
644
|
throw new Error(`Invalid lsp.${language}.initialization`);
|
|
645
|
+
if (settings !== void 0 && !record(settings)) throw new Error(`Invalid lsp.${language}.settings`);
|
|
646
|
+
if (languageId !== void 0 && (typeof languageId !== "string" || !languageId.trim()))
|
|
647
|
+
throw new Error(`Invalid lsp.${language}.languageId`);
|
|
635
648
|
return {
|
|
636
649
|
id: typeof value === "string" ? value : language,
|
|
637
650
|
command,
|
|
@@ -639,7 +652,9 @@ function server(value, language, source) {
|
|
|
639
652
|
explicit: !builtin,
|
|
640
653
|
source,
|
|
641
654
|
...env ? { env } : {},
|
|
642
|
-
...initialization ? { initialization } : {}
|
|
655
|
+
...initialization ? { initialization } : {},
|
|
656
|
+
...settings ? { settings } : {},
|
|
657
|
+
...typeof languageId === "string" ? { languageId } : {}
|
|
643
658
|
};
|
|
644
659
|
}
|
|
645
660
|
function freeze(value) {
|
|
@@ -1284,9 +1299,9 @@ function validateCwd(cwd) {
|
|
|
1284
1299
|
}
|
|
1285
1300
|
}
|
|
1286
1301
|
function wrap(proc) {
|
|
1287
|
-
const exitedPromise = new Promise((
|
|
1288
|
-
proc.once("close", (code) =>
|
|
1289
|
-
proc.once("error", () =>
|
|
1302
|
+
const exitedPromise = new Promise((resolve16) => {
|
|
1303
|
+
proc.once("close", (code) => resolve16(code ?? 0));
|
|
1304
|
+
proc.once("error", () => resolve16(1));
|
|
1290
1305
|
});
|
|
1291
1306
|
if (!proc.stdin || !proc.stdout || !proc.stderr) {
|
|
1292
1307
|
throw new LspProcessSpawnError("Spawned process is missing one of stdin/stdout/stderr pipes");
|
|
@@ -1573,11 +1588,11 @@ async function findTool(root, path, command, explicit = false, allowTemporary =
|
|
|
1573
1588
|
const name = command[0];
|
|
1574
1589
|
if (!name) throw new Error("Empty command");
|
|
1575
1590
|
const executableName = basename3(name).replace(/\.(?:exe|cmd|bat)$/i, "");
|
|
1576
|
-
if (automaticExecution() && (["uvx", "npx", "pipx"].includes(executableName) || executableName === "uv" && command[1] === "tool" && command[2] === "run"))
|
|
1591
|
+
if ((automaticExecution() || !allowTemporary) && (["uvx", "npx", "pipx"].includes(executableName) || executableName === "uv" && command[1] === "tool" && command[2] === "run"))
|
|
1577
1592
|
return resolution(
|
|
1578
1593
|
[...command],
|
|
1579
1594
|
"missing",
|
|
1580
|
-
|
|
1595
|
+
`${automaticExecution() ? "Automatic" : "Local-only"} temporary launcher disabled: ${name}; install a local server`
|
|
1581
1596
|
);
|
|
1582
1597
|
if (explicit) {
|
|
1583
1598
|
const entry = isAbsolute4(name) || name.includes("/") || name.includes("\\") ? resolve2(root, name) : await pathExecutable(name);
|
|
@@ -1638,10 +1653,10 @@ function languageFor(config, path) {
|
|
|
1638
1653
|
if (server2 !== false && server2.extensions.includes(extname(path))) return { language, server: server2 };
|
|
1639
1654
|
return void 0;
|
|
1640
1655
|
}
|
|
1641
|
-
async function resolveServer(root, path, config) {
|
|
1656
|
+
async function resolveServer(root, path, config, allowTemporary = true) {
|
|
1642
1657
|
const entry = languageFor(config, path);
|
|
1643
1658
|
if (!entry) throw new Error(`Language disabled or unsupported: ${extname(path) || path}`);
|
|
1644
|
-
const tool2 = await resolveTool(root, path, entry.server.command, entry.server.explicit);
|
|
1659
|
+
const tool2 = await resolveTool(root, path, entry.server.command, entry.server.explicit, allowTemporary);
|
|
1645
1660
|
return {
|
|
1646
1661
|
language: entry.language,
|
|
1647
1662
|
tool: tool2,
|
|
@@ -1651,7 +1666,9 @@ async function resolveServer(root, path, config) {
|
|
|
1651
1666
|
extensions: [...entry.server.extensions],
|
|
1652
1667
|
priority: 0,
|
|
1653
1668
|
...entry.server.env ? { env: { ...entry.server.env } } : {},
|
|
1654
|
-
...entry.server.initialization ? { initialization: { ...entry.server.initialization } } : {}
|
|
1669
|
+
...entry.server.initialization ? { initialization: { ...entry.server.initialization } } : {},
|
|
1670
|
+
...entry.server.settings ? { settings: { ...entry.server.settings } } : {},
|
|
1671
|
+
...entry.server.languageId ? { languageId: entry.server.languageId } : {}
|
|
1655
1672
|
}
|
|
1656
1673
|
};
|
|
1657
1674
|
}
|
|
@@ -1690,7 +1707,7 @@ import { basename as basename4, dirname as dirname4, extname as extname2, join a
|
|
|
1690
1707
|
async function run(command, args, cwd, signal, input, options = {}) {
|
|
1691
1708
|
signal.throwIfAborted();
|
|
1692
1709
|
const environment = subprocessEnvironment(options.environment ?? executionEnvironment());
|
|
1693
|
-
return new Promise((
|
|
1710
|
+
return new Promise((resolve16, reject) => {
|
|
1694
1711
|
const prepared = createSpawnCommand([command, ...args], process.platform, environment["ComSpec"], environment);
|
|
1695
1712
|
const child = spawn2(prepared.command, prepared.args, {
|
|
1696
1713
|
...HIDDEN_PROCESS,
|
|
@@ -1733,13 +1750,49 @@ async function run(command, args, cwd, signal, input, options = {}) {
|
|
|
1733
1750
|
if (code !== 0 && code !== 1 && !signal.aborted) void logEvent("abnormal-exit");
|
|
1734
1751
|
if (signal.aborted) reject(new Error("Runner cancelled"));
|
|
1735
1752
|
else if (exceeded) reject(new Error("Runner exceeded time/output budget"));
|
|
1736
|
-
else
|
|
1753
|
+
else resolve16({ stdout, stderr, code: code ?? -1 });
|
|
1737
1754
|
});
|
|
1738
1755
|
child.stdin.on("error", () => {
|
|
1739
1756
|
});
|
|
1740
1757
|
child.stdin.end(input);
|
|
1741
1758
|
});
|
|
1742
1759
|
}
|
|
1760
|
+
async function lintContent(runner, root, path, content, signal) {
|
|
1761
|
+
if (runner.name === "biome") throw new Error("unsupported: Biome diagnostics require a private project snapshot");
|
|
1762
|
+
const absolute = await workspacePath(root, path);
|
|
1763
|
+
const cwd = await lintDirectory(root, absolute);
|
|
1764
|
+
if (runner.name === "eslint") {
|
|
1765
|
+
const printed = await run(runner.command, [...runner.prefix, "--print-config", absolute], cwd, signal);
|
|
1766
|
+
if (printed.code !== 0) throw new Error(printed.stderr || "ESLint configuration unavailable");
|
|
1767
|
+
const config = JSON.parse(printed.stdout);
|
|
1768
|
+
if (!record(config)) throw new Error("unsupported: ESLint file has no matching configuration");
|
|
1769
|
+
const options = record(config["languageOptions"]) ? config["languageOptions"]["parserOptions"] : config["parserOptions"];
|
|
1770
|
+
if (record(options) && (options["project"] || options["projectService"]))
|
|
1771
|
+
throw new Error(
|
|
1772
|
+
"unsupported: type-aware ESLint project services cannot consume all plan overlays; use TypeScript diagnostics for types"
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
const args = runner.name === "ruff" ? [
|
|
1776
|
+
"check",
|
|
1777
|
+
"--no-fix",
|
|
1778
|
+
"--no-fix-only",
|
|
1779
|
+
"--no-cache",
|
|
1780
|
+
"--output-format",
|
|
1781
|
+
"json",
|
|
1782
|
+
"--stdin-filename",
|
|
1783
|
+
absolute,
|
|
1784
|
+
"-"
|
|
1785
|
+
] : ["--stdin", "--stdin-filename", absolute, "--format", "json", "--no-warn-ignored"];
|
|
1786
|
+
const output = await run(runner.command, [...runner.prefix, ...args], cwd, signal, content);
|
|
1787
|
+
if (![0, 1].includes(output.code)) throw new Error(output.stderr || `Runner exit ${output.code}`);
|
|
1788
|
+
const data = JSON.parse(output.stdout);
|
|
1789
|
+
if (runner.name === "eslint" && (!Array.isArray(data) || data.length !== 1))
|
|
1790
|
+
throw new Error("unsupported: ESLint ignored the file or produced no report");
|
|
1791
|
+
const findings = parseLint(runner.name, data, path, content);
|
|
1792
|
+
if (findings.some((finding) => /ignored|not processed|configuration.*not found/i.test(finding.message)))
|
|
1793
|
+
throw new Error("unsupported: lint file ignored or configuration unavailable");
|
|
1794
|
+
return findings.map(({ lint: _lint, ...finding }) => finding);
|
|
1795
|
+
}
|
|
1743
1796
|
async function exists(path) {
|
|
1744
1797
|
try {
|
|
1745
1798
|
await access3(path);
|
|
@@ -2981,6 +3034,9 @@ import { randomUUID as randomUUID5 } from "node:crypto";
|
|
|
2981
3034
|
import { lstat as lstat4, readFile as readFile11, writeFile as writeFile4 } from "node:fs/promises";
|
|
2982
3035
|
import { relative as relative4 } from "node:path";
|
|
2983
3036
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3037
|
+
function isPlanFailureCode(value) {
|
|
3038
|
+
return typeof value === "string" && ["consumed", "expired", "evicted", "unavailable", "stale", "conflict"].includes(value);
|
|
3039
|
+
}
|
|
2984
3040
|
async function fileIdentity(path) {
|
|
2985
3041
|
const info = await lstat4(path);
|
|
2986
3042
|
if (!info.isFile() || info.size > BUDGET.fileBytes) throw new Error("Unsupported or oversized edit target");
|
|
@@ -3038,22 +3094,49 @@ function diffLines(file) {
|
|
|
3038
3094
|
...after.slice(start, endAfter).map((line) => `+${line}`)
|
|
3039
3095
|
];
|
|
3040
3096
|
}
|
|
3041
|
-
var EditPlans;
|
|
3097
|
+
var PlanFailure, EditPlans;
|
|
3042
3098
|
var init_edit_plans = __esm({
|
|
3043
3099
|
"src/edit-plans.ts"() {
|
|
3044
3100
|
"use strict";
|
|
3045
3101
|
init_budgets();
|
|
3046
3102
|
init_files();
|
|
3047
3103
|
init_results();
|
|
3104
|
+
PlanFailure = class extends Error {
|
|
3105
|
+
constructor(planFailure, reason) {
|
|
3106
|
+
super(reason);
|
|
3107
|
+
this.planFailure = planFailure;
|
|
3108
|
+
}
|
|
3109
|
+
};
|
|
3048
3110
|
EditPlans = class {
|
|
3049
|
-
constructor() {
|
|
3111
|
+
constructor(now = Date.now) {
|
|
3112
|
+
this.now = now;
|
|
3050
3113
|
this.plans = /* @__PURE__ */ new Map();
|
|
3114
|
+
this.ended = /* @__PURE__ */ new Map();
|
|
3115
|
+
}
|
|
3116
|
+
remove(id, reason) {
|
|
3117
|
+
this.plans.delete(id);
|
|
3118
|
+
this.ended.set(id, { reason, until: this.now() + BUDGET.planTtl });
|
|
3119
|
+
while (this.ended.size > 64) this.ended.delete(this.ended.keys().next().value ?? "");
|
|
3120
|
+
}
|
|
3121
|
+
prune() {
|
|
3122
|
+
const now = this.now();
|
|
3123
|
+
for (const [id, entry] of this.ended) if (entry.until <= now) this.ended.delete(id);
|
|
3124
|
+
for (const [id, plan] of this.plans) if (Date.parse(plan.expiresAt) <= now) this.remove(id, "expired");
|
|
3125
|
+
}
|
|
3126
|
+
hasLivePlans() {
|
|
3127
|
+
this.prune();
|
|
3128
|
+
return [...this.plans.values()].some((plan) => plan.files.length > 0);
|
|
3051
3129
|
}
|
|
3052
3130
|
create(identity, files, paths = files.map((file) => file.path)) {
|
|
3131
|
+
this.prune();
|
|
3132
|
+
const now = this.now();
|
|
3053
3133
|
const plan = {
|
|
3054
3134
|
id: randomUUID5(),
|
|
3055
3135
|
identity,
|
|
3056
3136
|
paths,
|
|
3137
|
+
createdAt: new Date(now).toISOString(),
|
|
3138
|
+
expiresAt: new Date(now + BUDGET.planTtl).toISOString(),
|
|
3139
|
+
storage: "memory",
|
|
3057
3140
|
files: files.filter((file) => file.before !== file.after).sort((a, b) => a.path.localeCompare(b.path))
|
|
3058
3141
|
};
|
|
3059
3142
|
if (files.length > 200 || files.reduce((sum, file) => sum + Buffer.byteLength(file.before + file.after), 0) > 8 * BUDGET.outputBytes)
|
|
@@ -3061,29 +3144,37 @@ var init_edit_plans = __esm({
|
|
|
3061
3144
|
this.plans.set(plan.id, plan);
|
|
3062
3145
|
const memory = () => [...this.plans.values()].flatMap((plan2) => plan2.files).reduce((sum, file) => sum + Buffer.byteLength(file.before + file.after), 0);
|
|
3063
3146
|
while (this.plans.size > 16 || memory() > 8 * BUDGET.outputBytes)
|
|
3064
|
-
this.
|
|
3147
|
+
this.remove(this.plans.keys().next().value ?? "", "evicted");
|
|
3065
3148
|
return plan;
|
|
3066
3149
|
}
|
|
3067
3150
|
get(id) {
|
|
3151
|
+
this.prune();
|
|
3068
3152
|
const plan = this.plans.get(id);
|
|
3069
|
-
if (!plan)
|
|
3153
|
+
if (!plan) {
|
|
3154
|
+
const reason = this.ended.get(id)?.reason ?? "unavailable";
|
|
3155
|
+
throw new PlanFailure(reason, `Plan missing: ${reason}; generate a new preview`);
|
|
3156
|
+
}
|
|
3070
3157
|
return plan;
|
|
3071
3158
|
}
|
|
3072
3159
|
clear() {
|
|
3073
3160
|
this.plans.clear();
|
|
3161
|
+
this.ended.clear();
|
|
3074
3162
|
}
|
|
3075
|
-
discard(id) {
|
|
3076
|
-
this.plans.
|
|
3163
|
+
discard(id, reason = "stale") {
|
|
3164
|
+
if (this.plans.has(id)) this.remove(id, reason);
|
|
3077
3165
|
}
|
|
3078
3166
|
async apply(id, identity, root, signal) {
|
|
3079
3167
|
const plan = this.get(id);
|
|
3080
|
-
this.
|
|
3168
|
+
this.remove(id, "consumed");
|
|
3081
3169
|
if (plan.identity !== identity)
|
|
3082
|
-
throw new
|
|
3170
|
+
throw new PlanFailure(
|
|
3171
|
+
"stale",
|
|
3172
|
+
"Plan stale: workspace, configuration, tools or environment changed; preview again"
|
|
3173
|
+
);
|
|
3083
3174
|
const validate = async (file) => {
|
|
3084
3175
|
const actual = await workspacePath(root, file.path);
|
|
3085
3176
|
if (actual !== file.absolute || await fileIdentity(actual) !== file.fileIdentity || await readFile11(actual, "utf8") !== file.before)
|
|
3086
|
-
throw new
|
|
3177
|
+
throw new PlanFailure("conflict", `Plan conflict: ${file.path}`);
|
|
3087
3178
|
};
|
|
3088
3179
|
for (const file of plan.files) {
|
|
3089
3180
|
signal.throwIfAborted();
|
|
@@ -3104,7 +3195,7 @@ var init_edit_plans = __esm({
|
|
|
3104
3195
|
} catch (error) {
|
|
3105
3196
|
if (writing && !modified.includes(writing.path) && await readFile11(writing.absolute, "utf8").catch(() => null) !== writing.before)
|
|
3106
3197
|
modified.push(writing.path);
|
|
3107
|
-
throw new WriteFailure(message(error), modified);
|
|
3198
|
+
throw new WriteFailure(message(error), modified, error instanceof PlanFailure ? error.planFailure : void 0);
|
|
3108
3199
|
}
|
|
3109
3200
|
return modified;
|
|
3110
3201
|
}
|
|
@@ -3179,6 +3270,10 @@ var init_output_pages = __esm({
|
|
|
3179
3270
|
"state",
|
|
3180
3271
|
"job",
|
|
3181
3272
|
"plan",
|
|
3273
|
+
"createdAt",
|
|
3274
|
+
"expiresAt",
|
|
3275
|
+
"storage",
|
|
3276
|
+
"planFailure",
|
|
3182
3277
|
"partial",
|
|
3183
3278
|
"errors",
|
|
3184
3279
|
"warnings",
|
|
@@ -3233,6 +3328,66 @@ var init_output_pages = __esm({
|
|
|
3233
3328
|
}
|
|
3234
3329
|
});
|
|
3235
3330
|
|
|
3331
|
+
// src/plan-validation.ts
|
|
3332
|
+
function diagnosticFindings(path, items2) {
|
|
3333
|
+
return items2.filter((item) => item.severity === 1 || item.severity === 2).map((item) => ({
|
|
3334
|
+
path,
|
|
3335
|
+
line: item.range.start.line + 1,
|
|
3336
|
+
column: item.range.start.character + 1,
|
|
3337
|
+
severity: item.severity === 1 ? "error" : "warning",
|
|
3338
|
+
source: `${item.source ?? "lsp"}${item.code === void 0 ? "" : `/${item.code}`}`,
|
|
3339
|
+
...item.code !== void 0 ? { rule: String(item.code) } : {},
|
|
3340
|
+
endLine: item.range.end.line + 1,
|
|
3341
|
+
endColumn: item.range.end.character + 1,
|
|
3342
|
+
message: item.message
|
|
3343
|
+
}));
|
|
3344
|
+
}
|
|
3345
|
+
function validationResult(state, paths, reason, before = [], after = [], provider = "ty") {
|
|
3346
|
+
const introduced = addedFindings(before, after);
|
|
3347
|
+
const resolved = addedFindings(after, before);
|
|
3348
|
+
const errorsIntroduced = state === "complete" ? introduced.filter((item) => item.severity === "error").length : null;
|
|
3349
|
+
const errorsResolved = state === "complete" ? resolved.filter((item) => item.severity === "error").length : null;
|
|
3350
|
+
return {
|
|
3351
|
+
state,
|
|
3352
|
+
provider,
|
|
3353
|
+
paths,
|
|
3354
|
+
introduced,
|
|
3355
|
+
resolved,
|
|
3356
|
+
errorsIntroduced,
|
|
3357
|
+
errorsResolved,
|
|
3358
|
+
netDelta: errorsIntroduced === null || errorsResolved === null ? null : errorsIntroduced - errorsResolved,
|
|
3359
|
+
limitations: [
|
|
3360
|
+
"Only listed files and this provider were checked; builds and runtime behavior were not validated. Zero delta is not a safety guarantee."
|
|
3361
|
+
],
|
|
3362
|
+
...reason ? { reason } : {}
|
|
3363
|
+
};
|
|
3364
|
+
}
|
|
3365
|
+
function combineValidation(paths, checks) {
|
|
3366
|
+
const active = checks.filter((check) => check.state !== "skipped");
|
|
3367
|
+
const complete = active.filter((check) => check.state === "complete");
|
|
3368
|
+
const state = complete.length === active.length && complete.length ? "complete" : complete.length ? "partial" : active.some((check) => check.state === "pending") ? "pending" : active.some((check) => check.state === "failed") ? "failed" : active.length ? "unsupported" : "skipped";
|
|
3369
|
+
const result = validationResult(state, paths, void 0, [], [], "combined");
|
|
3370
|
+
result.checks = checks;
|
|
3371
|
+
result.introduced = complete.flatMap((check) => check.introduced);
|
|
3372
|
+
result.resolved = complete.flatMap((check) => check.resolved);
|
|
3373
|
+
if (state === "complete") {
|
|
3374
|
+
result.errorsIntroduced = complete.reduce((sum, check) => sum + (check.errorsIntroduced ?? 0), 0);
|
|
3375
|
+
result.errorsResolved = complete.reduce((sum, check) => sum + (check.errorsResolved ?? 0), 0);
|
|
3376
|
+
result.netDelta = result.errorsIntroduced - result.errorsResolved;
|
|
3377
|
+
}
|
|
3378
|
+
result.limitations = [
|
|
3379
|
+
"Only changed files and explicit validationPaths were checked; other dependents, builds and runtime behavior were not validated.",
|
|
3380
|
+
"Counts are provider observations; different providers can report the same problem. Consult checks for per-provider coverage and completeness."
|
|
3381
|
+
];
|
|
3382
|
+
return result;
|
|
3383
|
+
}
|
|
3384
|
+
var init_plan_validation = __esm({
|
|
3385
|
+
"src/plan-validation.ts"() {
|
|
3386
|
+
"use strict";
|
|
3387
|
+
init_diagnostic_delta();
|
|
3388
|
+
}
|
|
3389
|
+
});
|
|
3390
|
+
|
|
3236
3391
|
// src/symbols.ts
|
|
3237
3392
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
3238
3393
|
import { relative as relative5 } from "node:path";
|
|
@@ -3397,6 +3552,7 @@ var init_intelligence = __esm({
|
|
|
3397
3552
|
init_edit_plans();
|
|
3398
3553
|
init_files();
|
|
3399
3554
|
init_output_pages();
|
|
3555
|
+
init_plan_validation();
|
|
3400
3556
|
init_results();
|
|
3401
3557
|
init_runners();
|
|
3402
3558
|
init_symbols();
|
|
@@ -3429,14 +3585,33 @@ var init_intelligence = __esm({
|
|
|
3429
3585
|
while (this.actions.size > 512) this.actions.delete(this.actions.keys().next().value ?? "");
|
|
3430
3586
|
return value;
|
|
3431
3587
|
}
|
|
3432
|
-
preview(identity, files) {
|
|
3588
|
+
async preview(identity, files, args, signal) {
|
|
3433
3589
|
const plan = this.plans.create(identity, files, this.scope);
|
|
3590
|
+
let validation2;
|
|
3591
|
+
try {
|
|
3592
|
+
validation2 = args["validate"] === false ? validationResult(
|
|
3593
|
+
"skipped",
|
|
3594
|
+
plan.files.map((file) => file.path),
|
|
3595
|
+
"Validation explicitly disabled"
|
|
3596
|
+
) : await this.language.validatePlan(
|
|
3597
|
+
plan.files,
|
|
3598
|
+
signal,
|
|
3599
|
+
Array.isArray(args["validationPaths"]) ? args["validationPaths"].filter((path) => typeof path === "string") : []
|
|
3600
|
+
);
|
|
3601
|
+
} catch (error) {
|
|
3602
|
+
this.plans.discard(plan.id);
|
|
3603
|
+
throw error;
|
|
3604
|
+
}
|
|
3434
3605
|
return {
|
|
3435
3606
|
status: plan.files.length ? "planned" : "unchanged",
|
|
3436
3607
|
plan: plan.id,
|
|
3608
|
+
createdAt: plan.createdAt,
|
|
3609
|
+
expiresAt: plan.expiresAt,
|
|
3610
|
+
storage: plan.storage,
|
|
3437
3611
|
files: plan.files.map((file) => file.path),
|
|
3438
3612
|
diff: plan.files.flatMap(diffLines),
|
|
3439
|
-
|
|
3613
|
+
validation: validation2,
|
|
3614
|
+
note: "Only apply writes this single-use plan. Validation is scoped evidence, not a build or behavior guarantee; restart or eviction may invalidate it before expiry."
|
|
3440
3615
|
};
|
|
3441
3616
|
}
|
|
3442
3617
|
async execute(operation, args, paths, config, identity, signal, bindings = paths) {
|
|
@@ -3446,7 +3621,7 @@ var init_intelligence = __esm({
|
|
|
3446
3621
|
if (operation === "navigate") output = await this.language.navigate(args, signal);
|
|
3447
3622
|
else if (operation === "rename") {
|
|
3448
3623
|
const edit = await this.language.renameEdit(args, signal);
|
|
3449
|
-
output = this.preview(identity, await workspaceEdits(this.root, edit));
|
|
3624
|
+
output = await this.preview(identity, await workspaceEdits(this.root, edit), args, signal);
|
|
3450
3625
|
} else if (operation === "format") {
|
|
3451
3626
|
if (!config.formattingEnabled) throw new Error("Configuration error: formatting disabled");
|
|
3452
3627
|
const files = [];
|
|
@@ -3458,7 +3633,7 @@ var init_intelligence = __esm({
|
|
|
3458
3633
|
const after = formatted?.after ?? applyTextChanges(before, await this.language.formatEdits(path, signal));
|
|
3459
3634
|
files.push(await plannedFile(this.root, path, after, before));
|
|
3460
3635
|
}
|
|
3461
|
-
output = this.preview(identity, files);
|
|
3636
|
+
output = await this.preview(identity, files, args, signal);
|
|
3462
3637
|
} else if (operation === "inspect")
|
|
3463
3638
|
output = args["operation"] === "impact" ? await this.impact(args, paths, signal) : await this.inspect(args, signal);
|
|
3464
3639
|
else if (operation === "actions") output = await this.actionRequest(args, paths, config, identity, signal);
|
|
@@ -3755,7 +3930,10 @@ ${String(docs["url"])}` : "") + "\nConfiguration source origin is unknown.";
|
|
|
3755
3930
|
group.before
|
|
3756
3931
|
)
|
|
3757
3932
|
);
|
|
3758
|
-
return {
|
|
3933
|
+
return {
|
|
3934
|
+
...await this.preview(identity, files, args, signal),
|
|
3935
|
+
...issues.length ? { partial: true, issues } : {}
|
|
3936
|
+
};
|
|
3759
3937
|
}
|
|
3760
3938
|
return this.listActions(args, paths, config, identity, signal);
|
|
3761
3939
|
}
|
|
@@ -3904,6 +4082,198 @@ ${String(docs["url"])}` : "") + "\nConfiguration source origin is unknown.";
|
|
|
3904
4082
|
}
|
|
3905
4083
|
});
|
|
3906
4084
|
|
|
4085
|
+
// src/preview-workspace.ts
|
|
4086
|
+
import { access as access4, mkdir as mkdir5, mkdtemp, readdir as readdir4, readFile as readFile14, rm as rm4, stat as stat5, symlink, writeFile as writeFile5 } from "node:fs/promises";
|
|
4087
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
4088
|
+
import { dirname as dirname6, join as join11, relative as relative7, resolve as resolve8, sep as sep2 } from "node:path";
|
|
4089
|
+
async function withPreviewWorkspace(root, files, config, signal, use) {
|
|
4090
|
+
const snapshot = await inventory(root, 2e3, signal, config.exclude);
|
|
4091
|
+
if (!snapshot.complete) throw new Error("unsupported: preview snapshot exceeds 2000 files or is incomplete");
|
|
4092
|
+
const temporary2 = await mkdtemp(join11(tmpdir3(), "codeintel-preview-"));
|
|
4093
|
+
const shadow = join11(temporary2, "workspace");
|
|
4094
|
+
const map = (path) => {
|
|
4095
|
+
const absolute = resolve8(root, path);
|
|
4096
|
+
if (!inside(root, absolute)) throw new Error("Preview path outside workspace");
|
|
4097
|
+
return join11(shadow, relative7(root, absolute));
|
|
4098
|
+
};
|
|
4099
|
+
const translate = (text2) => {
|
|
4100
|
+
for (const [from, to] of [
|
|
4101
|
+
[root.replaceAll("\\", "/"), shadow.replaceAll("\\", "/")],
|
|
4102
|
+
[root.replaceAll("\\", "\\\\"), shadow.replaceAll("\\", "\\\\")],
|
|
4103
|
+
[root, shadow]
|
|
4104
|
+
]) {
|
|
4105
|
+
text2 = text2 === from ? to : text2;
|
|
4106
|
+
text2 = text2.replaceAll(`${from}/`, `${to}/`).replaceAll(`${from}\\`, `${to}\\`).replaceAll(`"${from}"`, `"${to}"`).replaceAll(`=${from}`, `=${to}`);
|
|
4107
|
+
}
|
|
4108
|
+
return text2;
|
|
4109
|
+
};
|
|
4110
|
+
try {
|
|
4111
|
+
await mkdir5(shadow);
|
|
4112
|
+
let bytes2 = 0;
|
|
4113
|
+
const selected = /* @__PURE__ */ new Set([...snapshot.files.keys(), ...files.map((file) => file.path)]);
|
|
4114
|
+
for (const file of files)
|
|
4115
|
+
for (let dir = dirname6(file.absolute); inside(root, dir); dir = dirname6(dir)) {
|
|
4116
|
+
for (const name of [
|
|
4117
|
+
"biome.json",
|
|
4118
|
+
"biome.jsonc",
|
|
4119
|
+
".clangd",
|
|
4120
|
+
"compile_commands.json",
|
|
4121
|
+
"compile_flags.txt",
|
|
4122
|
+
"package.json"
|
|
4123
|
+
])
|
|
4124
|
+
if (await access4(join11(dir, name)).then(
|
|
4125
|
+
() => true,
|
|
4126
|
+
() => false
|
|
4127
|
+
))
|
|
4128
|
+
selected.add(relative7(root, join11(dir, name)));
|
|
4129
|
+
if (dir === root || dirname6(dir) === dir) break;
|
|
4130
|
+
}
|
|
4131
|
+
if (selected.size > 2e3) throw new Error("unsupported: preview snapshot exceeds 2000 files");
|
|
4132
|
+
const names = [...selected];
|
|
4133
|
+
let copied = names.length;
|
|
4134
|
+
for (const path of names) {
|
|
4135
|
+
signal.throwIfAborted();
|
|
4136
|
+
const absolute = await workspacePath(root, path);
|
|
4137
|
+
bytes2 += (await stat5(absolute)).size;
|
|
4138
|
+
if (bytes2 > 32 * 1024 * 1024) throw new Error("unsupported: preview snapshot exceeds 32 MiB");
|
|
4139
|
+
const destination = map(path);
|
|
4140
|
+
await mkdir5(dirname6(destination), { recursive: true });
|
|
4141
|
+
const content = await readFile14(absolute);
|
|
4142
|
+
const configuration2 = /(?:^|[\\/])(?:biome\.jsonc?|\.clangd|compile_commands\.json|compile_flags\.txt)$/.test(
|
|
4143
|
+
path
|
|
4144
|
+
);
|
|
4145
|
+
await writeFile5(destination, configuration2 ? translate(content.toString("utf8")) : content);
|
|
4146
|
+
}
|
|
4147
|
+
for (const name of ["build", "dist", "target", "vendor"]) {
|
|
4148
|
+
const directory2 = join11(root, name);
|
|
4149
|
+
if (!(await stat5(directory2).catch(() => void 0))?.isDirectory()) continue;
|
|
4150
|
+
await mkdir5(map(name), { recursive: true });
|
|
4151
|
+
for (const entry of await readdir4(directory2, { withFileTypes: true })) {
|
|
4152
|
+
signal.throwIfAborted();
|
|
4153
|
+
const path = join11(name, entry.name), destination = map(path);
|
|
4154
|
+
if (await access4(destination).then(
|
|
4155
|
+
() => true,
|
|
4156
|
+
() => false
|
|
4157
|
+
))
|
|
4158
|
+
continue;
|
|
4159
|
+
if (entry.name === "compile_commands.json" || /\.(?:h|hh|hpp|hxx|inc|def|modulemap|json|txt|cmake)$/.test(entry.name)) {
|
|
4160
|
+
if (++copied > 2e3) throw new Error("unsupported: preview snapshot exceeds 2000 copied files");
|
|
4161
|
+
bytes2 += (await stat5(join11(root, path))).size;
|
|
4162
|
+
if (bytes2 > 32 * 1024 * 1024) throw new Error("unsupported: preview snapshot exceeds 32 MiB");
|
|
4163
|
+
const content = await readFile14(join11(root, path), "utf8");
|
|
4164
|
+
await writeFile5(destination, entry.name === "compile_commands.json" ? translate(content) : content);
|
|
4165
|
+
} else if (entry.isDirectory())
|
|
4166
|
+
await symlink(join11(root, path), destination, process.platform === "win32" ? "junction" : "dir");
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
const linked = /* @__PURE__ */ new Set();
|
|
4170
|
+
for (const file of files)
|
|
4171
|
+
for (let dir = dirname6(file.absolute); inside(root, dir); dir = dirname6(dir)) {
|
|
4172
|
+
const dependencies = join11(dir, "node_modules");
|
|
4173
|
+
if (!linked.has(dependencies) && await access4(dependencies).then(
|
|
4174
|
+
() => true,
|
|
4175
|
+
() => false
|
|
4176
|
+
)) {
|
|
4177
|
+
const destination = map(dependencies);
|
|
4178
|
+
await mkdir5(dirname6(destination), { recursive: true });
|
|
4179
|
+
await symlink(dependencies, destination, process.platform === "win32" ? "junction" : "dir");
|
|
4180
|
+
linked.add(dependencies);
|
|
4181
|
+
}
|
|
4182
|
+
if (dir === root || dirname6(dir) === dir || relative7(root, dir).split(sep2).includes("node_modules")) break;
|
|
4183
|
+
}
|
|
4184
|
+
return await use({
|
|
4185
|
+
root: shadow,
|
|
4186
|
+
map,
|
|
4187
|
+
translate,
|
|
4188
|
+
writePhase: async (after) => {
|
|
4189
|
+
for (const file of files) {
|
|
4190
|
+
signal.throwIfAborted();
|
|
4191
|
+
await writeFile5(map(file.path), after ? file.after : file.before);
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
});
|
|
4195
|
+
} finally {
|
|
4196
|
+
await rm4(temporary2, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
var init_preview_workspace = __esm({
|
|
4200
|
+
"src/preview-workspace.ts"() {
|
|
4201
|
+
"use strict";
|
|
4202
|
+
init_files();
|
|
4203
|
+
}
|
|
4204
|
+
});
|
|
4205
|
+
|
|
4206
|
+
// src/lint-preview.ts
|
|
4207
|
+
import { resolve as resolve9 } from "node:path";
|
|
4208
|
+
async function validateBiome(runner, root, scope2, files, config, signal) {
|
|
4209
|
+
return withPreviewWorkspace(root, scope2, config, signal, async (workspace) => {
|
|
4210
|
+
const collect = async (after2) => {
|
|
4211
|
+
await workspace.writePhase(after2);
|
|
4212
|
+
const environment = executionEnvironment();
|
|
4213
|
+
if (environment["BIOME_CONFIG_PATH"])
|
|
4214
|
+
environment["BIOME_CONFIG_PATH"] = workspace.translate(environment["BIOME_CONFIG_PATH"]);
|
|
4215
|
+
const output = await run(
|
|
4216
|
+
runner.command,
|
|
4217
|
+
[
|
|
4218
|
+
...runner.prefix,
|
|
4219
|
+
"lint",
|
|
4220
|
+
"--reporter=json",
|
|
4221
|
+
"--max-diagnostics=none",
|
|
4222
|
+
"--vcs-enabled=false",
|
|
4223
|
+
...files.map((file) => workspace.map(file.path))
|
|
4224
|
+
],
|
|
4225
|
+
workspace.root,
|
|
4226
|
+
signal,
|
|
4227
|
+
void 0,
|
|
4228
|
+
{ environment }
|
|
4229
|
+
);
|
|
4230
|
+
if (![0, 1].includes(output.code)) throw new Error(output.stderr || `Biome exit ${output.code}`);
|
|
4231
|
+
const data = JSON.parse(output.stdout);
|
|
4232
|
+
if (!record(data) || !record(data["summary"]) || Number(data["summary"]["skipped"]) > 0 || Number(data["summary"]["changed"]) + Number(data["summary"]["unchanged"]) !== files.length)
|
|
4233
|
+
throw new Error("unsupported: Biome did not process every requested file");
|
|
4234
|
+
const split = splitLint("biome", data, workspace.root, workspace.root);
|
|
4235
|
+
const findings = [];
|
|
4236
|
+
for (const file of files) {
|
|
4237
|
+
const path = workspace.map(file.path);
|
|
4238
|
+
const relative12 = path.slice(resolve9(workspace.root).length + 1);
|
|
4239
|
+
findings.push(
|
|
4240
|
+
...parseLint(
|
|
4241
|
+
"biome",
|
|
4242
|
+
split.get(relative12) ?? { diagnostics: [] },
|
|
4243
|
+
file.path,
|
|
4244
|
+
after2 ? file.after : file.before
|
|
4245
|
+
).map(({ lint: _lint, ...finding }) => finding)
|
|
4246
|
+
);
|
|
4247
|
+
}
|
|
4248
|
+
return findings;
|
|
4249
|
+
};
|
|
4250
|
+
const before = await collect(false), after = await collect(true);
|
|
4251
|
+
const result = validationResult(
|
|
4252
|
+
"complete",
|
|
4253
|
+
files.map((file) => file.path),
|
|
4254
|
+
void 0,
|
|
4255
|
+
before,
|
|
4256
|
+
after,
|
|
4257
|
+
"biome"
|
|
4258
|
+
);
|
|
4259
|
+
result.limitations.push(
|
|
4260
|
+
"Bounded temporary project snapshot; VCS discovery disabled after workspace file selection. Project file includes and rule configuration still apply."
|
|
4261
|
+
);
|
|
4262
|
+
return result;
|
|
4263
|
+
});
|
|
4264
|
+
}
|
|
4265
|
+
var init_lint_preview = __esm({
|
|
4266
|
+
"src/lint-preview.ts"() {
|
|
4267
|
+
"use strict";
|
|
4268
|
+
init_environment();
|
|
4269
|
+
init_lint_output();
|
|
4270
|
+
init_plan_validation();
|
|
4271
|
+
init_preview_workspace();
|
|
4272
|
+
init_results();
|
|
4273
|
+
init_runners();
|
|
4274
|
+
}
|
|
4275
|
+
});
|
|
4276
|
+
|
|
3907
4277
|
// src/lsp/constants.ts
|
|
3908
4278
|
var REQUEST_TIMEOUT_MS, INIT_TIMEOUT_MS, IDLE_TIMEOUT_MS, REAPER_INTERVAL_MS, STOP_HARD_KILL_TIMEOUT_MS, STOP_SIGKILL_GRACE_MS;
|
|
3909
4279
|
var init_constants = __esm({
|
|
@@ -4016,10 +4386,10 @@ var init_json_rpc_connection = __esm({
|
|
|
4016
4386
|
const id = this.nextRequestId;
|
|
4017
4387
|
this.nextRequestId += 1;
|
|
4018
4388
|
const message2 = params === void 0 ? { jsonrpc: "2.0", id, method } : { jsonrpc: "2.0", id, method, params };
|
|
4019
|
-
const responsePromise = new Promise((
|
|
4389
|
+
const responsePromise = new Promise((resolve16, reject) => {
|
|
4020
4390
|
this.pendingRequests.set(String(id), {
|
|
4021
4391
|
resolve(result) {
|
|
4022
|
-
|
|
4392
|
+
resolve16(result);
|
|
4023
4393
|
},
|
|
4024
4394
|
reject
|
|
4025
4395
|
});
|
|
@@ -4164,13 +4534,13 @@ var init_json_rpc_connection = __esm({
|
|
|
4164
4534
|
const payload = `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
4165
4535
|
\r
|
|
4166
4536
|
${body}`;
|
|
4167
|
-
return new Promise((
|
|
4537
|
+
return new Promise((resolve16, reject) => {
|
|
4168
4538
|
this.writer.write(payload, (error) => {
|
|
4169
4539
|
if (error) {
|
|
4170
4540
|
reject(error);
|
|
4171
4541
|
return;
|
|
4172
4542
|
}
|
|
4173
|
-
|
|
4543
|
+
resolve16();
|
|
4174
4544
|
});
|
|
4175
4545
|
});
|
|
4176
4546
|
}
|
|
@@ -4184,7 +4554,8 @@ ${body}`;
|
|
|
4184
4554
|
});
|
|
4185
4555
|
|
|
4186
4556
|
// src/lsp/transport.ts
|
|
4187
|
-
import { delimiter as delimiter3 } from "node:path";
|
|
4557
|
+
import { basename as basename5, delimiter as delimiter3 } from "node:path";
|
|
4558
|
+
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
4188
4559
|
function isRecord(value) {
|
|
4189
4560
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4190
4561
|
}
|
|
@@ -4238,6 +4609,9 @@ var init_transport = __esm({
|
|
|
4238
4609
|
command() {
|
|
4239
4610
|
return [...this.server.command];
|
|
4240
4611
|
}
|
|
4612
|
+
configurationSettings() {
|
|
4613
|
+
return { json: { validate: { enable: true } }, ...this.server.settings };
|
|
4614
|
+
}
|
|
4241
4615
|
async start() {
|
|
4242
4616
|
const env = {
|
|
4243
4617
|
...this.environment,
|
|
@@ -4254,7 +4628,7 @@ var init_transport = __esm({
|
|
|
4254
4628
|
env
|
|
4255
4629
|
});
|
|
4256
4630
|
this.startStderrReading();
|
|
4257
|
-
await new Promise((
|
|
4631
|
+
await new Promise((resolve16) => setTimeout(resolve16, 100));
|
|
4258
4632
|
if (this.proc.exitCode !== null) {
|
|
4259
4633
|
const stderr = this.stderrBuffer.join("\n");
|
|
4260
4634
|
throw new LspProcessExitedError(this.server.id, this.root, this.proc.exitCode, stderr.slice(-2e3));
|
|
@@ -4269,12 +4643,16 @@ var init_transport = __esm({
|
|
|
4269
4643
|
this.connection.onRequest("workspace/configuration", (params) => {
|
|
4270
4644
|
const items2 = parseConfigurationItems(params);
|
|
4271
4645
|
return items2.map((item) => {
|
|
4272
|
-
|
|
4273
|
-
|
|
4646
|
+
let value = this.configurationSettings();
|
|
4647
|
+
for (const key of item.section?.split(".") ?? []) value = isRecord(value) ? value[key] : void 0;
|
|
4648
|
+
return value ?? {};
|
|
4274
4649
|
});
|
|
4275
4650
|
});
|
|
4276
4651
|
this.connection.onRequest("client/registerCapability", () => null);
|
|
4277
4652
|
this.connection.onRequest("window/workDoneProgress/create", () => null);
|
|
4653
|
+
this.connection.onRequest("workspace/workspaceFolders", () => [
|
|
4654
|
+
{ uri: pathToFileURL2(this.root).href, name: basename5(this.root) }
|
|
4655
|
+
]);
|
|
4278
4656
|
this.connection.onClose(() => {
|
|
4279
4657
|
this.processExited = true;
|
|
4280
4658
|
});
|
|
@@ -4358,8 +4736,8 @@ var init_transport = __esm({
|
|
|
4358
4736
|
try {
|
|
4359
4737
|
proc.kill();
|
|
4360
4738
|
let timeoutId;
|
|
4361
|
-
const timeoutPromise = new Promise((
|
|
4362
|
-
timeoutId = setTimeout(
|
|
4739
|
+
const timeoutPromise = new Promise((resolve16) => {
|
|
4740
|
+
timeoutId = setTimeout(resolve16, STOP_HARD_KILL_TIMEOUT_MS);
|
|
4363
4741
|
});
|
|
4364
4742
|
await Promise.race([
|
|
4365
4743
|
proc.exited.then(() => {
|
|
@@ -4374,7 +4752,7 @@ var init_transport = __esm({
|
|
|
4374
4752
|
proc.kill("SIGKILL");
|
|
4375
4753
|
await Promise.race([
|
|
4376
4754
|
proc.exited,
|
|
4377
|
-
new Promise((
|
|
4755
|
+
new Promise((resolve16) => setTimeout(resolve16, STOP_SIGKILL_GRACE_MS))
|
|
4378
4756
|
]);
|
|
4379
4757
|
} catch (error) {
|
|
4380
4758
|
reportBestEffortCleanupError("hard process kill", error);
|
|
@@ -4395,7 +4773,7 @@ var init_transport = __esm({
|
|
|
4395
4773
|
});
|
|
4396
4774
|
|
|
4397
4775
|
// src/lsp/connection.ts
|
|
4398
|
-
import { pathToFileURL as
|
|
4776
|
+
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
4399
4777
|
var INITIALIZE_SETTLE_MS, LspClientConnection;
|
|
4400
4778
|
var init_connection = __esm({
|
|
4401
4779
|
"src/lsp/connection.ts"() {
|
|
@@ -4404,7 +4782,7 @@ var init_connection = __esm({
|
|
|
4404
4782
|
INITIALIZE_SETTLE_MS = 300;
|
|
4405
4783
|
LspClientConnection = class extends LspClientTransport {
|
|
4406
4784
|
async initialize() {
|
|
4407
|
-
const rootUri =
|
|
4785
|
+
const rootUri = pathToFileURL3(this.root).href;
|
|
4408
4786
|
await this.sendRequest("initialize", {
|
|
4409
4787
|
processId: process.pid,
|
|
4410
4788
|
rootUri,
|
|
@@ -4466,7 +4844,7 @@ var init_connection = __esm({
|
|
|
4466
4844
|
});
|
|
4467
4845
|
await this.sendNotification("initialized");
|
|
4468
4846
|
await this.sendNotification("workspace/didChangeConfiguration", {
|
|
4469
|
-
settings:
|
|
4847
|
+
settings: this.configurationSettings()
|
|
4470
4848
|
});
|
|
4471
4849
|
await new Promise((r) => setTimeout(r, INITIALIZE_SETTLE_MS));
|
|
4472
4850
|
}
|
|
@@ -4619,8 +4997,8 @@ var init_language_mappings = __esm({
|
|
|
4619
4997
|
|
|
4620
4998
|
// src/lsp/client.ts
|
|
4621
4999
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
4622
|
-
import { extname as extname4, resolve as
|
|
4623
|
-
import { pathToFileURL as
|
|
5000
|
+
import { extname as extname4, resolve as resolve10 } from "node:path";
|
|
5001
|
+
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
4624
5002
|
var POST_OPEN_DELAY_MS, POST_DIAGNOSTICS_WAIT_MS, LspClient;
|
|
4625
5003
|
var init_client = __esm({
|
|
4626
5004
|
"src/lsp/client.ts"() {
|
|
@@ -4641,12 +5019,12 @@ var init_client = __esm({
|
|
|
4641
5019
|
return this.diagnosticPullErrors;
|
|
4642
5020
|
}
|
|
4643
5021
|
async openFile(filePath) {
|
|
4644
|
-
const absPath =
|
|
4645
|
-
const uri =
|
|
5022
|
+
const absPath = resolve10(filePath);
|
|
5023
|
+
const uri = pathToFileURL4(absPath).href;
|
|
4646
5024
|
const text2 = readFileSync2(absPath, "utf-8");
|
|
4647
5025
|
if (!this.openedFiles.has(absPath)) {
|
|
4648
5026
|
const ext = extname4(absPath);
|
|
4649
|
-
const languageId = getLanguageId(ext);
|
|
5027
|
+
const languageId = this.server.languageId ?? getLanguageId(ext);
|
|
4650
5028
|
const version = 1;
|
|
4651
5029
|
await this.sendNotification("textDocument/didOpen", {
|
|
4652
5030
|
textDocument: {
|
|
@@ -4679,30 +5057,30 @@ var init_client = __esm({
|
|
|
4679
5057
|
});
|
|
4680
5058
|
}
|
|
4681
5059
|
async definition(filePath, line, character) {
|
|
4682
|
-
const absPath =
|
|
5060
|
+
const absPath = resolve10(filePath);
|
|
4683
5061
|
await this.openFile(absPath);
|
|
4684
5062
|
return this.sendRequest(
|
|
4685
5063
|
"textDocument/definition",
|
|
4686
5064
|
{
|
|
4687
|
-
textDocument: { uri:
|
|
5065
|
+
textDocument: { uri: pathToFileURL4(absPath).href },
|
|
4688
5066
|
position: { line: line - 1, character }
|
|
4689
5067
|
}
|
|
4690
5068
|
);
|
|
4691
5069
|
}
|
|
4692
5070
|
async references(filePath, line, character, includeDeclaration = true) {
|
|
4693
|
-
const absPath =
|
|
5071
|
+
const absPath = resolve10(filePath);
|
|
4694
5072
|
await this.openFile(absPath);
|
|
4695
5073
|
return this.sendRequest("textDocument/references", {
|
|
4696
|
-
textDocument: { uri:
|
|
5074
|
+
textDocument: { uri: pathToFileURL4(absPath).href },
|
|
4697
5075
|
position: { line: line - 1, character },
|
|
4698
5076
|
context: { includeDeclaration }
|
|
4699
5077
|
});
|
|
4700
5078
|
}
|
|
4701
5079
|
async documentSymbols(filePath) {
|
|
4702
|
-
const absPath =
|
|
5080
|
+
const absPath = resolve10(filePath);
|
|
4703
5081
|
await this.openFile(absPath);
|
|
4704
5082
|
return this.sendRequest("textDocument/documentSymbol", {
|
|
4705
|
-
textDocument: { uri:
|
|
5083
|
+
textDocument: { uri: pathToFileURL4(absPath).href }
|
|
4706
5084
|
});
|
|
4707
5085
|
}
|
|
4708
5086
|
async workspaceSymbols(query) {
|
|
@@ -4715,8 +5093,8 @@ var init_client = __esm({
|
|
|
4715
5093
|
return /unsupported|not supported|method not found|unknown request/i.test(error.message);
|
|
4716
5094
|
}
|
|
4717
5095
|
async diagnostics(filePath) {
|
|
4718
|
-
const absPath =
|
|
4719
|
-
const uri =
|
|
5096
|
+
const absPath = resolve10(filePath);
|
|
5097
|
+
const uri = pathToFileURL4(absPath).href;
|
|
4720
5098
|
await this.openFile(absPath);
|
|
4721
5099
|
await new Promise((r) => setTimeout(r, POST_DIAGNOSTICS_WAIT_MS));
|
|
4722
5100
|
try {
|
|
@@ -4734,21 +5112,21 @@ var init_client = __esm({
|
|
|
4734
5112
|
return { items: this.getStoredDiagnostics(uri) };
|
|
4735
5113
|
}
|
|
4736
5114
|
async prepareRename(filePath, line, character) {
|
|
4737
|
-
const absPath =
|
|
5115
|
+
const absPath = resolve10(filePath);
|
|
4738
5116
|
await this.openFile(absPath);
|
|
4739
5117
|
return this.sendRequest(
|
|
4740
5118
|
"textDocument/prepareRename",
|
|
4741
5119
|
{
|
|
4742
|
-
textDocument: { uri:
|
|
5120
|
+
textDocument: { uri: pathToFileURL4(absPath).href },
|
|
4743
5121
|
position: { line: line - 1, character }
|
|
4744
5122
|
}
|
|
4745
5123
|
);
|
|
4746
5124
|
}
|
|
4747
5125
|
async rename(filePath, line, character, newName) {
|
|
4748
|
-
const absPath =
|
|
5126
|
+
const absPath = resolve10(filePath);
|
|
4749
5127
|
await this.openFile(absPath);
|
|
4750
5128
|
return this.sendRequest("textDocument/rename", {
|
|
4751
|
-
textDocument: { uri:
|
|
5129
|
+
textDocument: { uri: pathToFileURL4(absPath).href },
|
|
4752
5130
|
position: { line: line - 1, character },
|
|
4753
5131
|
newName
|
|
4754
5132
|
});
|
|
@@ -4845,7 +5223,7 @@ async function stopClientBestEffort(client) {
|
|
|
4845
5223
|
}
|
|
4846
5224
|
function awaitWithSignal(promise, signal) {
|
|
4847
5225
|
if (!signal) return promise;
|
|
4848
|
-
return new Promise((
|
|
5226
|
+
return new Promise((resolve16, reject) => {
|
|
4849
5227
|
let settled = false;
|
|
4850
5228
|
const onAbort = () => {
|
|
4851
5229
|
if (settled) return;
|
|
@@ -4862,7 +5240,7 @@ function awaitWithSignal(promise, signal) {
|
|
|
4862
5240
|
if (settled) return;
|
|
4863
5241
|
settled = true;
|
|
4864
5242
|
signal.removeEventListener("abort", onAbort);
|
|
4865
|
-
|
|
5243
|
+
resolve16(value);
|
|
4866
5244
|
},
|
|
4867
5245
|
(err) => {
|
|
4868
5246
|
if (settled) return;
|
|
@@ -5139,7 +5517,7 @@ function createSharedAbortableOperation(run2, onSettled, onAbandoned) {
|
|
|
5139
5517
|
function awaitSharedAbortableOperation(operation, signal) {
|
|
5140
5518
|
signal?.throwIfAborted();
|
|
5141
5519
|
operation.waiterCount += 1;
|
|
5142
|
-
return new Promise((
|
|
5520
|
+
return new Promise((resolve16, reject) => {
|
|
5143
5521
|
let settled = false;
|
|
5144
5522
|
const onAbort = () => {
|
|
5145
5523
|
if (settled) return;
|
|
@@ -5155,7 +5533,7 @@ function awaitSharedAbortableOperation(operation, signal) {
|
|
|
5155
5533
|
settled = true;
|
|
5156
5534
|
signal?.removeEventListener("abort", onAbort);
|
|
5157
5535
|
releaseSharedOperationWaiter(operation);
|
|
5158
|
-
|
|
5536
|
+
resolve16(value);
|
|
5159
5537
|
},
|
|
5160
5538
|
(error) => {
|
|
5161
5539
|
if (settled) return;
|
|
@@ -5175,7 +5553,7 @@ var init_abortable_shared_operation = __esm({
|
|
|
5175
5553
|
|
|
5176
5554
|
// src/lsp/cargo-manifest-snapshot.ts
|
|
5177
5555
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
5178
|
-
import { dirname as
|
|
5556
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
5179
5557
|
function isMissingManifestError(error) {
|
|
5180
5558
|
if (!(error instanceof Error)) return false;
|
|
5181
5559
|
const code = "code" in error ? error.code : void 0;
|
|
@@ -5207,13 +5585,13 @@ function ancestorManifestPaths(manifestDir) {
|
|
|
5207
5585
|
let dir = manifestDir;
|
|
5208
5586
|
let prev = "";
|
|
5209
5587
|
while (dir !== prev) {
|
|
5210
|
-
const manifestPath =
|
|
5588
|
+
const manifestPath = join12(dir, "Cargo.toml");
|
|
5211
5589
|
if (!seen.has(manifestPath)) {
|
|
5212
5590
|
seen.add(manifestPath);
|
|
5213
5591
|
paths.push(manifestPath);
|
|
5214
5592
|
}
|
|
5215
5593
|
prev = dir;
|
|
5216
|
-
dir =
|
|
5594
|
+
dir = dirname7(dir);
|
|
5217
5595
|
}
|
|
5218
5596
|
return paths;
|
|
5219
5597
|
}
|
|
@@ -5657,7 +6035,7 @@ function skipVoid(str, ptr, banNewLines, banComments) {
|
|
|
5657
6035
|
}
|
|
5658
6036
|
return ptr;
|
|
5659
6037
|
}
|
|
5660
|
-
function skipUntil(str, ptr,
|
|
6038
|
+
function skipUntil(str, ptr, sep5, end, banNewLines = false) {
|
|
5661
6039
|
if (!end) {
|
|
5662
6040
|
ptr = indexOfNewline(str, ptr);
|
|
5663
6041
|
return ptr < 0 ? str.length : ptr;
|
|
@@ -5666,7 +6044,7 @@ function skipUntil(str, ptr, sep4, end, banNewLines = false) {
|
|
|
5666
6044
|
let c = str[i];
|
|
5667
6045
|
if (c === "#") {
|
|
5668
6046
|
i = indexOfNewline(str, i);
|
|
5669
|
-
} else if (c ===
|
|
6047
|
+
} else if (c === sep5) {
|
|
5670
6048
|
return i + 1;
|
|
5671
6049
|
} else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
|
|
5672
6050
|
return i;
|
|
@@ -6222,7 +6600,7 @@ var init_dist = __esm({
|
|
|
6222
6600
|
|
|
6223
6601
|
// src/lsp/cargo-metadata-parser.ts
|
|
6224
6602
|
import { readFileSync as readFileSync4, realpathSync, statSync as statSync2 } from "node:fs";
|
|
6225
|
-
import { isAbsolute as isAbsolute6, join as
|
|
6603
|
+
import { isAbsolute as isAbsolute6, join as join13, relative as relative8, sep as sep3 } from "node:path";
|
|
6226
6604
|
function isRecord2(value) {
|
|
6227
6605
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6228
6606
|
}
|
|
@@ -6242,7 +6620,7 @@ function canonicalManifest(path) {
|
|
|
6242
6620
|
return void 0;
|
|
6243
6621
|
}
|
|
6244
6622
|
}
|
|
6245
|
-
function
|
|
6623
|
+
function readFile15(path) {
|
|
6246
6624
|
try {
|
|
6247
6625
|
return readFileSync4(path, "utf8");
|
|
6248
6626
|
} catch {
|
|
@@ -6250,7 +6628,7 @@ function readFile14(path) {
|
|
|
6250
6628
|
}
|
|
6251
6629
|
}
|
|
6252
6630
|
function readCargoManifestKind(path) {
|
|
6253
|
-
const content =
|
|
6631
|
+
const content = readFile15(path);
|
|
6254
6632
|
if (content === void 0) return void 0;
|
|
6255
6633
|
try {
|
|
6256
6634
|
return Object.hasOwn(parse(content), "workspace") ? "workspace" : "ordinary";
|
|
@@ -6259,8 +6637,8 @@ function readCargoManifestKind(path) {
|
|
|
6259
6637
|
}
|
|
6260
6638
|
}
|
|
6261
6639
|
function isContainedPath(root, path) {
|
|
6262
|
-
const relativePath =
|
|
6263
|
-
return relativePath === "" || !isAbsolute6(relativePath) && relativePath !== ".." && !relativePath.startsWith(`..${
|
|
6640
|
+
const relativePath = relative8(root, path);
|
|
6641
|
+
return relativePath === "" || !isAbsolute6(relativePath) && relativePath !== ".." && !relativePath.startsWith(`..${sep3}`);
|
|
6264
6642
|
}
|
|
6265
6643
|
function parseCargoMetadata(output) {
|
|
6266
6644
|
let parsed;
|
|
@@ -6294,7 +6672,7 @@ function parseCargoMetadata(output) {
|
|
|
6294
6672
|
function validateCargoMetadata(requestedManifestPath, metadata) {
|
|
6295
6673
|
const workspaceRoot = canonicalDirectory(metadata.workspaceRoot);
|
|
6296
6674
|
if (workspaceRoot === void 0) return void 0;
|
|
6297
|
-
const rootManifestPath = canonicalManifest(
|
|
6675
|
+
const rootManifestPath = canonicalManifest(join13(workspaceRoot, "Cargo.toml"));
|
|
6298
6676
|
const requestedManifest = canonicalManifest(requestedManifestPath);
|
|
6299
6677
|
if (rootManifestPath === void 0 || requestedManifest === void 0) return void 0;
|
|
6300
6678
|
if (!isContainedPath(workspaceRoot, requestedManifest)) return void 0;
|
|
@@ -6386,8 +6764,8 @@ async function defaultCargoMetadataLoader(manifestPath, signal, environment = ex
|
|
|
6386
6764
|
let terminationError;
|
|
6387
6765
|
let settled = false;
|
|
6388
6766
|
let resolveTermination;
|
|
6389
|
-
const terminationComplete = new Promise((
|
|
6390
|
-
resolveTermination =
|
|
6767
|
+
const terminationComplete = new Promise((resolve16) => {
|
|
6768
|
+
resolveTermination = resolve16;
|
|
6391
6769
|
});
|
|
6392
6770
|
const finishTermination = () => {
|
|
6393
6771
|
resolveTermination?.();
|
|
@@ -6520,7 +6898,7 @@ var init_cargo_metadata_process = __esm({
|
|
|
6520
6898
|
|
|
6521
6899
|
// src/lsp/cargo-workspace-root.ts
|
|
6522
6900
|
import { existsSync as existsSync2, realpathSync as realpathSync2 } from "node:fs";
|
|
6523
|
-
import { dirname as
|
|
6901
|
+
import { dirname as dirname8, join as join14 } from "node:path";
|
|
6524
6902
|
function realpathSafe(path) {
|
|
6525
6903
|
try {
|
|
6526
6904
|
return realpathSync2.native(path);
|
|
@@ -6532,9 +6910,9 @@ function nearestCargoManifestDir(startDir) {
|
|
|
6532
6910
|
let dir = startDir;
|
|
6533
6911
|
let prev = "";
|
|
6534
6912
|
while (dir !== prev) {
|
|
6535
|
-
if (existsSync2(
|
|
6913
|
+
if (existsSync2(join14(dir, "Cargo.toml"))) return dir;
|
|
6536
6914
|
prev = dir;
|
|
6537
|
-
dir =
|
|
6915
|
+
dir = dirname8(dir);
|
|
6538
6916
|
}
|
|
6539
6917
|
return void 0;
|
|
6540
6918
|
}
|
|
@@ -6545,14 +6923,14 @@ function cacheEntryFor(root, memberManifestDir) {
|
|
|
6545
6923
|
function prepareCargoWorkspaceCache(manifestDir, metadata) {
|
|
6546
6924
|
const entries = /* @__PURE__ */ new Map();
|
|
6547
6925
|
for (const manifestPath of metadata.memberManifestPaths) {
|
|
6548
|
-
const manifestDir2 =
|
|
6926
|
+
const manifestDir2 = dirname8(manifestPath);
|
|
6549
6927
|
const entry = cacheEntryFor(metadata.workspaceRoot, manifestDir2);
|
|
6550
6928
|
if (entry === void 0) return void 0;
|
|
6551
6929
|
entries.set(manifestDir2, entry);
|
|
6552
6930
|
}
|
|
6553
|
-
const requestedManifestPath = canonicalManifest(
|
|
6931
|
+
const requestedManifestPath = canonicalManifest(join14(manifestDir, "Cargo.toml"));
|
|
6554
6932
|
if (requestedManifestPath === void 0) return void 0;
|
|
6555
|
-
const requestedEntry = cacheEntryFor(metadata.workspaceRoot,
|
|
6933
|
+
const requestedEntry = cacheEntryFor(metadata.workspaceRoot, dirname8(requestedManifestPath));
|
|
6556
6934
|
if (requestedEntry === void 0) return void 0;
|
|
6557
6935
|
entries.set(manifestDir, requestedEntry);
|
|
6558
6936
|
return { root: metadata.workspaceRoot, entries };
|
|
@@ -6620,7 +6998,7 @@ function createInFlightCargoWorkspaceRoot(request) {
|
|
|
6620
6998
|
async function loadCargoWorkspaceRoot(request) {
|
|
6621
6999
|
try {
|
|
6622
7000
|
request.signal?.throwIfAborted();
|
|
6623
|
-
const manifestPath =
|
|
7001
|
+
const manifestPath = join14(request.manifestDir, "Cargo.toml");
|
|
6624
7002
|
const output = await request.loader(manifestPath, request.signal);
|
|
6625
7003
|
request.signal?.throwIfAborted();
|
|
6626
7004
|
if (!snapshotsAreFresh(request.generation.snapshots)) {
|
|
@@ -6699,7 +7077,7 @@ var init_cargo_workspace_root = __esm({
|
|
|
6699
7077
|
|
|
6700
7078
|
// src/lsp/workspace-root.ts
|
|
6701
7079
|
import { existsSync as existsSync3, statSync as statSync3 } from "node:fs";
|
|
6702
|
-
import { dirname as
|
|
7080
|
+
import { dirname as dirname9, join as join15, resolve as resolve11 } from "node:path";
|
|
6703
7081
|
function isDirectoryPath(filePath) {
|
|
6704
7082
|
try {
|
|
6705
7083
|
return statSync3(filePath).isDirectory();
|
|
@@ -6708,10 +7086,10 @@ function isDirectoryPath(filePath) {
|
|
|
6708
7086
|
}
|
|
6709
7087
|
}
|
|
6710
7088
|
async function findWorkspaceRoot(filePath, server2, options = {}) {
|
|
6711
|
-
const abs =
|
|
7089
|
+
const abs = resolve11(filePath);
|
|
6712
7090
|
let dir = abs;
|
|
6713
7091
|
if (!isDirectoryPath(dir)) {
|
|
6714
|
-
dir =
|
|
7092
|
+
dir = dirname9(dir);
|
|
6715
7093
|
}
|
|
6716
7094
|
if (server2?.id === "rust") {
|
|
6717
7095
|
const cargoRoot = await resolveCargoWorkspaceRoot(dir, options);
|
|
@@ -6721,14 +7099,14 @@ async function findWorkspaceRoot(filePath, server2, options = {}) {
|
|
|
6721
7099
|
while (dir !== prevDir) {
|
|
6722
7100
|
const markers = server2?.extensions.some((extension) => [".c", ".cpp", ".cc", ".h"].includes(extension)) ? [".clangd", "compile_commands.json", "compile_flags.txt", ...WORKSPACE_MARKERS] : WORKSPACE_MARKERS;
|
|
6723
7101
|
for (const marker of markers) {
|
|
6724
|
-
if (existsSync3(
|
|
7102
|
+
if (existsSync3(join15(dir, marker))) {
|
|
6725
7103
|
return dir;
|
|
6726
7104
|
}
|
|
6727
7105
|
}
|
|
6728
7106
|
prevDir = dir;
|
|
6729
|
-
dir =
|
|
7107
|
+
dir = dirname9(dir);
|
|
6730
7108
|
}
|
|
6731
|
-
return
|
|
7109
|
+
return dirname9(abs);
|
|
6732
7110
|
}
|
|
6733
7111
|
var WORKSPACE_MARKERS;
|
|
6734
7112
|
var init_workspace_root = __esm({
|
|
@@ -6741,13 +7119,13 @@ var init_workspace_root = __esm({
|
|
|
6741
7119
|
|
|
6742
7120
|
// src/language.ts
|
|
6743
7121
|
import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
|
|
6744
|
-
import { readFile as
|
|
6745
|
-
import { dirname as
|
|
6746
|
-
import { fileURLToPath as fileURLToPath5, pathToFileURL as
|
|
7122
|
+
import { readFile as readFile16, stat as stat6 } from "node:fs/promises";
|
|
7123
|
+
import { basename as basename6, dirname as dirname10, extname as extname5, resolve as resolve12 } from "node:path";
|
|
7124
|
+
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL5 } from "node:url";
|
|
6747
7125
|
function diagnosticUriKey(uri) {
|
|
6748
7126
|
try {
|
|
6749
7127
|
const path = fileURLToPath5(uri);
|
|
6750
|
-
return
|
|
7128
|
+
return pathToFileURL5(
|
|
6751
7129
|
process.platform === "win32" ? path.replace(/^[A-Z]:/, (drive) => drive.toLowerCase()) : path
|
|
6752
7130
|
).href;
|
|
6753
7131
|
} catch {
|
|
@@ -6762,12 +7140,15 @@ var init_language = __esm({
|
|
|
6762
7140
|
init_config_files();
|
|
6763
7141
|
init_environment();
|
|
6764
7142
|
init_files();
|
|
7143
|
+
init_lint_preview();
|
|
6765
7144
|
init_log();
|
|
6766
7145
|
init_client();
|
|
6767
7146
|
init_language_mappings();
|
|
6768
7147
|
init_manager();
|
|
6769
7148
|
init_workspace_root();
|
|
6770
7149
|
init_metrics();
|
|
7150
|
+
init_plan_validation();
|
|
7151
|
+
init_preview_workspace();
|
|
6771
7152
|
init_results();
|
|
6772
7153
|
init_runners();
|
|
6773
7154
|
init_symbols();
|
|
@@ -6813,7 +7194,7 @@ var init_language = __esm({
|
|
|
6813
7194
|
const busy = () => this.serverBusy || this.progress.size > 0 || Date.now() - this.progressAt < BUDGET.lspQuiet;
|
|
6814
7195
|
while (busy() && Date.now() < deadline) {
|
|
6815
7196
|
signal?.throwIfAborted();
|
|
6816
|
-
await new Promise((
|
|
7197
|
+
await new Promise((resolve16) => setTimeout(resolve16, 25));
|
|
6817
7198
|
}
|
|
6818
7199
|
signal?.throwIfAborted();
|
|
6819
7200
|
return !busy();
|
|
@@ -6917,7 +7298,7 @@ var init_language = __esm({
|
|
|
6917
7298
|
if (!this.capabilities[capability[method] ?? ""]) return { status: "unsupported", operation: method };
|
|
6918
7299
|
await this.openFile(path);
|
|
6919
7300
|
return this.sendRequest(`textDocument/${method}`, {
|
|
6920
|
-
textDocument: { uri:
|
|
7301
|
+
textDocument: { uri: pathToFileURL5(path).href },
|
|
6921
7302
|
position: { line: line - 1, character }
|
|
6922
7303
|
});
|
|
6923
7304
|
}
|
|
@@ -6956,10 +7337,12 @@ var init_language = __esm({
|
|
|
6956
7337
|
});
|
|
6957
7338
|
}
|
|
6958
7339
|
async openFile(path) {
|
|
6959
|
-
|
|
6960
|
-
|
|
7340
|
+
await this.openContent(path, await readFile16(path, "utf8"));
|
|
7341
|
+
}
|
|
7342
|
+
async openContent(path, content, force = false) {
|
|
7343
|
+
const uri = pathToFileURL5(path).href;
|
|
6961
7344
|
const previous = this.versions.get(uri);
|
|
6962
|
-
if (previous?.content === content && !this.dirty.has(uri)) return;
|
|
7345
|
+
if (!force && previous?.content === content && !this.dirty.has(uri)) return;
|
|
6963
7346
|
this.dirty.delete(uri);
|
|
6964
7347
|
this.published.delete(diagnosticUriKey(uri));
|
|
6965
7348
|
const version = (previous?.version ?? 0) + 1;
|
|
@@ -6971,11 +7354,93 @@ var init_language = __esm({
|
|
|
6971
7354
|
});
|
|
6972
7355
|
} else {
|
|
6973
7356
|
await this.sendNotification("textDocument/didOpen", {
|
|
6974
|
-
textDocument: {
|
|
7357
|
+
textDocument: {
|
|
7358
|
+
uri,
|
|
7359
|
+
version,
|
|
7360
|
+
languageId: this.server.languageId ?? getLanguageId(extname5(path)),
|
|
7361
|
+
text: content
|
|
7362
|
+
}
|
|
6975
7363
|
});
|
|
6976
7364
|
}
|
|
6977
7365
|
if (this.isRustAnalyzer()) this.progressAt = Date.now();
|
|
6978
7366
|
}
|
|
7367
|
+
async overlayDiagnostics(path, signal) {
|
|
7368
|
+
if (!this.connection) throw new Error("LSP connection unavailable");
|
|
7369
|
+
const uri = pathToFileURL5(path).href;
|
|
7370
|
+
const version = this.versions.get(uri)?.version;
|
|
7371
|
+
if (!this.capabilities["diagnosticProvider"]) {
|
|
7372
|
+
if (this.server.command.some((part) => /typescript-language-server/.test(part)))
|
|
7373
|
+
return this.typescriptOverlayDiagnostics(path, signal);
|
|
7374
|
+
return this.versionedOverlayDiagnostics(uri, version, signal);
|
|
7375
|
+
}
|
|
7376
|
+
const result = await this.connection.sendRequest(
|
|
7377
|
+
"textDocument/diagnostic",
|
|
7378
|
+
{ textDocument: { uri } },
|
|
7379
|
+
signal
|
|
7380
|
+
);
|
|
7381
|
+
signal.throwIfAborted();
|
|
7382
|
+
if (result.kind !== "full" && this.server.command.some((part) => /gopls(?:\.exe)?$/.test(part)))
|
|
7383
|
+
return this.versionedOverlayDiagnostics(uri, version, signal);
|
|
7384
|
+
if (version !== this.versions.get(uri)?.version || result.kind !== "full" || !Array.isArray(result.items))
|
|
7385
|
+
throw new Error("pending: no full diagnostics for the expected overlay version");
|
|
7386
|
+
return result.items;
|
|
7387
|
+
}
|
|
7388
|
+
async versionedOverlayDiagnostics(uri, version, signal) {
|
|
7389
|
+
while (!signal.aborted) {
|
|
7390
|
+
const published = this.published.get(diagnosticUriKey(uri));
|
|
7391
|
+
if (published && version !== void 0 && published.version === version && Date.now() - published.time >= BUDGET.lspQuiet && !this.progress.size)
|
|
7392
|
+
return published.items;
|
|
7393
|
+
await new Promise((resolve16) => setTimeout(resolve16, 25));
|
|
7394
|
+
}
|
|
7395
|
+
signal.throwIfAborted();
|
|
7396
|
+
throw new Error("pending: no versioned overlay diagnostics");
|
|
7397
|
+
}
|
|
7398
|
+
async reassertOverlay(path, content) {
|
|
7399
|
+
if (this.server.command.some((part) => /clangd(?:\.exe)?$/.test(part))) {
|
|
7400
|
+
await this.openContent(path, `${content}
|
|
7401
|
+
`, true);
|
|
7402
|
+
await this.query("textDocument/documentSymbol", { textDocument: { uri: pathToFileURL5(path).href } });
|
|
7403
|
+
}
|
|
7404
|
+
await this.openContent(path, content, true);
|
|
7405
|
+
}
|
|
7406
|
+
async typescriptOverlayDiagnostics(path, signal) {
|
|
7407
|
+
if (!this.connection) throw new Error("LSP connection unavailable");
|
|
7408
|
+
const items2 = [];
|
|
7409
|
+
for (const command of ["syntacticDiagnosticsSync", "semanticDiagnosticsSync"]) {
|
|
7410
|
+
const response = await this.connection.sendRequest(
|
|
7411
|
+
"workspace/executeCommand",
|
|
7412
|
+
{
|
|
7413
|
+
command: "typescript.tsserverRequest",
|
|
7414
|
+
arguments: [
|
|
7415
|
+
command,
|
|
7416
|
+
{ file: path, includeLinePosition: false },
|
|
7417
|
+
{ executionTarget: 0, expectsResult: true, isAsync: false }
|
|
7418
|
+
]
|
|
7419
|
+
},
|
|
7420
|
+
signal
|
|
7421
|
+
);
|
|
7422
|
+
if (!record(response) || !Array.isArray(response["body"]) || response["success"] === false)
|
|
7423
|
+
throw new Error("unsupported: synchronous tsserver diagnostics unavailable");
|
|
7424
|
+
for (const item of response["body"]) {
|
|
7425
|
+
if (!record(item) || !record(item["start"]) || !record(item["end"]) || typeof item["text"] !== "string")
|
|
7426
|
+
throw new Error("Invalid tsserver diagnostic");
|
|
7427
|
+
const at = (value) => {
|
|
7428
|
+
const line = value["line"], offset = value["offset"];
|
|
7429
|
+
if (typeof line !== "number" || typeof offset !== "number" || line < 1 || offset < 1)
|
|
7430
|
+
throw new Error("Invalid tsserver diagnostic position");
|
|
7431
|
+
return { line: line - 1, character: offset - 1 };
|
|
7432
|
+
};
|
|
7433
|
+
items2.push({
|
|
7434
|
+
range: { start: at(item["start"]), end: at(item["end"]) },
|
|
7435
|
+
severity: item["category"] === "error" ? 1 : 2,
|
|
7436
|
+
message: item["text"],
|
|
7437
|
+
source: "typescript",
|
|
7438
|
+
...typeof item["code"] === "number" ? { code: item["code"] } : {}
|
|
7439
|
+
});
|
|
7440
|
+
}
|
|
7441
|
+
}
|
|
7442
|
+
return items2;
|
|
7443
|
+
}
|
|
6979
7444
|
async refresh(changes) {
|
|
6980
7445
|
changes = changes.filter(
|
|
6981
7446
|
({ path }) => inside(this.root, path) && (!configurationImpact(path).length || configurationImpact(path).includes("types")) && (this.server.extensions.includes(extname5(path)) || configurationLanguages(path).some(
|
|
@@ -6991,7 +7456,7 @@ var init_language = __esm({
|
|
|
6991
7456
|
this.pulls.delete(uri);
|
|
6992
7457
|
}
|
|
6993
7458
|
for (const { path, type } of changes) {
|
|
6994
|
-
const uri =
|
|
7459
|
+
const uri = pathToFileURL5(path).href;
|
|
6995
7460
|
if (type === 3) {
|
|
6996
7461
|
if (this.versions.has(uri)) await this.sendNotification("textDocument/didClose", { textDocument: { uri } });
|
|
6997
7462
|
this.versions.delete(uri);
|
|
@@ -7001,11 +7466,11 @@ var init_language = __esm({
|
|
|
7001
7466
|
} else if (this.versions.has(uri)) await this.openFile(path);
|
|
7002
7467
|
}
|
|
7003
7468
|
await this.sendNotification("workspace/didChangeWatchedFiles", {
|
|
7004
|
-
changes: changes.map(({ path, type }) => ({ uri:
|
|
7469
|
+
changes: changes.map(({ path, type }) => ({ uri: pathToFileURL5(path).href, type }))
|
|
7005
7470
|
});
|
|
7006
7471
|
}
|
|
7007
7472
|
async collect(path, signal) {
|
|
7008
|
-
const uri =
|
|
7473
|
+
const uri = pathToFileURL5(path).href;
|
|
7009
7474
|
const cold = !this.versions.has(uri);
|
|
7010
7475
|
await this.openFile(path);
|
|
7011
7476
|
const settling = Date.now() + (cold ? BUDGET.lspCold : BUDGET.lspWarm);
|
|
@@ -7052,7 +7517,7 @@ var init_language = __esm({
|
|
|
7052
7517
|
};
|
|
7053
7518
|
}
|
|
7054
7519
|
}
|
|
7055
|
-
await new Promise((
|
|
7520
|
+
await new Promise((resolve16) => setTimeout(resolve16, 25));
|
|
7056
7521
|
}
|
|
7057
7522
|
return { items: [], ready: false };
|
|
7058
7523
|
}
|
|
@@ -7060,12 +7525,12 @@ var init_language = __esm({
|
|
|
7060
7525
|
if (!this.supports("format")) throw new Error("unsupported: document formatting");
|
|
7061
7526
|
await this.openFile(path);
|
|
7062
7527
|
return await this.sendRequest("textDocument/formatting", {
|
|
7063
|
-
textDocument: { uri:
|
|
7528
|
+
textDocument: { uri: pathToFileURL5(path).href },
|
|
7064
7529
|
options
|
|
7065
7530
|
}) ?? [];
|
|
7066
7531
|
}
|
|
7067
7532
|
};
|
|
7068
|
-
Languages = class {
|
|
7533
|
+
Languages = class _Languages {
|
|
7069
7534
|
constructor(root, config) {
|
|
7070
7535
|
this.root = root;
|
|
7071
7536
|
this.config = config;
|
|
@@ -7096,7 +7561,7 @@ var init_language = __esm({
|
|
|
7096
7561
|
(path) => files.get(path) !== this.snapshot.get(path)
|
|
7097
7562
|
);
|
|
7098
7563
|
const events = changed.map((path) => ({
|
|
7099
|
-
path:
|
|
7564
|
+
path: resolve12(this.root, path),
|
|
7100
7565
|
type: !files.has(path) ? 3 : !this.snapshot.has(path) ? 1 : 2
|
|
7101
7566
|
}));
|
|
7102
7567
|
this.snapshot = new Map(files);
|
|
@@ -7119,7 +7584,7 @@ var init_language = __esm({
|
|
|
7119
7584
|
this.config = config;
|
|
7120
7585
|
}
|
|
7121
7586
|
async invalidate(path) {
|
|
7122
|
-
const directory2 =
|
|
7587
|
+
const directory2 = dirname10(resolve12(this.root, path));
|
|
7123
7588
|
const language = configurationImpact(path).length ? void 0 : (await resolveServer(this.root, path, this.config).catch(() => void 0))?.language;
|
|
7124
7589
|
const languages = language ? [language] : configurationLanguages(path);
|
|
7125
7590
|
for (const managed of this.manager.getSnapshot()) {
|
|
@@ -7139,22 +7604,22 @@ var init_language = __esm({
|
|
|
7139
7604
|
reason = message(error);
|
|
7140
7605
|
}
|
|
7141
7606
|
const failure = resolved ? [...this.failures.values()].filter(
|
|
7142
|
-
(entry) => entry.identity === resolved?.tool.identity && inside(entry.root,
|
|
7607
|
+
(entry) => entry.identity === resolved?.tool.identity && inside(entry.root, resolve12(this.root, path))
|
|
7143
7608
|
).sort((a, b) => b.root.length - a.root.length)[0] : void 0;
|
|
7144
7609
|
const identity = resolved ? `${resolved.server.id}:${hash(JSON.stringify([resolved.server, resolved.tool.identity])).slice(0, 16)}` : "";
|
|
7145
7610
|
const client = [...this.clients].find(
|
|
7146
|
-
(client2) => client2.isAlive() && client2.serverIdentity() === identity && inside(client2.workspaceRoot(),
|
|
7611
|
+
(client2) => client2.isAlive() && client2.serverIdentity() === identity && inside(client2.workspaceRoot(), resolve12(this.root, path))
|
|
7147
7612
|
);
|
|
7148
7613
|
let lint;
|
|
7149
7614
|
try {
|
|
7150
|
-
lint = await select(this.root,
|
|
7615
|
+
lint = await select(this.root, resolve12(this.root, path), false, this.config) ?? {
|
|
7151
7616
|
unavailable: "Lint disabled or no matching runner"
|
|
7152
7617
|
};
|
|
7153
7618
|
} catch (error) {
|
|
7154
7619
|
lint = { unavailable: message(error) };
|
|
7155
7620
|
}
|
|
7156
7621
|
const lintHealth = await lintAvailability(this.root, path, this.config);
|
|
7157
|
-
const formatter = await select(this.root,
|
|
7622
|
+
const formatter = await select(this.root, resolve12(this.root, path), true, this.config).catch(() => void 0);
|
|
7158
7623
|
const unavailable = !this.config.valid || !resolved || resolved.tool.source === "missing" || !!failure;
|
|
7159
7624
|
const status = (operation) => unavailable ? "unavailable" : !client ? "unverified" : client.supports(operation) ? "available" : "unsupported";
|
|
7160
7625
|
return {
|
|
@@ -7196,13 +7661,290 @@ var init_language = __esm({
|
|
|
7196
7661
|
}
|
|
7197
7662
|
);
|
|
7198
7663
|
}
|
|
7199
|
-
async
|
|
7200
|
-
const
|
|
7664
|
+
async warmup(paths, signal) {
|
|
7665
|
+
const items2 = [];
|
|
7666
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7667
|
+
for (const path of paths) {
|
|
7668
|
+
const start = Date.now();
|
|
7669
|
+
try {
|
|
7670
|
+
signal.throwIfAborted();
|
|
7671
|
+
const target = await this.serverTarget(await workspacePath(this.root, path), signal, false);
|
|
7672
|
+
const key = JSON.stringify([target.root, target.resolved.server, target.resolved.tool.identity]);
|
|
7673
|
+
if (seen.has(key)) continue;
|
|
7674
|
+
seen.add(key);
|
|
7675
|
+
const reused = this.manager.getSnapshot().some(
|
|
7676
|
+
(entry) => entry.root === target.root && entry.alive && entry.serverId === `${target.resolved.server.id}:${hash(JSON.stringify([target.resolved.server, target.resolved.tool.identity])).slice(0, 16)}`
|
|
7677
|
+
);
|
|
7678
|
+
await withExecution(
|
|
7679
|
+
executionEnvironment(),
|
|
7680
|
+
true,
|
|
7681
|
+
() => this.withLspClient(path, async () => void 0, "warmup", { manager: this.manager, signal, target })
|
|
7682
|
+
);
|
|
7683
|
+
items2.push({ path, status: reused ? "reused" : "initialized", elapsedMs: Date.now() - start });
|
|
7684
|
+
} catch (error) {
|
|
7685
|
+
const reason = message(error);
|
|
7686
|
+
items2.push({
|
|
7687
|
+
path,
|
|
7688
|
+
status: signal.aborted ? "pending" : /missing|disabled|unsupported|launcher disabled/i.test(reason) ? "unsupported" : "failed",
|
|
7689
|
+
reason,
|
|
7690
|
+
elapsedMs: Date.now() - start
|
|
7691
|
+
});
|
|
7692
|
+
}
|
|
7693
|
+
}
|
|
7694
|
+
const ready = items2.filter((entry) => ["initialized", "reused"].includes(entry.status)).length;
|
|
7695
|
+
return {
|
|
7696
|
+
status: ready === items2.length && ready ? "complete" : ready ? "partial" : items2.some((entry) => entry.status === "pending") ? "pending" : items2.some((entry) => entry.status === "failed") ? "failed" : "unsupported",
|
|
7697
|
+
items: items2,
|
|
7698
|
+
note: "Initialization only; no diagnostics baseline collected. Local tools only."
|
|
7699
|
+
};
|
|
7700
|
+
}
|
|
7701
|
+
async validatePlan(files, signal, validationPaths = []) {
|
|
7702
|
+
signal.throwIfAborted();
|
|
7703
|
+
const paths = [.../* @__PURE__ */ new Set([...files.map((file) => file.path), ...validationPaths])];
|
|
7704
|
+
if (!files.length) return validationResult("skipped", paths, "No changed files", [], [], "combined");
|
|
7705
|
+
if (files.length > BUDGET.validationFiles || validationPaths.length > BUDGET.validationExtraFiles)
|
|
7706
|
+
return validationResult(
|
|
7707
|
+
"unsupported",
|
|
7708
|
+
paths,
|
|
7709
|
+
"Validation limit: 20 changed and 20 additional files",
|
|
7710
|
+
[],
|
|
7711
|
+
[],
|
|
7712
|
+
"combined"
|
|
7713
|
+
);
|
|
7714
|
+
const bounded2 = AbortSignal.any([signal, AbortSignal.timeout(BUDGET.validation)]);
|
|
7715
|
+
const scope2 = [...files];
|
|
7716
|
+
let bytes2 = files.reduce((sum, file) => sum + Buffer.byteLength(file.before + file.after), 0);
|
|
7717
|
+
for (const path of validationPaths) {
|
|
7718
|
+
bounded2.throwIfAborted();
|
|
7719
|
+
const absolute = await workspacePath(this.root, path);
|
|
7720
|
+
if (scope2.some((file) => file.absolute === absolute)) continue;
|
|
7721
|
+
bytes2 += 2 * (await stat6(absolute)).size;
|
|
7722
|
+
if (bytes2 > BUDGET.validationBytes)
|
|
7723
|
+
return validationResult(
|
|
7724
|
+
"unsupported",
|
|
7725
|
+
paths,
|
|
7726
|
+
"Validation limit: 2 MiB including additional files",
|
|
7727
|
+
[],
|
|
7728
|
+
[],
|
|
7729
|
+
"combined"
|
|
7730
|
+
);
|
|
7731
|
+
const content = await readFile16(absolute, "utf8");
|
|
7732
|
+
scope2.push({ path, absolute, before: content, after: content, fileIdentity: "validation-only" });
|
|
7733
|
+
}
|
|
7734
|
+
if (scope2.reduce((sum, file) => sum + Buffer.byteLength(file.before + file.after), 0) > BUDGET.validationBytes)
|
|
7735
|
+
return validationResult(
|
|
7736
|
+
"unsupported",
|
|
7737
|
+
paths,
|
|
7738
|
+
"Validation limit: 2 MiB of before/after content including additional files",
|
|
7739
|
+
[],
|
|
7740
|
+
[],
|
|
7741
|
+
"combined"
|
|
7742
|
+
);
|
|
7743
|
+
const reports = [];
|
|
7744
|
+
const jobs = [];
|
|
7745
|
+
const groups = /* @__PURE__ */ new Map();
|
|
7746
|
+
const lintGroups = /* @__PURE__ */ new Map();
|
|
7747
|
+
const failure = (provider, paths2, error, aborted = bounded2.aborted) => {
|
|
7748
|
+
signal.throwIfAborted();
|
|
7749
|
+
const reason = message(error);
|
|
7750
|
+
return validationResult(
|
|
7751
|
+
aborted || /pending|cancel|modified|timeout/i.test(reason) ? "pending" : /missing|not installed|unavailable|unsupported|disabled/i.test(reason) ? "unsupported" : "failed",
|
|
7752
|
+
paths2,
|
|
7753
|
+
reason,
|
|
7754
|
+
[],
|
|
7755
|
+
[],
|
|
7756
|
+
provider
|
|
7757
|
+
);
|
|
7758
|
+
};
|
|
7759
|
+
for (const file of scope2) {
|
|
7760
|
+
const language = codeLanguage(this.config, file.path);
|
|
7761
|
+
const supported = language && ["python", "typescript", "go", "cpp"].includes(language);
|
|
7762
|
+
if (!supported) {
|
|
7763
|
+
reports.push(
|
|
7764
|
+
validationResult(
|
|
7765
|
+
"unsupported",
|
|
7766
|
+
[file.path],
|
|
7767
|
+
"No plan diagnostic provider for this language",
|
|
7768
|
+
[],
|
|
7769
|
+
[],
|
|
7770
|
+
"lsp"
|
|
7771
|
+
)
|
|
7772
|
+
);
|
|
7773
|
+
} else if (this.config.servers[language] !== false) {
|
|
7774
|
+
try {
|
|
7775
|
+
const target = await this.serverTarget(file.absolute, bounded2, false);
|
|
7776
|
+
const provider = language === "python" ? "ty" : language === "typescript" ? "typescript" : language === "go" ? "gopls" : "clangd";
|
|
7777
|
+
const commands = target.resolved.server.command.join(" ");
|
|
7778
|
+
const known = provider === "typescript" ? /typescript-language-server|(?:^|[\\/ ])(?:tsc|tsgo)(?:\.exe|\.cmd)?(?: |$)/i.test(commands) : target.resolved.server.id === provider || new RegExp(provider, "i").test(basename6(target.resolved.tool.command[0] ?? ""));
|
|
7779
|
+
if (!known) throw new Error("unsupported: configured server has no validated overlay adapter");
|
|
7780
|
+
if (provider === "gopls")
|
|
7781
|
+
target.resolved.server.initialization = {
|
|
7782
|
+
...target.resolved.server.initialization,
|
|
7783
|
+
pullDiagnostics: true,
|
|
7784
|
+
diagnosticsTrigger: "Edit"
|
|
7785
|
+
};
|
|
7786
|
+
if (provider === "clangd") target.resolved.server.command.push("--background-index=false");
|
|
7787
|
+
const key = JSON.stringify([target.root, target.resolved.server, target.resolved.tool.identity]);
|
|
7788
|
+
const group = groups.get(key) ?? { target, provider, files: [] };
|
|
7789
|
+
group.files.push(file);
|
|
7790
|
+
groups.set(key, group);
|
|
7791
|
+
} catch (error) {
|
|
7792
|
+
reports.push(failure(language, [file.path], error));
|
|
7793
|
+
}
|
|
7794
|
+
}
|
|
7795
|
+
try {
|
|
7796
|
+
const runner = await select(this.root, file.absolute, false, this.config, false, bounded2);
|
|
7797
|
+
if (runner) {
|
|
7798
|
+
const key = JSON.stringify(runner);
|
|
7799
|
+
const group = lintGroups.get(key) ?? { runner, files: [] };
|
|
7800
|
+
group.files.push(file);
|
|
7801
|
+
lintGroups.set(key, group);
|
|
7802
|
+
}
|
|
7803
|
+
} catch (error) {
|
|
7804
|
+
reports.push(failure("lint", [file.path], error));
|
|
7805
|
+
}
|
|
7806
|
+
}
|
|
7807
|
+
for (const { target, provider, files: files2 } of groups.values())
|
|
7808
|
+
jobs.push(async () => {
|
|
7809
|
+
if ([...groups.values()].filter((group) => group.target.resolved.language === target.resolved.language).length > 1)
|
|
7810
|
+
return validationResult(
|
|
7811
|
+
"unsupported",
|
|
7812
|
+
files2.map((file) => file.path),
|
|
7813
|
+
"Cross-project overlays for one language are unsupported; narrow the plan and validationPaths to one project",
|
|
7814
|
+
[],
|
|
7815
|
+
[],
|
|
7816
|
+
provider
|
|
7817
|
+
);
|
|
7818
|
+
const checkSignal = AbortSignal.any([bounded2, AbortSignal.timeout(BUDGET.validationServer)]);
|
|
7819
|
+
const paths2 = files2.map((file) => file.path);
|
|
7820
|
+
const run2 = async (root, files3, target2, phase) => {
|
|
7821
|
+
const isolated = new _Languages(root, this.config);
|
|
7822
|
+
try {
|
|
7823
|
+
return await withExecution(
|
|
7824
|
+
executionEnvironment(),
|
|
7825
|
+
true,
|
|
7826
|
+
() => isolated.withLspClient(
|
|
7827
|
+
target2.root,
|
|
7828
|
+
async (client) => {
|
|
7829
|
+
if (!(client instanceof Client)) throw new Error("Unexpected LSP client");
|
|
7830
|
+
const collect = async (after2) => {
|
|
7831
|
+
await phase?.(after2);
|
|
7832
|
+
if (phase && after2)
|
|
7833
|
+
await client.refresh(files3.map((file) => ({ path: file.absolute, type: 2 })));
|
|
7834
|
+
for (const file of files3) {
|
|
7835
|
+
checkSignal.throwIfAborted();
|
|
7836
|
+
await client.openContent(file.absolute, after2 ? file.after : file.before, true);
|
|
7837
|
+
}
|
|
7838
|
+
for (const file of files3) {
|
|
7839
|
+
checkSignal.throwIfAborted();
|
|
7840
|
+
await client.reassertOverlay(file.absolute, after2 ? file.after : file.before);
|
|
7841
|
+
}
|
|
7842
|
+
const findings = [];
|
|
7843
|
+
for (const file of files3)
|
|
7844
|
+
findings.push(
|
|
7845
|
+
...diagnosticFindings(
|
|
7846
|
+
file.path,
|
|
7847
|
+
await client.overlayDiagnostics(file.absolute, checkSignal)
|
|
7848
|
+
)
|
|
7849
|
+
);
|
|
7850
|
+
return findings;
|
|
7851
|
+
};
|
|
7852
|
+
const before = await collect(false), after = await collect(true);
|
|
7853
|
+
if (provider === "clangd" && before.some(
|
|
7854
|
+
(finding) => /file not found|cannot open (?:source|include) file|unknown argument/i.test(
|
|
7855
|
+
finding.message
|
|
7856
|
+
)
|
|
7857
|
+
))
|
|
7858
|
+
return validationResult(
|
|
7859
|
+
"unsupported",
|
|
7860
|
+
paths2,
|
|
7861
|
+
"C/C++ baseline compilation context is incomplete",
|
|
7862
|
+
[],
|
|
7863
|
+
[],
|
|
7864
|
+
provider
|
|
7865
|
+
);
|
|
7866
|
+
return validationResult("complete", paths2, void 0, before, after, provider);
|
|
7867
|
+
},
|
|
7868
|
+
"plan validation",
|
|
7869
|
+
{ manager: isolated.manager, signal: checkSignal, target: target2 }
|
|
7870
|
+
)
|
|
7871
|
+
);
|
|
7872
|
+
} finally {
|
|
7873
|
+
await isolated.close();
|
|
7874
|
+
}
|
|
7875
|
+
};
|
|
7876
|
+
try {
|
|
7877
|
+
if (provider !== "clangd") return await run2(this.root, files2, target);
|
|
7878
|
+
return await withPreviewWorkspace(this.root, scope2, this.config, checkSignal, async (workspace) => {
|
|
7879
|
+
const mapped = files2.map((file) => ({ ...file, absolute: workspace.map(file.path) }));
|
|
7880
|
+
const shadowTarget = {
|
|
7881
|
+
...target,
|
|
7882
|
+
root: workspace.map(target.root),
|
|
7883
|
+
resolved: {
|
|
7884
|
+
...target.resolved,
|
|
7885
|
+
server: {
|
|
7886
|
+
...target.resolved.server,
|
|
7887
|
+
command: target.resolved.server.command.map(workspace.translate)
|
|
7888
|
+
}
|
|
7889
|
+
}
|
|
7890
|
+
};
|
|
7891
|
+
const result = await run2(workspace.root, mapped, shadowTarget, workspace.writePhase);
|
|
7892
|
+
result.limitations.push(
|
|
7893
|
+
"Bounded temporary project snapshot with private header contents and remapped local compilation paths."
|
|
7894
|
+
);
|
|
7895
|
+
return result;
|
|
7896
|
+
});
|
|
7897
|
+
} catch (error) {
|
|
7898
|
+
return failure(provider, paths2, error, checkSignal.aborted);
|
|
7899
|
+
}
|
|
7900
|
+
});
|
|
7901
|
+
for (const { runner, files: files2 } of lintGroups.values())
|
|
7902
|
+
jobs.push(async () => {
|
|
7903
|
+
const paths2 = files2.map((file) => file.path);
|
|
7904
|
+
try {
|
|
7905
|
+
return await withExecution(executionEnvironment(), true, async () => {
|
|
7906
|
+
if (runner.name === "biome")
|
|
7907
|
+
return validateBiome(runner, this.root, scope2, files2, this.config, bounded2);
|
|
7908
|
+
const before = [], after = [];
|
|
7909
|
+
for (const file of files2) {
|
|
7910
|
+
before.push(...await lintContent(runner, this.root, file.path, file.before, bounded2));
|
|
7911
|
+
after.push(...await lintContent(runner, this.root, file.path, file.after, bounded2));
|
|
7912
|
+
}
|
|
7913
|
+
const result = validationResult("complete", paths2, void 0, before, after, runner.name);
|
|
7914
|
+
result.limitations.push(
|
|
7915
|
+
"Stdin lint is file-level; custom rules reading other files still see original disk contents."
|
|
7916
|
+
);
|
|
7917
|
+
return result;
|
|
7918
|
+
});
|
|
7919
|
+
} catch (error) {
|
|
7920
|
+
return failure(runner.name, paths2, error);
|
|
7921
|
+
}
|
|
7922
|
+
});
|
|
7923
|
+
let next = 0;
|
|
7924
|
+
const completed = new Array(jobs.length);
|
|
7925
|
+
const settled = await Promise.allSettled(
|
|
7926
|
+
Array.from({ length: Math.min(4, jobs.length) }, async () => {
|
|
7927
|
+
while (next < jobs.length) {
|
|
7928
|
+
const index = next++, job = jobs[index];
|
|
7929
|
+
if (job) completed[index] = await job();
|
|
7930
|
+
}
|
|
7931
|
+
})
|
|
7932
|
+
);
|
|
7933
|
+
signal.throwIfAborted();
|
|
7934
|
+
for (const result of settled) if (result.status === "rejected") throw result.reason;
|
|
7935
|
+
return combineValidation(paths, [...reports, ...completed]);
|
|
7936
|
+
}
|
|
7937
|
+
async serverTarget(path, signal, allowTemporary = true) {
|
|
7938
|
+
const resolved = await resolveServer(this.root, path, this.config, allowTemporary);
|
|
7201
7939
|
if (resolved.tool.source === "missing") throw new Error(resolved.tool.note);
|
|
7202
|
-
let root = await findWorkspaceRoot(path, resolved.server, { signal
|
|
7940
|
+
let root = await findWorkspaceRoot(path, resolved.server, { signal });
|
|
7203
7941
|
if (!inside(this.root, root)) root = this.root;
|
|
7204
7942
|
if (resolved.language === "python" && resolved.tool.source === "project" && resolved.tool.command[0]?.includes(".venv"))
|
|
7205
|
-
root =
|
|
7943
|
+
root = dirname10(dirname10(dirname10(resolved.tool.command[0])));
|
|
7944
|
+
return { root, resolved };
|
|
7945
|
+
}
|
|
7946
|
+
async withLspClient(path, fn, _tool, options) {
|
|
7947
|
+
const { root, resolved } = options.target ?? await this.serverTarget(path, options.signal);
|
|
7206
7948
|
const failureKey = hash(JSON.stringify([root, resolved.server, resolved.tool.identity]));
|
|
7207
7949
|
const failed = this.failures.get(failureKey);
|
|
7208
7950
|
if (failed && Date.now() < failed.retryAt) throw new Error(failed.reason);
|
|
@@ -7323,7 +8065,7 @@ var init_language = __esm({
|
|
|
7323
8065
|
}
|
|
7324
8066
|
const pos = await position2(path, args, symbols);
|
|
7325
8067
|
await client.openFile(path);
|
|
7326
|
-
const textDocument = { uri:
|
|
8068
|
+
const textDocument = { uri: pathToFileURL5(path).href };
|
|
7327
8069
|
const point = { line: pos.line - 1, character: pos.column - 1 };
|
|
7328
8070
|
let result;
|
|
7329
8071
|
if (operation === "rename") {
|
|
@@ -7405,8 +8147,8 @@ var init_language = __esm({
|
|
|
7405
8147
|
|
|
7406
8148
|
// src/engine.ts
|
|
7407
8149
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
7408
|
-
import { readFile as
|
|
7409
|
-
import { dirname as
|
|
8150
|
+
import { readFile as readFile17, stat as stat7 } from "node:fs/promises";
|
|
8151
|
+
import { dirname as dirname11, relative as relative9, sep as sep4 } from "node:path";
|
|
7410
8152
|
var Engine;
|
|
7411
8153
|
var init_engine = __esm({
|
|
7412
8154
|
"src/engine.ts"() {
|
|
@@ -7414,6 +8156,7 @@ var init_engine = __esm({
|
|
|
7414
8156
|
init_budgets();
|
|
7415
8157
|
init_config();
|
|
7416
8158
|
init_config_files();
|
|
8159
|
+
init_edit_plans();
|
|
7417
8160
|
init_environment();
|
|
7418
8161
|
init_files();
|
|
7419
8162
|
init_hook_events();
|
|
@@ -7464,26 +8207,40 @@ var init_engine = __esm({
|
|
|
7464
8207
|
this.generation = 0;
|
|
7465
8208
|
this.pages = /* @__PURE__ */ new Map();
|
|
7466
8209
|
}
|
|
8210
|
+
hasLivePlans() {
|
|
8211
|
+
return this.intelligence?.plans.hasLivePlans() ?? false;
|
|
8212
|
+
}
|
|
7467
8213
|
async warmup(signal) {
|
|
7468
8214
|
const config = await configuration(this.root);
|
|
7469
8215
|
if (config.automaticDiagnostics.postToolUse === "off" && config.automaticDiagnostics.stop === "off") return;
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
8216
|
+
await this.dispatch("warmup", {}, signal);
|
|
8217
|
+
}
|
|
8218
|
+
async prepareServers(args, config, signal) {
|
|
8219
|
+
if (args["cursor"]) throw new Error("Cursor invalid; repeat warmup without cursor");
|
|
8220
|
+
const bounded2 = AbortSignal.any([signal, AbortSignal.timeout(BUDGET.warmup)]);
|
|
8221
|
+
let paths;
|
|
8222
|
+
if (args["path"] || args["paths"]) paths = (await this.paths(args, bounded2)).paths;
|
|
8223
|
+
else {
|
|
8224
|
+
const snapshot = await inventory(this.root, BUDGET.files, bounded2, config.exclude);
|
|
8225
|
+
if (!snapshot.complete)
|
|
8226
|
+
return { operation: "warmup", status: "partial", items: [], reason: "Workspace inventory incomplete" };
|
|
8227
|
+
const representatives = /* @__PURE__ */ new Map();
|
|
8228
|
+
for (const path of [...snapshot.files.keys()].sort()) {
|
|
8229
|
+
const language = codeLanguage(config, path);
|
|
8230
|
+
if (!language || configurationImpact(path).length || ["json", "yaml", "css", "html"].includes(language))
|
|
8231
|
+
continue;
|
|
8232
|
+
const entry = representatives.get(language);
|
|
8233
|
+
if (entry) entry.count++;
|
|
8234
|
+
else representatives.set(language, { path, count: 1 });
|
|
8235
|
+
}
|
|
8236
|
+
paths = [...representatives.entries()].sort(([a, x], [b, y]) => y.count - x.count || a.localeCompare(b)).slice(0, 2).map(([, entry]) => entry.path);
|
|
7480
8237
|
}
|
|
8238
|
+
if (paths.length > 200) throw new Error("At most 200 explicit warmup files; narrow scope");
|
|
7481
8239
|
this.language ??= new Languages(this.root, config);
|
|
7482
8240
|
await this.language.update(config);
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
}
|
|
8241
|
+
const result = await this.language.warmup(paths, bounded2);
|
|
8242
|
+
signal.throwIfAborted();
|
|
8243
|
+
return { operation: "warmup", ...result };
|
|
7487
8244
|
}
|
|
7488
8245
|
session(id, turn) {
|
|
7489
8246
|
let session = this.sessions.get(id);
|
|
@@ -7538,16 +8295,16 @@ var init_engine = __esm({
|
|
|
7538
8295
|
signal.throwIfAborted();
|
|
7539
8296
|
try {
|
|
7540
8297
|
const absolute = await workspacePath(this.root, requested);
|
|
7541
|
-
const path =
|
|
8298
|
+
const path = relative9(this.root, absolute);
|
|
7542
8299
|
session.touched.add(path);
|
|
7543
8300
|
session.current.add(path);
|
|
7544
|
-
if ((await
|
|
8301
|
+
if ((await stat7(absolute)).size > BUDGET.fileBytes) {
|
|
7545
8302
|
results.set(path, { path, state: "skipped", findings: [], note: "File exceeds 1 MiB" });
|
|
7546
8303
|
continue;
|
|
7547
8304
|
}
|
|
7548
8305
|
const identity = await analysisIdentity(this.root, [path], config, signal);
|
|
7549
8306
|
this.identities.set(path, identity);
|
|
7550
|
-
const content = hash(await
|
|
8307
|
+
const content = hash(await readFile17(absolute, { encoding: "utf8", signal }));
|
|
7551
8308
|
const key = `${source === "both" ? "" : `${source}:`}${path}`;
|
|
7552
8309
|
const cached = this.cache.get(key);
|
|
7553
8310
|
if (snapshot.complete && cached?.version === this.projectVersion(snapshot, path, config) && cached.identity === identity && cached.content === content && cached.result.state === "complete")
|
|
@@ -7574,7 +8331,7 @@ var init_engine = __esm({
|
|
|
7574
8331
|
note: "Checker returned no result"
|
|
7575
8332
|
};
|
|
7576
8333
|
try {
|
|
7577
|
-
if (hash(await
|
|
8334
|
+
if (hash(await readFile17(await workspacePath(this.root, entry.path), { encoding: "utf8", signal })) !== entry.content || await analysisIdentity(this.root, [entry.path], await configuration(this.root), signal) !== entry.identity || this.projectVersion(latestInventory(this.root) ?? snapshot, entry.path, config) !== this.projectVersion(snapshot, entry.path, config)) {
|
|
7578
8335
|
result.state = "stale";
|
|
7579
8336
|
result.note = "File/tool/configuration changed during diagnostics; retry";
|
|
7580
8337
|
}
|
|
@@ -7613,13 +8370,13 @@ var init_engine = __esm({
|
|
|
7613
8370
|
return withToolResolution(() => this.execute(operation, args, signal));
|
|
7614
8371
|
});
|
|
7615
8372
|
this.queue = task.catch(() => void 0);
|
|
7616
|
-
return new Promise((
|
|
8373
|
+
return new Promise((resolve16, reject) => {
|
|
7617
8374
|
const abort = () => {
|
|
7618
8375
|
if (!started || !writes) reject(new Error("Request cancelled while queued or executing"));
|
|
7619
8376
|
};
|
|
7620
8377
|
signal.addEventListener("abort", abort, { once: true });
|
|
7621
8378
|
if (signal.aborted) abort();
|
|
7622
|
-
void task.then(
|
|
8379
|
+
void task.then(resolve16, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
7623
8380
|
});
|
|
7624
8381
|
}
|
|
7625
8382
|
async paths(args, signal) {
|
|
@@ -7633,7 +8390,7 @@ var init_engine = __esm({
|
|
|
7633
8390
|
if (typeof value !== "string") continue;
|
|
7634
8391
|
signal.throwIfAborted();
|
|
7635
8392
|
const absolute = await workspacePath(this.root, value);
|
|
7636
|
-
if ((await
|
|
8393
|
+
if ((await stat7(absolute)).isFile()) paths.add(relative9(this.root, absolute));
|
|
7637
8394
|
else {
|
|
7638
8395
|
const scoped = await inventory(this.root, BUDGET.files, signal, config.exclude, absolute);
|
|
7639
8396
|
complete &&= scoped.complete;
|
|
@@ -7713,6 +8470,7 @@ var init_engine = __esm({
|
|
|
7713
8470
|
};
|
|
7714
8471
|
}
|
|
7715
8472
|
if (!(operation === "diagnostics" && args["run"] === "cached")) assertConfiguration(config);
|
|
8473
|
+
if (operation === "warmup") return this.prepareServers(args, config, signal);
|
|
7716
8474
|
if (operation === "automatic_batch") {
|
|
7717
8475
|
const paths = Array.isArray(args["paths"]) ? args["paths"].filter((path) => typeof path === "string") : [];
|
|
7718
8476
|
return {
|
|
@@ -7767,14 +8525,18 @@ var init_engine = __esm({
|
|
|
7767
8525
|
.../* @__PURE__ */ new Set([
|
|
7768
8526
|
...paths,
|
|
7769
8527
|
...typeof args["path"] === "string" ? [args["path"]] : [],
|
|
7770
|
-
...Array.isArray(args["paths"]) ? args["paths"].filter((path) => typeof path === "string") : []
|
|
8528
|
+
...Array.isArray(args["paths"]) ? args["paths"].filter((path) => typeof path === "string") : [],
|
|
8529
|
+
...Array.isArray(args["validationPaths"]) ? args["validationPaths"].filter((path) => typeof path === "string") : []
|
|
7771
8530
|
])
|
|
7772
8531
|
];
|
|
7773
8532
|
let identity;
|
|
7774
8533
|
try {
|
|
7775
8534
|
identity = await this.intelligenceIdentity(bindings, config, signal);
|
|
7776
8535
|
} catch (error) {
|
|
7777
|
-
if (operation === "apply")
|
|
8536
|
+
if (operation === "apply") {
|
|
8537
|
+
this.intelligence.plans.discard(text(args["plan"]));
|
|
8538
|
+
throw new PlanFailure("stale", `Plan stale: ${message(error)}; preview again`);
|
|
8539
|
+
}
|
|
7778
8540
|
throw error;
|
|
7779
8541
|
}
|
|
7780
8542
|
if (operation === "apply") {
|
|
@@ -7798,8 +8560,10 @@ var init_engine = __esm({
|
|
|
7798
8560
|
}
|
|
7799
8561
|
}
|
|
7800
8562
|
const output = await this.intelligence.execute(operation, args, paths, config, identity, signal, bindings);
|
|
7801
|
-
if (identity !== await this.intelligenceIdentity(bindings, config, signal))
|
|
8563
|
+
if (identity !== await this.intelligenceIdentity(bindings, config, signal)) {
|
|
8564
|
+
if (typeof output["plan"] === "string") this.intelligence.plans.discard(output["plan"]);
|
|
7802
8565
|
throw new Error("Workspace or tools changed during query; repeat without cursor");
|
|
8566
|
+
}
|
|
7803
8567
|
return { ...output, operation };
|
|
7804
8568
|
}
|
|
7805
8569
|
throw new Error("Unknown tool");
|
|
@@ -7907,13 +8671,13 @@ var init_engine = __esm({
|
|
|
7907
8671
|
for (const value of values) {
|
|
7908
8672
|
if (typeof value !== "string") throw new Error("paths requires strings");
|
|
7909
8673
|
const absolute = await workspacePath(this.root, value);
|
|
7910
|
-
const path =
|
|
7911
|
-
if ((await
|
|
8674
|
+
const path = relative9(this.root, absolute);
|
|
8675
|
+
if ((await stat7(absolute)).isFile()) {
|
|
7912
8676
|
if (!codeLanguage(config, path)) throw new Error(`Language disabled or unsupported: ${path}`);
|
|
7913
8677
|
selected2.add(path);
|
|
7914
8678
|
} else
|
|
7915
8679
|
for (const candidate of (snapshot.complete ? snapshot : await inventory(this.root, BUDGET.files, signal, config.exclude, absolute)).files.keys())
|
|
7916
|
-
if ((!path || candidate.startsWith(`${path}${
|
|
8680
|
+
if ((!path || candidate.startsWith(`${path}${sep4}`)) && codeLanguage(config, candidate))
|
|
7917
8681
|
selected2.add(candidate);
|
|
7918
8682
|
}
|
|
7919
8683
|
paths = [...selected2].sort();
|
|
@@ -8024,11 +8788,11 @@ var init_engine = __esm({
|
|
|
8024
8788
|
}
|
|
8025
8789
|
projectVersion(snapshot, path, config) {
|
|
8026
8790
|
const language = codeLanguage(config, path);
|
|
8027
|
-
let owner =
|
|
8791
|
+
let owner = dirname11(path);
|
|
8028
8792
|
while (owner !== "." && ![...snapshot.files.keys()].some(
|
|
8029
|
-
(candidate) =>
|
|
8793
|
+
(candidate) => dirname11(candidate) === owner && configurationLanguages(candidate).includes(language ?? "")
|
|
8030
8794
|
))
|
|
8031
|
-
owner =
|
|
8795
|
+
owner = dirname11(owner);
|
|
8032
8796
|
const prefix = owner === "." ? "" : `${owner}/`;
|
|
8033
8797
|
return hash(
|
|
8034
8798
|
JSON.stringify(
|
|
@@ -8043,9 +8807,9 @@ var init_engine = __esm({
|
|
|
8043
8807
|
if (existing) return existing;
|
|
8044
8808
|
try {
|
|
8045
8809
|
const absolute = await workspacePath(this.root, path);
|
|
8046
|
-
const info = await
|
|
8810
|
+
const info = await stat7(absolute);
|
|
8047
8811
|
if (info.size > BUDGET.fileBytes) return "oversized";
|
|
8048
|
-
return hash(await
|
|
8812
|
+
return hash(await readFile17(absolute, { encoding: "utf8", signal }));
|
|
8049
8813
|
} catch {
|
|
8050
8814
|
signal.throwIfAborted();
|
|
8051
8815
|
return "unavailable";
|
|
@@ -8075,29 +8839,29 @@ var init_engine = __esm({
|
|
|
8075
8839
|
// src/ipc.ts
|
|
8076
8840
|
import { spawn as spawn4 } from "node:child_process";
|
|
8077
8841
|
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
8078
|
-
import { chmod, lstat as lstat5, mkdir as
|
|
8842
|
+
import { chmod, lstat as lstat5, mkdir as mkdir6, readFile as readFile18, writeFile as writeFile6 } from "node:fs/promises";
|
|
8079
8843
|
import { createConnection } from "node:net";
|
|
8080
|
-
import { homedir as homedir6, tmpdir as
|
|
8081
|
-
import { join as
|
|
8844
|
+
import { homedir as homedir6, tmpdir as tmpdir4 } from "node:os";
|
|
8845
|
+
import { join as join16 } from "node:path";
|
|
8082
8846
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
8083
8847
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
8084
8848
|
async function serviceLocation(root) {
|
|
8085
8849
|
const user = process.getuid?.() ?? hash(homedir6()).slice(0, 10);
|
|
8086
|
-
const directory2 =
|
|
8087
|
-
await
|
|
8850
|
+
const directory2 = join16(tmpdir4(), `clsp6-${user}`);
|
|
8851
|
+
await mkdir6(directory2, { recursive: true, mode: 448 });
|
|
8088
8852
|
const info = await lstat5(directory2);
|
|
8089
8853
|
if (info.isSymbolicLink() || process.platform !== "win32" && (info.uid !== process.getuid?.() || info.mode & 63))
|
|
8090
8854
|
throw new Error("Unsafe service directory permissions");
|
|
8091
8855
|
const identity = await workspaceIdentity(root);
|
|
8092
|
-
const address = process.platform === "win32" ? `\\\\.\\pipe\\codex-lsp-${user}-${identity}` :
|
|
8093
|
-
return { directory: directory2, identity, address, endpoint:
|
|
8856
|
+
const address = process.platform === "win32" ? `\\\\.\\pipe\\codex-lsp-${user}-${identity}` : join16(directory2, `${identity.slice(0, 32)}.sock`);
|
|
8857
|
+
return { directory: directory2, identity, address, endpoint: join16(directory2, `${identity}.endpoint`) };
|
|
8094
8858
|
}
|
|
8095
8859
|
async function readEndpoint(location) {
|
|
8096
8860
|
try {
|
|
8097
8861
|
const info = await lstat5(location.endpoint);
|
|
8098
8862
|
if (info.isSymbolicLink() || process.platform !== "win32" && (info.uid !== process.getuid?.() || info.mode & 63))
|
|
8099
8863
|
throw new Error("Unsafe service endpoint permissions");
|
|
8100
|
-
const value = JSON.parse(await
|
|
8864
|
+
const value = JSON.parse(await readFile18(location.endpoint, "utf8"));
|
|
8101
8865
|
if (!record(value) || value["identity"] !== location.identity || value["protocol"] !== SERVICE_PROTOCOL || typeof value["pid"] !== "number" || typeof value["token"] !== "string" || value["address"] !== location.address)
|
|
8102
8866
|
throw new Error("Invalid service endpoint");
|
|
8103
8867
|
return value;
|
|
@@ -8108,7 +8872,7 @@ async function readEndpoint(location) {
|
|
|
8108
8872
|
}
|
|
8109
8873
|
async function exchange(endpoint, operation, args, signal) {
|
|
8110
8874
|
signal.throwIfAborted();
|
|
8111
|
-
return new Promise((
|
|
8875
|
+
return new Promise((resolve16, reject) => {
|
|
8112
8876
|
const socket = createConnection(endpoint.address);
|
|
8113
8877
|
let received = "";
|
|
8114
8878
|
let settled = false;
|
|
@@ -8119,7 +8883,7 @@ async function exchange(endpoint, operation, args, signal) {
|
|
|
8119
8883
|
signal.removeEventListener("abort", cancel);
|
|
8120
8884
|
socket.destroy();
|
|
8121
8885
|
if (error) reject(error);
|
|
8122
|
-
else if (output)
|
|
8886
|
+
else if (output) resolve16(output);
|
|
8123
8887
|
else reject(new Error("Missing service output"));
|
|
8124
8888
|
};
|
|
8125
8889
|
const cancel = () => {
|
|
@@ -8159,8 +8923,9 @@ async function exchange(endpoint, operation, args, signal) {
|
|
|
8159
8923
|
finish(
|
|
8160
8924
|
Array.isArray(paths) ? new WriteFailure(
|
|
8161
8925
|
response["error"],
|
|
8162
|
-
paths.filter((path) => typeof path === "string")
|
|
8163
|
-
|
|
8926
|
+
paths.filter((path) => typeof path === "string"),
|
|
8927
|
+
typeof response["planFailure"] === "string" ? response["planFailure"] : void 0
|
|
8928
|
+
) : isPlanFailureCode(response["planFailure"]) ? new PlanFailure(response["planFailure"], response["error"]) : new Error(response["error"])
|
|
8164
8929
|
);
|
|
8165
8930
|
} else if (record(response["output"]) && typeof response["output"]["operation"] === "string")
|
|
8166
8931
|
finish(void 0, response["output"]);
|
|
@@ -8233,7 +8998,7 @@ async function ensureService(root, signal) {
|
|
|
8233
8998
|
async function publishEndpoint(location, token) {
|
|
8234
8999
|
if (process.platform !== "win32") await chmod(location.address, 384);
|
|
8235
9000
|
const temporary2 = `${location.endpoint}.${randomUUID9()}`;
|
|
8236
|
-
await
|
|
9001
|
+
await writeFile6(
|
|
8237
9002
|
temporary2,
|
|
8238
9003
|
JSON.stringify({
|
|
8239
9004
|
pid: process.pid,
|
|
@@ -8251,6 +9016,7 @@ var init_ipc = __esm({
|
|
|
8251
9016
|
"use strict";
|
|
8252
9017
|
init_directory_lock();
|
|
8253
9018
|
init_directory_lock();
|
|
9019
|
+
init_edit_plans();
|
|
8254
9020
|
init_environment();
|
|
8255
9021
|
init_files();
|
|
8256
9022
|
init_process_options();
|
|
@@ -8261,8 +9027,8 @@ var init_ipc = __esm({
|
|
|
8261
9027
|
|
|
8262
9028
|
// src/hook-inbox.ts
|
|
8263
9029
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
8264
|
-
import { lstat as lstat6, readdir as
|
|
8265
|
-
import { join as
|
|
9030
|
+
import { lstat as lstat6, readdir as readdir5, readFile as readFile19, rm as rm5, writeFile as writeFile7 } from "node:fs/promises";
|
|
9031
|
+
import { join as join17 } from "node:path";
|
|
8266
9032
|
async function registerHook(root, input, signal) {
|
|
8267
9033
|
const location = await serviceLocation(root);
|
|
8268
9034
|
const parent = text(input["session_id"]);
|
|
@@ -8328,8 +9094,8 @@ async function registerHook(root, input, signal) {
|
|
|
8328
9094
|
return true;
|
|
8329
9095
|
});
|
|
8330
9096
|
if (terminal) return { id, session };
|
|
8331
|
-
const path =
|
|
8332
|
-
await
|
|
9097
|
+
const path = join17(location.directory, `${location.identity}.${id}.hook`);
|
|
9098
|
+
await writeFile7(
|
|
8333
9099
|
path,
|
|
8334
9100
|
JSON.stringify({
|
|
8335
9101
|
protocol: SERVICE_PROTOCOL,
|
|
@@ -8350,14 +9116,14 @@ async function registerHook(root, input, signal) {
|
|
|
8350
9116
|
async function registeredHooks(root) {
|
|
8351
9117
|
const location = await serviceLocation(root);
|
|
8352
9118
|
const result = [];
|
|
8353
|
-
for (const name of (await
|
|
9119
|
+
for (const name of (await readdir5(location.directory)).sort()) {
|
|
8354
9120
|
if (!name.startsWith(`${location.identity}.`) || !name.endsWith(".hook")) continue;
|
|
8355
|
-
const path =
|
|
9121
|
+
const path = join17(location.directory, name);
|
|
8356
9122
|
try {
|
|
8357
9123
|
const info = await lstat6(path);
|
|
8358
9124
|
if (info.isSymbolicLink() || info.size > 1024 * 1024 || process.platform !== "win32" && (info.uid !== process.getuid?.() || info.mode & 63))
|
|
8359
9125
|
throw new Error("Unsafe hook registration");
|
|
8360
|
-
const value = JSON.parse(await
|
|
9126
|
+
const value = JSON.parse(await readFile19(path, "utf8"));
|
|
8361
9127
|
if (!record(value) || value["identity"] !== location.identity || value["protocol"] !== SERVICE_PROTOCOL || typeof value["epoch"] !== "string" || typeof value["id"] !== "string" || typeof value["created"] !== "number" || typeof value["session"] !== "string" || !record(value["input"]) || !record(value["environment"]) || !Object.values(value["environment"]).every((entry) => typeof entry === "string"))
|
|
8362
9128
|
throw new Error("Invalid hook registration");
|
|
8363
9129
|
result.push({
|
|
@@ -8370,7 +9136,7 @@ async function registeredHooks(root) {
|
|
|
8370
9136
|
environment: value["environment"]
|
|
8371
9137
|
});
|
|
8372
9138
|
} catch {
|
|
8373
|
-
await
|
|
9139
|
+
await rm5(path, { force: true });
|
|
8374
9140
|
}
|
|
8375
9141
|
}
|
|
8376
9142
|
return result;
|
|
@@ -8399,6 +9165,7 @@ var init_runtime = __esm({
|
|
|
8399
9165
|
"use strict";
|
|
8400
9166
|
init_budgets();
|
|
8401
9167
|
init_config();
|
|
9168
|
+
init_edit_plans();
|
|
8402
9169
|
init_engine();
|
|
8403
9170
|
init_environment();
|
|
8404
9171
|
init_files();
|
|
@@ -8571,9 +9338,18 @@ var init_runtime = __esm({
|
|
|
8571
9338
|
const endpoint = existing ?? await ensureService(root, signal);
|
|
8572
9339
|
return await this.output(root, operation, args, await exchange(endpoint, operation, args, signal), signal);
|
|
8573
9340
|
} catch (error) {
|
|
8574
|
-
if ([
|
|
8575
|
-
|
|
8576
|
-
|
|
9341
|
+
if ([
|
|
9342
|
+
"check",
|
|
9343
|
+
"diagnostics",
|
|
9344
|
+
"status",
|
|
9345
|
+
"warmup",
|
|
9346
|
+
"navigate",
|
|
9347
|
+
"rename",
|
|
9348
|
+
"format",
|
|
9349
|
+
"inspect",
|
|
9350
|
+
"actions",
|
|
9351
|
+
"apply"
|
|
9352
|
+
].includes(operation))
|
|
8577
9353
|
return this.output(
|
|
8578
9354
|
root,
|
|
8579
9355
|
operation,
|
|
@@ -8583,6 +9359,7 @@ var init_runtime = __esm({
|
|
|
8583
9359
|
isError: true,
|
|
8584
9360
|
status: failureKind(message(error)),
|
|
8585
9361
|
reason: message(error),
|
|
9362
|
+
...error instanceof PlanFailure || error instanceof WriteFailure && error.planFailure ? { planFailure: error.planFailure } : {},
|
|
8586
9363
|
...operation === "apply" ? { modifiedPaths: error instanceof WriteFailure ? error.modifiedPaths : [] } : {}
|
|
8587
9364
|
},
|
|
8588
9365
|
new AbortController().signal
|
|
@@ -8660,8 +9437,8 @@ async function runHookCli() {
|
|
|
8660
9437
|
const output = renderHook(result["output"]);
|
|
8661
9438
|
if (output) {
|
|
8662
9439
|
await new Promise(
|
|
8663
|
-
(
|
|
8664
|
-
`, (error) => error ? reject(error) :
|
|
9440
|
+
(resolve16, reject) => process.stdout.write(`${JSON.stringify(output)}
|
|
9441
|
+
`, (error) => error ? reject(error) : resolve16())
|
|
8665
9442
|
);
|
|
8666
9443
|
if (typeof result["deliveryId"] === "string") {
|
|
8667
9444
|
await runtime.request(
|
|
@@ -8708,6 +9485,10 @@ function tool(name, description, properties, required, readOnly) {
|
|
|
8708
9485
|
};
|
|
8709
9486
|
}
|
|
8710
9487
|
function validateArguments(name, args) {
|
|
9488
|
+
if (name === "actions" && (args["validate"] !== void 0 || args["validationPaths"] !== void 0) && args["operation"] !== "preview" && !args["cursor"])
|
|
9489
|
+
throw new Error("validate requires actions operation=preview");
|
|
9490
|
+
if (args["validate"] === false && args["validationPaths"] !== void 0)
|
|
9491
|
+
throw new Error("validationPaths requires validation enabled");
|
|
8711
9492
|
if (args["refresh"] !== void 0 && !(name === "status" || ["diagnostics", "check"].includes(name) && args["run"] !== "cached"))
|
|
8712
9493
|
throw new Error("refresh requires active diagnostics or status");
|
|
8713
9494
|
const definition = TOOLS.find((entry) => entry.name === name);
|
|
@@ -8726,8 +9507,8 @@ function validateArguments(name, args) {
|
|
|
8726
9507
|
if (typeof value !== "number" || !Number.isInteger(value) || typeof schema["minimum"] === "number" && value < schema["minimum"] || typeof schema["maximum"] === "number" && value > schema["maximum"])
|
|
8727
9508
|
throw new Error(`Invalid integer: ${key}`);
|
|
8728
9509
|
}
|
|
8729
|
-
if (schema["type"] === "array" && (!Array.isArray(value) || value.length > 200 || !value.every((item) => typeof item === "string")))
|
|
8730
|
-
throw new Error(`${key} must contain at most 200 strings`);
|
|
9510
|
+
if (schema["type"] === "array" && (!Array.isArray(value) || value.length > (typeof schema["maxItems"] === "number" ? schema["maxItems"] : 200) || !value.every((item) => typeof item === "string")))
|
|
9511
|
+
throw new Error(`${key} must contain at most ${schema["maxItems"] ?? 200} strings`);
|
|
8731
9512
|
if (Array.isArray(schema["enum"]) && !schema["enum"].includes(value)) throw new Error(`Invalid ${key}`);
|
|
8732
9513
|
}
|
|
8733
9514
|
}
|
|
@@ -8759,7 +9540,7 @@ async function runMcp(input = process.stdin, output = process.stdout) {
|
|
|
8759
9540
|
if (method === "initialize") {
|
|
8760
9541
|
ok({
|
|
8761
9542
|
protocolVersion: text(params["protocolVersion"], "2024-11-05"),
|
|
8762
|
-
serverInfo: { name: "codex-codeintel", version: "0.10.
|
|
9543
|
+
serverInfo: { name: "codex-codeintel", version: "0.10.1" },
|
|
8763
9544
|
// keep in sync with package.json
|
|
8764
9545
|
capabilities: { tools: { listChanged: false } }
|
|
8765
9546
|
});
|
|
@@ -8872,7 +9653,7 @@ async function runMcp(input = process.stdin, output = process.stdout) {
|
|
|
8872
9653
|
output.removeListener("error", exit);
|
|
8873
9654
|
}
|
|
8874
9655
|
}
|
|
8875
|
-
var string, scope, paging, selector, cursor, choice, LEGACY_TOOLS, TOOLS;
|
|
9656
|
+
var string, scope, paging, selector, cursor, validation, choice, LEGACY_TOOLS, TOOLS;
|
|
8876
9657
|
var init_protocol = __esm({
|
|
8877
9658
|
"src/protocol.ts"() {
|
|
8878
9659
|
"use strict";
|
|
@@ -8896,6 +9677,18 @@ var init_protocol = __esm({
|
|
|
8896
9677
|
pattern: string
|
|
8897
9678
|
};
|
|
8898
9679
|
cursor = { cursor: string };
|
|
9680
|
+
validation = {
|
|
9681
|
+
validate: {
|
|
9682
|
+
type: "boolean",
|
|
9683
|
+
description: "Preview scoped diagnostics with installed local language/lint tools; false skips simulation."
|
|
9684
|
+
},
|
|
9685
|
+
validationPaths: {
|
|
9686
|
+
type: "array",
|
|
9687
|
+
items: string,
|
|
9688
|
+
maxItems: 20,
|
|
9689
|
+
description: "Up to 20 additional existing files (for example unchanged callers) to check against all plan overlays."
|
|
9690
|
+
}
|
|
9691
|
+
};
|
|
8899
9692
|
choice = (...values) => ({ type: "string", enum: values });
|
|
8900
9693
|
LEGACY_TOOLS = {
|
|
8901
9694
|
check_project: "check",
|
|
@@ -8936,6 +9729,13 @@ var init_protocol = __esm({
|
|
|
8936
9729
|
[],
|
|
8937
9730
|
true
|
|
8938
9731
|
),
|
|
9732
|
+
tool(
|
|
9733
|
+
"warmup",
|
|
9734
|
+
"Initialize local LSP servers without diagnostics or downloads. Explicit files or two representative languages.",
|
|
9735
|
+
{ ...scope, ...cursor },
|
|
9736
|
+
[],
|
|
9737
|
+
true
|
|
9738
|
+
),
|
|
8939
9739
|
tool(
|
|
8940
9740
|
"navigate",
|
|
8941
9741
|
"Read-only navigation; 1-based UTF-16 positions, symbol path or @@ anchor. Follow next for large results.",
|
|
@@ -8967,14 +9767,14 @@ var init_protocol = __esm({
|
|
|
8967
9767
|
tool(
|
|
8968
9768
|
"rename",
|
|
8969
9769
|
"Preview a cross-file rename. Returns a plan; only apply writes files.",
|
|
8970
|
-
{ ...scope, ...selector, ...cursor, newName: string },
|
|
9770
|
+
{ ...scope, ...selector, ...cursor, ...validation, newName: string },
|
|
8971
9771
|
["path", "newName"],
|
|
8972
9772
|
true
|
|
8973
9773
|
),
|
|
8974
9774
|
tool(
|
|
8975
9775
|
"format",
|
|
8976
9776
|
"Preview formatting of explicit files. Returns a plan; only apply writes files.",
|
|
8977
|
-
{ ...scope, ...cursor },
|
|
9777
|
+
{ ...scope, ...cursor, ...validation },
|
|
8978
9778
|
[],
|
|
8979
9779
|
true
|
|
8980
9780
|
),
|
|
@@ -9009,7 +9809,8 @@ var init_protocol = __esm({
|
|
|
9009
9809
|
source: choice("lint", "lsp", "both"),
|
|
9010
9810
|
rule: string,
|
|
9011
9811
|
ids: { type: "array", items: string, maxItems: 200 },
|
|
9012
|
-
details: { type: "boolean" }
|
|
9812
|
+
details: { type: "boolean" },
|
|
9813
|
+
...validation
|
|
9013
9814
|
},
|
|
9014
9815
|
["operation"],
|
|
9015
9816
|
true
|
|
@@ -9215,10 +10016,10 @@ var init_automatic = __esm({
|
|
|
9215
10016
|
// src/deadline.ts
|
|
9216
10017
|
function waitWithSignal(task, signal) {
|
|
9217
10018
|
signal.throwIfAborted();
|
|
9218
|
-
return new Promise((
|
|
10019
|
+
return new Promise((resolve16, reject) => {
|
|
9219
10020
|
const abort = () => reject(signal.reason);
|
|
9220
10021
|
signal.addEventListener("abort", abort, { once: true });
|
|
9221
|
-
task.then(
|
|
10022
|
+
task.then(resolve16, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
9222
10023
|
});
|
|
9223
10024
|
}
|
|
9224
10025
|
var init_deadline = __esm({
|
|
@@ -9228,7 +10029,7 @@ var init_deadline = __esm({
|
|
|
9228
10029
|
});
|
|
9229
10030
|
|
|
9230
10031
|
// src/hook-coordinator.ts
|
|
9231
|
-
import { rm as
|
|
10032
|
+
import { rm as rm6 } from "node:fs/promises";
|
|
9232
10033
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
9233
10034
|
var HookCoordinator;
|
|
9234
10035
|
var init_hook_coordinator = __esm({
|
|
@@ -9287,7 +10088,7 @@ var init_hook_coordinator = __esm({
|
|
|
9287
10088
|
const state = await store.read(entry.session);
|
|
9288
10089
|
const status = state.hookEvents[entry.id]?.state;
|
|
9289
10090
|
if (parent.epoch !== entry.epoch || state.epoch !== entry.epoch || status && !["queued", "running"].includes(status)) {
|
|
9290
|
-
await
|
|
10091
|
+
await rm6(entry.path, { force: true });
|
|
9291
10092
|
return;
|
|
9292
10093
|
}
|
|
9293
10094
|
this.seen.add(entry.path);
|
|
@@ -9297,7 +10098,7 @@ var init_hook_coordinator = __esm({
|
|
|
9297
10098
|
const item = current.hookEvents[entry.id];
|
|
9298
10099
|
if (item) item.state = "complete";
|
|
9299
10100
|
});
|
|
9300
|
-
await
|
|
10101
|
+
await rm6(entry.path, { force: true });
|
|
9301
10102
|
this.seen.delete(entry.path);
|
|
9302
10103
|
this.changed();
|
|
9303
10104
|
return;
|
|
@@ -9388,7 +10189,7 @@ var init_hook_coordinator = __esm({
|
|
|
9388
10189
|
} finally {
|
|
9389
10190
|
if (this.jobs.get(key) === job) this.jobs.delete(key);
|
|
9390
10191
|
for (const entry of job.entries) {
|
|
9391
|
-
await
|
|
10192
|
+
await rm6(entry.path, { force: true }).catch(() => logEvent("cancel-cleanup-failure"));
|
|
9392
10193
|
this.seen.delete(entry.path);
|
|
9393
10194
|
}
|
|
9394
10195
|
this.tasks.delete(job);
|
|
@@ -9439,8 +10240,8 @@ var init_hook_coordinator = __esm({
|
|
|
9439
10240
|
const first = await inspect();
|
|
9440
10241
|
if (!["queued", "running"].includes(first.state ?? "") || input["revision"] !== this.revision) return first;
|
|
9441
10242
|
let wake = () => void 0;
|
|
9442
|
-
const change = new Promise((
|
|
9443
|
-
wake =
|
|
10243
|
+
const change = new Promise((resolve16) => {
|
|
10244
|
+
wake = resolve16;
|
|
9444
10245
|
this.listeners.add(wake);
|
|
9445
10246
|
});
|
|
9446
10247
|
try {
|
|
@@ -9584,14 +10385,14 @@ var init_go_output = __esm({
|
|
|
9584
10385
|
});
|
|
9585
10386
|
|
|
9586
10387
|
// src/project-output.ts
|
|
9587
|
-
import { readFile as
|
|
9588
|
-
import { relative as
|
|
10388
|
+
import { readFile as readFile20 } from "node:fs/promises";
|
|
10389
|
+
import { relative as relative10, resolve as resolve13 } from "node:path";
|
|
9589
10390
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
9590
10391
|
async function projectOutput(check, output, root, cwd) {
|
|
9591
10392
|
const pathOf = (file) => {
|
|
9592
|
-
const absolute =
|
|
10393
|
+
const absolute = resolve13(cwd, file);
|
|
9593
10394
|
if (!inside(root, absolute)) throw new Error("Checker diagnostic outside workspace");
|
|
9594
|
-
return
|
|
10395
|
+
return relative10(root, absolute);
|
|
9595
10396
|
};
|
|
9596
10397
|
if (check.parser === "go") {
|
|
9597
10398
|
const findings2 = [];
|
|
@@ -9635,7 +10436,7 @@ async function projectOutput(check, output, root, cwd) {
|
|
|
9635
10436
|
findings2.map(async (finding) => {
|
|
9636
10437
|
let content = contents.get(finding.path);
|
|
9637
10438
|
if (!content) {
|
|
9638
|
-
content =
|
|
10439
|
+
content = readFile20(resolve13(root, finding.path), "utf8");
|
|
9639
10440
|
contents.set(finding.path, content);
|
|
9640
10441
|
}
|
|
9641
10442
|
const line = (await content).split("\n")[finding.line - 1] ?? "";
|
|
@@ -9678,7 +10479,7 @@ async function projectOutput(check, output, root, cwd) {
|
|
|
9678
10479
|
const split = splitLint(check.parser, JSON.parse(output), root, cwd);
|
|
9679
10480
|
const findings2 = [];
|
|
9680
10481
|
for (const [path, data] of split)
|
|
9681
|
-
findings2.push(...parseLint(check.parser, data, path, await
|
|
10482
|
+
findings2.push(...parseLint(check.parser, data, path, await readFile20(resolve13(root, path), "utf8")));
|
|
9682
10483
|
return findings2;
|
|
9683
10484
|
}
|
|
9684
10485
|
if (check.parser === "json") {
|
|
@@ -9749,9 +10550,9 @@ var init_project_output = __esm({
|
|
|
9749
10550
|
|
|
9750
10551
|
// src/project-checks.ts
|
|
9751
10552
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
9752
|
-
import { readdir as
|
|
10553
|
+
import { readdir as readdir6, readFile as readFile21, stat as stat8 } from "node:fs/promises";
|
|
9753
10554
|
import * as nodePath2 from "node:path";
|
|
9754
|
-
import { basename as
|
|
10555
|
+
import { basename as basename7, join as join18, relative as relative11, resolve as resolve14 } from "node:path";
|
|
9755
10556
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
9756
10557
|
function normalizedPattern(pattern) {
|
|
9757
10558
|
return pattern.replace(/^(?:\.\/)+/, "").replace(/\/$/, "");
|
|
@@ -9767,7 +10568,7 @@ function covers(check, path) {
|
|
|
9767
10568
|
if (check.parser === "tsc" && !/\.[cm]?[jt]sx?$/.test(path)) return false;
|
|
9768
10569
|
if (check.parser === "cargo" && !/\.rs$/.test(path)) return false;
|
|
9769
10570
|
if (check.parser === "go" && !/\.go$/.test(path)) return false;
|
|
9770
|
-
const local =
|
|
10571
|
+
const local = relative11(check.cwd === "." ? "" : check.cwd, path).split("\\").join("/");
|
|
9771
10572
|
return local !== ".." && !local.startsWith("../") && check.coverage.some((pattern) => nodePath2.matchesGlob(local, pattern)) && !check.excluded?.some((pattern) => {
|
|
9772
10573
|
const normalized = normalizedPattern(pattern);
|
|
9773
10574
|
return normalized === "." || normalized === "" || nodePath2.matchesGlob(local, normalized) || nodePath2.matchesGlob(local, `${normalized}/**`);
|
|
@@ -9786,6 +10587,7 @@ var init_project_checks = __esm({
|
|
|
9786
10587
|
init_files();
|
|
9787
10588
|
init_go_output();
|
|
9788
10589
|
init_identity();
|
|
10590
|
+
init_log();
|
|
9789
10591
|
init_metadata();
|
|
9790
10592
|
init_project_output();
|
|
9791
10593
|
init_results();
|
|
@@ -9836,13 +10638,15 @@ var init_project_checks = __esm({
|
|
|
9836
10638
|
return { introduced, resolved, unattributed };
|
|
9837
10639
|
}
|
|
9838
10640
|
get active() {
|
|
9839
|
-
return [...this.jobs.values()].some(
|
|
10641
|
+
return [...this.jobs.values()].some(
|
|
10642
|
+
(job) => job.persisting || job.results.some((result) => result.state === "running")
|
|
10643
|
+
);
|
|
9840
10644
|
}
|
|
9841
10645
|
async discover(config, signal) {
|
|
9842
10646
|
if (config.projectChecks !== "auto") return [...config.projectChecks];
|
|
9843
10647
|
const checks = [];
|
|
9844
|
-
const names = await
|
|
9845
|
-
const has = async (name) => (await
|
|
10648
|
+
const names = await readdir6(this.root);
|
|
10649
|
+
const has = async (name) => (await stat8(join18(this.root, name)).catch(() => void 0))?.isFile();
|
|
9846
10650
|
if (await has("tsconfig.json")) {
|
|
9847
10651
|
checks.push({
|
|
9848
10652
|
name: "typescript",
|
|
@@ -9882,7 +10686,7 @@ var init_project_checks = __esm({
|
|
|
9882
10686
|
);
|
|
9883
10687
|
if (!enabled) continue;
|
|
9884
10688
|
const expected = path.endsWith("py") ? "ruff" : config.javascript === "eslint" || !await has("biome.json") && !await has("biome.jsonc") ? "eslint" : "biome";
|
|
9885
|
-
const runner = await select(this.root,
|
|
10689
|
+
const runner = await select(this.root, join18(this.root, path), false, config, false, signal).catch(() => ({
|
|
9886
10690
|
name: expected,
|
|
9887
10691
|
command: expected,
|
|
9888
10692
|
prefix: []
|
|
@@ -9908,7 +10712,7 @@ var init_project_checks = __esm({
|
|
|
9908
10712
|
const cwd = await workspacePath(this.root, check.cwd);
|
|
9909
10713
|
const tool2 = await resolveTool(
|
|
9910
10714
|
config.projectChecks === "auto" ? this.root : cwd,
|
|
9911
|
-
|
|
10715
|
+
join18(cwd, "representative"),
|
|
9912
10716
|
check.command,
|
|
9913
10717
|
config.projectChecks !== "auto",
|
|
9914
10718
|
false
|
|
@@ -9921,16 +10725,16 @@ var init_project_checks = __esm({
|
|
|
9921
10725
|
// Only interpreter launch scripts belong to the tool identity.
|
|
9922
10726
|
tool2.command.slice(1).filter(
|
|
9923
10727
|
(arg, index, args) => /^(?:node|python(?:\d+(?:\.\d+)*)?|bash|sh)(?:\.exe)?$/.test(
|
|
9924
|
-
|
|
10728
|
+
basename7(tool2.command[0] ?? "")
|
|
9925
10729
|
) && /\.(?:[cm]?js|py|sh)$/.test(arg) && !args.slice(0, index).some((previous) => /\.(?:[cm]?js|py|sh)$/.test(previous))
|
|
9926
10730
|
).map(async (arg) => {
|
|
9927
|
-
const info = await
|
|
10731
|
+
const info = await stat8(resolve14(cwd, arg)).catch(() => void 0);
|
|
9928
10732
|
return [arg, info?.size, info?.mtimeMs, info?.ctimeMs, info?.ino];
|
|
9929
10733
|
})
|
|
9930
10734
|
),
|
|
9931
10735
|
await analysisIdentity(
|
|
9932
10736
|
this.root,
|
|
9933
|
-
[
|
|
10737
|
+
[relative11(this.root, join18(cwd, "representative"))],
|
|
9934
10738
|
config,
|
|
9935
10739
|
signal,
|
|
9936
10740
|
false
|
|
@@ -9942,7 +10746,7 @@ var init_project_checks = __esm({
|
|
|
9942
10746
|
}
|
|
9943
10747
|
output(job, cursor2 = 0) {
|
|
9944
10748
|
const diagnostics = job.results.flatMap((result) => result.findings);
|
|
9945
|
-
const state = job.results.some((result) => result.state === "running") ? "running" : job.results.some((result) => result.state === "stale") ? "stale" : job.results.some((result) => result.state === "failed") || !job.checks.length ? "failed" : "complete";
|
|
10749
|
+
const state = job.persisting || job.results.some((result) => result.state === "running") ? "running" : job.results.some((result) => result.state === "stale") ? "stale" : job.results.some((result) => result.state === "failed") || !job.checks.length ? "failed" : "complete";
|
|
9946
10750
|
return {
|
|
9947
10751
|
operation: "check",
|
|
9948
10752
|
job: job.id,
|
|
@@ -10006,7 +10810,7 @@ var init_project_checks = __esm({
|
|
|
10006
10810
|
if (!Number.isInteger(cursor2) || cursor2 < 0) throw new Error("Invalid project cursor");
|
|
10007
10811
|
return cursor2;
|
|
10008
10812
|
}
|
|
10009
|
-
async start(checks, config, environment, identity, before) {
|
|
10813
|
+
async start(checks, config, environment, identity, before, baseline) {
|
|
10010
10814
|
const job = {
|
|
10011
10815
|
id: randomUUID11(),
|
|
10012
10816
|
identity,
|
|
@@ -10023,8 +10827,18 @@ var init_project_checks = __esm({
|
|
|
10023
10827
|
state: "running",
|
|
10024
10828
|
findings: []
|
|
10025
10829
|
})),
|
|
10026
|
-
done: []
|
|
10830
|
+
done: [],
|
|
10831
|
+
persisting: !!baseline
|
|
10027
10832
|
};
|
|
10833
|
+
if (baseline) {
|
|
10834
|
+
const state = await new Metadata(this.root).update(baseline.session, job.controller.signal, (current) => {
|
|
10835
|
+
if (current.epoch !== baseline.epoch || current.edited || current.turn === "__ended__") return false;
|
|
10836
|
+
current.shown["baselineJob"] = job.id;
|
|
10837
|
+
return true;
|
|
10838
|
+
});
|
|
10839
|
+
if (state.shown["baselineJob"] !== job.id) throw new Error("Session changed before baseline started");
|
|
10840
|
+
this.sessionJobs.set(baseline.session, job.id);
|
|
10841
|
+
}
|
|
10028
10842
|
this.jobs.set(job.id, job);
|
|
10029
10843
|
this.latest = job.id;
|
|
10030
10844
|
for (const [index, check] of checks.entries())
|
|
@@ -10039,7 +10853,7 @@ var init_project_checks = __esm({
|
|
|
10039
10853
|
const beforeFiles = before;
|
|
10040
10854
|
const tool2 = await resolveTool(
|
|
10041
10855
|
config.projectChecks === "auto" ? this.root : cwd,
|
|
10042
|
-
|
|
10856
|
+
join18(cwd, "representative"),
|
|
10043
10857
|
check.command,
|
|
10044
10858
|
config.projectChecks !== "auto",
|
|
10045
10859
|
false
|
|
@@ -10055,7 +10869,7 @@ var init_project_checks = __esm({
|
|
|
10055
10869
|
{ timeout: BUDGET.project, environment }
|
|
10056
10870
|
);
|
|
10057
10871
|
if (shown.code !== 0) throw new Error(`Go package discovery failed: ${shown.stderr}`);
|
|
10058
|
-
result.coverage = goFiles(shown.stdout).map((path) =>
|
|
10872
|
+
result.coverage = goFiles(shown.stdout).map((path) => relative11(cwd, path).replaceAll("\\", "/"));
|
|
10059
10873
|
}
|
|
10060
10874
|
if (config.projectChecks === "auto" && check.parser === "tsc") {
|
|
10061
10875
|
const shown = await run(tool2.command[0] ?? "", ["--showConfig"], cwd, signal, void 0, {
|
|
@@ -10066,7 +10880,7 @@ var init_project_checks = __esm({
|
|
|
10066
10880
|
if (shown.code !== 0 || !record(data))
|
|
10067
10881
|
throw new Error("TypeScript effective configuration unavailable");
|
|
10068
10882
|
const pkg = JSON.parse(
|
|
10069
|
-
await
|
|
10883
|
+
await readFile21(join18(cwd, "package.json"), "utf8").catch(() => "{}")
|
|
10070
10884
|
);
|
|
10071
10885
|
if (Array.isArray(data["references"]) && data["references"].length || record(pkg) && pkg["workspaces"])
|
|
10072
10886
|
throw new Error("TypeScript references/multi-package projects require explicit projectChecks");
|
|
@@ -10080,8 +10894,8 @@ var init_project_checks = __esm({
|
|
|
10080
10894
|
result.excluded.push("**/*.{js,jsx,mjs,cjs}");
|
|
10081
10895
|
}
|
|
10082
10896
|
if (config.projectChecks === "auto" && check.parser === "ty") {
|
|
10083
|
-
const content = await
|
|
10084
|
-
const data = content ? parse(content) : parse(await
|
|
10897
|
+
const content = await readFile21(join18(cwd, "ty.toml"), "utf8").catch(() => void 0);
|
|
10898
|
+
const data = content ? parse(content) : parse(await readFile21(join18(cwd, "pyproject.toml"), "utf8").catch(() => ""));
|
|
10085
10899
|
const toolConfig = content ? data : record(data["tool"]) && record(data["tool"]["ty"]) ? data["tool"]["ty"] : {};
|
|
10086
10900
|
const src = record(toolConfig["src"]) ? toolConfig["src"] : {};
|
|
10087
10901
|
if (Array.isArray(src["include"]))
|
|
@@ -10116,15 +10930,49 @@ var init_project_checks = __esm({
|
|
|
10116
10930
|
}
|
|
10117
10931
|
})
|
|
10118
10932
|
);
|
|
10933
|
+
if (baseline)
|
|
10934
|
+
job.done.push(
|
|
10935
|
+
Promise.allSettled(job.done).then(async () => {
|
|
10936
|
+
try {
|
|
10937
|
+
await withExecution(
|
|
10938
|
+
environment,
|
|
10939
|
+
true,
|
|
10940
|
+
() => this.saveBaseline(
|
|
10941
|
+
job,
|
|
10942
|
+
baseline,
|
|
10943
|
+
AbortSignal.any([job.controller.signal, AbortSignal.timeout(BUDGET.lock)])
|
|
10944
|
+
)
|
|
10945
|
+
);
|
|
10946
|
+
} catch {
|
|
10947
|
+
if (!job.controller.signal.aborted) await logEvent("background-failure");
|
|
10948
|
+
} finally {
|
|
10949
|
+
job.persisting = false;
|
|
10950
|
+
}
|
|
10951
|
+
})
|
|
10952
|
+
);
|
|
10119
10953
|
while (this.jobs.size > 32) {
|
|
10120
10954
|
const first = [...this.jobs.values()].find(
|
|
10121
|
-
(entry) => entry.id !== job.id && entry.results.every((result) => result.state !== "running")
|
|
10955
|
+
(entry) => entry.id !== job.id && !entry.persisting && entry.results.every((result) => result.state !== "running")
|
|
10122
10956
|
);
|
|
10123
10957
|
if (!first) break;
|
|
10124
10958
|
this.jobs.delete(first.id);
|
|
10125
10959
|
}
|
|
10126
10960
|
return job;
|
|
10127
10961
|
}
|
|
10962
|
+
async saveBaseline(job, baseline, signal) {
|
|
10963
|
+
const store = new Metadata(this.root);
|
|
10964
|
+
let reference = "";
|
|
10965
|
+
await store.update(baseline.session, signal, async (current) => {
|
|
10966
|
+
if (current.epoch !== baseline.epoch || current.turn === "__ended__" || current.shown["baselineJob"] !== job.id || (await configuration(this.root)).version !== job.configuration)
|
|
10967
|
+
return false;
|
|
10968
|
+
reference = await store.shared({ identity: job.identity, results: job.results, before: job.before });
|
|
10969
|
+
current.diagnosticBaseline = reference;
|
|
10970
|
+
current.configuration = job.configuration;
|
|
10971
|
+
return true;
|
|
10972
|
+
});
|
|
10973
|
+
if (!reference) throw new Error("Session or configuration changed before baseline was saved");
|
|
10974
|
+
return reference;
|
|
10975
|
+
}
|
|
10128
10976
|
async baseline(session, paths, environment, signal, wait) {
|
|
10129
10977
|
const config = await configuration(this.root);
|
|
10130
10978
|
assertConfiguration(config);
|
|
@@ -10159,13 +11007,7 @@ var init_project_checks = __esm({
|
|
|
10159
11007
|
if (!record(before) || Object.keys(before).length !== snapshot2.files.size || [...snapshot2.files].some(([path, content]) => before[path] !== content))
|
|
10160
11008
|
throw new Error("Files changed before baseline started; diagnostics remain unattributed");
|
|
10161
11009
|
}
|
|
10162
|
-
job = await this.start(checks, config, environment, identity, snapshot2);
|
|
10163
|
-
this.sessionJobs.set(session, job.id);
|
|
10164
|
-
await store.update(session, signal, (current2) => {
|
|
10165
|
-
if (current2.epoch !== state.epoch || current2.turn === "__ended__") return false;
|
|
10166
|
-
current2.shown["baselineJob"] = job?.id ?? "";
|
|
10167
|
-
return true;
|
|
10168
|
-
});
|
|
11010
|
+
job = await this.start(checks, config, environment, identity, snapshot2, { session, epoch: state.epoch });
|
|
10169
11011
|
}
|
|
10170
11012
|
const selected = job.checks.map((check, index) => ({ check, index })).filter(({ check }) => !paths || paths.some((path) => covers(check, path)));
|
|
10171
11013
|
await Promise.race([
|
|
@@ -10175,13 +11017,7 @@ var init_project_checks = __esm({
|
|
|
10175
11017
|
recovered = { identity, results: job.results, before: job.before };
|
|
10176
11018
|
}
|
|
10177
11019
|
const results = recovered.results;
|
|
10178
|
-
const reference = await store.shared(recovered);
|
|
10179
|
-
await store.update(session, signal, (current) => {
|
|
10180
|
-
if (current.epoch !== state.epoch || current.turn === "__ended__") return false;
|
|
10181
|
-
current.diagnosticBaseline = reference;
|
|
10182
|
-
current.configuration = config.version;
|
|
10183
|
-
return true;
|
|
10184
|
-
});
|
|
11020
|
+
const reference = job ? await this.saveBaseline(job, { session, epoch: state.epoch }, signal) : await store.shared(recovered);
|
|
10185
11021
|
const snapshot = paths ? void 0 : await inventory(this.root, BUDGET.files, signal, config.exclude);
|
|
10186
11022
|
const targets = paths ?? [...snapshot?.files.keys() ?? []].filter(
|
|
10187
11023
|
(path) => codeLanguage(config, path) && !/(?:json|yaml|css|html)$/.test(codeLanguage(config, path) ?? "")
|
|
@@ -10250,7 +11086,7 @@ var init_project_checks = __esm({
|
|
|
10250
11086
|
});
|
|
10251
11087
|
|
|
10252
11088
|
// src/service.ts
|
|
10253
|
-
import { rm as
|
|
11089
|
+
import { rm as rm7 } from "node:fs/promises";
|
|
10254
11090
|
import { createServer } from "node:net";
|
|
10255
11091
|
async function runService(root, identity, token) {
|
|
10256
11092
|
const location = await serviceLocation(root);
|
|
@@ -10261,8 +11097,8 @@ async function runService(root, identity, token) {
|
|
|
10261
11097
|
if (await existingService(root, startup)) return;
|
|
10262
11098
|
const previous = await readEndpoint(location);
|
|
10263
11099
|
if (previous && alive(previous.pid)) throw new Error("Service alive but unavailable; refusing duplicate startup");
|
|
10264
|
-
await
|
|
10265
|
-
if (process.platform !== "win32") await
|
|
11100
|
+
await rm7(location.endpoint, { force: true });
|
|
11101
|
+
if (process.platform !== "win32") await rm7(location.address, { force: true });
|
|
10266
11102
|
await serve(root, identity, token);
|
|
10267
11103
|
} finally {
|
|
10268
11104
|
await release();
|
|
@@ -10372,9 +11208,17 @@ async function serve(root, identity, token) {
|
|
|
10372
11208
|
}
|
|
10373
11209
|
return { operation, ...await coordinator.request(args, controller.signal) };
|
|
10374
11210
|
}
|
|
10375
|
-
if (![
|
|
10376
|
-
|
|
10377
|
-
|
|
11211
|
+
if (![
|
|
11212
|
+
"status",
|
|
11213
|
+
"diagnostics",
|
|
11214
|
+
"warmup",
|
|
11215
|
+
"navigate",
|
|
11216
|
+
"rename",
|
|
11217
|
+
"format",
|
|
11218
|
+
"inspect",
|
|
11219
|
+
"actions",
|
|
11220
|
+
"apply"
|
|
11221
|
+
].includes(operation))
|
|
10378
11222
|
throw new Error("Unknown service operation");
|
|
10379
11223
|
const config = await configuration(root);
|
|
10380
11224
|
if (!config.valid || config.automaticDiagnostics.postToolUse === "off" && config.automaticDiagnostics.stop === "off")
|
|
@@ -10405,7 +11249,7 @@ async function serve(root, identity, token) {
|
|
|
10405
11249
|
} catch (error) {
|
|
10406
11250
|
if (!socket.destroyed)
|
|
10407
11251
|
socket.end(
|
|
10408
|
-
`${JSON.stringify({ error: message(error), ...error instanceof WriteFailure ? { modifiedPaths: error.modifiedPaths } : {} })}
|
|
11252
|
+
`${JSON.stringify({ error: message(error), ...error instanceof WriteFailure ? { modifiedPaths: error.modifiedPaths } : {}, ...error instanceof PlanFailure || error instanceof WriteFailure && error.planFailure ? { planFailure: error.planFailure } : {} })}
|
|
10409
11253
|
`
|
|
10410
11254
|
);
|
|
10411
11255
|
} finally {
|
|
@@ -10446,7 +11290,7 @@ async function serve(root, identity, token) {
|
|
|
10446
11290
|
await projects.dispose();
|
|
10447
11291
|
await engine.dispose();
|
|
10448
11292
|
if ((await readEndpoint(location).catch(() => void 0))?.token === token)
|
|
10449
|
-
await
|
|
11293
|
+
await rm7(location.endpoint, { force: true });
|
|
10450
11294
|
};
|
|
10451
11295
|
let revalidation;
|
|
10452
11296
|
const idle = setInterval(() => {
|
|
@@ -10457,7 +11301,7 @@ async function serve(root, identity, token) {
|
|
|
10457
11301
|
});
|
|
10458
11302
|
if (requests || coordinator.active || automatic.active || projects.active || warming || warmupTimer && !warmupTask)
|
|
10459
11303
|
lastActivity = Date.now();
|
|
10460
|
-
else if (Date.now() - lastActivity >= BUDGET.idle) void stop();
|
|
11304
|
+
else if (Date.now() - lastActivity >= BUDGET.idle && !engine.hasLivePlans()) void stop();
|
|
10461
11305
|
}, 250);
|
|
10462
11306
|
process.once("SIGTERM", () => {
|
|
10463
11307
|
void stop();
|
|
@@ -10465,9 +11309,9 @@ async function serve(root, identity, token) {
|
|
|
10465
11309
|
process.once("SIGINT", () => {
|
|
10466
11310
|
void stop();
|
|
10467
11311
|
});
|
|
10468
|
-
await new Promise((
|
|
11312
|
+
await new Promise((resolve16, reject) => {
|
|
10469
11313
|
server2.once("error", reject);
|
|
10470
|
-
server2.listen(location.address,
|
|
11314
|
+
server2.listen(location.address, resolve16);
|
|
10471
11315
|
});
|
|
10472
11316
|
await publishEndpoint(location, token);
|
|
10473
11317
|
await coordinator.ingest();
|
|
@@ -10479,6 +11323,7 @@ var init_service = __esm({
|
|
|
10479
11323
|
init_budgets();
|
|
10480
11324
|
init_config();
|
|
10481
11325
|
init_directory_lock();
|
|
11326
|
+
init_edit_plans();
|
|
10482
11327
|
init_engine();
|
|
10483
11328
|
init_environment();
|
|
10484
11329
|
init_hook_coordinator();
|
|
@@ -10496,37 +11341,48 @@ var main_exports = {};
|
|
|
10496
11341
|
__export(main_exports, {
|
|
10497
11342
|
main: () => main
|
|
10498
11343
|
});
|
|
11344
|
+
import { resolve as resolve15 } from "node:path";
|
|
10499
11345
|
async function main() {
|
|
10500
11346
|
restoreInstalledHome();
|
|
10501
11347
|
const [command = "mcp"] = process.argv.slice(2);
|
|
10502
|
-
if (
|
|
11348
|
+
if (["--help", "help", "-h"].includes(command)) {
|
|
11349
|
+
process.stdout.write("Usage: codeintel [mcp | hook | check [workspace] | warmup [workspace] [--path path]]\n");
|
|
11350
|
+
} else if (command === "mcp") await runMcp();
|
|
10503
11351
|
else if (command === "hook") await runHookCli();
|
|
10504
11352
|
else if (command === "service")
|
|
10505
11353
|
await runService(process.argv[3] ?? "", process.argv[4] ?? "", process.argv[5] ?? "");
|
|
10506
|
-
else if (command === "check") {
|
|
11354
|
+
else if (command === "check" || command === "warmup") {
|
|
10507
11355
|
const runtime = new Runtime();
|
|
10508
|
-
const options = { run: "active" };
|
|
11356
|
+
const options = command === "check" ? { run: "active" } : {};
|
|
10509
11357
|
const tokens = process.argv.slice(3);
|
|
10510
11358
|
const workspace = tokens[0] && !tokens[0].startsWith("--") ? tokens.shift() ?? process.cwd() : process.cwd();
|
|
10511
11359
|
while (tokens.length) {
|
|
10512
11360
|
const flag = tokens.shift();
|
|
10513
|
-
if (flag === "--refresh") options["refresh"] = true;
|
|
10514
|
-
else if (
|
|
11361
|
+
if (command === "check" && flag === "--refresh") options["refresh"] = true;
|
|
11362
|
+
else if (command === "warmup" && flag === "--path") {
|
|
11363
|
+
const value = tokens.shift();
|
|
11364
|
+
if (!value || value.startsWith("--")) throw new Error("Missing value for --path");
|
|
11365
|
+
const paths = options["paths"] ?? [];
|
|
11366
|
+
paths.push(value);
|
|
11367
|
+
if (paths.length > 200) throw new Error("At most 200 paths");
|
|
11368
|
+
options["paths"] = paths;
|
|
11369
|
+
} else if (command === "check" && ["--run", "--job", "--cursor"].includes(flag ?? "") || flag === "--cursor") {
|
|
10515
11370
|
const value = tokens.shift();
|
|
10516
11371
|
if (!value || value.startsWith("--")) throw new Error(`Missing value for ${flag}`);
|
|
10517
11372
|
options[flag?.slice(2) ?? ""] = value;
|
|
10518
11373
|
} else throw new Error(`Unknown option ${flag}`);
|
|
10519
11374
|
}
|
|
10520
|
-
if (!["active", "cached"].includes(String(options["run"])))
|
|
11375
|
+
if (command === "check" && !["active", "cached"].includes(String(options["run"])))
|
|
11376
|
+
throw new Error("run must be active or cached");
|
|
10521
11377
|
try {
|
|
10522
11378
|
process.stdout.write(
|
|
10523
|
-
`${JSON.stringify(await runtime.request(workspace,
|
|
11379
|
+
`${JSON.stringify(await runtime.request(resolve15(workspace), command, options, AbortSignal.timeout(BUDGET.request)))}
|
|
10524
11380
|
`
|
|
10525
11381
|
);
|
|
10526
11382
|
} finally {
|
|
10527
11383
|
await runtime.close();
|
|
10528
11384
|
}
|
|
10529
|
-
} else throw new Error("Usage:
|
|
11385
|
+
} else throw new Error("Usage: codeintel [mcp | hook | check [workspace] | warmup [workspace] [--path path]]");
|
|
10530
11386
|
}
|
|
10531
11387
|
var init_main = __esm({
|
|
10532
11388
|
"src/main.ts"() {
|
|
@@ -10540,10 +11396,17 @@ var init_main = __esm({
|
|
|
10540
11396
|
}
|
|
10541
11397
|
});
|
|
10542
11398
|
|
|
11399
|
+
// src/node-version.ts
|
|
11400
|
+
function supportsNode(version) {
|
|
11401
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
|
11402
|
+
if (!match) return false;
|
|
11403
|
+
const major = Number(match[1]), minor = Number(match[2]);
|
|
11404
|
+
return major === 24 && minor >= 20 || major === 26 && minor >= 13;
|
|
11405
|
+
}
|
|
11406
|
+
|
|
10543
11407
|
// src/cli.ts
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
const reason = `Codex CodeIntel requires Node >=22.12.0; found ${process.versions.node}.`;
|
|
11408
|
+
if (!supportsNode(process.versions.node)) {
|
|
11409
|
+
const reason = `CodeIntel requires Node ^24.20.0 || ^26.13.0; found ${process.versions.node}.`;
|
|
10547
11410
|
if (process.argv[2] === "hook") {
|
|
10548
11411
|
let event = "";
|
|
10549
11412
|
let passthrough = false;
|