cwtools-shared 0.1.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.
Files changed (53) hide show
  1. package/dist/generated/mcpTools.d.ts +1112 -0
  2. package/dist/generated/mcpTools.js +1247 -0
  3. package/dist/host/diagnostics.d.ts +41 -0
  4. package/dist/host/diagnostics.js +18 -0
  5. package/dist/host/filesystem.d.ts +18 -0
  6. package/dist/host/filesystem.js +2 -0
  7. package/dist/host/hostServices.d.ts +49 -0
  8. package/dist/host/hostServices.js +2 -0
  9. package/dist/host/indexing.d.ts +54 -0
  10. package/dist/host/indexing.js +2 -0
  11. package/dist/host/lsp.d.ts +8 -0
  12. package/dist/host/lsp.js +27 -0
  13. package/dist/host/readiness.d.ts +11 -0
  14. package/dist/host/readiness.js +71 -0
  15. package/dist/host/vanillaCache.d.ts +12 -0
  16. package/dist/host/vanillaCache.js +56 -0
  17. package/dist/host/vsCodeHostServices.d.ts +27 -0
  18. package/dist/host/vsCodeHostServices.js +30 -0
  19. package/dist/index.d.ts +25 -0
  20. package/dist/index.js +41 -0
  21. package/dist/knowledge/diagnosticRouting.d.ts +13 -0
  22. package/dist/knowledge/diagnosticRouting.js +57 -0
  23. package/dist/knowledge/gameKnowledge.d.ts +20 -0
  24. package/dist/knowledge/gameKnowledge.js +52 -0
  25. package/dist/knowledge/rules.d.ts +162 -0
  26. package/dist/knowledge/rules.js +1383 -0
  27. package/dist/knowledge/workflowHints.d.ts +11 -0
  28. package/dist/knowledge/workflowHints.js +31 -0
  29. package/dist/project/knowledge.d.ts +15 -0
  30. package/dist/project/knowledge.js +209 -0
  31. package/dist/project/profile.d.ts +45 -0
  32. package/dist/project/profile.js +177 -0
  33. package/dist/safety/localisation.d.ts +33 -0
  34. package/dist/safety/localisation.js +105 -0
  35. package/dist/safety/paths.d.ts +21 -0
  36. package/dist/safety/paths.js +122 -0
  37. package/dist/safety/writes.d.ts +3 -0
  38. package/dist/safety/writes.js +19 -0
  39. package/dist/tools/mcpSchema.d.ts +5 -0
  40. package/dist/tools/mcpSchema.js +19 -0
  41. package/dist/tools/names.d.ts +6 -0
  42. package/dist/tools/names.js +51 -0
  43. package/dist/tools/pdxBlock.d.ts +17 -0
  44. package/dist/tools/pdxBlock.js +140 -0
  45. package/dist/tools/registry.d.ts +10 -0
  46. package/dist/tools/registry.js +2 -0
  47. package/dist/tools/schema.d.ts +36 -0
  48. package/dist/tools/schema.js +21 -0
  49. package/dist/tools/symbols.d.ts +60 -0
  50. package/dist/tools/symbols.js +625 -0
  51. package/dist/tools/toolHandlers.d.ts +4 -0
  52. package/dist/tools/toolHandlers.js +291 -0
  53. package/package.json +24 -0
