@nexrall/code-core 1.3.1 → 1.4.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.
@@ -0,0 +1,282 @@
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.isTsLike = isTsLike;
37
+ exports.tsGoToDefinition = tsGoToDefinition;
38
+ exports.tsFindReferences = tsFindReferences;
39
+ exports.tsGetHover = tsGetHover;
40
+ exports.tsServiceAvailable = tsServiceAvailable;
41
+ exports._resetTsServiceCache = _resetTsServiceCache;
42
+ const path = __importStar(require("path"));
43
+ const fs = __importStar(require("fs"));
44
+ const module_1 = require("module");
45
+ // ─── Real semantic navigation for TS/JS (GAP A) ───────────────────────────────
46
+ //
47
+ // The VS Code extension serves go_to_definition / find_references / get_hover
48
+ // through the editor's language server. The CLI had NO semantic layer — only the
49
+ // regex-based symbol scanner (symbols.ts), which can list top-level declarations
50
+ // but cannot resolve "where is THIS reference defined" or "who calls X" with type
51
+ // awareness. For the dominant case (TypeScript / JavaScript projects) we can close
52
+ // that gap by driving the TypeScript compiler's own LanguageService in-process.
53
+ //
54
+ // Design constraints:
55
+ // • `typescript` is heavy (~10MB). We do NOT bundle it — it's marked `external`
56
+ // in the CLI/VS Code esbuild configs and resolved LAZILY at runtime.
57
+ // • We prefer the USER PROJECT's own typescript (node_modules) so results match
58
+ // the version/config they build with; fall back to ours; degrade to a clear
59
+ // message if neither resolves (the regex tools still work as a floor).
60
+ // • One LanguageService per workspace root, cached, with an incrementing file
61
+ // version map so edits during a session are picked up on the next call.
62
+ const TS_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.mts', '.cts']);
63
+ function isTsLike(filePath) {
64
+ return TS_EXTS.has(path.extname(filePath).toLowerCase());
65
+ }
66
+ let _tsResolveTried = false;
67
+ let _ts = null;
68
+ /** Lazily load the `typescript` module — user project's copy first, then ours. */
69
+ function loadTs(workDir) {
70
+ if (_tsResolveTried)
71
+ return _ts;
72
+ _tsResolveTried = true;
73
+ const candidates = [];
74
+ // 1. User project copy: resolve from the workspace root.
75
+ try {
76
+ const req = (0, module_1.createRequire)(path.join(workDir, 'package.json'));
77
+ candidates.push(req.resolve('typescript'));
78
+ }
79
+ catch { /* no local copy */ }
80
+ // 2. Our own dependency (dev/runtime).
81
+ try {
82
+ candidates.push(require.resolve('typescript'));
83
+ }
84
+ catch { /* ignore */ }
85
+ for (const c of candidates) {
86
+ try {
87
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
88
+ _ts = require(c);
89
+ if (_ts && typeof _ts.createLanguageService === 'function')
90
+ return _ts;
91
+ }
92
+ catch { /* try next */ }
93
+ }
94
+ _ts = null;
95
+ return null;
96
+ }
97
+ const _services = new Map();
98
+ function getService(workDir) {
99
+ const cached = _services.get(workDir);
100
+ if (cached)
101
+ return cached;
102
+ const ts = loadTs(workDir);
103
+ if (!ts)
104
+ return null;
105
+ const versions = new Map();
106
+ const snapshots = new Map();
107
+ const readSource = (fileName) => {
108
+ const cachedSrc = snapshots.get(fileName);
109
+ if (cachedSrc !== undefined)
110
+ return cachedSrc;
111
+ try {
112
+ const src = fs.readFileSync(fileName, 'utf-8');
113
+ snapshots.set(fileName, src);
114
+ return src;
115
+ }
116
+ catch {
117
+ return undefined;
118
+ }
119
+ };
120
+ // Seed compiler options from the project's tsconfig if present.
121
+ let options = { allowJs: true, checkJs: false, target: 99 /* ESNext */, module: 99 };
122
+ try {
123
+ const cfgPath = ts.findConfigFile(workDir, ts.sys.fileExists, 'tsconfig.json');
124
+ if (cfgPath) {
125
+ const parsed = ts.readConfigFile(cfgPath, ts.sys.readFile);
126
+ const conf = ts.parseJsonConfigFileContent(parsed.config ?? {}, ts.sys, path.dirname(cfgPath));
127
+ if (conf.options)
128
+ options = { ...conf.options, allowJs: true };
129
+ }
130
+ }
131
+ catch { /* use defaults */ }
132
+ const host = {
133
+ getScriptFileNames: () => Array.from(versions.keys()),
134
+ getScriptVersion: (f) => String(versions.get(f) ?? 0),
135
+ getScriptSnapshot: (f) => {
136
+ const src = readSource(f);
137
+ return src === undefined ? undefined : ts.ScriptSnapshot.fromString(src);
138
+ },
139
+ getCurrentDirectory: () => workDir,
140
+ getCompilationSettings: () => options,
141
+ getDefaultLibFileName: (o) => ts.getDefaultLibFilePath(o),
142
+ fileExists: ts.sys.fileExists,
143
+ readFile: ts.sys.readFile,
144
+ readDirectory: ts.sys.readDirectory,
145
+ directoryExists: ts.sys.directoryExists,
146
+ getDirectories: ts.sys.getDirectories,
147
+ };
148
+ const service = ts.createLanguageService(host, ts.createDocumentRegistry());
149
+ const svc = { ts, service, root: workDir, versions, snapshots };
150
+ _services.set(workDir, svc);
151
+ return svc;
152
+ }
153
+ /** Register/refresh a file in the service, re-reading from disk (picks up edits). */
154
+ function ensureFile(svc, absPath) {
155
+ try {
156
+ const src = fs.readFileSync(absPath, 'utf-8');
157
+ const prev = svc.snapshots.get(absPath);
158
+ if (prev !== src) {
159
+ svc.snapshots.set(absPath, src);
160
+ svc.versions.set(absPath, (svc.versions.get(absPath) ?? 0) + 1);
161
+ }
162
+ else if (!svc.versions.has(absPath)) {
163
+ svc.versions.set(absPath, 1);
164
+ }
165
+ return true;
166
+ }
167
+ catch {
168
+ return false;
169
+ }
170
+ }
171
+ /** Convert 1-based line + character to a 0-based offset in `source`. */
172
+ function posToOffset(source, line, character) {
173
+ const lines = source.split('\n');
174
+ let offset = 0;
175
+ for (let i = 0; i < line - 1 && i < lines.length; i++)
176
+ offset += lines[i].length + 1;
177
+ return offset + Math.max(0, character - 1);
178
+ }
179
+ /** Convert a 0-based offset back to 1-based line + character. */
180
+ function offsetToPos(source, offset) {
181
+ let line = 1;
182
+ let last = 0;
183
+ for (let i = 0; i < offset && i < source.length; i++) {
184
+ if (source[i] === '\n') {
185
+ line++;
186
+ last = i + 1;
187
+ }
188
+ }
189
+ return { line, character: offset - last + 1 };
190
+ }
191
+ function relOf(root, abs) {
192
+ const r = path.relative(root, abs);
193
+ return r.startsWith('..') ? abs : r;
194
+ }
195
+ /** go_to_definition — resolve the symbol at (line, character) to its declaration(s). */
196
+ function tsGoToDefinition(deps) {
197
+ const svc = getService(deps.workDir);
198
+ if (!svc)
199
+ return null; // caller falls back to regex
200
+ if (!ensureFile(svc, deps.filePath))
201
+ return { error: `Cannot read ${deps.filePath}` };
202
+ const source = svc.snapshots.get(deps.filePath) ?? '';
203
+ const offset = posToOffset(source, deps.line, deps.character);
204
+ let defs;
205
+ try {
206
+ defs = svc.service.getDefinitionAtPosition(deps.filePath, offset);
207
+ }
208
+ catch (e) {
209
+ return { error: `Language service error: ${e.message}` };
210
+ }
211
+ if (!defs || defs.length === 0)
212
+ return { output: 'No definition found at that position.' };
213
+ const out = defs.map((d) => {
214
+ const src = svc.snapshots.get(d.fileName) ?? (fs.existsSync(d.fileName) ? fs.readFileSync(d.fileName, 'utf-8') : '');
215
+ const pos = offsetToPos(src, d.textSpan.start);
216
+ return `${relOf(svc.root, d.fileName)}:${pos.line}:${pos.character} ${d.kind ?? ''} ${d.name ?? ''}`.trimEnd();
217
+ });
218
+ return { output: `Definition(s):\n${out.join('\n')}` };
219
+ }
220
+ /** find_references — every usage of the symbol at (line, character) across the project. */
221
+ function tsFindReferences(deps) {
222
+ const svc = getService(deps.workDir);
223
+ if (!svc)
224
+ return null;
225
+ if (!ensureFile(svc, deps.filePath))
226
+ return { error: `Cannot read ${deps.filePath}` };
227
+ const source = svc.snapshots.get(deps.filePath) ?? '';
228
+ const offset = posToOffset(source, deps.line, deps.character);
229
+ let refs;
230
+ try {
231
+ refs = svc.service.getReferencesAtPosition(deps.filePath, offset);
232
+ }
233
+ catch (e) {
234
+ return { error: `Language service error: ${e.message}` };
235
+ }
236
+ if (!refs || refs.length === 0)
237
+ return { output: 'No references found.' };
238
+ const lines = [];
239
+ for (const r of refs) {
240
+ if (r.isDefinition && deps.includeDeclaration === false)
241
+ continue;
242
+ const src = svc.snapshots.get(r.fileName) ?? (fs.existsSync(r.fileName) ? fs.readFileSync(r.fileName, 'utf-8') : '');
243
+ const pos = offsetToPos(src, r.textSpan.start);
244
+ const lineText = (src.split('\n')[pos.line - 1] ?? '').trim().slice(0, 120);
245
+ lines.push(`${relOf(svc.root, r.fileName)}:${pos.line}:${pos.character}${r.isDefinition ? ' [def]' : ''} ${lineText}`);
246
+ }
247
+ return { output: `${lines.length} reference(s):\n${lines.join('\n')}` };
248
+ }
249
+ /** get_hover — the inferred type + doc for the symbol at (line, character). */
250
+ function tsGetHover(deps) {
251
+ const svc = getService(deps.workDir);
252
+ if (!svc)
253
+ return null;
254
+ if (!ensureFile(svc, deps.filePath))
255
+ return { error: `Cannot read ${deps.filePath}` };
256
+ const source = svc.snapshots.get(deps.filePath) ?? '';
257
+ const offset = posToOffset(source, deps.line, deps.character);
258
+ let info;
259
+ try {
260
+ info = svc.service.getQuickInfoAtPosition(deps.filePath, offset);
261
+ }
262
+ catch (e) {
263
+ return { error: `Language service error: ${e.message}` };
264
+ }
265
+ if (!info)
266
+ return { output: 'No type information at that position.' };
267
+ const ts = svc.ts;
268
+ const sig = info.displayParts ? ts.displayPartsToString(info.displayParts) : '';
269
+ const doc = info.documentation ? ts.displayPartsToString(info.documentation) : '';
270
+ return { output: doc ? `${sig}\n\n${doc}` : sig || 'No type information.' };
271
+ }
272
+ /** Test/diagnostic helper: is a real TS language service available for this workspace? */
273
+ function tsServiceAvailable(workDir) {
274
+ return getService(workDir) !== null;
275
+ }
276
+ /** Reset cached services + module resolution (tests only). */
277
+ function _resetTsServiceCache() {
278
+ _services.clear();
279
+ _tsResolveTried = false;
280
+ _ts = null;
281
+ }
282
+ //# sourceMappingURL=tsLangService.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexrall/code-core",
3
- "version": "1.3.1",
3
+ "version": "1.4.1",
4
4
  "description": "Core agent loop, tools, and extension primitives for Nexrall Code — embed an AI coding agent in any Node.js application.",
5
5
  "license": "MIT",
6
6
  "author": "Nexrall <support@nexrall.com> (https://nexrall.com)",