@rohal12/spindle-lsp 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -2979,6 +2979,197 @@ var init_format2 = __esm({
2979
2979
  }
2980
2980
  });
2981
2981
 
2982
+ // src/mcp/server.ts
2983
+ var server_exports = {};
2984
+ __export(server_exports, {
2985
+ startMcpServer: () => startMcpServer
2986
+ });
2987
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync2 } from "node:fs";
2988
+ import { resolve as resolve2 } from "node:path";
2989
+ import { pathToFileURL as pathToFileURL3, fileURLToPath as fileURLToPath3 } from "node:url";
2990
+ import { glob as glob3 } from "glob";
2991
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2992
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2993
+ import { z } from "zod";
2994
+ function filterBySeverity2(diagnostics, minSeverity) {
2995
+ const minIdx = SEVERITY_ORDER2.indexOf(minSeverity);
2996
+ return diagnostics.filter((d) => {
2997
+ const idx = SEVERITY_ORDER2.indexOf(d.severity);
2998
+ return idx >= minIdx;
2999
+ });
3000
+ }
3001
+ async function resolveFiles(pattern, cwd) {
3002
+ const matches = await glob3(pattern, {
3003
+ cwd,
3004
+ absolute: true,
3005
+ nodir: true
3006
+ });
3007
+ return [...new Set(matches)];
3008
+ }
3009
+ function findConfigRoot(files) {
3010
+ const dirs = files.map((f) => resolve2(f, ".."));
3011
+ let configRoot = dirs.reduce((a, b) => {
3012
+ while (!b.startsWith(a)) a = resolve2(a, "..");
3013
+ return a;
3014
+ });
3015
+ let search = configRoot;
3016
+ for (let i = 0; i < 10; i++) {
3017
+ if (findConfigFile(search)) {
3018
+ configRoot = search;
3019
+ break;
3020
+ }
3021
+ const parent = resolve2(search, "..");
3022
+ if (parent === search) break;
3023
+ search = parent;
3024
+ }
3025
+ return configRoot;
3026
+ }
3027
+ function createWorkspace(files) {
3028
+ const configRoot = findConfigRoot(files);
3029
+ const projectConfig = loadConfigFromDisk(configRoot);
3030
+ const workspace = new WorkspaceModel();
3031
+ const fileContents = /* @__PURE__ */ new Map();
3032
+ for (const filePath of files) {
3033
+ try {
3034
+ const text = readFileSync6(filePath, "utf-8");
3035
+ const uri = pathToFileURL3(filePath).toString();
3036
+ fileContents.set(uri, text);
3037
+ } catch {
3038
+ }
3039
+ }
3040
+ workspace.initialize(fileContents);
3041
+ if (Object.keys(projectConfig.macros).length > 0) {
3042
+ workspace.macros.loadSupplements(projectConfig.macros);
3043
+ }
3044
+ return workspace;
3045
+ }
3046
+ async function startMcpServer() {
3047
+ const server = new McpServer({
3048
+ name: "spindle-lsp",
3049
+ version: "0.3.5"
3050
+ });
3051
+ server.tool(
3052
+ "spindle_check",
3053
+ "Run diagnostics on .tw/.twee files. Returns structured diagnostic results.",
3054
+ {
3055
+ path: z.string().default("**/*.{tw,twee}").describe("Glob pattern or directory to check"),
3056
+ severity: z.enum(["error", "warning", "info", "hint"]).optional().describe("Minimum severity to include")
3057
+ },
3058
+ async (args2) => {
3059
+ const cwd = process.cwd();
3060
+ const files = await resolveFiles(args2.path, cwd);
3061
+ if (files.length === 0) {
3062
+ return {
3063
+ content: [{ type: "text", text: JSON.stringify([], null, 2) }]
3064
+ };
3065
+ }
3066
+ const workspace = createWorkspace(files);
3067
+ try {
3068
+ const results = [];
3069
+ for (const [uri] of workspace.documents.getUris().map((u) => [u])) {
3070
+ let diags = computeDiagnostics(uri, workspace);
3071
+ if (args2.severity) {
3072
+ diags = filterBySeverity2(diags, args2.severity);
3073
+ }
3074
+ const filePath = fileURLToPath3(uri);
3075
+ const relativePath = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
3076
+ for (const d of diags) {
3077
+ results.push({
3078
+ file: relativePath,
3079
+ line: d.range.start.line + 1,
3080
+ column: d.range.start.character + 1,
3081
+ severity: d.severity,
3082
+ code: d.code,
3083
+ message: d.message
3084
+ });
3085
+ }
3086
+ }
3087
+ return {
3088
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }]
3089
+ };
3090
+ } finally {
3091
+ workspace.dispose();
3092
+ }
3093
+ }
3094
+ );
3095
+ server.tool(
3096
+ "spindle_format",
3097
+ "Format .tw/.twee files in place.",
3098
+ {
3099
+ path: z.string().default("**/*.{tw,twee}").describe("Glob pattern or directory to format")
3100
+ },
3101
+ async (args2) => {
3102
+ const cwd = process.cwd();
3103
+ const files = await resolveFiles(args2.path, cwd);
3104
+ let formatted = 0;
3105
+ let unchanged = 0;
3106
+ const changedFiles = [];
3107
+ for (const filePath of files) {
3108
+ try {
3109
+ const text = readFileSync6(filePath, "utf-8");
3110
+ const result = formatDocument(text);
3111
+ if (result !== text) {
3112
+ writeFileSync2(filePath, result, "utf-8");
3113
+ formatted++;
3114
+ const relativePath = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
3115
+ changedFiles.push(relativePath);
3116
+ } else {
3117
+ unchanged++;
3118
+ }
3119
+ } catch {
3120
+ }
3121
+ }
3122
+ const output = { formatted, unchanged, files: changedFiles };
3123
+ return {
3124
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }]
3125
+ };
3126
+ }
3127
+ );
3128
+ server.tool(
3129
+ "spindle_format_check",
3130
+ "Check formatting without modifying files. Returns list of files that need formatting.",
3131
+ {
3132
+ path: z.string().default("**/*.{tw,twee}").describe("Glob pattern or directory to check")
3133
+ },
3134
+ async (args2) => {
3135
+ const cwd = process.cwd();
3136
+ const files = await resolveFiles(args2.path, cwd);
3137
+ const needsFormatting = [];
3138
+ const alreadyFormatted = [];
3139
+ for (const filePath of files) {
3140
+ try {
3141
+ const text = readFileSync6(filePath, "utf-8");
3142
+ const result = formatDocument(text);
3143
+ const relativePath = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
3144
+ if (result !== text) {
3145
+ needsFormatting.push(relativePath);
3146
+ } else {
3147
+ alreadyFormatted.push(relativePath);
3148
+ }
3149
+ } catch {
3150
+ }
3151
+ }
3152
+ const output = { needsFormatting, alreadyFormatted };
3153
+ return {
3154
+ content: [{ type: "text", text: JSON.stringify(output, null, 2) }]
3155
+ };
3156
+ }
3157
+ );
3158
+ const transport = new StdioServerTransport();
3159
+ await server.connect(transport);
3160
+ }
3161
+ var SEVERITY_ORDER2;
3162
+ var init_server = __esm({
3163
+ "src/mcp/server.ts"() {
3164
+ "use strict";
3165
+ init_workspace_model();
3166
+ init_config_loader();
3167
+ init_diagnostics();
3168
+ init_format();
3169
+ SEVERITY_ORDER2 = ["hint", "info", "warning", "error"];
3170
+ }
3171
+ });
3172
+
2982
3173
  // src/core/plugin/plugin-loader.ts