@@ -0,0 +1,625 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getCompletionAtWithHost = getCompletionAtWithHost;
37
+ exports.documentSymbolsWithHost = documentSymbolsWithHost;
38
+ exports.workspaceSymbolsWithHost = workspaceSymbolsWithHost;
39
+ exports.queryDefinitionWithHost = queryDefinitionWithHost;
40
+ exports.queryDefinitionByNameWithHost = queryDefinitionByNameWithHost;
41
+ exports.queryReferencesWithHost = queryReferencesWithHost;
42
+ exports.inferDocumentSymbolsFromText = inferDocumentSymbolsFromText;
43
+ const path = __importStar(require("path"));
44
+ const paths_1 = require("../safety/paths");
45
+ const schema_1 = require("./schema");
46
+ const SYMBOL_KINDS = [
47
+ 'Unknown',
48
+ 'File',
49
+ 'Module',
50
+ 'Namespace',
51
+ 'Package',
52
+ 'Class',
53
+ 'Method',
54
+ 'Property',
55
+ 'Field',
56
+ 'Constructor',
57
+ 'Enum',
58
+ 'Interface',
59
+ 'Function',
60
+ 'Variable',
61
+ 'Constant',
62
+ 'String',
63
+ 'Number',
64
+ 'Boolean',
65
+ 'Array',
66
+ 'Object',
67
+ 'Key',
68
+ 'Null',
69
+ 'EnumMember',
70
+ 'Struct',
71
+ 'Event',
72
+ 'Operator',
73
+ 'TypeParameter',
74
+ ];
75
+ async function getCompletionAtWithHost(host, args) {
76
+ const resolution = resolveReadableFile(host, args.file);
77
+ if ('error' in resolution)
78
+ return resolution.error;
79
+ const limit = clampNumber(args.limit, 30, 1, 200);
80
+ const fileUri = toFileUri(resolution.resolvedPath);
81
+ const localContext = await buildLocalCompletionContext(host, resolution.resolvedPath, args.line, args.column);
82
+ const rawContext = await host.lsp.executeCommand('cwtools.ai.getCompletionContext', [fileUri, args.line, args.column], { timeoutMs: 5000 });
83
+ const contextUnavailable = isUnavailable(rawContext);
84
+ const context = !contextUnavailable && rawContext && typeof rawContext === 'object' && rawContext.ok === true
85
+ ? { ...localContext, ...rawContext, source: 'cwtools.ai.getCompletionContext' }
86
+ : localContext;
87
+ const diagnostics = await host.diagnostics.getDiagnostics({ file: resolution.resolvedPath }).catch(() => undefined);
88
+ const completionResult = await requestLsp(host, 'textDocument/completion', {
89
+ textDocument: { uri: fileUri },
90
+ position: { line: args.line, character: args.column },
91
+ context: { triggerKind: 1 },
92
+ }, 10000);
93
+ const completions = normalizeCompletions(completionResult.data).slice(0, limit);
94
+ return {
95
+ ok: !completionResult.unavailable || completions.length > 0,
96
+ status: completionResult.unavailable && completions.length === 0 ? 'unavailable' : 'ready',
97
+ source: completionResult.unavailable ? 'cwtools-completion-context' : 'cwtools-lsp-completion',
98
+ data: {
99
+ completions,
100
+ context,
101
+ totalAvailable: normalizeCompletions(completionResult.data).length,
102
+ freshness: diagnostics?.freshness,
103
+ diagnosticsStatus: diagnostics?.status,
104
+ candidateSource: completionResult.unavailable ? 'local_context_only' : 'textDocument/completion',
105
+ ...(completionResult.unavailable ? {
106
+ _warning: 'Completion provider is unavailable; returning local position context only.',
107
+ _nextSteps: ['Start CWTools LSP, wait for diagnostics to become fresh, then retry get_completion_at.'],
108
+ } : {}),
109
+ },
110
+ error: completionResult.unavailable && completions.length === 0
111
+ ? { code: 'lsp_unavailable', message: completionResult.message ?? 'Completion provider is unavailable.' }
112
+ : undefined,
113
+ };
114
+ }
115
+ async function documentSymbolsWithHost(host, args) {
116
+ const resolution = resolveReadableFile(host, args.file);
117
+ if ('error' in resolution) {
118
+ return resolution.error;
119
+ }
120
+ const requested = await requestLsp(host, 'textDocument/documentSymbol', {
121
+ textDocument: { uri: toFileUri(resolution.resolvedPath) },
122
+ }, 10000);
123
+ const lspSymbols = normalizeDocumentSymbols(requested.data, host.workspaceRoot);
124
+ if (!requested.unavailable && lspSymbols.length > 0) {
125
+ return {
126
+ ok: true,
127
+ status: 'ready',
128
+ source: 'cwtools-lsp-symbols',
129
+ data: { symbols: lspSymbols, lineNumberBase: 0, source: 'cwtools-lsp-symbols' },
130
+ };
131
+ }
132
+ const read = await host.filesystem.readTextFile(resolution.resolvedPath);
133
+ const symbols = read.exists ? inferDocumentSymbolsFromText(read.hasBom ? read.content.slice(1) : read.content) : [];
134
+ return {
135
+ ok: true,
136
+ status: 'ready',
137
+ source: 'cwtools-node-symbols',
138
+ data: {
139
+ symbols,
140
+ lineNumberBase: 0,
141
+ source: 'cwtools-node-symbols',
142
+ warnings: [
143
+ requested.unavailable
144
+ ? 'LSP document symbols are unavailable; returned Node-inferred block symbols.'
145
+ : 'LSP returned no document symbols; returned Node-inferred block symbols.',
146
+ ],
147
+ },
148
+ };
149
+ }
150
+ async function workspaceSymbolsWithHost(host, args) {
151
+ const query = String(args.query ?? '').trim();
152
+ if (!query) {
153
+ return (0, schema_1.toolDenied)('invalid_arguments', 'workspace_symbols requires a non-empty query.');
154
+ }
155
+ const limit = clampNumber(args.limit, 20, 1, 100);
156
+ const requested = await requestLsp(host, 'workspace/symbol', { query }, 10000);
157
+ const symbols = normalizeWorkspaceSymbols(requested.data, host.workspaceRoot).slice(0, limit);
158
+ if (!requested.unavailable && symbols.length > 0) {
159
+ return {
160
+ ok: true,
161
+ status: 'ready',
162
+ source: 'cwtools-lsp-symbols',
163
+ data: { symbols, source: 'cwtools-lsp-symbols' },
164
+ };
165
+ }
166
+ if (host.indexing) {
167
+ const indexed = await host.indexing.queryWorkspace({ name: query, limit });
168
+ const status = indexed.status === 'ready'
169
+ ? 'ready'
170
+ : indexed.status === 'partial'
171
+ ? 'partial'
172
+ : indexed.status === 'error'
173
+ ? 'error'
174
+ : indexed.status === 'unavailable'
175
+ ? 'unavailable'
176
+ : 'loading';
177
+ return {
178
+ ok: true,
179
+ status,
180
+ source: 'cwtools-index',
181
+ data: {
182
+ symbols: indexed.entries.map(entry => ({
183
+ name: entry.name,
184
+ kind: entry.kind,
185
+ file: entry.file,
186
+ line: entry.line,
187
+ source: entry.source,
188
+ containerName: entry.container,
189
+ })),
190
+ source: 'cwtools-index',
191
+ warnings: [indexed._hint ?? 'LSP workspace symbols unavailable; returned thin index matches.'],
192
+ },
193
+ };
194
+ }
195
+ return {
196
+ ok: false,
197
+ status: 'unavailable',
198
+ source: 'cwtools-lsp-symbols',
199
+ data: {
200
+ symbols: [],
201
+ source: 'cwtools-lsp-symbols',
202
+ warnings: ['No LSP or workspace index is available for workspace_symbols.'],
203
+ },
204
+ error: { code: 'lsp_unavailable', message: requested.message ?? 'Workspace symbol provider is unavailable.' },
205
+ };
206
+ }
207
+ async function queryDefinitionWithHost(host, args) {
208
+ const resolution = resolveReadableFile(host, args.file);
209
+ if ('error' in resolution)
210
+ return resolution.error;
211
+ const fileUri = toFileUri(resolution.resolvedPath);
212
+ const commandResult = await host.lsp.executeCommand('cwtools.ai.queryDefinition', [fileUri, args.line, args.column], { timeoutMs: 10000 });
213
+ if (!isUnavailable(commandResult)) {
214
+ return {
215
+ ok: true,
216
+ status: 'ready',
217
+ source: 'cwtools-lsp-definition',
218
+ data: commandResult,
219
+ };
220
+ }
221
+ const requested = await requestLsp(host, 'textDocument/definition', {
222
+ textDocument: { uri: fileUri },
223
+ position: { line: args.line, character: args.column },
224
+ }, 10000);
225
+ const locations = normalizeLocations(requested.data, host.workspaceRoot);
226
+ return {
227
+ ok: !requested.unavailable,
228
+ status: requested.unavailable ? 'unavailable' : 'ready',
229
+ source: 'cwtools-lsp-definition',
230
+ data: { locations },
231
+ error: requested.unavailable
232
+ ? { code: 'lsp_unavailable', message: requested.message ?? 'Definition provider is unavailable.' }
233
+ : undefined,
234
+ };
235
+ }
236
+ async function queryDefinitionByNameWithHost(host, args) {
237
+ const symbolName = String(args.symbolName ?? '').trim();
238
+ if (!symbolName)
239
+ return (0, schema_1.toolDenied)('invalid_arguments', 'query_definition_by_name requires symbolName.');
240
+ const commandResult = await host.lsp.executeCommand('cwtools.ai.queryDefinitionByName', [symbolName], { timeoutMs: 10000 });
241
+ if (!isUnavailable(commandResult)) {
242
+ return {
243
+ ok: true,
244
+ status: 'ready',
245
+ source: 'cwtools-lsp-definition',
246
+ data: commandResult,
247
+ };
248
+ }
249
+ const symbols = await workspaceSymbolsWithHost(host, { query: symbolName, limit: 20 });
250
+ const matches = symbols.data?.symbols.filter(symbol => symbol.name === symbolName) ?? [];
251
+ return {
252
+ ok: matches.length > 0,
253
+ status: symbols.status,
254
+ source: symbols.source,
255
+ data: {
256
+ symbolName,
257
+ matches,
258
+ _warning: matches.length === 0 ? 'No exact definition match found in the fallback workspace symbol index.' : undefined,
259
+ },
260
+ error: matches.length === 0
261
+ ? { code: 'definition_not_found', message: `No definition found for '${symbolName}'.` }
262
+ : undefined,
263
+ };
264
+ }
265
+ async function queryReferencesWithHost(host, args) {
266
+ const identifier = String(args.identifier ?? '').trim();
267
+ if (!identifier)
268
+ return (0, schema_1.toolDenied)('invalid_arguments', 'query_references requires identifier.');
269
+ const limit = clampNumber(args.limit, 50, 1, 200);
270
+ const definition = await queryDefinitionByNameWithHost(host, { symbolName: identifier });
271
+ const firstMatch = definition.data && typeof definition.data === 'object'
272
+ ? getFirstDefinitionMatch(definition.data)
273
+ : undefined;
274
+ if (firstMatch?.file !== undefined && firstMatch.line !== undefined && host.lsp.request) {
275
+ const absolute = path.isAbsolute(firstMatch.file)
276
+ ? firstMatch.file
277
+ : path.resolve(host.workspaceRoot, firstMatch.file);
278
+ const requested = await requestLsp(host, 'textDocument/references', {
279
+ textDocument: { uri: toFileUri(absolute) },
280
+ position: { line: firstMatch.line, character: firstMatch.column ?? 0 },
281
+ context: { includeDeclaration: true },
282
+ }, 10000);
283
+ const references = normalizeLocations(requested.data, host.workspaceRoot).slice(0, limit);
284
+ if (!requested.unavailable && references.length > 0) {
285
+ return {
286
+ ok: true,
287
+ status: 'ready',
288
+ source: 'cwtools-lsp-references',
289
+ data: { identifier, references, total: references.length },
290
+ };
291
+ }
292
+ }
293
+ const references = await textSearchReferences(host, identifier, args.file, limit);
294
+ return {
295
+ ok: true,
296
+ status: 'ready',
297
+ source: 'cwtools-text-references',
298
+ data: {
299
+ identifier,
300
+ references,
301
+ total: references.length,
302
+ _warning: 'Fallback text references are lexical evidence, not semantic CWTools reference resolution.',
303
+ },
304
+ };
305
+ }
306
+ function inferDocumentSymbolsFromText(content) {
307
+ const lines = content.split(/\r?\n/);
308
+ const symbols = [];
309
+ for (let index = 0; index < lines.length; index++) {
310
+ const line = lines[index] ?? '';
311
+ const topLevel = line.match(/^([@\w][\w.:-]*)\s*=\s*\{/);
312
+ if (!topLevel?.[1])
313
+ continue;
314
+ const startLine = index;
315
+ if (startLine === undefined || symbols.some(symbol => symbol.range.startLine === startLine))
316
+ continue;
317
+ const endLine = findBlockEnd(lines, startLine);
318
+ const id = findIdInRange(lines, startLine, endLine);
319
+ const name = id ?? topLevel[1];
320
+ symbols.push({
321
+ name,
322
+ kind: id ? 'Event' : 'Object',
323
+ range: { startLine, endLine, startColumn: 0, endColumn: lines[endLine]?.length ?? 0 },
324
+ });
325
+ index = endLine;
326
+ }
327
+ return symbols;
328
+ }
329
+ function resolveReadableFile(host, file) {
330
+ const resolution = (0, paths_1.resolveWorkspacePath)(host.workspaceRoot, file);
331
+ if (!resolution.ok || !resolution.resolvedPath) {
332
+ return {
333
+ ok: false,
334
+ error: (0, schema_1.toolDenied)('outside_workspace', `Path '${file}' is outside the workspace root.`),
335
+ };
336
+ }
337
+ return { ok: true, resolvedPath: resolution.resolvedPath };
338
+ }
339
+ async function buildLocalCompletionContext(host, filePath, line, column) {
340
+ const read = await host.filesystem.readTextFile(filePath);
341
+ const content = read.hasBom ? read.content.slice(1) : read.content;
342
+ const lines = content.split(/\r?\n/);
343
+ const lineText = lines[line] ?? '';
344
+ const boundedColumn = Math.max(0, Math.min(column, lineText.length));
345
+ const linePrefix = lineText.slice(0, boundedColumn);
346
+ return {
347
+ file: filePath,
348
+ line,
349
+ column,
350
+ linePrefix,
351
+ tokenPrefix: linePrefix.match(/[A-Za-z0-9_.:-]+$/)?.[0],
352
+ source: 'local_text_context',
353
+ };
354
+ }
355
+ async function requestLsp(host, method, params, timeoutMs) {
356
+ if (!host.lsp.request) {
357
+ return { unavailable: true, message: 'LSP host does not expose generic request support.' };
358
+ }
359
+ const data = await host.lsp.request(method, params, { timeoutMs });
360
+ if (isUnavailable(data)) {
361
+ const record = asRecord(data);
362
+ const error = asRecord(record.error);
363
+ return { unavailable: true, data, message: String(error.message ?? `${method} is unavailable.`) };
364
+ }
365
+ return { unavailable: false, data };
366
+ }
367
+ function normalizeDocumentSymbols(value, workspaceRoot) {
368
+ if (!Array.isArray(value))
369
+ return [];
370
+ if (value.some(item => asRecord(item).location)) {
371
+ return normalizeWorkspaceSymbols(value, workspaceRoot).map(symbol => ({
372
+ name: symbol.name,
373
+ kind: symbol.kind,
374
+ range: {
375
+ startLine: symbol.line,
376
+ endLine: symbol.line,
377
+ startColumn: symbol.column,
378
+ endColumn: symbol.column,
379
+ },
380
+ }));
381
+ }
382
+ const mapSymbol = (symbol, depth) => {
383
+ if (!symbol.name || !symbol.range)
384
+ return undefined;
385
+ const children = depth < 2 && Array.isArray(symbol.children)
386
+ ? symbol.children.map(child => mapSymbol(child, depth + 1)).filter(isDefined)
387
+ : undefined;
388
+ return {
389
+ name: symbol.name,
390
+ kind: kindName(symbol.kind),
391
+ range: {
392
+ startLine: numberOr(symbol.range.start?.line, 0),
393
+ endLine: numberOr(symbol.range.end?.line, numberOr(symbol.range.start?.line, 0)),
394
+ startColumn: numberOrUndefined(symbol.range.start?.character),
395
+ endColumn: numberOrUndefined(symbol.range.end?.character),
396
+ },
397
+ children: children && children.length > 0 ? children : undefined,
398
+ _hasDeeper: depth >= 2 && Array.isArray(symbol.children) && symbol.children.length > 0 ? true : undefined,
399
+ };
400
+ };
401
+ return value.map(item => mapSymbol(item, 0)).filter(isDefined);
402
+ }
403
+ function normalizeWorkspaceSymbols(value, workspaceRoot) {
404
+ if (!Array.isArray(value))
405
+ return [];
406
+ return value.map(item => {
407
+ const symbol = item;
408
+ const location = symbol.location;
409
+ const filePath = location?.uri ? uriToPath(location.uri) : '';
410
+ return {
411
+ name: String(symbol.name ?? ''),
412
+ kind: kindName(symbol.kind),
413
+ file: filePath ? path.relative(workspaceRoot, filePath).replace(/\\/g, '/') : '',
414
+ line: numberOr(location?.range?.start?.line, 0),
415
+ column: numberOrUndefined(location?.range?.start?.character),
416
+ containerName: typeof symbol.containerName === 'string' ? symbol.containerName : undefined,
417
+ };
418
+ }).filter(symbol => symbol.name.length > 0);
419
+ }
420
+ function normalizeLocations(value, workspaceRoot) {
421
+ const array = Array.isArray(value) ? value : value ? [value] : [];
422
+ return array.map(item => {
423
+ const record = asRecord(item);
424
+ const targetUri = typeof record.targetUri === 'string' ? record.targetUri : undefined;
425
+ const uri = typeof record.uri === 'string' ? record.uri : targetUri;
426
+ const rawRange = asRecord(record.range ?? record.targetRange);
427
+ const start = asRecord(rawRange.start);
428
+ const end = asRecord(rawRange.end);
429
+ const filePath = uri ? uriToPath(uri) : '';
430
+ const origin = classifyOrigin(filePath, workspaceRoot);
431
+ return {
432
+ file: filePath ? path.relative(workspaceRoot, filePath).replace(/\\/g, '/') : '',
433
+ range: {
434
+ startLine: numberOr(start.line, 0),
435
+ startColumn: numberOr(start.character, 0),
436
+ endLine: numberOr(end.line, numberOr(start.line, 0)),
437
+ endColumn: numberOr(end.character, numberOr(start.character, 0)),
438
+ },
439
+ source: 'lsp',
440
+ origin,
441
+ };
442
+ }).filter(location => location.file.length > 0);
443
+ }
444
+ function normalizeCompletions(value) {
445
+ const rawItems = Array.isArray(value)
446
+ ? value
447
+ : Array.isArray(asRecord(value).items)
448
+ ? asRecord(value).items
449
+ : [];
450
+ return rawItems.map(item => {
451
+ const record = asRecord(item);
452
+ const labelRecord = asRecord(record.label);
453
+ const textEdit = asRecord(record.textEdit);
454
+ return {
455
+ label: typeof record.label === 'string' ? record.label : String(labelRecord.label ?? ''),
456
+ kind: kindName(record.kind),
457
+ description: typeof record.detail === 'string' ? record.detail : undefined,
458
+ insertText: typeof record.insertText === 'string'
459
+ ? record.insertText
460
+ : typeof textEdit.newText === 'string'
461
+ ? textEdit.newText
462
+ : undefined,
463
+ filterText: typeof record.filterText === 'string' ? record.filterText : undefined,
464
+ sortText: typeof record.sortText === 'string' ? record.sortText : undefined,
465
+ };
466
+ }).filter(item => String(item.label).length > 0);
467
+ }
468
+ async function textSearchReferences(host, identifier, file, limit) {
469
+ const files = file ? [file] : await host.filesystem.glob('**/*', { limit: 5000 });
470
+ const references = [];
471
+ for (const candidate of files) {
472
+ if (references.length >= limit)
473
+ break;
474
+ if (!/\.(txt|yml|gui|gfx|asset)$/i.test(candidate))
475
+ continue;
476
+ const resolution = (0, paths_1.resolveWorkspacePath)(host.workspaceRoot, candidate);
477
+ if (!resolution.ok || !resolution.resolvedPath)
478
+ continue;
479
+ const read = await host.filesystem.readTextFile(resolution.resolvedPath);
480
+ if (!read.exists)
481
+ continue;
482
+ const content = read.hasBom ? read.content.slice(1) : read.content;
483
+ const lines = content.split(/\r?\n/);
484
+ for (let index = 0; index < lines.length && references.length < limit; index++) {
485
+ const column = (lines[index] ?? '').indexOf(identifier);
486
+ if (column < 0)
487
+ continue;
488
+ references.push({
489
+ file: resolution.relativePath ?? candidate,
490
+ line: index,
491
+ column,
492
+ content: (lines[index] ?? '').trim().slice(0, 240),
493
+ source: 'text-search',
494
+ origin: classifyOrigin(resolution.resolvedPath, host.workspaceRoot),
495
+ });
496
+ }
497
+ }
498
+ return references;
499
+ }
500
+ function classifyOrigin(filePath, workspaceRoot) {
501
+ if (!filePath)
502
+ return 'external';
503
+ const normalized = filePath.replace(/\\/g, '/').toLowerCase();
504
+ if ((0, paths_1.isPathInsideOrEqual)(workspaceRoot, filePath)) {
505
+ return normalized.includes('/generated/') || normalized.includes('/.cwtools/') || normalized.includes('/.cwtools-ai/')
506
+ ? 'generated'
507
+ : 'workspace';
508
+ }
509
+ if (normalized.includes('/vanilla/') || normalized.includes('/cache/') || normalized.includes('/steamapps/common/')) {
510
+ return 'vanilla';
511
+ }
512
+ if (normalized.includes('/generated/') || normalized.includes('/.cwtools/') || normalized.includes('/.cwtools-ai/'))
513
+ return 'generated';
514
+ return 'external';
515
+ }
516
+ function getFirstDefinitionMatch(data) {
517
+ if (typeof data.file === 'string') {
518
+ return {
519
+ file: data.file,
520
+ line: numberOrUndefined(data.line),
521
+ column: numberOrUndefined(data.column),
522
+ };
523
+ }
524
+ const matches = Array.isArray(data.matches) ? data.matches : [];
525
+ const first = asRecord(matches[0]);
526
+ if (typeof first.file !== 'string')
527
+ return undefined;
528
+ return {
529
+ file: first.file,
530
+ line: numberOrUndefined(first.line),
531
+ column: numberOrUndefined(first.column),
532
+ };
533
+ }
534
+ function findIdInRange(lines, startLine, endLine) {
535
+ for (let index = startLine; index <= endLine; index++) {
536
+ const id = (lines[index] ?? '').match(/^\s*id\s*=\s*"?([\w.:-]+)"?/);
537
+ if (id?.[1])
538
+ return id[1];
539
+ }
540
+ return undefined;
541
+ }
542
+ function findBlockEnd(lines, startLine) {
543
+ let depth = 0;
544
+ let sawOpen = false;
545
+ for (let index = startLine; index < lines.length; index++) {
546
+ const counts = countBraces(lines[index] ?? '');
547
+ depth += counts.open - counts.close;
548
+ sawOpen || (sawOpen = counts.open > 0);
549
+ if (sawOpen && depth <= 0)
550
+ return index;
551
+ }
552
+ return startLine;
553
+ }
554
+ function countBraces(line) {
555
+ let open = 0;
556
+ let close = 0;
557
+ let inString = false;
558
+ let escaped = false;
559
+ for (let index = 0; index < line.length; index++) {
560
+ const char = line[index];
561
+ if (!inString && char === '#')
562
+ break;
563
+ if (char === '"' && !escaped)
564
+ inString = !inString;
565
+ else if (!inString && char === '{')
566
+ open++;
567
+ else if (!inString && char === '}')
568
+ close++;
569
+ escaped = char === '\\' && !escaped;
570
+ if (char !== '\\')
571
+ escaped = false;
572
+ }
573
+ return { open, close };
574
+ }
575
+ function toFileUri(filePath) {
576
+ const resolved = path.resolve(filePath).replace(/\\/g, '/');
577
+ const withLeadingSlash = resolved.startsWith('/') ? resolved : `/${resolved}`;
578
+ return `file://${encodeURI(withLeadingSlash).replace(/#/g, '%23')}`;
579
+ }
580
+ function uriToPath(uri) {
581
+ if (!uri.startsWith('file://'))
582
+ return uri;
583
+ let decoded = decodeURIComponent(uri.slice('file://'.length));
584
+ if (/^\/[A-Za-z]:\//.test(decoded))
585
+ decoded = decoded.slice(1);
586
+ return decoded.replace(/\//g, path.sep);
587
+ }
588
+ function isUnavailable(value) {
589
+ const record = asRecord(value);
590
+ const error = asRecord(record.error);
591
+ return record.status === 'unavailable' || error.code === 'lsp_unavailable';
592
+ }
593
+ function asRecord(value) {
594
+ return value && typeof value === 'object' && !Array.isArray(value)
595
+ ? value
596
+ : {};
597
+ }
598
+ function kindName(value) {
599
+ if (typeof value === 'string')
600
+ return value;
601
+ if (typeof value === 'number')
602
+ return SYMBOL_KINDS[value] ?? String(value);
603
+ return 'Unknown';
604
+ }
605
+ function clampNumber(value, fallback, min, max) {
606
+ const parsed = typeof value === 'number' ? value : Number(value);
607
+ if (!Number.isFinite(parsed))
608
+ return fallback;
609
+ return Math.max(min, Math.min(max, Math.trunc(parsed)));
610
+ }
611
+ function numberOr(value, fallback) {
612
+ return numberOrUndefined(value) ?? fallback;
613
+ }
614
+ function numberOrUndefined(value) {
615
+ if (typeof value === 'number')
616
+ return value;
617
+ if (typeof value === 'string' && value.trim()) {
618
+ const parsed = Number(value);
619
+ return Number.isFinite(parsed) ? parsed : undefined;
620
+ }
621
+ return undefined;
622
+ }
623
+ function isDefined(value) {
624
+ return value !== undefined;
625
+ }
@@ -0,0 +1,4 @@
1
+ import type { HostServices } from '../host/hostServices';
2
+ import { type SharedToolResult } from './schema';
3
+ export type SharedToolDispatcher = (host: HostServices, name: string, args: Record<string, unknown>) => Promise<SharedToolResult>;
4
+ export declare const defaultSharedToolDispatcher: SharedToolDispatcher;