2983
3174
  function loadPlugins(allPlugins2, config) {
2984
3175
  const disabled = new Set(config.disabledPlugins ?? []);
@@ -4833,8 +5024,8 @@ var init_plugins = __esm({
4833
5024
  });
4834
5025
 
4835
5026
  // src/server/server.ts
4836
- var server_exports = {};
4837
- __export(server_exports, {
5027
+ var server_exports2 = {};
5028
+ __export(server_exports2, {
4838
5029
  startServer: () => startServer
4839
5030
  });
4840
5031
  import {
@@ -4844,18 +5035,18 @@ import {
4844
5035
  FileChangeType
4845
5036
  } from "vscode-languageserver/node.js";
4846
5037
  import { TextDocument } from "vscode-languageserver-textdocument";
4847
- import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "node:url";
4848
- import { readFileSync as readFileSync6 } from "node:fs";
4849
- import { glob as glob3 } from "glob";
5038
+ import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL4 } from "node:url";
5039
+ import { readFileSync as readFileSync7 } from "node:fs";
5040
+ import { glob as glob4 } from "glob";
4850
5041
  function uriToFsPath(uri) {
4851
5042
  try {
4852
- return fileURLToPath3(uri);
5043
+ return fileURLToPath4(uri);
4853
5044
  } catch {
4854
5045
  return uri;
4855
5046
  }
4856
5047
  }
4857
5048
  function fsPathToUri(fsPath) {
4858
- return pathToFileURL3(fsPath).toString();
5049
+ return pathToFileURL4(fsPath).toString();
4859
5050
  }
4860
5051
  function startServer(_args) {
4861
5052
  const connection = createConnection(ProposedFeatures.all);
@@ -4954,7 +5145,7 @@ function startServer(_args) {
4954
5145
  } else {
4955
5146
  try {
4956
5147
  const fsPath = uriToFsPath(change.uri);
4957
- const text = readFileSync6(fsPath, "utf-8");
5148
+ const text = readFileSync7(fsPath, "utf-8");
4958
5149
  if (workspace.documents.has(change.uri)) {
4959
5150
  workspace.documents.update(change.uri, text);
4960
5151
  } else {
@@ -4977,14 +5168,14 @@ async function scanWorkspaceFiles(root) {
4977
5168
  const contents = /* @__PURE__ */ new Map();
4978
5169
  if (!root) return contents;
4979
5170
  try {
4980
- const files = await glob3("**/*.{tw,twee}", {
5171
+ const files = await glob4("**/*.{tw,twee}", {
4981
5172
  cwd: root,
4982
5173
  absolute: true,
4983
5174
  nodir: true
4984
5175
  });
4985
5176
  for (const filePath of files) {
4986
5177
  try {
4987
- const text = readFileSync6(filePath, "utf-8");
5178
+ const text = readFileSync7(filePath, "utf-8");
4988
5179
  const uri = fsPathToUri(filePath);
4989
5180
  contents.set(uri, text);
4990
5181
  } catch {
@@ -4994,7 +5185,7 @@ async function scanWorkspaceFiles(root) {
4994
5185
  }
4995
5186
  return contents;
4996
5187
  }
4997
- var init_server = __esm({
5188
+ var init_server2 = __esm({
4998
5189
  "src/server/server.ts"() {
4999
5190
  "use strict";
5000
5191
  init_workspace_model();
@@ -5015,8 +5206,11 @@ if (args[0] === "check") {
5015
5206
  const { runFormat: runFormat2 } = await Promise.resolve().then(() => (init_format2(), format_exports));
5016
5207
  const exitCode = await runFormat2(args.slice(1));
5017
5208
  process.exit(exitCode);
5209
+ } else if (args[0] === "mcp") {
5210
+ const { startMcpServer: startMcpServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
5211
+ await startMcpServer2();
5018
5212
  } else {
5019
- const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
5213
+ const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server2(), server_exports2));
5020
5214
  startServer2(args);
5021
5215
  }
5022
5216
  //# sourceMappingURL=bin.js.map