@proxysoul/soulforge 2.13.2 → 2.14.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.
@@ -109,6 +109,9 @@ function createWorkerHandler(handlers, onInit, onDispose) {
109
109
  const allHandlers = {
110
110
  ...handlers,
111
111
  __memoryUsage: () => {
112
+ try {
113
+ Bun.gc(true);
114
+ } catch {}
112
115
  const usage = process.memoryUsage();
113
116
  return {
114
117
  heapUsed: usage.heapUsed,
@@ -26068,6 +26071,8 @@ class RepoMap {
26068
26071
  const language = INDEXABLE_EXTENSIONS[ext];
26069
26072
  if (!language)
26070
26073
  return;
26074
+ if (language === "unknown")
26075
+ return;
26071
26076
  this.pendingReindex.set(absPath, {
26072
26077
  relPath,
26073
26078
  language
@@ -28291,321 +28296,6 @@ var init_protocol = __esm(() => {
28291
28296
  FILE_URI_SAFE_RE = /[^A-Za-z0-9\-._~!$&'()*+,;=:@]/g;
28292
28297
  });
28293
28298
 
28294
- // src/config/index.ts
28295
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
28296
- import { homedir as homedir4 } from "os";
28297
- import { join as join8 } from "path";
28298
- function loadConfig() {
28299
- if (!existsSync6(CONFIG_DIR)) {
28300
- mkdirSync2(CONFIG_DIR, {
28301
- recursive: true,
28302
- mode: 448
28303
- });
28304
- }
28305
- if (!existsSync6(CONFIG_FILE)) {
28306
- writeFileSync3(CONFIG_FILE, JSON.stringify(DEFAULT_CONFIG, null, 2));
28307
- return DEFAULT_CONFIG;
28308
- }
28309
- try {
28310
- const raw2 = readFileSync4(CONFIG_FILE, "utf-8");
28311
- return {
28312
- ...DEFAULT_CONFIG,
28313
- ...JSON.parse(raw2)
28314
- };
28315
- } catch (err2) {
28316
- process.stderr.write(`[soulforge] Failed to parse ${CONFIG_FILE}: ${err2 instanceof Error ? err2.message : String(err2)} \u2014 using defaults
28317
- `);
28318
- return DEFAULT_CONFIG;
28319
- }
28320
- }
28321
- function loadProjectConfig(cwd) {
28322
- const projectFile = join8(cwd, ".soulforge", "config.json");
28323
- if (!existsSync6(projectFile))
28324
- return null;
28325
- try {
28326
- const raw2 = readFileSync4(projectFile, "utf-8");
28327
- return JSON.parse(raw2);
28328
- } catch (err2) {
28329
- process.stderr.write(`[soulforge] Failed to parse ${projectFile}: ${err2 instanceof Error ? err2.message : String(err2)} \u2014 ignoring project config
28330
- `);
28331
- return null;
28332
- }
28333
- }
28334
- var CONFIG_DIR, CONFIG_FILE, DEFAULT_CONFIG;
28335
- var init_config = __esm(() => {
28336
- init_ensure_soulforge_dir();
28337
- CONFIG_DIR = join8(homedir4(), ".soulforge");
28338
- CONFIG_FILE = join8(CONFIG_DIR, "config.json");
28339
- DEFAULT_CONFIG = {
28340
- defaultModel: "none",
28341
- routerRules: [],
28342
- editor: {
28343
- command: "nvim",
28344
- args: []
28345
- },
28346
- theme: {
28347
- name: "dark",
28348
- transparent: true
28349
- },
28350
- nvimConfig: "default",
28351
- editorIntegration: {
28352
- diagnostics: true,
28353
- symbols: true,
28354
- hover: true,
28355
- references: true,
28356
- definition: true,
28357
- codeActions: true,
28358
- editorContext: true,
28359
- rename: true,
28360
- lspStatus: true,
28361
- format: true,
28362
- syncEditorOnEdit: false
28363
- },
28364
- codeExecution: true,
28365
- webSearch: true,
28366
- compaction: {
28367
- strategy: "v2",
28368
- triggerThreshold: 0.7,
28369
- resetThreshold: 0.4,
28370
- keepRecent: 4,
28371
- maxToolResults: 30,
28372
- llmExtraction: true
28373
- }
28374
- };
28375
- });
28376
-
28377
- // src/core/intelligence/backends/lsp/server-registry.ts
28378
- import { execSync as execSync3 } from "child_process";
28379
- import { existsSync as existsSync7 } from "fs";
28380
- import { homedir as homedir5 } from "os";
28381
- import { join as join9 } from "path";
28382
- function commandExists(cmd) {
28383
- try {
28384
- execSync3(`command -v ${cmd}`, {
28385
- stdio: "ignore"
28386
- });
28387
- return true;
28388
- } catch {
28389
- return false;
28390
- }
28391
- }
28392
- function commandExistsInMason(cmd) {
28393
- const fullPath = join9(MASON_BIN_DIR, cmd);
28394
- return existsSync7(fullPath) ? fullPath : null;
28395
- }
28396
- function findInSoulforge(cmd) {
28397
- const npmBin = join9(SOULFORGE_BIN_DIR, "node_modules", ".bin", cmd);
28398
- if (existsSync7(npmBin))
28399
- return npmBin;
28400
- const directBin = join9(SOULFORGE_BIN_DIR, "bin", cmd);
28401
- if (existsSync7(directBin))
28402
- return directBin;
28403
- return null;
28404
- }
28405
- function resolveCommand(cmd) {
28406
- const cached = probeCache.get(cmd);
28407
- if (cached !== undefined)
28408
- return cached;
28409
- if (commandExists(cmd)) {
28410
- probeCache.set(cmd, cmd);
28411
- return cmd;
28412
- }
28413
- const sfPath = findInSoulforge(cmd);
28414
- if (sfPath) {
28415
- probeCache.set(cmd, sfPath);
28416
- return sfPath;
28417
- }
28418
- const masonPath = commandExistsInMason(cmd);
28419
- if (masonPath) {
28420
- probeCache.set(cmd, masonPath);
28421
- return masonPath;
28422
- }
28423
- probeCache.set(cmd, null);
28424
- return null;
28425
- }
28426
- function isServerDisabled(cmd) {
28427
- const cwd = process.cwd();
28428
- const global2 = loadConfig();
28429
- const project = loadProjectConfig(cwd);
28430
- const disabled = project?.disabledLspServers ?? global2.disabledLspServers ?? [];
28431
- return disabled.includes(cmd);
28432
- }
28433
- function findServersForLanguage(language) {
28434
- const candidates = SERVER_CANDIDATES[language];
28435
- if (!candidates)
28436
- return [];
28437
- const results = [];
28438
- for (const candidate of candidates) {
28439
- if (isServerDisabled(candidate.command))
28440
- continue;
28441
- const resolved = resolveCommand(candidate.command);
28442
- if (resolved) {
28443
- results.push({
28444
- command: resolved,
28445
- args: candidate.args,
28446
- language
28447
- });
28448
- }
28449
- }
28450
- return results;
28451
- }
28452
- var SERVER_CANDIDATES, MASON_BIN_DIR, SOULFORGE_BIN_DIR, probeCache;
28453
- var init_server_registry = __esm(() => {
28454
- init_config();
28455
- SERVER_CANDIDATES = {
28456
- typescript: [{
28457
- command: "typescript-language-server",
28458
- args: ["--stdio"]
28459
- }, {
28460
- command: "biome",
28461
- args: ["lsp-proxy"]
28462
- }, {
28463
- command: "deno",
28464
- args: ["lsp"]
28465
- }, {
28466
- command: "vscode-eslint-language-server",
28467
- args: ["--stdio"]
28468
- }],
28469
- javascript: [{
28470
- command: "typescript-language-server",
28471
- args: ["--stdio"]
28472
- }, {
28473
- command: "biome",
28474
- args: ["lsp-proxy"]
28475
- }, {
28476
- command: "deno",
28477
- args: ["lsp"]
28478
- }, {
28479
- command: "vscode-eslint-language-server",
28480
- args: ["--stdio"]
28481
- }],
28482
- python: [{
28483
- command: "pyright-langserver",
28484
- args: ["--stdio"]
28485
- }, {
28486
- command: "pylsp",
28487
- args: []
28488
- }],
28489
- go: [{
28490
- command: "gopls",
28491
- args: ["serve"]
28492
- }],
28493
- rust: [{
28494
- command: "rust-analyzer",
28495
- args: []
28496
- }],
28497
- lua: [{
28498
- command: "lua-language-server",
28499
- args: []
28500
- }],
28501
- c: [{
28502
- command: "clangd",
28503
- args: []
28504
- }],
28505
- cpp: [{
28506
- command: "clangd",
28507
- args: []
28508
- }],
28509
- ruby: [{
28510
- command: "solargraph",
28511
- args: ["stdio"]
28512
- }],
28513
- php: [{
28514
- command: "intelephense",
28515
- args: ["--stdio"]
28516
- }],
28517
- zig: [{
28518
- command: "zls",
28519
- args: []
28520
- }],
28521
- bash: [{
28522
- command: "bash-language-server",
28523
- args: ["start"]
28524
- }],
28525
- css: [{
28526
- command: "vscode-css-language-server",
28527
- args: ["--stdio"]
28528
- }, {
28529
- command: "biome",
28530
- args: ["lsp-proxy"]
28531
- }, {
28532
- command: "tailwindcss-language-server",
28533
- args: ["--stdio"]
28534
- }],
28535
- html: [{
28536
- command: "vscode-html-language-server",
28537
- args: ["--stdio"]
28538
- }, {
28539
- command: "emmet-language-server",
28540
- args: ["--stdio"]
28541
- }],
28542
- json: [{
28543
- command: "vscode-json-language-server",
28544
- args: ["--stdio"]
28545
- }, {
28546
- command: "biome",
28547
- args: ["lsp-proxy"]
28548
- }],
28549
- yaml: [{
28550
- command: "yaml-language-server",
28551
- args: ["--stdio"]
28552
- }],
28553
- toml: [{
28554
- command: "taplo",
28555
- args: ["lsp", "stdio"]
28556
- }],
28557
- dockerfile: [{
28558
- command: "docker-langserver",
28559
- args: ["--stdio"]
28560
- }],
28561
- java: [{
28562
- command: "jdtls",
28563
- args: []
28564
- }],
28565
- kotlin: [{
28566
- command: "kotlin-language-server",
28567
- args: []
28568
- }],
28569
- scala: [{
28570
- command: "metals",
28571
- args: []
28572
- }],
28573
- csharp: [{
28574
- command: "csharp-ls",
28575
- args: []
28576
- }, {
28577
- command: "OmniSharp",
28578
- args: ["--languageserver"]
28579
- }],
28580
- swift: [{
28581
- command: "sourcekit-lsp",
28582
- args: []
28583
- }],
28584
- dart: [{
28585
- command: "dart",
28586
- args: ["language-server", "--protocol=lsp"]
28587
- }],
28588
- elixir: [{
28589
- command: "elixir-ls",
28590
- args: []
28591
- }, {
28592
- command: "expert",
28593
- args: []
28594
- }],
28595
- ocaml: [{
28596
- command: "ocamllsp",
28597
- args: []
28598
- }],
28599
- vue: [{
28600
- command: "vue-language-server",
28601
- args: ["--stdio"]
28602
- }]
28603
- };
28604
- MASON_BIN_DIR = join9(homedir5(), ".local", "share", "soulforge", "mason", "bin");
28605
- SOULFORGE_BIN_DIR = join9(homedir5(), ".soulforge", "lsp-servers");
28606
- probeCache = new Map;
28607
- });
28608
-
28609
28299
  // node_modules/zustand/esm/vanilla.mjs
28610
28300
  var createStoreImpl = (createState) => {
28611
28301
  let state;
@@ -29540,6 +29230,320 @@ var init_errors = __esm(() => {
29540
29230
  })));
29541
29231
  });
29542
29232
 
29233
+ // src/config/index.ts
29234
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
29235
+ import { homedir as homedir4 } from "os";
29236
+ import { join as join8 } from "path";
29237
+ function loadConfig() {
29238
+ if (!existsSync6(CONFIG_DIR)) {
29239
+ mkdirSync2(CONFIG_DIR, {
29240
+ recursive: true,
29241
+ mode: 448
29242
+ });
29243
+ }
29244
+ if (!existsSync6(CONFIG_FILE)) {
29245
+ writeFileSync3(CONFIG_FILE, JSON.stringify(DEFAULT_CONFIG, null, 2));
29246
+ return DEFAULT_CONFIG;
29247
+ }
29248
+ try {
29249
+ const raw2 = readFileSync4(CONFIG_FILE, "utf-8");
29250
+ return {
29251
+ ...DEFAULT_CONFIG,
29252
+ ...JSON.parse(raw2)
29253
+ };
29254
+ } catch (err2) {
29255
+ logBackgroundError("config", `Failed to parse ${CONFIG_FILE}: ${err2 instanceof Error ? err2.message : String(err2)} \u2014 using defaults`);
29256
+ return DEFAULT_CONFIG;
29257
+ }
29258
+ }
29259
+ function loadProjectConfig(cwd) {
29260
+ const projectFile = join8(cwd, ".soulforge", "config.json");
29261
+ if (!existsSync6(projectFile))
29262
+ return null;
29263
+ try {
29264
+ const raw2 = readFileSync4(projectFile, "utf-8");
29265
+ return JSON.parse(raw2);
29266
+ } catch (err2) {
29267
+ logBackgroundError("config", `Failed to parse ${projectFile}: ${err2 instanceof Error ? err2.message : String(err2)} \u2014 ignoring project config`);
29268
+ return null;
29269
+ }
29270
+ }
29271
+ var CONFIG_DIR, CONFIG_FILE, DEFAULT_CONFIG;
29272
+ var init_config = __esm(() => {
29273
+ init_ensure_soulforge_dir();
29274
+ init_errors();
29275
+ CONFIG_DIR = join8(homedir4(), ".soulforge");
29276
+ CONFIG_FILE = join8(CONFIG_DIR, "config.json");
29277
+ DEFAULT_CONFIG = {
29278
+ defaultModel: "none",
29279
+ routerRules: [],
29280
+ editor: {
29281
+ command: "nvim",
29282
+ args: []
29283
+ },
29284
+ theme: {
29285
+ name: "dark",
29286
+ transparent: true
29287
+ },
29288
+ nvimConfig: "default",
29289
+ editorIntegration: {
29290
+ diagnostics: true,
29291
+ symbols: true,
29292
+ hover: true,
29293
+ references: true,
29294
+ definition: true,
29295
+ codeActions: true,
29296
+ editorContext: true,
29297
+ rename: true,
29298
+ lspStatus: true,
29299
+ format: true,
29300
+ syncEditorOnEdit: false
29301
+ },
29302
+ codeExecution: true,
29303
+ webSearch: true,
29304
+ compaction: {
29305
+ strategy: "v2",
29306
+ triggerThreshold: 0.7,
29307
+ resetThreshold: 0.4,
29308
+ keepRecent: 4,
29309
+ maxToolResults: 30,
29310
+ llmExtraction: true
29311
+ }
29312
+ };
29313
+ });
29314
+
29315
+ // src/core/intelligence/backends/lsp/server-registry.ts
29316
+ import { execSync as execSync3 } from "child_process";
29317
+ import { existsSync as existsSync7 } from "fs";
29318
+ import { homedir as homedir5 } from "os";
29319
+ import { join as join9 } from "path";
29320
+ function commandExists(cmd) {
29321
+ try {
29322
+ execSync3(`command -v ${cmd}`, {
29323
+ stdio: "ignore"
29324
+ });
29325
+ return true;
29326
+ } catch {
29327
+ return false;
29328
+ }
29329
+ }
29330
+ function commandExistsInMason(cmd) {
29331
+ const fullPath = join9(MASON_BIN_DIR, cmd);
29332
+ return existsSync7(fullPath) ? fullPath : null;
29333
+ }
29334
+ function findInSoulforge(cmd) {
29335
+ const npmBin = join9(SOULFORGE_BIN_DIR, "node_modules", ".bin", cmd);
29336
+ if (existsSync7(npmBin))
29337
+ return npmBin;
29338
+ const directBin = join9(SOULFORGE_BIN_DIR, "bin", cmd);
29339
+ if (existsSync7(directBin))
29340
+ return directBin;
29341
+ return null;
29342
+ }
29343
+ function resolveCommand(cmd) {
29344
+ const cached = probeCache.get(cmd);
29345
+ if (cached !== undefined)
29346
+ return cached;
29347
+ if (commandExists(cmd)) {
29348
+ probeCache.set(cmd, cmd);
29349
+ return cmd;
29350
+ }
29351
+ const sfPath = findInSoulforge(cmd);
29352
+ if (sfPath) {
29353
+ probeCache.set(cmd, sfPath);
29354
+ return sfPath;
29355
+ }
29356
+ const masonPath = commandExistsInMason(cmd);
29357
+ if (masonPath) {
29358
+ probeCache.set(cmd, masonPath);
29359
+ return masonPath;
29360
+ }
29361
+ probeCache.set(cmd, null);
29362
+ return null;
29363
+ }
29364
+ function isServerDisabled(cmd) {
29365
+ const cwd = process.cwd();
29366
+ const global2 = loadConfig();
29367
+ const project = loadProjectConfig(cwd);
29368
+ const disabled = project?.disabledLspServers ?? global2.disabledLspServers ?? [];
29369
+ return disabled.includes(cmd);
29370
+ }
29371
+ function findServersForLanguage(language) {
29372
+ const candidates = SERVER_CANDIDATES[language];
29373
+ if (!candidates)
29374
+ return [];
29375
+ const results = [];
29376
+ for (const candidate of candidates) {
29377
+ if (isServerDisabled(candidate.command))
29378
+ continue;
29379
+ const resolved = resolveCommand(candidate.command);
29380
+ if (resolved) {
29381
+ results.push({
29382
+ command: resolved,
29383
+ args: candidate.args,
29384
+ language
29385
+ });
29386
+ }
29387
+ }
29388
+ return results;
29389
+ }
29390
+ var SERVER_CANDIDATES, MASON_BIN_DIR, SOULFORGE_BIN_DIR, probeCache;
29391
+ var init_server_registry = __esm(() => {
29392
+ init_config();
29393
+ SERVER_CANDIDATES = {
29394
+ typescript: [{
29395
+ command: "typescript-language-server",
29396
+ args: ["--stdio"]
29397
+ }, {
29398
+ command: "biome",
29399
+ args: ["lsp-proxy"]
29400
+ }, {
29401
+ command: "deno",
29402
+ args: ["lsp"]
29403
+ }, {
29404
+ command: "vscode-eslint-language-server",
29405
+ args: ["--stdio"]
29406
+ }],
29407
+ javascript: [{
29408
+ command: "typescript-language-server",
29409
+ args: ["--stdio"]
29410
+ }, {
29411
+ command: "biome",
29412
+ args: ["lsp-proxy"]
29413
+ }, {
29414
+ command: "deno",
29415
+ args: ["lsp"]
29416
+ }, {
29417
+ command: "vscode-eslint-language-server",
29418
+ args: ["--stdio"]
29419
+ }],
29420
+ python: [{
29421
+ command: "pyright-langserver",
29422
+ args: ["--stdio"]
29423
+ }, {
29424
+ command: "pylsp",
29425
+ args: []
29426
+ }],
29427
+ go: [{
29428
+ command: "gopls",
29429
+ args: ["serve"]
29430
+ }],
29431
+ rust: [{
29432
+ command: "rust-analyzer",
29433
+ args: []
29434
+ }],
29435
+ lua: [{
29436
+ command: "lua-language-server",
29437
+ args: []
29438
+ }],
29439
+ c: [{
29440
+ command: "clangd",
29441
+ args: []
29442
+ }],
29443
+ cpp: [{
29444
+ command: "clangd",
29445
+ args: []
29446
+ }],
29447
+ ruby: [{
29448
+ command: "solargraph",
29449
+ args: ["stdio"]
29450
+ }],
29451
+ php: [{
29452
+ command: "intelephense",
29453
+ args: ["--stdio"]
29454
+ }],
29455
+ zig: [{
29456
+ command: "zls",
29457
+ args: []
29458
+ }],
29459
+ bash: [{
29460
+ command: "bash-language-server",
29461
+ args: ["start"]
29462
+ }],
29463
+ css: [{
29464
+ command: "vscode-css-language-server",
29465
+ args: ["--stdio"]
29466
+ }, {
29467
+ command: "biome",
29468
+ args: ["lsp-proxy"]
29469
+ }, {
29470
+ command: "tailwindcss-language-server",
29471
+ args: ["--stdio"]
29472
+ }],
29473
+ html: [{
29474
+ command: "vscode-html-language-server",
29475
+ args: ["--stdio"]
29476
+ }, {
29477
+ command: "emmet-language-server",
29478
+ args: ["--stdio"]
29479
+ }],
29480
+ json: [{
29481
+ command: "vscode-json-language-server",
29482
+ args: ["--stdio"]
29483
+ }, {
29484
+ command: "biome",
29485
+ args: ["lsp-proxy"]
29486
+ }],
29487
+ yaml: [{
29488
+ command: "yaml-language-server",
29489
+ args: ["--stdio"]
29490
+ }],
29491
+ toml: [{
29492
+ command: "taplo",
29493
+ args: ["lsp", "stdio"]
29494
+ }],
29495
+ dockerfile: [{
29496
+ command: "docker-langserver",
29497
+ args: ["--stdio"]
29498
+ }],
29499
+ java: [{
29500
+ command: "jdtls",
29501
+ args: []
29502
+ }],
29503
+ kotlin: [{
29504
+ command: "kotlin-language-server",
29505
+ args: []
29506
+ }],
29507
+ scala: [{
29508
+ command: "metals",
29509
+ args: []
29510
+ }],
29511
+ csharp: [{
29512
+ command: "csharp-ls",
29513
+ args: []
29514
+ }, {
29515
+ command: "OmniSharp",
29516
+ args: ["--languageserver"]
29517
+ }],
29518
+ swift: [{
29519
+ command: "sourcekit-lsp",
29520
+ args: []
29521
+ }],
29522
+ dart: [{
29523
+ command: "dart",
29524
+ args: ["language-server", "--protocol=lsp"]
29525
+ }],
29526
+ elixir: [{
29527
+ command: "elixir-ls",
29528
+ args: []
29529
+ }, {
29530
+ command: "expert",
29531
+ args: []
29532
+ }],
29533
+ ocaml: [{
29534
+ command: "ocamllsp",
29535
+ args: []
29536
+ }],
29537
+ vue: [{
29538
+ command: "vue-language-server",
29539
+ args: ["--stdio"]
29540
+ }]
29541
+ };
29542
+ MASON_BIN_DIR = join9(homedir5(), ".local", "share", "soulforge", "mason", "bin");
29543
+ SOULFORGE_BIN_DIR = join9(homedir5(), ".soulforge", "lsp-servers");
29544
+ probeCache = new Map;
29545
+ });
29546
+
29543
29547
  // src/core/process-tracker.ts
29544
29548
  function trackProcess(proc) {
29545
29549
  tracked.add(proc);
@@ -29552,36 +29556,48 @@ var init_process_tracker = __esm(() => {
29552
29556
  });
29553
29557
 
29554
29558
  // src/core/intelligence/backends/lsp/pid-tracker.ts
29555
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, renameSync, writeFileSync as writeFileSync4 } from "fs";
29556
- import { homedir as homedir6 } from "os";
29559
+ import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
29560
+ import { homedir as homedir6, userInfo } from "os";
29557
29561
  import { join as join10 } from "path";
29558
29562
  function ensureDir() {
29559
- const dir = join10(homedir6(), ".soulforge");
29560
- if (!existsSync8(dir))
29561
- mkdirSync3(dir, {
29562
- recursive: true
29563
- });
29563
+ if (!existsSync8(SOULFORGE_DIR)) {
29564
+ try {
29565
+ mkdirSync3(SOULFORGE_DIR, {
29566
+ recursive: true
29567
+ });
29568
+ } catch {}
29569
+ }
29564
29570
  }
29565
- function flush() {
29571
+ function trackLspPid(pid) {
29572
+ activePids.add(pid);
29566
29573
  try {
29567
29574
  ensureDir();
29568
- const tmp = `${PID_FILE}.${String(process.pid)}.tmp`;
29569
- writeFileSync4(tmp, JSON.stringify([...activePids]), "utf-8");
29570
- renameSync(tmp, PID_FILE);
29575
+ appendFileSync2(PID_LOG, `+${String(pid)}
29576
+ `, "utf-8");
29571
29577
  } catch {}
29572
29578
  }
29573
- function trackLspPid(pid) {
29574
- activePids.add(pid);
29575
- flush();
29576
- }
29577
29579
  function untrackLspPid(pid) {
29578
29580
  activePids.delete(pid);
29579
- flush();
29581
+ try {
29582
+ appendFileSync2(PID_LOG, `-${String(pid)}
29583
+ `, "utf-8");
29584
+ } catch {}
29580
29585
  }
29581
- var PID_FILE, activePids;
29586
+ var SOULFORGE_DIR, PID_LOG, activePids, SOULFORGE_PATH_MARKERS;
29582
29587
  var init_pid_tracker = __esm(() => {
29583
- PID_FILE = join10(homedir6(), ".soulforge", "lsp-pids.json");
29588
+ SOULFORGE_DIR = join10(homedir6(), ".soulforge");
29589
+ PID_LOG = join10(SOULFORGE_DIR, "lsp-pids.log");
29584
29590
  activePids = new Set;
29591
+ SOULFORGE_PATH_MARKERS = [
29592
+ join10(homedir6(), ".local", "share", "soulforge", "mason"),
29593
+ join10(homedir6(), ".soulforge", "lsp-servers"),
29594
+ join10(homedir6(), ".soulforge", "bin"),
29595
+ "node_modules/.bin/biome",
29596
+ "node_modules/@biomejs/",
29597
+ "node_modules/.bin/typescript-language-server",
29598
+ "node_modules/typescript/lib/tsserver",
29599
+ "node_modules/.bin/vtsls"
29600
+ ];
29585
29601
  });
29586
29602
 
29587
29603
  // src/core/intelligence/backends/lsp/standalone-client.ts
@@ -29806,6 +29822,13 @@ class StandaloneLspClient {
29806
29822
  if (!oldest)
29807
29823
  break;
29808
29824
  this.openDocuments.delete(oldest);
29825
+ this.diagnostics.delete(oldest);
29826
+ const waiters = this.diagnosticWaiters.get(oldest);
29827
+ if (waiters) {
29828
+ for (const w3 of waiters)
29829
+ w3();
29830
+ this.diagnosticWaiters.delete(oldest);
29831
+ }
29809
29832
  this.notify("textDocument/didClose", {
29810
29833
  textDocument: {
29811
29834
  uri: oldest
@@ -277755,774 +277778,2424 @@ __export(exports_ts_morph, {
277755
277778
  TsMorphBackend: () => TsMorphBackend
277756
277779
  });
277757
277780
  import { resolve as resolve6 } from "path";
277781
+ function validTargetsFor(action) {
277782
+ return ACTION_VALID_TARGETS[action];
277783
+ }
277784
+ function targetsHint(action) {
277785
+ const valid2 = validTargetsFor(action);
277786
+ if (!valid2 || valid2.length === 0)
277787
+ return "";
277788
+ return ` Valid targets for ${action}: ${valid2.map((t) => `"${t}"`).join(", ")}.`;
277789
+ }
277790
+ function editDistance(a2, b3) {
277791
+ if (a2 === b3)
277792
+ return 0;
277793
+ if (!a2.length)
277794
+ return b3.length;
277795
+ if (!b3.length)
277796
+ return a2.length;
277797
+ const prev = Array.from({
277798
+ length: b3.length + 1
277799
+ }, (_3, j2) => j2);
277800
+ for (let i4 = 1;i4 <= a2.length; i4++) {
277801
+ const curr = [i4];
277802
+ for (let j2 = 1;j2 <= b3.length; j2++) {
277803
+ const cost = a2[i4 - 1] === b3[j2 - 1] ? 0 : 1;
277804
+ const del = (prev[j2] ?? 0) + 1;
277805
+ const ins = (curr[j2 - 1] ?? 0) + 1;
277806
+ const sub = (prev[j2 - 1] ?? 0) + cost;
277807
+ curr.push(Math.min(del, ins, sub));
277808
+ }
277809
+ for (let j2 = 0;j2 <= b3.length; j2++)
277810
+ prev[j2] = curr[j2] ?? 0;
277811
+ }
277812
+ return prev[b3.length] ?? 0;
277813
+ }
277814
+ function closestSymbolName(needle, listings) {
277815
+ if (!needle || listings.length === 0)
277816
+ return null;
277817
+ const candidates = [];
277818
+ for (const line of listings) {
277819
+ const m3 = /"([^"]+)"/.exec(line);
277820
+ if (m3?.[1])
277821
+ candidates.push(m3[1]);
277822
+ }
277823
+ if (candidates.length === 0)
277824
+ return null;
277825
+ const target = needle.toLowerCase();
277826
+ const threshold = Math.max(2, Math.ceil(needle.length * 0.4));
277827
+ let best = null;
277828
+ for (const c of candidates) {
277829
+ const bare = c.includes(".") ? c.split(".").pop() ?? c : c;
277830
+ const d2 = editDistance(target, bare.toLowerCase());
277831
+ if (d2 <= threshold && (!best || d2 < best.d))
277832
+ best = {
277833
+ name: c,
277834
+ d: d2
277835
+ };
277836
+ }
277837
+ return best?.name ?? null;
277838
+ }
277758
277839
  async function getTsMorph() {
277759
277840
  if (!tsMorphModule) {
277760
277841
  tsMorphModule = await Promise.resolve().then(() => __toESM(require_ts_morph(), 1));
277761
277842
  }
277762
277843
  return tsMorphModule;
277763
277844
  }
277764
-
277765
- class TsMorphBackend {
277766
- name = "ts-morph";
277767
- tier = 2;
277768
- project = null;
277769
- cwd = "";
277770
- supportsLanguage(language) {
277771
- return language === "typescript" || language === "javascript";
277772
- }
277773
- async initialize(cwd) {
277774
- this.cwd = cwd;
277775
- }
277776
- dispose() {
277777
- this.project = null;
277778
- }
277779
- async findDefinition(file, symbol, line, column) {
277780
- const sourceFile = await this.getSourceFile(file);
277781
- if (!sourceFile)
277782
- return null;
277783
- const node = this.findNode(sourceFile, symbol, line, column);
277784
- if (!node)
277785
- return null;
277786
- const ts = await getTsMorph();
277787
- if (!ts.Node.isIdentifier(node)) {
277788
- return null;
277845
+ function fuzzyMatchMultilineInText(haystack, needle) {
277846
+ if (!needle.includes(`
277847
+ `))
277848
+ return null;
277849
+ const hLines = haystack.split(`
277850
+ `);
277851
+ const nLines = needle.split(`
277852
+ `);
277853
+ if (nLines.length === 0 || nLines.length > hLines.length)
277854
+ return null;
277855
+ const norm = (s2) => s2.replace(/\r$/, "").replace(/^[\t ]+/, "").trimEnd();
277856
+ const hNorm = hLines.map(norm);
277857
+ const nNorm = nLines.map(norm);
277858
+ const offsets = [0];
277859
+ for (let i4 = 0;i4 < hLines.length; i4++) {
277860
+ offsets.push((offsets[i4] ?? 0) + (hLines[i4] ?? "").length + 1);
277861
+ }
277862
+ let match = null;
277863
+ for (let i4 = 0;i4 <= hLines.length - nLines.length; i4++) {
277864
+ let ok = true;
277865
+ for (let j2 = 0;j2 < nLines.length; j2++) {
277866
+ if (hNorm[i4 + j2] !== nNorm[j2]) {
277867
+ ok = false;
277868
+ break;
277869
+ }
277789
277870
  }
277790
- const defs = node.getDefinitionNodes();
277791
- if (defs.length === 0)
277871
+ if (!ok)
277872
+ continue;
277873
+ const start2 = offsets[i4] ?? 0;
277874
+ const endLineIdx = i4 + nLines.length - 1;
277875
+ const end = (offsets[endLineIdx] ?? 0) + (hLines[endLineIdx] ?? "").length;
277876
+ const found = {
277877
+ index: start2,
277878
+ length: end - start2
277879
+ };
277880
+ if (match !== null)
277792
277881
  return null;
277793
- return defs.map((d2) => ({
277794
- file: d2.getSourceFile().getFilePath(),
277795
- line: d2.getStartLineNumber(),
277796
- column: d2.getStart() - d2.getStartLinePos() + 1
277797
- }));
277882
+ match = found;
277798
277883
  }
277799
- async findReferences(file, symbol, line, column) {
277800
- const sourceFile = await this.getSourceFile(file);
277801
- if (!sourceFile)
277802
- return null;
277803
- const node = this.findNode(sourceFile, symbol, line, column);
277804
- if (!node)
277805
- return null;
277806
- const ts = await getTsMorph();
277807
- if (!ts.Node.isIdentifier(node))
277808
- return null;
277809
- const refs = node.findReferencesAsNodes();
277810
- if (refs.length === 0)
277811
- return null;
277812
- return refs.map((r4) => ({
277813
- file: r4.getSourceFile().getFilePath(),
277814
- line: r4.getStartLineNumber(),
277815
- column: r4.getStart() - r4.getStartLinePos() + 1
277816
- }));
277884
+ return match;
277885
+ }
277886
+ function stripCommonIndent(s2) {
277887
+ if (!s2.includes(`
277888
+ `))
277889
+ return s2;
277890
+ const lines = s2.split(`
277891
+ `);
277892
+ let minIndent = Infinity;
277893
+ for (const line of lines) {
277894
+ if (line.trim() === "")
277895
+ continue;
277896
+ const m3 = line.match(/^[\t ]*/);
277897
+ const len = m3 ? m3[0].length : 0;
277898
+ if (len < minIndent)
277899
+ minIndent = len;
277900
+ if (minIndent === 0)
277901
+ break;
277817
277902
  }
277818
- async findSymbols(file, query) {
277819
- const sourceFile = await this.getSourceFile(file);
277820
- if (!sourceFile)
277821
- return null;
277822
- const symbols = [];
277823
- const ts = await getTsMorph();
277824
- for (const fn of sourceFile.getFunctions()) {
277825
- const name2 = fn.getName();
277826
- if (!name2)
277827
- continue;
277828
- if (query && !name2.toLowerCase().includes(query.toLowerCase()))
277829
- continue;
277830
- symbols.push({
277831
- name: name2,
277832
- kind: "function",
277833
- location: {
277834
- file: resolve6(file),
277835
- line: fn.getStartLineNumber(),
277836
- column: 1,
277837
- endLine: fn.getEndLineNumber()
277838
- }
277839
- });
277903
+ if (!Number.isFinite(minIndent) || minIndent === 0)
277904
+ return s2;
277905
+ const indent = minIndent;
277906
+ return lines.map((l3) => l3.length >= indent ? l3.slice(indent) : l3).join(`
277907
+ `);
277908
+ }
277909
+ var ACTION_VALID_TARGETS, tsMorphModule = null, TsMorphBackend;
277910
+ var init_ts_morph = __esm(() => {
277911
+ ACTION_VALID_TARGETS = {
277912
+ set_body: ["function", "method", "arrow_function", "constructor"],
277913
+ add_statement: ["function", "method", "arrow_function", "constructor"],
277914
+ insert_statement: ["function", "method", "arrow_function", "constructor"],
277915
+ remove_statement: ["function", "method", "arrow_function", "constructor"],
277916
+ set_return_type: ["function", "method", "arrow_function"],
277917
+ set_async: ["function", "method", "arrow_function"],
277918
+ set_generator: ["function", "method"],
277919
+ add_parameter: ["function", "method", "arrow_function", "constructor"],
277920
+ remove_parameter: ["function", "method", "arrow_function", "constructor"],
277921
+ add_overload: ["function", "method"],
277922
+ set_type: ["variable", "constant", "property", "parameter"],
277923
+ set_initializer: ["variable", "constant", "property"],
277924
+ remove_initializer: ["variable", "constant", "property"],
277925
+ set_declaration_kind: ["variable", "constant"],
277926
+ add_property: ["class", "interface", "property"],
277927
+ remove_property: ["class", "interface"],
277928
+ add_method: ["class", "interface", "method"],
277929
+ remove_method: ["class", "interface"],
277930
+ add_member: ["class", "interface", "enum"],
277931
+ remove_member: ["class", "interface", "enum"],
277932
+ add_constructor: ["class"],
277933
+ add_getter: ["class"],
277934
+ add_setter: ["class"],
277935
+ set_extends: ["class"],
277936
+ remove_extends: ["class"],
277937
+ add_extends: ["interface"],
277938
+ add_implements: ["class"],
277939
+ remove_implements: ["class"],
277940
+ extract_interface: ["class"],
277941
+ set_value: ["enum"],
277942
+ set_const_enum: ["enum"],
277943
+ set_export: ["function", "class", "interface", "type", "enum", "variable", "constant"],
277944
+ set_default_export: ["function", "class", "interface", "variable"],
277945
+ set_abstract: ["class", "method"],
277946
+ set_static: ["property", "method"],
277947
+ set_readonly: ["property"],
277948
+ set_scope: ["property", "method", "constructor"],
277949
+ set_optional: ["property", "parameter", "method"],
277950
+ set_overrides: ["method", "property"],
277951
+ set_ambient: ["function", "class", "interface", "type", "enum", "variable"],
277952
+ add_decorator: ["class", "method", "property"],
277953
+ remove_decorator: ["class", "method", "property"],
277954
+ add_type_parameter: ["function", "class", "interface", "type", "method"],
277955
+ add_jsdoc: ["function", "class", "interface", "type", "enum", "method", "property", "variable"],
277956
+ remove_jsdoc: ["function", "class", "interface", "type", "enum", "method", "property", "variable"],
277957
+ unwrap: ["function", "namespace"]
277958
+ };
277959
+ TsMorphBackend = class TsMorphBackend {
277960
+ name = "ts-morph";
277961
+ tier = 2;
277962
+ project = null;
277963
+ cwd = "";
277964
+ static SOURCE_FILE_CAP = 200;
277965
+ sourceFileLru = new Map;
277966
+ supportsLanguage(language) {
277967
+ return language === "typescript" || language === "javascript";
277840
277968
  }
277841
- for (const cls of sourceFile.getClasses()) {
277842
- const name2 = cls.getName();
277843
- if (!name2)
277844
- continue;
277845
- if (query && !name2.toLowerCase().includes(query.toLowerCase()))
277846
- continue;
277847
- symbols.push({
277848
- name: name2,
277849
- kind: "class",
277850
- location: {
277851
- file: resolve6(file),
277852
- line: cls.getStartLineNumber(),
277853
- column: 1,
277854
- endLine: cls.getEndLineNumber()
277855
- }
277856
- });
277969
+ async initialize(cwd) {
277970
+ this.cwd = cwd;
277857
277971
  }
277858
- for (const iface of sourceFile.getInterfaces()) {
277859
- const name2 = iface.getName();
277860
- if (query && !name2.toLowerCase().includes(query.toLowerCase()))
277861
- continue;
277862
- symbols.push({
277863
- name: name2,
277864
- kind: "interface",
277865
- location: {
277866
- file: resolve6(file),
277867
- line: iface.getStartLineNumber(),
277868
- column: 1,
277869
- endLine: iface.getEndLineNumber()
277870
- }
277871
- });
277972
+ dispose() {
277973
+ this.project = null;
277872
277974
  }
277873
- for (const ta of sourceFile.getTypeAliases()) {
277874
- const name2 = ta.getName();
277875
- if (query && !name2.toLowerCase().includes(query.toLowerCase()))
277876
- continue;
277877
- symbols.push({
277878
- name: name2,
277879
- kind: "type",
277880
- location: {
277881
- file: resolve6(file),
277882
- line: ta.getStartLineNumber(),
277883
- column: 1,
277884
- endLine: ta.getEndLineNumber()
277885
- }
277886
- });
277975
+ async findDefinition(file, symbol, line, column) {
277976
+ const sourceFile = await this.getSourceFile(file);
277977
+ if (!sourceFile)
277978
+ return null;
277979
+ const node = this.findNode(sourceFile, symbol, line, column);
277980
+ if (!node)
277981
+ return null;
277982
+ const ts = await getTsMorph();
277983
+ if (!ts.Node.isIdentifier(node)) {
277984
+ return null;
277985
+ }
277986
+ const defs = node.getDefinitionNodes();
277987
+ if (defs.length === 0)
277988
+ return null;
277989
+ return defs.map((d2) => ({
277990
+ file: d2.getSourceFile().getFilePath(),
277991
+ line: d2.getStartLineNumber(),
277992
+ column: d2.getStart() - d2.getStartLinePos() + 1
277993
+ }));
277887
277994
  }
277888
- for (const en of sourceFile.getEnums()) {
277889
- const name2 = en.getName();
277890
- if (query && !name2.toLowerCase().includes(query.toLowerCase()))
277891
- continue;
277892
- symbols.push({
277893
- name: name2,
277894
- kind: "enum",
277895
- location: {
277896
- file: resolve6(file),
277897
- line: en.getStartLineNumber(),
277898
- column: 1,
277899
- endLine: en.getEndLineNumber()
277900
- }
277901
- });
277995
+ async findReferences(file, symbol, line, column) {
277996
+ const sourceFile = await this.getSourceFile(file);
277997
+ if (!sourceFile)
277998
+ return null;
277999
+ const node = this.findNode(sourceFile, symbol, line, column);
278000
+ if (!node)
278001
+ return null;
278002
+ const ts = await getTsMorph();
278003
+ if (!ts.Node.isIdentifier(node))
278004
+ return null;
278005
+ const refs = node.findReferencesAsNodes();
278006
+ if (refs.length === 0)
278007
+ return null;
278008
+ return refs.map((r4) => ({
278009
+ file: r4.getSourceFile().getFilePath(),
278010
+ line: r4.getStartLineNumber(),
278011
+ column: r4.getStart() - r4.getStartLinePos() + 1
278012
+ }));
277902
278013
  }
277903
- for (const stmt of sourceFile.getVariableStatements()) {
277904
- for (const decl of stmt.getDeclarations()) {
277905
- const name2 = decl.getName();
278014
+ async findSymbols(file, query) {
278015
+ const sourceFile = await this.getSourceFile(file);
278016
+ if (!sourceFile)
278017
+ return null;
278018
+ const symbols = [];
278019
+ const ts = await getTsMorph();
278020
+ for (const fn of sourceFile.getFunctions()) {
278021
+ const name2 = fn.getName();
278022
+ if (!name2)
278023
+ continue;
277906
278024
  if (query && !name2.toLowerCase().includes(query.toLowerCase()))
277907
278025
  continue;
277908
- const isConst = stmt.getDeclarationKind() === ts.VariableDeclarationKind.Const;
277909
278026
  symbols.push({
277910
278027
  name: name2,
277911
- kind: isConst ? "constant" : "variable",
278028
+ kind: "function",
277912
278029
  location: {
277913
278030
  file: resolve6(file),
277914
- line: decl.getStartLineNumber(),
278031
+ line: fn.getStartLineNumber(),
277915
278032
  column: 1,
277916
- endLine: decl.getEndLineNumber()
278033
+ endLine: fn.getEndLineNumber()
277917
278034
  }
277918
278035
  });
277919
278036
  }
277920
- }
277921
- return symbols;
277922
- }
277923
- async findImports(file) {
277924
- const sourceFile = await this.getSourceFile(file);
277925
- if (!sourceFile)
277926
- return null;
277927
- return sourceFile.getImportDeclarations().map((imp) => {
277928
- const specifiers = [];
277929
- const defaultImport = imp.getDefaultImport();
277930
- if (defaultImport)
277931
- specifiers.push(defaultImport.getText());
277932
- for (const named of imp.getNamedImports()) {
277933
- specifiers.push(named.getName());
277934
- }
277935
- const namespaceImport = imp.getNamespaceImport();
277936
- return {
277937
- source: imp.getModuleSpecifierValue(),
277938
- specifiers,
277939
- isDefault: !!defaultImport,
277940
- isNamespace: !!namespaceImport,
277941
- location: {
277942
- file: resolve6(file),
277943
- line: imp.getStartLineNumber(),
277944
- column: 1,
277945
- endLine: imp.getEndLineNumber()
277946
- }
277947
- };
277948
- });
277949
- }
277950
- async findExports(file) {
277951
- const sourceFile = await this.getSourceFile(file);
277952
- if (!sourceFile)
277953
- return null;
277954
- const exports = [];
277955
- for (const exp of sourceFile.getExportedDeclarations()) {
277956
- const [name2, decls] = exp;
277957
- for (const decl of decls) {
277958
- let kind = "variable";
277959
- const ts = await getTsMorph();
277960
- if (ts.Node.isFunctionDeclaration(decl))
277961
- kind = "function";
277962
- else if (ts.Node.isClassDeclaration(decl))
277963
- kind = "class";
277964
- else if (ts.Node.isInterfaceDeclaration(decl))
277965
- kind = "interface";
277966
- else if (ts.Node.isTypeAliasDeclaration(decl))
277967
- kind = "type";
277968
- else if (ts.Node.isEnumDeclaration(decl))
277969
- kind = "enum";
277970
- exports.push({
278037
+ for (const cls of sourceFile.getClasses()) {
278038
+ const name2 = cls.getName();
278039
+ if (!name2)
278040
+ continue;
278041
+ if (query && !name2.toLowerCase().includes(query.toLowerCase()))
278042
+ continue;
278043
+ symbols.push({
277971
278044
  name: name2,
277972
- isDefault: name2 === "default",
277973
- kind,
278045
+ kind: "class",
278046
+ location: {
278047
+ file: resolve6(file),
278048
+ line: cls.getStartLineNumber(),
278049
+ column: 1,
278050
+ endLine: cls.getEndLineNumber()
278051
+ }
278052
+ });
278053
+ }
278054
+ for (const iface of sourceFile.getInterfaces()) {
278055
+ const name2 = iface.getName();
278056
+ if (query && !name2.toLowerCase().includes(query.toLowerCase()))
278057
+ continue;
278058
+ symbols.push({
278059
+ name: name2,
278060
+ kind: "interface",
278061
+ location: {
278062
+ file: resolve6(file),
278063
+ line: iface.getStartLineNumber(),
278064
+ column: 1,
278065
+ endLine: iface.getEndLineNumber()
278066
+ }
278067
+ });
278068
+ }
278069
+ for (const ta of sourceFile.getTypeAliases()) {
278070
+ const name2 = ta.getName();
278071
+ if (query && !name2.toLowerCase().includes(query.toLowerCase()))
278072
+ continue;
278073
+ symbols.push({
278074
+ name: name2,
278075
+ kind: "type",
278076
+ location: {
278077
+ file: resolve6(file),
278078
+ line: ta.getStartLineNumber(),
278079
+ column: 1,
278080
+ endLine: ta.getEndLineNumber()
278081
+ }
278082
+ });
278083
+ }
278084
+ for (const en of sourceFile.getEnums()) {
278085
+ const name2 = en.getName();
278086
+ if (query && !name2.toLowerCase().includes(query.toLowerCase()))
278087
+ continue;
278088
+ symbols.push({
278089
+ name: name2,
278090
+ kind: "enum",
277974
278091
  location: {
277975
278092
  file: resolve6(file),
277976
- line: decl.getStartLineNumber(),
278093
+ line: en.getStartLineNumber(),
277977
278094
  column: 1,
277978
- endLine: decl.getEndLineNumber()
278095
+ endLine: en.getEndLineNumber()
277979
278096
  }
277980
278097
  });
277981
278098
  }
278099
+ for (const stmt of sourceFile.getVariableStatements()) {
278100
+ for (const decl of stmt.getDeclarations()) {
278101
+ const name2 = decl.getName();
278102
+ if (query && !name2.toLowerCase().includes(query.toLowerCase()))
278103
+ continue;
278104
+ const isConst = stmt.getDeclarationKind() === ts.VariableDeclarationKind.Const;
278105
+ symbols.push({
278106
+ name: name2,
278107
+ kind: isConst ? "constant" : "variable",
278108
+ location: {
278109
+ file: resolve6(file),
278110
+ line: decl.getStartLineNumber(),
278111
+ column: 1,
278112
+ endLine: decl.getEndLineNumber()
278113
+ }
278114
+ });
278115
+ }
278116
+ }
278117
+ return symbols;
277982
278118
  }
277983
- return exports;
277984
- }
277985
- async getDiagnostics(file) {
277986
- const sourceFile = await this.getSourceFile(file);
277987
- if (!sourceFile)
277988
- return null;
277989
- const project = this.getProject();
277990
- if (!project)
277991
- return null;
277992
- const preDiags = project.getPreEmitDiagnostics().filter((d2) => d2.getSourceFile()?.getFilePath() === sourceFile.getFilePath());
277993
- const ts = await getTsMorph();
277994
- return preDiags.map((d2) => {
277995
- const start2 = d2.getStart();
277996
- let line = 1;
277997
- let column = 1;
277998
- if (start2 !== undefined) {
277999
- const lineAndCol = sourceFile.getLineAndColumnAtPos(start2);
278000
- line = lineAndCol.line;
278001
- column = lineAndCol.column;
278002
- }
278003
- const catMap = {
278004
- [ts.DiagnosticCategory.Error]: "error",
278005
- [ts.DiagnosticCategory.Warning]: "warning",
278006
- [ts.DiagnosticCategory.Suggestion]: "hint",
278007
- [ts.DiagnosticCategory.Message]: "info"
278008
- };
278009
- return {
278010
- file: resolve6(file),
278011
- line,
278012
- column,
278013
- severity: catMap[d2.getCategory()] ?? "error",
278014
- message: d2.getMessageText().toString(),
278015
- code: d2.getCode(),
278016
- source: "typescript"
278017
- };
278018
- });
278019
- }
278020
- async getTypeInfo(file, symbol, line, column) {
278021
- const sourceFile = await this.getSourceFile(file);
278022
- if (!sourceFile)
278023
- return null;
278024
- const node = this.findNode(sourceFile, symbol, line, column);
278025
- if (!node)
278026
- return null;
278027
- const nodeType = node.getType();
278028
- return {
278029
- symbol,
278030
- type: nodeType.getText(node),
278031
- documentation: this.getNodeDocumentation(node)
278032
- };
278033
- }
278034
- async getFileOutline(file) {
278035
- const [symbols, imports, exports] = await Promise.all([this.findSymbols(file), this.findImports(file), this.findExports(file)]);
278036
- if (!symbols)
278037
- return null;
278038
- const language = this.detectLang(file);
278039
- return {
278040
- file: resolve6(file),
278041
- language,
278042
- symbols,
278043
- imports: imports ?? [],
278044
- exports: exports ?? []
278045
- };
278046
- }
278047
- async readSymbol(file, symbolName, symbolKind) {
278048
- const sourceFile = await this.getSourceFile(file);
278049
- if (!sourceFile)
278050
- return null;
278051
- const ts = await getTsMorph();
278052
- const language = this.detectLang(file);
278053
- const candidates = [];
278054
- if (!symbolKind || symbolKind === "function") {
278055
- candidates.push(...sourceFile.getFunctions().filter((f3) => f3.getName() === symbolName));
278119
+ async findImports(file) {
278120
+ const sourceFile = await this.getSourceFile(file);
278121
+ if (!sourceFile)
278122
+ return null;
278123
+ return sourceFile.getImportDeclarations().map((imp) => {
278124
+ const specifiers = [];
278125
+ const defaultImport = imp.getDefaultImport();
278126
+ if (defaultImport)
278127
+ specifiers.push(defaultImport.getText());
278128
+ for (const named of imp.getNamedImports()) {
278129
+ specifiers.push(named.getName());
278130
+ }
278131
+ const namespaceImport = imp.getNamespaceImport();
278132
+ return {
278133
+ source: imp.getModuleSpecifierValue(),
278134
+ specifiers,
278135
+ isDefault: !!defaultImport,
278136
+ isNamespace: !!namespaceImport,
278137
+ location: {
278138
+ file: resolve6(file),
278139
+ line: imp.getStartLineNumber(),
278140
+ column: 1,
278141
+ endLine: imp.getEndLineNumber()
278142
+ }
278143
+ };
278144
+ });
278056
278145
  }
278057
- if (!symbolKind || symbolKind === "class") {
278058
- candidates.push(...sourceFile.getClasses().filter((c) => c.getName() === symbolName));
278146
+ async findExports(file) {
278147
+ const sourceFile = await this.getSourceFile(file);
278148
+ if (!sourceFile)
278149
+ return null;
278150
+ const exports = [];
278151
+ for (const exp of sourceFile.getExportedDeclarations()) {
278152
+ const [name2, decls] = exp;
278153
+ for (const decl of decls) {
278154
+ let kind = "variable";
278155
+ const ts = await getTsMorph();
278156
+ if (ts.Node.isFunctionDeclaration(decl))
278157
+ kind = "function";
278158
+ else if (ts.Node.isClassDeclaration(decl))
278159
+ kind = "class";
278160
+ else if (ts.Node.isInterfaceDeclaration(decl))
278161
+ kind = "interface";
278162
+ else if (ts.Node.isTypeAliasDeclaration(decl))
278163
+ kind = "type";
278164
+ else if (ts.Node.isEnumDeclaration(decl))
278165
+ kind = "enum";
278166
+ exports.push({
278167
+ name: name2,
278168
+ isDefault: name2 === "default",
278169
+ kind,
278170
+ location: {
278171
+ file: resolve6(file),
278172
+ line: decl.getStartLineNumber(),
278173
+ column: 1,
278174
+ endLine: decl.getEndLineNumber()
278175
+ }
278176
+ });
278177
+ }
278178
+ }
278179
+ return exports;
278059
278180
  }
278060
- if (!symbolKind || symbolKind === "interface") {
278061
- candidates.push(...sourceFile.getInterfaces().filter((i4) => i4.getName() === symbolName));
278181
+ async getDiagnostics(file) {
278182
+ const sourceFile = await this.getSourceFile(file);
278183
+ if (!sourceFile)
278184
+ return null;
278185
+ const project = this.getProject();
278186
+ if (!project)
278187
+ return null;
278188
+ const preDiags = project.getPreEmitDiagnostics().filter((d2) => d2.getSourceFile()?.getFilePath() === sourceFile.getFilePath());
278189
+ const ts = await getTsMorph();
278190
+ return preDiags.map((d2) => {
278191
+ const start2 = d2.getStart();
278192
+ let line = 1;
278193
+ let column = 1;
278194
+ if (start2 !== undefined) {
278195
+ const lineAndCol = sourceFile.getLineAndColumnAtPos(start2);
278196
+ line = lineAndCol.line;
278197
+ column = lineAndCol.column;
278198
+ }
278199
+ const catMap = {
278200
+ [ts.DiagnosticCategory.Error]: "error",
278201
+ [ts.DiagnosticCategory.Warning]: "warning",
278202
+ [ts.DiagnosticCategory.Suggestion]: "hint",
278203
+ [ts.DiagnosticCategory.Message]: "info"
278204
+ };
278205
+ return {
278206
+ file: resolve6(file),
278207
+ line,
278208
+ column,
278209
+ severity: catMap[d2.getCategory()] ?? "error",
278210
+ message: d2.getMessageText().toString(),
278211
+ code: d2.getCode(),
278212
+ source: "typescript"
278213
+ };
278214
+ });
278062
278215
  }
278063
- if (!symbolKind || symbolKind === "type") {
278064
- candidates.push(...sourceFile.getTypeAliases().filter((t) => t.getName() === symbolName));
278216
+ async getTypeInfo(file, symbol, line, column) {
278217
+ const sourceFile = await this.getSourceFile(file);
278218
+ if (!sourceFile)
278219
+ return null;
278220
+ const node = this.findNode(sourceFile, symbol, line, column);
278221
+ if (!node)
278222
+ return null;
278223
+ const nodeType = node.getType();
278224
+ return {
278225
+ symbol,
278226
+ type: nodeType.getText(node),
278227
+ documentation: this.getNodeDocumentation(node)
278228
+ };
278065
278229
  }
278066
- if (!symbolKind || symbolKind === "enum") {
278067
- candidates.push(...sourceFile.getEnums().filter((e) => e.getName() === symbolName));
278230
+ async getFileOutline(file) {
278231
+ const [symbols, imports, exports] = await Promise.all([this.findSymbols(file), this.findImports(file), this.findExports(file)]);
278232
+ if (!symbols)
278233
+ return null;
278234
+ const language = this.detectLang(file);
278235
+ return {
278236
+ file: resolve6(file),
278237
+ language,
278238
+ symbols,
278239
+ imports: imports ?? [],
278240
+ exports: exports ?? []
278241
+ };
278068
278242
  }
278069
- if (!symbolKind || symbolKind === "variable" || symbolKind === "constant") {
278070
- for (const stmt of sourceFile.getVariableStatements()) {
278071
- for (const decl of stmt.getDeclarations()) {
278072
- if (decl.getName() === symbolName) {
278073
- candidates.push(stmt);
278243
+ async readSymbol(file, symbolName, symbolKind) {
278244
+ const sourceFile = await this.getSourceFile(file);
278245
+ if (!sourceFile)
278246
+ return null;
278247
+ const ts = await getTsMorph();
278248
+ const language = this.detectLang(file);
278249
+ const candidates = [];
278250
+ if (!symbolKind || symbolKind === "function") {
278251
+ candidates.push(...sourceFile.getFunctions().filter((f3) => f3.getName() === symbolName));
278252
+ }
278253
+ if (!symbolKind || symbolKind === "class") {
278254
+ candidates.push(...sourceFile.getClasses().filter((c) => c.getName() === symbolName));
278255
+ }
278256
+ if (!symbolKind || symbolKind === "interface") {
278257
+ candidates.push(...sourceFile.getInterfaces().filter((i4) => i4.getName() === symbolName));
278258
+ }
278259
+ if (!symbolKind || symbolKind === "type") {
278260
+ candidates.push(...sourceFile.getTypeAliases().filter((t) => t.getName() === symbolName));
278261
+ }
278262
+ if (!symbolKind || symbolKind === "enum") {
278263
+ candidates.push(...sourceFile.getEnums().filter((e) => e.getName() === symbolName));
278264
+ }
278265
+ if (!symbolKind || symbolKind === "variable" || symbolKind === "constant") {
278266
+ for (const stmt of sourceFile.getVariableStatements()) {
278267
+ for (const decl of stmt.getDeclarations()) {
278268
+ if (decl.getName() === symbolName) {
278269
+ candidates.push(stmt);
278270
+ }
278074
278271
  }
278075
278272
  }
278076
278273
  }
278274
+ const target = candidates[0];
278275
+ if (!target)
278276
+ return null;
278277
+ let kind = "unknown";
278278
+ if (ts.Node.isFunctionDeclaration(target))
278279
+ kind = "function";
278280
+ else if (ts.Node.isClassDeclaration(target))
278281
+ kind = "class";
278282
+ else if (ts.Node.isInterfaceDeclaration(target))
278283
+ kind = "interface";
278284
+ else if (ts.Node.isTypeAliasDeclaration(target))
278285
+ kind = "type";
278286
+ else if (ts.Node.isEnumDeclaration(target))
278287
+ kind = "enum";
278288
+ else if (ts.Node.isVariableStatement(target))
278289
+ kind = "variable";
278290
+ return {
278291
+ content: target.getFullText().trimStart(),
278292
+ location: {
278293
+ file: resolve6(file),
278294
+ line: target.getStartLineNumber(),
278295
+ column: 1,
278296
+ endLine: target.getEndLineNumber()
278297
+ },
278298
+ symbolName,
278299
+ symbolKind: kind,
278300
+ language
278301
+ };
278077
278302
  }
278078
- const target = candidates[0];
278079
- if (!target)
278080
- return null;
278081
- let kind = "unknown";
278082
- if (ts.Node.isFunctionDeclaration(target))
278083
- kind = "function";
278084
- else if (ts.Node.isClassDeclaration(target))
278085
- kind = "class";
278086
- else if (ts.Node.isInterfaceDeclaration(target))
278087
- kind = "interface";
278088
- else if (ts.Node.isTypeAliasDeclaration(target))
278089
- kind = "type";
278090
- else if (ts.Node.isEnumDeclaration(target))
278091
- kind = "enum";
278092
- else if (ts.Node.isVariableStatement(target))
278093
- kind = "variable";
278094
- return {
278095
- content: target.getFullText().trimStart(),
278096
- location: {
278097
- file: resolve6(file),
278098
- line: target.getStartLineNumber(),
278099
- column: 1,
278100
- endLine: target.getEndLineNumber()
278101
- },
278102
- symbolName,
278103
- symbolKind: kind,
278104
- language
278105
- };
278106
- }
278107
- async readScope(file, startLine, endLine) {
278108
- const sourceFile = await this.getSourceFile(file);
278109
- if (!sourceFile)
278110
- return null;
278111
- const language = this.detectLang(file);
278112
- const fullText = sourceFile.getFullText();
278113
- const lines = fullText.split(`
278303
+ async readScope(file, startLine, endLine) {
278304
+ const sourceFile = await this.getSourceFile(file);
278305
+ if (!sourceFile)
278306
+ return null;
278307
+ const language = this.detectLang(file);
278308
+ const fullText = sourceFile.getFullText();
278309
+ const lines = fullText.split(`
278114
278310
  `);
278115
- const startIdx = Math.max(0, startLine - 1);
278116
- const endIdx = endLine ? Math.min(endLine - 1, lines.length - 1) : Math.min(startIdx + 50, lines.length - 1);
278117
- const blockContent = lines.slice(startIdx, endIdx + 1).join(`
278311
+ const startIdx = Math.max(0, startLine - 1);
278312
+ const endIdx = endLine ? Math.min(endLine - 1, lines.length - 1) : Math.min(startIdx + 50, lines.length - 1);
278313
+ const blockContent = lines.slice(startIdx, endIdx + 1).join(`
278118
278314
  `);
278119
- return {
278120
- content: blockContent,
278121
- location: {
278122
- file: resolve6(file),
278123
- line: startLine,
278124
- column: 1,
278125
- endLine: endIdx + 1
278126
- },
278127
- language
278128
- };
278129
- }
278130
- async rename(file, symbol, newName, line, column) {
278131
- const sourceFile = await this.getSourceFile(file);
278132
- if (!sourceFile)
278133
- return null;
278134
- const node = this.findNode(sourceFile, symbol, line, column);
278135
- if (!node)
278136
- return null;
278137
- const ts = await getTsMorph();
278138
- if (!ts.Node.isIdentifier(node))
278139
- return null;
278140
- const refs = node.findReferencesAsNodes();
278141
- const affectedFiles = new Set;
278142
- for (const ref of refs) {
278143
- affectedFiles.add(ref.getSourceFile().getFilePath());
278144
- }
278145
- affectedFiles.add(sourceFile.getFilePath());
278146
- const beforeContent = new Map;
278147
- for (const filePath of affectedFiles) {
278148
- const sf = this.getProject()?.getSourceFile(filePath);
278149
- if (sf)
278150
- beforeContent.set(filePath, sf.getFullText());
278151
- }
278152
- node.rename(newName);
278153
- const edits = [];
278154
- for (const filePath of affectedFiles) {
278155
- const sf = this.getProject()?.getSourceFile(filePath);
278156
- const before = beforeContent.get(filePath);
278157
- if (sf && before) {
278158
- const after = sf.getFullText();
278159
- if (before !== after) {
278160
- edits.push({
278161
- file: filePath,
278162
- oldContent: before,
278163
- newContent: after
278164
- });
278315
+ return {
278316
+ content: blockContent,
278317
+ location: {
278318
+ file: resolve6(file),
278319
+ line: startLine,
278320
+ column: 1,
278321
+ endLine: endIdx + 1
278322
+ },
278323
+ language
278324
+ };
278325
+ }
278326
+ async rename(file, symbol, newName, line, column) {
278327
+ const sourceFile = await this.getSourceFile(file);
278328
+ if (!sourceFile)
278329
+ return null;
278330
+ const node = this.findNode(sourceFile, symbol, line, column);
278331
+ if (!node)
278332
+ return null;
278333
+ const ts = await getTsMorph();
278334
+ if (!ts.Node.isIdentifier(node))
278335
+ return null;
278336
+ const refs = node.findReferencesAsNodes();
278337
+ const affectedFiles = new Set;
278338
+ for (const ref of refs) {
278339
+ affectedFiles.add(ref.getSourceFile().getFilePath());
278340
+ }
278341
+ affectedFiles.add(sourceFile.getFilePath());
278342
+ const beforeContent = new Map;
278343
+ for (const filePath of affectedFiles) {
278344
+ const sf = this.getProject()?.getSourceFile(filePath);
278345
+ if (sf)
278346
+ beforeContent.set(filePath, sf.getFullText());
278347
+ }
278348
+ node.rename(newName);
278349
+ const edits = [];
278350
+ for (const filePath of affectedFiles) {
278351
+ const sf = this.getProject()?.getSourceFile(filePath);
278352
+ const before = beforeContent.get(filePath);
278353
+ if (sf && before) {
278354
+ const after = sf.getFullText();
278355
+ if (before !== after) {
278356
+ edits.push({
278357
+ file: filePath,
278358
+ oldContent: before,
278359
+ newContent: after
278360
+ });
278361
+ }
278165
278362
  }
278166
278363
  }
278364
+ return {
278365
+ edits,
278366
+ description: `Renamed '${symbol}' to '${newName}' in ${String(edits.length)} file(s)`
278367
+ };
278167
278368
  }
278168
- return {
278169
- edits,
278170
- description: `Renamed '${symbol}' to '${newName}' in ${String(edits.length)} file(s)`
278171
- };
278172
- }
278173
- async extractFunction(file, startLine, endLine, functionName) {
278174
- const sourceFile = await this.getSourceFile(file);
278175
- if (!sourceFile)
278176
- return null;
278177
- const ts = await getTsMorph();
278178
- const fullText = sourceFile.getFullText();
278179
- const lines = fullText.split(`
278369
+ async extractFunction(file, startLine, endLine, functionName) {
278370
+ const sourceFile = await this.getSourceFile(file);
278371
+ if (!sourceFile)
278372
+ return null;
278373
+ const ts = await getTsMorph();
278374
+ const fullText = sourceFile.getFullText();
278375
+ const lines = fullText.split(`
278180
278376
  `);
278181
- const startIdx = Math.max(0, startLine - 1);
278182
- const endIdx = Math.min(endLine - 1, lines.length - 1);
278183
- const extractedLines = lines.slice(startIdx, endIdx + 1);
278184
- const extractedCode = extractedLines.join(`
278377
+ const startIdx = Math.max(0, startLine - 1);
278378
+ const endIdx = Math.min(endLine - 1, lines.length - 1);
278379
+ const extractedLines = lines.slice(startIdx, endIdx + 1);
278380
+ const extractedCode = extractedLines.join(`
278185
278381
  `);
278186
- const indent = (extractedLines[0] ?? "").match(/^(\s*)/)?.[1] ?? "";
278187
- const startPos = sourceFile.compilerNode.getPositionOfLineAndCharacter(startIdx, 0);
278188
- const endPos = sourceFile.compilerNode.getPositionOfLineAndCharacter(endIdx, (lines[endIdx] ?? "").length);
278189
- const params = [];
278190
- const paramNames = new Set;
278191
- sourceFile.forEachDescendant((node) => {
278192
- if (!ts.Node.isIdentifier(node))
278193
- return;
278194
- const nodeStart = node.getStart();
278195
- if (nodeStart < startPos || nodeStart > endPos)
278196
- return;
278197
- const name2 = node.getText();
278198
- if (paramNames.has(name2))
278199
- return;
278200
- const defs = node.getDefinitionNodes();
278201
- for (const def of defs) {
278202
- const defStart = def.getStartLineNumber();
278203
- if (def.getSourceFile() === sourceFile && (defStart < startLine || defStart > endLine)) {
278204
- const nodeType = node.getType();
278205
- const typeText = nodeType.getText(node);
278206
- params.push(`${name2}: ${typeText}`);
278207
- paramNames.add(name2);
278208
- break;
278382
+ const indent = (extractedLines[0] ?? "").match(/^(\s*)/)?.[1] ?? "";
278383
+ const startPos = sourceFile.compilerNode.getPositionOfLineAndCharacter(startIdx, 0);
278384
+ const endPos = sourceFile.compilerNode.getPositionOfLineAndCharacter(endIdx, (lines[endIdx] ?? "").length);
278385
+ const params = [];
278386
+ const paramNames = new Set;
278387
+ sourceFile.forEachDescendant((node) => {
278388
+ if (!ts.Node.isIdentifier(node))
278389
+ return;
278390
+ const nodeStart = node.getStart();
278391
+ if (nodeStart < startPos || nodeStart > endPos)
278392
+ return;
278393
+ const name2 = node.getText();
278394
+ if (paramNames.has(name2))
278395
+ return;
278396
+ const defs = node.getDefinitionNodes();
278397
+ for (const def of defs) {
278398
+ const defStart = def.getStartLineNumber();
278399
+ if (def.getSourceFile() === sourceFile && (defStart < startLine || defStart > endLine)) {
278400
+ const nodeType = node.getType();
278401
+ const typeText = nodeType.getText(node);
278402
+ params.push(`${name2}: ${typeText}`);
278403
+ paramNames.add(name2);
278404
+ break;
278405
+ }
278209
278406
  }
278210
- }
278211
- });
278212
- const lastLine = extractedLines[extractedLines.length - 1]?.trim() ?? "";
278213
- const hasReturn = lastLine.startsWith("return ");
278214
- const paramList = params.join(", ");
278215
- const argList = [...paramNames].join(", ");
278216
- const newFunc = `
278407
+ });
278408
+ const lastLine = extractedLines[extractedLines.length - 1]?.trim() ?? "";
278409
+ const hasReturn = lastLine.startsWith("return ");
278410
+ const paramList = params.join(", ");
278411
+ const argList = [...paramNames].join(", ");
278412
+ const newFunc = `
278217
278413
  function ${functionName}(${paramList}) {
278218
278414
  ${extractedCode}
278219
278415
  }
278220
278416
  `;
278221
- const callExpr = hasReturn ? `${indent}return ${functionName}(${argList});` : `${indent}${functionName}(${argList});`;
278222
- const newLines = [...lines];
278223
- newLines.splice(startIdx, endIdx - startIdx + 1, callExpr);
278224
- const newContent = `${newLines.join(`
278417
+ const callExpr = hasReturn ? `${indent}return ${functionName}(${argList});` : `${indent}${functionName}(${argList});`;
278418
+ const newLines = [...lines];
278419
+ newLines.splice(startIdx, endIdx - startIdx + 1, callExpr);
278420
+ const newContent = `${newLines.join(`
278225
278421
  `)}
278226
278422
  ${newFunc}`;
278227
- return {
278228
- edits: [{
278229
- file: resolve6(file),
278230
- oldContent: fullText,
278231
- newContent
278232
- }],
278233
- description: `Extracted lines ${String(startLine)}-${String(endLine)} into function '${functionName}(${paramList})'`
278234
- };
278235
- }
278236
- async extractVariable(file, startLine, endLine, variableName) {
278237
- const sourceFile = await this.getSourceFile(file);
278238
- if (!sourceFile)
278239
- return null;
278240
- const fullText = sourceFile.getFullText();
278241
- const lines = fullText.split(`
278423
+ return {
278424
+ edits: [{
278425
+ file: resolve6(file),
278426
+ oldContent: fullText,
278427
+ newContent
278428
+ }],
278429
+ description: `Extracted lines ${String(startLine)}-${String(endLine)} into function '${functionName}(${paramList})'`
278430
+ };
278431
+ }
278432
+ async extractVariable(file, startLine, endLine, variableName) {
278433
+ const sourceFile = await this.getSourceFile(file);
278434
+ if (!sourceFile)
278435
+ return null;
278436
+ const fullText = sourceFile.getFullText();
278437
+ const lines = fullText.split(`
278242
278438
  `);
278243
- const startIdx = Math.max(0, startLine - 1);
278244
- const endIdx = Math.min(endLine - 1, lines.length - 1);
278245
- const extractedCode = lines.slice(startIdx, endIdx + 1).join(`
278439
+ const startIdx = Math.max(0, startLine - 1);
278440
+ const endIdx = Math.min(endLine - 1, lines.length - 1);
278441
+ const extractedCode = lines.slice(startIdx, endIdx + 1).join(`
278246
278442
  `).trim();
278247
- const indent = (lines[startIdx] ?? "").match(/^(\s*)/)?.[1] ?? "";
278248
- const declaration = `${indent}const ${variableName} = ${extractedCode};`;
278249
- const replacement = `${indent}${variableName}`;
278250
- const newLines = [...lines];
278251
- newLines.splice(startIdx, endIdx - startIdx + 1, declaration, replacement);
278252
- return {
278253
- edits: [{
278254
- file: resolve6(file),
278255
- oldContent: fullText,
278256
- newContent: newLines.join(`
278257
- `)
278258
- }],
278259
- description: `Extracted lines ${String(startLine)}-${String(endLine)} into variable '${variableName}'`
278260
- };
278261
- }
278262
- async findImplementation(file, symbol, line, column) {
278263
- const sourceFile = await this.getSourceFile(file);
278264
- if (!sourceFile)
278265
- return null;
278266
- const node = this.findNode(sourceFile, symbol, line, column);
278267
- if (!node)
278268
- return null;
278269
- const ts = await getTsMorph();
278270
- if (!ts.Node.isIdentifier(node))
278271
- return null;
278272
- const impls = node.getImplementations();
278273
- if (impls.length === 0)
278274
- return null;
278275
- return impls.map((impl) => {
278276
- const sf = impl.getSourceFile();
278277
- const pos = sf.getLineAndColumnAtPos(impl.getTextSpan().getStart());
278443
+ const indent = (lines[startIdx] ?? "").match(/^(\s*)/)?.[1] ?? "";
278444
+ const declaration = `${indent}const ${variableName} = ${extractedCode};`;
278445
+ const replacement = `${indent}${variableName}`;
278446
+ const newLines = [...lines];
278447
+ newLines.splice(startIdx, endIdx - startIdx + 1, declaration, replacement);
278278
278448
  return {
278279
- file: sf.getFilePath(),
278280
- line: pos.line,
278281
- column: pos.column
278449
+ edits: [{
278450
+ file: resolve6(file),
278451
+ oldContent: fullText,
278452
+ newContent: newLines.join(`
278453
+ `)
278454
+ }],
278455
+ description: `Extracted lines ${String(startLine)}-${String(endLine)} into variable '${variableName}'`
278282
278456
  };
278283
- });
278284
- }
278285
- async getTypeHierarchy(file, symbol, line, column) {
278286
- const sourceFile = await this.getSourceFile(file);
278287
- if (!sourceFile)
278288
- return null;
278289
- const node = this.findNode(sourceFile, symbol, line, column);
278290
- if (!node)
278291
- return null;
278292
- const ts = await getTsMorph();
278293
- const parent = ts.Node.isIdentifier(node) ? node.getParent() : node;
278294
- if (!parent)
278295
- return null;
278296
- const item = {
278297
- name: symbol,
278298
- kind: "function",
278299
- file: resolve6(file),
278300
- line: node.getStartLineNumber()
278301
- };
278302
- const supertypes = [];
278303
- const subtypes = [];
278304
- if (ts.Node.isClassDeclaration(parent)) {
278305
- item.kind = "class";
278306
- const baseClass = parent.getBaseClass();
278307
- if (baseClass) {
278308
- supertypes.push({
278309
- name: baseClass.getName() ?? "anonymous",
278310
- kind: "class",
278311
- file: baseClass.getSourceFile().getFilePath(),
278312
- line: baseClass.getStartLineNumber()
278313
- });
278314
- }
278315
- for (const impl of parent.getImplements()) {
278316
- const typeNode = impl.getExpression();
278317
- const defs = ts.Node.isIdentifier(typeNode) ? typeNode.getDefinitionNodes() : [];
278318
- for (const def of defs) {
278457
+ }
278458
+ async findImplementation(file, symbol, line, column) {
278459
+ const sourceFile = await this.getSourceFile(file);
278460
+ if (!sourceFile)
278461
+ return null;
278462
+ const node = this.findNode(sourceFile, symbol, line, column);
278463
+ if (!node)
278464
+ return null;
278465
+ const ts = await getTsMorph();
278466
+ if (!ts.Node.isIdentifier(node))
278467
+ return null;
278468
+ const impls = node.getImplementations();
278469
+ if (impls.length === 0)
278470
+ return null;
278471
+ return impls.map((impl) => {
278472
+ const sf = impl.getSourceFile();
278473
+ const pos = sf.getLineAndColumnAtPos(impl.getTextSpan().getStart());
278474
+ return {
278475
+ file: sf.getFilePath(),
278476
+ line: pos.line,
278477
+ column: pos.column
278478
+ };
278479
+ });
278480
+ }
278481
+ async getTypeHierarchy(file, symbol, line, column) {
278482
+ const sourceFile = await this.getSourceFile(file);
278483
+ if (!sourceFile)
278484
+ return null;
278485
+ const node = this.findNode(sourceFile, symbol, line, column);
278486
+ if (!node)
278487
+ return null;
278488
+ const ts = await getTsMorph();
278489
+ const parent = ts.Node.isIdentifier(node) ? node.getParent() : node;
278490
+ if (!parent)
278491
+ return null;
278492
+ const item = {
278493
+ name: symbol,
278494
+ kind: "function",
278495
+ file: resolve6(file),
278496
+ line: node.getStartLineNumber()
278497
+ };
278498
+ const supertypes = [];
278499
+ const subtypes = [];
278500
+ if (ts.Node.isClassDeclaration(parent)) {
278501
+ item.kind = "class";
278502
+ const baseClass = parent.getBaseClass();
278503
+ if (baseClass) {
278319
278504
  supertypes.push({
278320
- name: typeNode.getText(),
278321
- kind: "interface",
278322
- file: def.getSourceFile().getFilePath(),
278323
- line: def.getStartLineNumber()
278505
+ name: baseClass.getName() ?? "anonymous",
278506
+ kind: "class",
278507
+ file: baseClass.getSourceFile().getFilePath(),
278508
+ line: baseClass.getStartLineNumber()
278324
278509
  });
278325
278510
  }
278511
+ for (const impl of parent.getImplements()) {
278512
+ const typeNode = impl.getExpression();
278513
+ const defs = ts.Node.isIdentifier(typeNode) ? typeNode.getDefinitionNodes() : [];
278514
+ for (const def of defs) {
278515
+ supertypes.push({
278516
+ name: typeNode.getText(),
278517
+ kind: "interface",
278518
+ file: def.getSourceFile().getFilePath(),
278519
+ line: def.getStartLineNumber()
278520
+ });
278521
+ }
278522
+ }
278523
+ const refs = parent.getNameNode()?.findReferencesAsNodes() ?? [];
278524
+ for (const ref of refs) {
278525
+ const refParent = ref.getParent();
278526
+ if (refParent && ts.Node.isHeritageClause(refParent.getParent() ?? refParent)) {
278527
+ const classDecl = refParent.getParent()?.getParent();
278528
+ if (classDecl && ts.Node.isClassDeclaration(classDecl)) {
278529
+ subtypes.push({
278530
+ name: classDecl.getName() ?? "anonymous",
278531
+ kind: "class",
278532
+ file: classDecl.getSourceFile().getFilePath(),
278533
+ line: classDecl.getStartLineNumber()
278534
+ });
278535
+ }
278536
+ }
278537
+ }
278538
+ } else if (ts.Node.isInterfaceDeclaration(parent)) {
278539
+ item.kind = "interface";
278540
+ for (const ext of parent.getExtends()) {
278541
+ const typeNode = ext.getExpression();
278542
+ const defs = ts.Node.isIdentifier(typeNode) ? typeNode.getDefinitionNodes() : [];
278543
+ for (const def of defs) {
278544
+ supertypes.push({
278545
+ name: typeNode.getText(),
278546
+ kind: "interface",
278547
+ file: def.getSourceFile().getFilePath(),
278548
+ line: def.getStartLineNumber()
278549
+ });
278550
+ }
278551
+ }
278552
+ const refs = parent.getNameNode().findReferencesAsNodes();
278553
+ for (const ref of refs) {
278554
+ const refParent = ref.getParent();
278555
+ if (refParent && ts.Node.isHeritageClause(refParent.getParent() ?? refParent)) {
278556
+ const container = refParent.getParent()?.getParent();
278557
+ if (container && ts.Node.isClassDeclaration(container)) {
278558
+ subtypes.push({
278559
+ name: container.getName() ?? "anonymous",
278560
+ kind: "class",
278561
+ file: container.getSourceFile().getFilePath(),
278562
+ line: container.getStartLineNumber()
278563
+ });
278564
+ }
278565
+ }
278566
+ }
278567
+ } else {
278568
+ return null;
278326
278569
  }
278327
- const refs = parent.getNameNode()?.findReferencesAsNodes() ?? [];
278328
- for (const ref of refs) {
278329
- const refParent = ref.getParent();
278330
- if (refParent && ts.Node.isHeritageClause(refParent.getParent() ?? refParent)) {
278331
- const classDecl = refParent.getParent()?.getParent();
278332
- if (classDecl && ts.Node.isClassDeclaration(classDecl)) {
278333
- subtypes.push({
278334
- name: classDecl.getName() ?? "anonymous",
278335
- kind: "class",
278336
- file: classDecl.getSourceFile().getFilePath(),
278337
- line: classDecl.getStartLineNumber()
278570
+ return {
278571
+ item,
278572
+ supertypes,
278573
+ subtypes
278574
+ };
278575
+ }
278576
+ async findUnused(file) {
278577
+ const sourceFile = await this.getSourceFile(file);
278578
+ if (!sourceFile)
278579
+ return null;
278580
+ const ts = await getTsMorph();
278581
+ const unused = [];
278582
+ for (const imp of sourceFile.getImportDeclarations()) {
278583
+ const defaultImport = imp.getDefaultImport();
278584
+ if (defaultImport) {
278585
+ const refs = defaultImport.findReferencesAsNodes();
278586
+ if (refs.length <= 1) {
278587
+ unused.push({
278588
+ name: defaultImport.getText(),
278589
+ kind: "import",
278590
+ file: resolve6(file),
278591
+ line: imp.getStartLineNumber()
278592
+ });
278593
+ }
278594
+ }
278595
+ for (const named of imp.getNamedImports()) {
278596
+ const nameNode = named.getAliasNode();
278597
+ const effectiveNode = nameNode ?? named.getNameNode();
278598
+ const refs = "findReferencesAsNodes" in effectiveNode ? effectiveNode.findReferencesAsNodes() : [];
278599
+ if (refs.length <= 1) {
278600
+ unused.push({
278601
+ name: named.getName(),
278602
+ kind: "import",
278603
+ file: resolve6(file),
278604
+ line: imp.getStartLineNumber()
278338
278605
  });
278339
278606
  }
278340
278607
  }
278341
278608
  }
278342
- } else if (ts.Node.isInterfaceDeclaration(parent)) {
278343
- item.kind = "interface";
278344
- for (const ext of parent.getExtends()) {
278345
- const typeNode = ext.getExpression();
278346
- const defs = ts.Node.isIdentifier(typeNode) ? typeNode.getDefinitionNodes() : [];
278347
- for (const def of defs) {
278348
- supertypes.push({
278349
- name: typeNode.getText(),
278350
- kind: "interface",
278351
- file: def.getSourceFile().getFilePath(),
278352
- line: def.getStartLineNumber()
278609
+ const project = this.getProject();
278610
+ if (project) {
278611
+ for (const [name2, decls] of sourceFile.getExportedDeclarations()) {
278612
+ if (name2 === "default")
278613
+ continue;
278614
+ const decl = decls[0];
278615
+ if (!decl)
278616
+ continue;
278617
+ const nameNode = ts.Node.isIdentifier(decl) ? decl : ("getNameNode" in decl) && typeof decl.getNameNode === "function" ? decl.getNameNode() : null;
278618
+ if (!nameNode || !ts.Node.isIdentifier(nameNode))
278619
+ continue;
278620
+ const refs = nameNode.findReferencesAsNodes();
278621
+ const externalRefs = refs.filter((r4) => r4.getSourceFile() !== sourceFile);
278622
+ if (externalRefs.length === 0) {
278623
+ unused.push({
278624
+ name: name2,
278625
+ kind: "export",
278626
+ file: resolve6(file),
278627
+ line: decl.getStartLineNumber()
278628
+ });
278629
+ }
278630
+ }
278631
+ }
278632
+ return unused.length > 0 ? unused : null;
278633
+ }
278634
+ static SURGICAL_ACTIONS = [
278635
+ "set_type",
278636
+ "set_return_type",
278637
+ "set_value",
278638
+ "set_initializer",
278639
+ "remove_initializer",
278640
+ "set_async",
278641
+ "set_generator",
278642
+ "set_export",
278643
+ "set_default_export",
278644
+ "set_abstract",
278645
+ "set_static",
278646
+ "set_readonly",
278647
+ "set_scope",
278648
+ "set_optional",
278649
+ "set_overrides",
278650
+ "set_ambient",
278651
+ "set_const_enum",
278652
+ "rename",
278653
+ "rename_global",
278654
+ "remove",
278655
+ "add_parameter",
278656
+ "remove_parameter",
278657
+ "set_declaration_kind",
278658
+ "set_body",
278659
+ "add_statement",
278660
+ "insert_statement",
278661
+ "remove_statement",
278662
+ "add_property",
278663
+ "remove_property",
278664
+ "add_method",
278665
+ "remove_method",
278666
+ "add_member",
278667
+ "remove_member",
278668
+ "add_constructor",
278669
+ "add_getter",
278670
+ "add_setter",
278671
+ "add_decorator",
278672
+ "remove_decorator",
278673
+ "add_overload",
278674
+ "set_extends",
278675
+ "remove_extends",
278676
+ "add_implements",
278677
+ "remove_implements",
278678
+ "add_type_parameter",
278679
+ "add_extends",
278680
+ "add_jsdoc",
278681
+ "remove_jsdoc",
278682
+ "unwrap",
278683
+ "set_structure",
278684
+ "extract_interface",
278685
+ "replace",
278686
+ "replace_in_body",
278687
+ "create_file",
278688
+ "add_import",
278689
+ "remove_import",
278690
+ "add_named_import",
278691
+ "remove_named_import",
278692
+ "set_module_specifier",
278693
+ "add_export_declaration",
278694
+ "add_named_reexport",
278695
+ "add_namespace",
278696
+ "organize_imports",
278697
+ "fix_missing_imports",
278698
+ "fix_unused",
278699
+ "add_function",
278700
+ "add_class",
278701
+ "add_interface",
278702
+ "add_type_alias",
278703
+ "add_enum",
278704
+ "add_variable",
278705
+ "insert_text"
278706
+ ];
278707
+ async surgicalEdit(file, operations) {
278708
+ const sourceFile = await this.getSourceFile(file);
278709
+ if (!sourceFile)
278710
+ return {
278711
+ ok: false,
278712
+ error: `Could not parse file: ${file}`
278713
+ };
278714
+ const ts = await getTsMorph();
278715
+ const before = sourceFile.getFullText();
278716
+ const details = [];
278717
+ for (let i4 = 0;i4 < operations.length; i4++) {
278718
+ const op = operations[i4];
278719
+ const label = operations.length > 1 ? `Op ${String(i4 + 1)}/${String(operations.length)}: ` : "";
278720
+ try {
278721
+ const detail = await this.executeSurgicalOp(sourceFile, op, ts);
278722
+ details.push(`${label}${detail}`);
278723
+ } catch (err2) {
278724
+ const msg = err2 instanceof Error ? err2.message : String(err2);
278725
+ sourceFile.replaceWithText(before);
278726
+ return {
278727
+ ok: false,
278728
+ error: `${label}${msg}
278729
+ NO edits were applied (atomic rollback).`
278730
+ };
278731
+ }
278732
+ }
278733
+ const after = sourceFile.getFullText();
278734
+ if (before === after) {
278735
+ const allIdempotent = details.every((d2) => /already present|nothing to add|nothing to do|no-op/i.test(d2));
278736
+ if (allIdempotent) {
278737
+ return {
278738
+ ok: true,
278739
+ before,
278740
+ after,
278741
+ details
278742
+ };
278743
+ }
278744
+ return {
278745
+ ok: false,
278746
+ error: "No changes produced by the operations."
278747
+ };
278748
+ }
278749
+ sourceFile.refreshFromFileSystemSync();
278750
+ return {
278751
+ ok: true,
278752
+ before,
278753
+ after,
278754
+ details
278755
+ };
278756
+ }
278757
+ async executeSurgicalOp(sf, op, ts) {
278758
+ const action = op.action;
278759
+ switch (action) {
278760
+ case "organize_imports":
278761
+ sf.organizeImports();
278762
+ return "Organized imports";
278763
+ case "fix_missing_imports":
278764
+ sf.fixMissingImports();
278765
+ return "Fixed missing imports";
278766
+ case "fix_unused":
278767
+ sf.fixUnusedIdentifiers();
278768
+ return "Fixed unused identifiers";
278769
+ case "add_import": {
278770
+ if (!op.value)
278771
+ throw new Error('add_import requires value (module specifier, e.g. "node:fs"). Pass named imports via newCode (comma-separated), default import via name. Example: { action:"add_import", value:"node:fs", newCode:"readFile, writeFile" }');
278772
+ const desiredNamed = op.newCode?.split(",").map((s2) => s2.trim()).filter(Boolean) ?? [];
278773
+ const desiredDefault = op.name?.trim();
278774
+ const existing = sf.getImportDeclaration(op.value);
278775
+ if (existing) {
278776
+ const addedNamed = [];
278777
+ const existingNames = new Set(existing.getNamedImports().map((n) => n.getName()));
278778
+ for (const n of desiredNamed) {
278779
+ if (!existingNames.has(n)) {
278780
+ existing.addNamedImport(n);
278781
+ addedNamed.push(n);
278782
+ }
278783
+ }
278784
+ let addedDefault = "";
278785
+ if (desiredDefault && !existing.getDefaultImport()) {
278786
+ existing.setDefaultImport(desiredDefault);
278787
+ addedDefault = desiredDefault;
278788
+ }
278789
+ if (addedNamed.length === 0 && !addedDefault) {
278790
+ return `Import from "${op.value}" already present \u2014 nothing to add`;
278791
+ }
278792
+ const parts2 = [];
278793
+ if (addedDefault)
278794
+ parts2.push(addedDefault);
278795
+ if (addedNamed.length)
278796
+ parts2.push(`{ ${addedNamed.join(", ")} }`);
278797
+ return `Merged import ${parts2.join(", ")} into existing "${op.value}"`;
278798
+ }
278799
+ sf.addImportDeclaration({
278800
+ moduleSpecifier: op.value,
278801
+ ...desiredNamed.length > 0 ? {
278802
+ namedImports: desiredNamed
278803
+ } : {},
278804
+ ...desiredDefault ? {
278805
+ defaultImport: desiredDefault
278806
+ } : {}
278353
278807
  });
278808
+ const specDesc = desiredNamed.length ? `{ ${desiredNamed.join(", ")} }` : desiredDefault ?? "*";
278809
+ return `Added import ${specDesc} from "${op.value}"`;
278810
+ }
278811
+ case "remove_import": {
278812
+ if (!op.value)
278813
+ throw new Error("remove_import requires value (module specifier)");
278814
+ const imp = sf.getImportDeclaration(op.value);
278815
+ if (!imp)
278816
+ throw new Error(`Import "${op.value}" not found`);
278817
+ imp.remove();
278818
+ return `Removed import from "${op.value}"`;
278819
+ }
278820
+ case "add_function": {
278821
+ if (!op.newCode)
278822
+ throw new Error("add_function requires newCode");
278823
+ sf.addStatements(op.newCode);
278824
+ return `Added function${op.name ? ` "${op.name}"` : ""}`;
278825
+ }
278826
+ case "add_class": {
278827
+ if (!op.newCode)
278828
+ throw new Error("add_class requires newCode");
278829
+ sf.addStatements(op.newCode);
278830
+ return `Added class${op.name ? ` "${op.name}"` : ""}`;
278831
+ }
278832
+ case "add_interface": {
278833
+ if (!op.newCode)
278834
+ throw new Error("add_interface requires newCode");
278835
+ sf.addStatements(op.newCode);
278836
+ return `Added interface${op.name ? ` "${op.name}"` : ""}`;
278837
+ }
278838
+ case "add_type_alias": {
278839
+ if (!op.newCode)
278840
+ throw new Error("add_type_alias requires newCode");
278841
+ sf.addStatements(op.newCode);
278842
+ return `Added type alias${op.name ? ` "${op.name}"` : ""}`;
278843
+ }
278844
+ case "add_enum": {
278845
+ if (!op.newCode)
278846
+ throw new Error("add_enum requires newCode");
278847
+ sf.addStatements(op.newCode);
278848
+ return `Added enum${op.name ? ` "${op.name}"` : ""}`;
278849
+ }
278850
+ case "add_variable": {
278851
+ if (!op.newCode)
278852
+ throw new Error("add_variable requires newCode");
278853
+ sf.addStatements(op.newCode);
278854
+ return `Added variable${op.name ? ` "${op.name}"` : ""}`;
278855
+ }
278856
+ case "add_named_import": {
278857
+ if (!op.value)
278858
+ throw new Error("add_named_import requires value (module specifier)");
278859
+ if (!op.newCode)
278860
+ throw new Error("add_named_import requires newCode (import names, comma-separated). " + "Tip: add_import is also idempotent and creates the declaration if missing \u2014 prefer it.");
278861
+ let imp = sf.getImportDeclaration(op.value);
278862
+ if (!imp) {
278863
+ imp = sf.addImportDeclaration({
278864
+ moduleSpecifier: op.value
278865
+ });
278866
+ }
278867
+ const names = op.newCode.split(",").map((s2) => s2.trim()).filter(Boolean);
278868
+ const existing = new Set(imp.getNamedImports().map((n) => n.getName()));
278869
+ const added = [];
278870
+ for (const n of names) {
278871
+ if (!existing.has(n)) {
278872
+ imp.addNamedImport(n);
278873
+ added.push(n);
278874
+ }
278875
+ }
278876
+ if (added.length === 0) {
278877
+ return `All requested named imports already present in "${op.value}" \u2014 nothing to add`;
278878
+ }
278879
+ return `Added { ${added.join(", ")} } to import from "${op.value}"`;
278880
+ }
278881
+ case "remove_named_import": {
278882
+ if (!op.value)
278883
+ throw new Error("remove_named_import requires value (module specifier)");
278884
+ if (!op.name)
278885
+ throw new Error("remove_named_import requires name (import name to remove)");
278886
+ const rimp = sf.getImportDeclaration(op.value);
278887
+ if (!rimp)
278888
+ throw new Error(`Import from "${op.value}" not found`);
278889
+ const namedImp = rimp.getNamedImports().find((n) => n.getName() === op.name);
278890
+ if (!namedImp)
278891
+ throw new Error(`Named import "${op.name}" not found in import from "${op.value}"`);
278892
+ namedImp.remove();
278893
+ return `Removed "${op.name}" from import from "${op.value}"`;
278894
+ }
278895
+ case "set_module_specifier": {
278896
+ if (!op.value)
278897
+ throw new Error("set_module_specifier requires value (old module specifier)");
278898
+ if (!op.newCode)
278899
+ throw new Error("set_module_specifier requires newCode (new module specifier)");
278900
+ const simp = sf.getImportDeclaration(op.value);
278901
+ if (!simp)
278902
+ throw new Error(`Import from "${op.value}" not found`);
278903
+ simp.setModuleSpecifier(op.newCode);
278904
+ return `Changed import path "${op.value}" \u2192 "${op.newCode}"`;
278905
+ }
278906
+ case "insert_text": {
278907
+ if (!op.newCode)
278908
+ throw new Error("insert_text requires newCode");
278909
+ let insertIdx;
278910
+ let anchorDesc;
278911
+ if (op.value === "after-imports") {
278912
+ const imports = sf.getImportDeclarations();
278913
+ insertIdx = imports.length;
278914
+ anchorDesc = `after imports (index ${String(insertIdx)})`;
278915
+ } else if (op.value === "before-exports") {
278916
+ const stmts = sf.getStatements();
278917
+ const firstExportIdx = stmts.findIndex((s2) => s2.getKindName() === "ExportDeclaration" || s2.getKindName() === "ExportAssignment");
278918
+ insertIdx = firstExportIdx === -1 ? stmts.length : firstExportIdx;
278919
+ anchorDesc = `before exports (index ${String(insertIdx)})`;
278920
+ } else if (op.index === -1) {
278921
+ const stmts = sf.getStatements();
278922
+ insertIdx = stmts.length;
278923
+ anchorDesc = `bottom (index ${String(insertIdx)})`;
278924
+ } else if (op.index == null) {
278925
+ throw new Error('insert_text requires an anchor: pass index (0 = top, -1 = bottom, N = slot), or value ("after-imports" | "before-exports"). Silent default removed to prevent accidental top-of-file insertion.');
278926
+ } else {
278927
+ insertIdx = op.index;
278928
+ anchorDesc = `index ${String(insertIdx)}`;
278929
+ }
278930
+ sf.insertStatements(insertIdx, op.newCode);
278931
+ return `Inserted text at ${anchorDesc}`;
278932
+ }
278933
+ case "add_export_declaration":
278934
+ case "add_named_reexport": {
278935
+ if (!op.value)
278936
+ throw new Error(`${action} requires value (module specifier, e.g. "./bridge.js")`);
278937
+ const desiredNames = op.newCode?.split(",").map((s2) => s2.trim()).filter(Boolean) ?? [];
278938
+ const existingExport = sf.getExportDeclarations().find((e) => e.getModuleSpecifierValue() === op.value);
278939
+ if (existingExport) {
278940
+ if (existingExport.isNamespaceExport()) {
278941
+ if (desiredNames.length === 0) {
278942
+ return `Namespace re-export from "${op.value}" already present \u2014 nothing to add`;
278943
+ }
278944
+ throw new Error(`Cannot merge named exports into existing \`export * from "${op.value}"\`. Remove the namespace re-export first or use a different module specifier.`);
278945
+ }
278946
+ const existingNames = new Set(existingExport.getNamedExports().map((n) => n.getName()));
278947
+ const added = [];
278948
+ for (const n of desiredNames) {
278949
+ if (!existingNames.has(n)) {
278950
+ existingExport.addNamedExport(n);
278951
+ added.push(n);
278952
+ }
278953
+ }
278954
+ if (added.length === 0) {
278955
+ return `All requested re-exports already present in "${op.value}" \u2014 nothing to add`;
278956
+ }
278957
+ return `Merged { ${added.join(", ")} } into existing re-export from "${op.value}"`;
278958
+ }
278959
+ sf.addExportDeclaration({
278960
+ moduleSpecifier: op.value,
278961
+ ...desiredNames.length > 0 ? {
278962
+ namedExports: desiredNames
278963
+ } : {}
278964
+ });
278965
+ const expDesc = desiredNames.length ? `{ ${desiredNames.join(", ")} }` : "*";
278966
+ return `Added export ${expDesc} from "${op.value}"`;
278967
+ }
278968
+ case "add_namespace": {
278969
+ if (!op.name)
278970
+ throw new Error("add_namespace requires name");
278971
+ sf.addModule({
278972
+ name: op.name,
278973
+ statements: op.newCode
278974
+ });
278975
+ return `Added namespace "${op.name}"`;
278354
278976
  }
278355
278977
  }
278356
- const refs = parent.getNameNode().findReferencesAsNodes();
278357
- for (const ref of refs) {
278358
- const refParent = ref.getParent();
278359
- if (refParent && ts.Node.isHeritageClause(refParent.getParent() ?? refParent)) {
278360
- const container = refParent.getParent()?.getParent();
278361
- if (container && ts.Node.isClassDeclaration(container)) {
278362
- subtypes.push({
278363
- name: container.getName() ?? "anonymous",
278364
- kind: "class",
278365
- file: container.getSourceFile().getFilePath(),
278366
- line: container.getStartLineNumber()
278978
+ if (!op.target || !op.name) {
278979
+ throw new Error(`Action "${action}" requires target and name.${targetsHint(action)}`);
278980
+ }
278981
+ let node = this.findSymbolNode(sf, op.target, op.name, ts);
278982
+ const ADD_MEMBER_ACTIONS = new Set(["add_property", "add_method"]);
278983
+ if (!node && ADD_MEMBER_ACTIONS.has(action) && op.name.includes(".")) {
278984
+ const [ownerName] = op.name.split(".");
278985
+ if (ownerName) {
278986
+ const owner = sf.getClass(ownerName) ?? sf.getInterface(ownerName) ?? null;
278987
+ if (owner)
278988
+ node = owner;
278989
+ }
278990
+ }
278991
+ if (!node) {
278992
+ const available = this.listSymbolsOfKind(sf, op.target, ts);
278993
+ const inherited = this.listInheritedMembers(sf, op.target, op.name, ts);
278994
+ const allCandidates = [...available, ...inherited];
278995
+ const closest = closestSymbolName(op.name, allCandidates);
278996
+ const didYouMean = closest ? `
278997
+ Did you mean: "${closest}"?` : "";
278998
+ const availBlock = available.length > 0 ? `
278999
+ Available ${op.target}s:
279000
+ ${available.map((s2) => ` ${s2}`).join(`
279001
+ `)}` : `
279002
+ No ${op.target}s found in this file.`;
279003
+ const inheritedBlock = inherited.length > 0 ? `
279004
+ Inherited ${op.target}s (from base class/interface):
279005
+ ${inherited.map((s2) => ` ${s2}`).join(`
279006
+ `)}` : "";
279007
+ throw new Error(`Symbol not found: ${op.target} "${op.name}".${didYouMean}${availBlock}${inheritedBlock}`);
279008
+ }
279009
+ switch (action) {
279010
+ case "set_type": {
279011
+ if (!op.value)
279012
+ throw new Error("set_type requires value");
279013
+ if ("setType" in node && typeof node.setType === "function") {
279014
+ node.setType(op.value);
279015
+ return `Set type of ${op.target} "${op.name}" \u2192 ${op.value}`;
279016
+ }
279017
+ if (ts.Node.isVariableStatement(node)) {
279018
+ const decl = node.getDeclarations().find((d2) => d2.getName() === op.name);
279019
+ if (decl) {
279020
+ decl.setType(op.value);
279021
+ return `Set type of variable "${op.name}" \u2192 ${op.value}`;
279022
+ }
279023
+ }
279024
+ if (ts.Node.isTypeAliasDeclaration(node)) {
279025
+ throw new Error(`set_type not supported on type aliases. Use: { action:"replace", target:"type", name:"${op.name}", newCode:"type ${op.name} = \u2026" } to rewrite the whole alias.`);
279026
+ }
279027
+ throw new Error(`Cannot set type on ${op.target} "${op.name}".${targetsHint("set_type")}`);
279028
+ }
279029
+ case "set_return_type": {
279030
+ if (!op.value)
279031
+ throw new Error("set_return_type requires value");
279032
+ if ("setReturnType" in node && typeof node.setReturnType === "function") {
279033
+ node.setReturnType(op.value);
279034
+ return `Set return type of ${op.target} "${op.name}" \u2192 ${op.value}`;
279035
+ }
279036
+ throw new Error(`Cannot set return type on ${op.target} "${op.name}"`);
279037
+ }
279038
+ case "set_value": {
279039
+ if (op.value === undefined)
279040
+ throw new Error("set_value requires value");
279041
+ if (ts.Node.isEnumDeclaration(node)) {
279042
+ const [memberName, memberValue] = op.value.split("=").map((s2) => s2.trim());
279043
+ if (!memberName)
279044
+ throw new Error("set_value on enum requires value format: memberName=newValue");
279045
+ const member = node.getMember(memberName);
279046
+ if (!member)
279047
+ throw new Error(`Enum member "${memberName}" not found in "${op.name}"`);
279048
+ if (memberValue !== undefined)
279049
+ member.setValue(Number.isNaN(Number(memberValue)) ? memberValue : Number(memberValue));
279050
+ return `Set enum "${op.name}".${memberName} = ${String(memberValue)}`;
279051
+ }
279052
+ throw new Error(`set_value not supported on ${op.target}`);
279053
+ }
279054
+ case "set_initializer": {
279055
+ if (!op.value)
279056
+ throw new Error("set_initializer requires value");
279057
+ if (ts.Node.isVariableStatement(node)) {
279058
+ const decl = node.getDeclarations().find((d2) => d2.getName() === op.name);
279059
+ if (decl) {
279060
+ decl.setInitializer(op.value);
279061
+ return `Set initializer of "${op.name}" \u2192 ${op.value}`;
279062
+ }
279063
+ }
279064
+ if ("setInitializer" in node && typeof node.setInitializer === "function") {
279065
+ node.setInitializer(op.value);
279066
+ return `Set initializer of "${op.name}" \u2192 ${op.value}`;
279067
+ }
279068
+ throw new Error(`Cannot set initializer on ${op.target} "${op.name}"`);
279069
+ }
279070
+ case "set_async": {
279071
+ if ("setIsAsync" in node && typeof node.setIsAsync === "function") {
279072
+ const val = op.value !== "false";
279073
+ node.setIsAsync(val);
279074
+ return `Set ${op.target} "${op.name}" async \u2192 ${String(val)}`;
279075
+ }
279076
+ throw new Error(`Cannot set async on ${op.target} "${op.name}"`);
279077
+ }
279078
+ case "set_generator": {
279079
+ if ("setIsGenerator" in node && typeof node.setIsGenerator === "function") {
279080
+ const val = op.value !== "false";
279081
+ node.setIsGenerator(val);
279082
+ return `Set ${op.target} "${op.name}" generator \u2192 ${String(val)}`;
279083
+ }
279084
+ throw new Error(`Cannot set generator on ${op.target} "${op.name}"`);
279085
+ }
279086
+ case "set_export": {
279087
+ if ("setIsExported" in node && typeof node.setIsExported === "function") {
279088
+ const val = op.value !== "false";
279089
+ node.setIsExported(val);
279090
+ return `Set ${op.target} "${op.name}" exported \u2192 ${String(val)}`;
279091
+ }
279092
+ throw new Error(`Cannot set export on ${op.target} "${op.name}"`);
279093
+ }
279094
+ case "set_default_export": {
279095
+ if ("setIsDefaultExport" in node && typeof node.setIsDefaultExport === "function") {
279096
+ const val = op.value !== "false";
279097
+ node.setIsDefaultExport(val);
279098
+ return `Set ${op.target} "${op.name}" default export \u2192 ${String(val)}`;
279099
+ }
279100
+ throw new Error(`Cannot set default export on ${op.target} "${op.name}"`);
279101
+ }
279102
+ case "rename":
279103
+ case "rename_global": {
279104
+ if (!op.value)
279105
+ throw new Error(`${action} requires value (new name)`);
279106
+ const isGlobal = action === "rename_global";
279107
+ const newName = op.value;
279108
+ const localRename = (n) => {
279109
+ const nameNode = n.getNameNode?.();
279110
+ if (nameNode && typeof nameNode.replaceWithText === "function") {
279111
+ nameNode.replaceWithText(newName);
279112
+ return true;
279113
+ }
279114
+ return false;
279115
+ };
279116
+ if (isGlobal) {
279117
+ if (ts.Node.isVariableStatement(node)) {
279118
+ const decl = node.getDeclarations().find((d2) => d2.getName() === op.name);
279119
+ const nameNode = decl?.getNameNode();
279120
+ if (nameNode && ts.Node.isIdentifier(nameNode)) {
279121
+ nameNode.rename(newName);
279122
+ return `Renamed (global) variable "${op.name}" \u2192 "${newName}"`;
279123
+ }
279124
+ }
279125
+ if ("rename" in node && typeof node.rename === "function") {
279126
+ node.rename(newName);
279127
+ return `Renamed (global) ${op.target} "${op.name}" \u2192 "${newName}"`;
279128
+ }
279129
+ throw new Error(`Cannot rename ${op.target} "${op.name}" (global)`);
279130
+ }
279131
+ if (ts.Node.isVariableStatement(node)) {
279132
+ const decl = node.getDeclarations().find((d2) => d2.getName() === op.name);
279133
+ if (!decl)
279134
+ throw new Error(`Variable "${op.name}" not found in declaration`);
279135
+ if (localRename(decl)) {
279136
+ return `Renamed variable "${op.name}" \u2192 "${newName}" (declaration only \u2014 references untouched)`;
279137
+ }
279138
+ }
279139
+ if (localRename(node)) {
279140
+ return `Renamed ${op.target} "${op.name}" \u2192 "${newName}" (declaration only \u2014 references untouched)`;
279141
+ }
279142
+ throw new Error(`Cannot rename ${op.target} "${op.name}"`);
279143
+ }
279144
+ case "remove": {
279145
+ const line = node.getStartLineNumber();
279146
+ if ("remove" in node && typeof node.remove === "function") {
279147
+ node.remove();
279148
+ return `Removed ${op.target} "${op.name}" (was at line ${String(line)})`;
279149
+ }
279150
+ throw new Error(`Cannot remove ${op.target} "${op.name}"`);
279151
+ }
279152
+ case "set_optional": {
279153
+ const val = op.value !== "false";
279154
+ if ("setHasQuestionToken" in node && typeof node.setHasQuestionToken === "function") {
279155
+ node.setHasQuestionToken(val);
279156
+ return `Set ${op.target} "${op.name}" optional \u2192 ${String(val)}`;
279157
+ }
279158
+ throw new Error(`Cannot set optional on ${op.target} "${op.name}"`);
279159
+ }
279160
+ case "add_parameter": {
279161
+ if (!op.value)
279162
+ throw new Error("add_parameter requires value (name: type)");
279163
+ if ("addParameter" in node && typeof node.addParameter === "function") {
279164
+ const [pName, pType] = op.value.split(":").map((s2) => s2.trim());
279165
+ node.addParameter({
279166
+ name: pName ?? op.value,
279167
+ ...pType ? {
279168
+ type: pType
279169
+ } : {}
279170
+ });
279171
+ return `Added parameter "${op.value}" to ${op.target} "${op.name}"`;
279172
+ }
279173
+ throw new Error(`Cannot add parameter to ${op.target} "${op.name}"`);
279174
+ }
279175
+ case "remove_parameter": {
279176
+ if (!op.value)
279177
+ throw new Error("remove_parameter requires value (parameter name)");
279178
+ if ("getParameters" in node && typeof node.getParameters === "function") {
279179
+ const params = node.getParameters();
279180
+ const param = params.find((p2) => p2.getName() === op.value);
279181
+ if (!param)
279182
+ throw new Error(`Parameter "${op.value}" not found in ${op.target} "${op.name}"`);
279183
+ param.remove();
279184
+ return `Removed parameter "${op.value}" from ${op.target} "${op.name}"`;
279185
+ }
279186
+ throw new Error(`Cannot remove parameter from ${op.target} "${op.name}"`);
279187
+ }
279188
+ case "set_body": {
279189
+ if (!op.newCode)
279190
+ throw new Error("set_body requires newCode");
279191
+ if (ts.Node.isVariableStatement(node)) {
279192
+ const decl = node.getDeclarations().find((d2) => d2.getName() === op.name);
279193
+ const init2 = decl?.getInitializer();
279194
+ if (init2 && (init2.getKindName() === "ArrowFunction" || init2.getKindName() === "FunctionExpression")) {
279195
+ throw new Error(`Cannot set body on variable "${op.name}" directly \u2014 the initializer is an arrow/function expression. Use: { action:"set_body", target:"arrow_function", name:"${op.name}", newCode:"\u2026" }.`);
279196
+ }
279197
+ }
279198
+ if ("setBodyText" in node && typeof node.setBodyText === "function") {
279199
+ node.setBodyText(op.newCode);
279200
+ return `Set body of ${op.target} "${op.name}"`;
279201
+ }
279202
+ throw new Error(`Cannot set body on ${op.target} "${op.name}" \u2014 no body (interface signature? type alias? use replace instead).${targetsHint("set_body")}`);
279203
+ }
279204
+ case "add_statement": {
279205
+ if (!op.newCode)
279206
+ throw new Error("add_statement requires newCode");
279207
+ if (ts.Node.isVariableStatement(node)) {
279208
+ const decl = node.getDeclarations().find((d2) => d2.getName() === op.name);
279209
+ const init2 = decl?.getInitializer();
279210
+ if (init2 && (init2.getKindName() === "ArrowFunction" || init2.getKindName() === "FunctionExpression")) {
279211
+ throw new Error(`Cannot add statement to variable "${op.name}" directly \u2014 the initializer is an arrow/function expression. Use: { action:"add_statement", target:"arrow_function", name:"${op.name}", newCode:"\u2026" }.`);
279212
+ }
279213
+ }
279214
+ if (ts.Node.isArrowFunction(node)) {
279215
+ const body4 = node.getBody();
279216
+ if (!ts.Node.isBlock(body4)) {
279217
+ const exprText = body4.getText();
279218
+ node.setBodyText(`return ${exprText};
279219
+ ${op.newCode}`);
279220
+ return `Added statement to arrow_function "${op.name}" (expression body wrapped into block)`;
279221
+ }
279222
+ }
279223
+ if ("addStatements" in node && typeof node.addStatements === "function") {
279224
+ node.addStatements(op.newCode);
279225
+ return `Added statement to ${op.target} "${op.name}"`;
279226
+ }
279227
+ throw new Error(`Cannot add statement to ${op.target} "${op.name}".${targetsHint("add_statement")}`);
279228
+ }
279229
+ case "insert_statement": {
279230
+ if (!op.newCode)
279231
+ throw new Error("insert_statement requires newCode");
279232
+ const idx = op.index ?? 0;
279233
+ if ("insertStatements" in node && typeof node.insertStatements === "function") {
279234
+ node.insertStatements(idx, op.newCode);
279235
+ return `Inserted statement at index ${String(idx)} in ${op.target} "${op.name}"`;
279236
+ }
279237
+ throw new Error(`Cannot insert statement in ${op.target} "${op.name}"`);
279238
+ }
279239
+ case "remove_statement": {
279240
+ const idx = op.index ?? 0;
279241
+ if ("removeStatement" in node && typeof node.removeStatement === "function") {
279242
+ node.removeStatement(idx);
279243
+ return `Removed statement at index ${String(idx)} from ${op.target} "${op.name}"`;
279244
+ }
279245
+ throw new Error(`Cannot remove statement from ${op.target} "${op.name}"`);
279246
+ }
279247
+ case "add_property": {
279248
+ if (!op.newCode)
279249
+ throw new Error("add_property requires newCode (name: type or name = value)");
279250
+ let container = node;
279251
+ if (op.target === "property" && op.name.includes(".")) {
279252
+ const parent = node.getParent();
279253
+ if (parent && (ts.Node.isClassDeclaration(parent) || ts.Node.isInterfaceDeclaration(parent))) {
279254
+ container = parent;
279255
+ }
279256
+ }
279257
+ if (ts.Node.isInterfaceDeclaration(container)) {
279258
+ const optional = op.newCode.includes("?:");
279259
+ const [pName, pType] = op.newCode.replace("?:", ":").split(":").map((s2) => s2.trim());
279260
+ container.addProperty({
279261
+ name: pName ?? op.newCode,
279262
+ type: pType,
279263
+ hasQuestionToken: optional
279264
+ });
279265
+ return `Added property "${pName ?? op.newCode}" to interface "${container.getName() ?? op.name}"`;
279266
+ }
279267
+ if (ts.Node.isClassDeclaration(container)) {
279268
+ container.addProperty({
279269
+ name: op.newCode.split(/[=:]/)[0]?.trim() ?? op.newCode
279270
+ });
279271
+ const props = container.getProperties();
279272
+ const last = props[props.length - 1];
279273
+ if (last)
279274
+ last.replaceWithText(op.newCode);
279275
+ return `Added property to class "${container.getName() ?? op.name}"`;
279276
+ }
279277
+ throw new Error(`Cannot add property to ${op.target} "${op.name}". For classes/interfaces pass target:"class"|"interface" with name=ContainerName, or pass target:"property" with a dotted name like "Container.prop" to auto-resolve the owner.`);
279278
+ }
279279
+ case "remove_property": {
279280
+ if (!op.value)
279281
+ throw new Error("remove_property requires value (property name)");
279282
+ if (ts.Node.isInterfaceDeclaration(node)) {
279283
+ const prop = node.getProperty(op.value);
279284
+ if (!prop)
279285
+ throw new Error(`Property "${op.value}" not found in interface "${op.name}"`);
279286
+ prop.remove();
279287
+ return `Removed property "${op.value}" from interface "${op.name}"`;
279288
+ }
279289
+ if (ts.Node.isClassDeclaration(node)) {
279290
+ const prop = node.getProperty(op.value);
279291
+ if (!prop)
279292
+ throw new Error(`Property "${op.value}" not found in class "${op.name}"`);
279293
+ prop.remove();
279294
+ return `Removed property "${op.value}" from class "${op.name}"`;
279295
+ }
279296
+ throw new Error(`Cannot remove property from ${op.target} "${op.name}"`);
279297
+ }
279298
+ case "add_method": {
279299
+ if (!op.newCode)
279300
+ throw new Error("add_method requires newCode");
279301
+ let container = node;
279302
+ if ((op.target === "method" || op.target === "function") && op.name.includes(".")) {
279303
+ const parent = node.getParent();
279304
+ if (parent && (ts.Node.isClassDeclaration(parent) || ts.Node.isInterfaceDeclaration(parent))) {
279305
+ container = parent;
279306
+ }
279307
+ }
279308
+ if (ts.Node.isClassDeclaration(container)) {
279309
+ const methodMatch = op.newCode.match(/(?:async\s+)?(\w+)\s*\(/);
279310
+ if (methodMatch) {
279311
+ const methodName = methodMatch[1] ?? "method";
279312
+ const method = container.addMethod({
279313
+ name: methodName
279314
+ });
279315
+ method.replaceWithText(op.newCode);
279316
+ } else {
279317
+ container.addMember(op.newCode);
279318
+ }
279319
+ return `Added method to class "${container.getName() ?? op.name}"`;
279320
+ }
279321
+ if (ts.Node.isInterfaceDeclaration(container)) {
279322
+ const sigMatch = op.newCode.match(/(\w+)\s*\(/);
279323
+ if (sigMatch) {
279324
+ const sigName = sigMatch[1] ?? "method";
279325
+ const sig = container.addMethod({
279326
+ name: sigName
279327
+ });
279328
+ sig.replaceWithText(op.newCode);
279329
+ } else {
279330
+ container.addMember(op.newCode);
279331
+ }
279332
+ return `Added method signature to interface "${container.getName() ?? op.name}"`;
279333
+ }
279334
+ throw new Error(`Cannot add method to ${op.target} "${op.name}". For classes/interfaces pass target:"class"|"interface" with name=ContainerName, or pass target:"method" with a dotted name like "Container.methodName" to auto-resolve the owner.`);
279335
+ }
279336
+ case "add_member": {
279337
+ if (!op.newCode)
279338
+ throw new Error("add_member requires newCode");
279339
+ if (ts.Node.isEnumDeclaration(node)) {
279340
+ const eqIdx = op.newCode.indexOf("=");
279341
+ if (eqIdx >= 0) {
279342
+ const mName = op.newCode.slice(0, eqIdx).trim();
279343
+ const mVal = op.newCode.slice(eqIdx + 1).trim();
279344
+ node.addMember({
279345
+ name: mName,
279346
+ value: Number.isNaN(Number(mVal)) ? mVal : Number(mVal)
279347
+ });
279348
+ } else {
279349
+ node.addMember({
279350
+ name: op.newCode.trim()
279351
+ });
279352
+ }
279353
+ return `Added member to enum "${op.name}"`;
279354
+ }
279355
+ if ("addMember" in node && typeof node.addMember === "function") {
279356
+ node.addMember(op.newCode);
279357
+ return `Added member to ${op.target} "${op.name}"`;
279358
+ }
279359
+ throw new Error(`Cannot add member to ${op.target} "${op.name}"`);
279360
+ }
279361
+ case "remove_member": {
279362
+ if (!op.value)
279363
+ throw new Error("remove_member requires value (member name)");
279364
+ if (ts.Node.isEnumDeclaration(node)) {
279365
+ const member = node.getMember(op.value);
279366
+ if (!member)
279367
+ throw new Error(`Member "${op.value}" not found in enum "${op.name}"`);
279368
+ member.remove();
279369
+ return `Removed member "${op.value}" from enum "${op.name}"`;
279370
+ }
279371
+ if (ts.Node.isClassDeclaration(node)) {
279372
+ const method = node.getMethod(op.value);
279373
+ if (method) {
279374
+ method.remove();
279375
+ return `Removed method "${op.value}" from class "${op.name}"`;
279376
+ }
279377
+ const prop = node.getProperty(op.value);
279378
+ if (prop) {
279379
+ prop.remove();
279380
+ return `Removed property "${op.value}" from class "${op.name}"`;
279381
+ }
279382
+ throw new Error(`Member "${op.value}" not found in class "${op.name}"`);
279383
+ }
279384
+ throw new Error(`Cannot remove member from ${op.target} "${op.name}"`);
279385
+ }
279386
+ case "set_extends": {
279387
+ if (!op.value)
279388
+ throw new Error("set_extends requires value (base class/interface name)");
279389
+ if (ts.Node.isClassDeclaration(node)) {
279390
+ node.setExtends(op.value);
279391
+ return `Set class "${op.name}" extends ${op.value}`;
279392
+ }
279393
+ throw new Error(`Cannot set extends on ${op.target} "${op.name}"`);
279394
+ }
279395
+ case "remove_extends": {
279396
+ if (ts.Node.isClassDeclaration(node)) {
279397
+ node.removeExtends();
279398
+ return `Removed extends from class "${op.name}"`;
279399
+ }
279400
+ throw new Error(`Cannot remove extends from ${op.target} "${op.name}"`);
279401
+ }
279402
+ case "add_implements": {
279403
+ if (!op.value)
279404
+ throw new Error("add_implements requires value (interface name(s), comma-separated)");
279405
+ if (ts.Node.isClassDeclaration(node)) {
279406
+ const ifaces = op.value.split(",").map((s2) => s2.trim()).filter(Boolean);
279407
+ node.addImplements(ifaces);
279408
+ return `Added implements ${ifaces.join(", ")} to class "${op.name}"`;
279409
+ }
279410
+ throw new Error(`Cannot add implements to ${op.target} "${op.name}"`);
279411
+ }
279412
+ case "remove_implements": {
279413
+ if (op.index === undefined)
279414
+ throw new Error("remove_implements requires index");
279415
+ if (ts.Node.isClassDeclaration(node)) {
279416
+ node.removeImplements(op.index);
279417
+ return `Removed implements at index ${String(op.index)} from class "${op.name}"`;
279418
+ }
279419
+ throw new Error(`Cannot remove implements from ${op.target} "${op.name}"`);
279420
+ }
279421
+ case "add_constructor": {
279422
+ let cls;
279423
+ let existingCtor;
279424
+ if (ts.Node.isClassDeclaration(node)) {
279425
+ cls = node;
279426
+ existingCtor = node.getConstructors()[0];
279427
+ } else if (ts.Node.isConstructorDeclaration(node)) {
279428
+ existingCtor = node;
279429
+ const parent = node.getParent();
279430
+ if (parent && ts.Node.isClassDeclaration(parent))
279431
+ cls = parent;
279432
+ } else if (op.target === "method" && op.name.includes(".")) {
279433
+ const [className] = op.name.split(".");
279434
+ if (className)
279435
+ cls = sf.getClass(className);
279436
+ existingCtor = cls?.getConstructors()[0];
279437
+ }
279438
+ if (!cls) {
279439
+ throw new Error(`Cannot add constructor to ${op.target} "${op.name}". Use target:"class" with name=ClassName.${targetsHint("add_constructor")}`);
279440
+ }
279441
+ if (existingCtor) {
279442
+ if (op.newCode)
279443
+ existingCtor.setBodyText(op.newCode);
279444
+ return `Modified existing constructor of class "${cls.getName() ?? op.name}"`;
279445
+ }
279446
+ const ctor = cls.addConstructor({});
279447
+ if (op.newCode)
279448
+ ctor.setBodyText(op.newCode);
279449
+ return `Added constructor to class "${cls.getName() ?? op.name}"`;
279450
+ }
279451
+ case "add_getter": {
279452
+ if (!op.value)
279453
+ throw new Error("add_getter requires value (getter name)");
279454
+ if (ts.Node.isClassDeclaration(node)) {
279455
+ node.addGetAccessor({
279456
+ name: op.value,
279457
+ statements: op.newCode ? [op.newCode] : []
279458
+ });
279459
+ return `Added getter "${op.value}" to class "${op.name}"`;
279460
+ }
279461
+ throw new Error(`Cannot add getter to ${op.target} "${op.name}"`);
279462
+ }
279463
+ case "add_setter": {
279464
+ if (!op.value)
279465
+ throw new Error("add_setter requires value (setter name)");
279466
+ if (ts.Node.isClassDeclaration(node)) {
279467
+ const paramParts = op.newCode?.split(":").map((s2) => s2.trim());
279468
+ const paramName = paramParts?.[0] ?? "value";
279469
+ const paramType = paramParts?.[1];
279470
+ node.addSetAccessor({
279471
+ name: op.value,
279472
+ parameters: [{
279473
+ name: paramName,
279474
+ ...paramType ? {
279475
+ type: paramType
279476
+ } : {}
279477
+ }]
278367
279478
  });
279479
+ return `Added setter "${op.value}" to class "${op.name}"`;
279480
+ }
279481
+ throw new Error(`Cannot add setter to ${op.target} "${op.name}"`);
279482
+ }
279483
+ case "set_abstract": {
279484
+ const val = op.value !== "false";
279485
+ if ("setIsAbstract" in node && typeof node.setIsAbstract === "function") {
279486
+ node.setIsAbstract(val);
279487
+ return `Set ${op.target} "${op.name}" abstract \u2192 ${String(val)}`;
279488
+ }
279489
+ throw new Error(`Cannot set abstract on ${op.target} "${op.name}"`);
279490
+ }
279491
+ case "set_static": {
279492
+ const val = op.value !== "false";
279493
+ if ("setIsStatic" in node && typeof node.setIsStatic === "function") {
279494
+ node.setIsStatic(val);
279495
+ return `Set "${op.name}" static \u2192 ${String(val)}`;
279496
+ }
279497
+ throw new Error(`Cannot set static on ${op.target} "${op.name}"`);
279498
+ }
279499
+ case "set_readonly": {
279500
+ const val = op.value !== "false";
279501
+ if ("setIsReadonly" in node && typeof node.setIsReadonly === "function") {
279502
+ node.setIsReadonly(val);
279503
+ return `Set "${op.name}" readonly \u2192 ${String(val)}`;
279504
+ }
279505
+ throw new Error(`Cannot set readonly on ${op.target} "${op.name}"`);
279506
+ }
279507
+ case "set_scope": {
279508
+ if (!op.value)
279509
+ throw new Error("set_scope requires value (public/protected/private)");
279510
+ if ("setScope" in node && typeof node.setScope === "function") {
279511
+ node.setScope(op.value);
279512
+ return `Set scope of "${op.name}" \u2192 ${op.value}`;
278368
279513
  }
279514
+ throw new Error(`Cannot set scope on ${op.target} "${op.name}"`);
278369
279515
  }
279516
+ case "set_overrides": {
279517
+ const val = op.value !== "false";
279518
+ if ("setHasOverrideKeyword" in node && typeof node.setHasOverrideKeyword === "function") {
279519
+ node.setHasOverrideKeyword(val);
279520
+ return `Set "${op.name}" override \u2192 ${String(val)}`;
279521
+ }
279522
+ throw new Error(`Cannot set override on ${op.target} "${op.name}"`);
279523
+ }
279524
+ case "set_ambient": {
279525
+ const val = op.value !== "false";
279526
+ if ("setHasDeclareKeyword" in node && typeof node.setHasDeclareKeyword === "function") {
279527
+ node.setHasDeclareKeyword(val);
279528
+ return `Set ${op.target} "${op.name}" ambient (declare) \u2192 ${String(val)}`;
279529
+ }
279530
+ throw new Error(`Cannot set ambient on ${op.target} "${op.name}"`);
279531
+ }
279532
+ case "set_const_enum": {
279533
+ if (ts.Node.isEnumDeclaration(node)) {
279534
+ const val = op.value !== "false";
279535
+ node.setIsConstEnum(val);
279536
+ return `Set enum "${op.name}" const \u2192 ${String(val)}`;
279537
+ }
279538
+ throw new Error(`set_const_enum only works on enums, not ${op.target}`);
279539
+ }
279540
+ case "remove_initializer": {
279541
+ if (ts.Node.isVariableStatement(node)) {
279542
+ const decl = node.getDeclarations().find((d2) => d2.getName() === op.name);
279543
+ if (decl && "removeInitializer" in decl) {
279544
+ decl.removeInitializer();
279545
+ return `Removed initializer from "${op.name}"`;
279546
+ }
279547
+ }
279548
+ if ("removeInitializer" in node && typeof node.removeInitializer === "function") {
279549
+ node.removeInitializer();
279550
+ return `Removed initializer from "${op.name}"`;
279551
+ }
279552
+ throw new Error(`Cannot remove initializer from ${op.target} "${op.name}"`);
279553
+ }
279554
+ case "set_declaration_kind": {
279555
+ if (!op.value)
279556
+ throw new Error("set_declaration_kind requires value (const/let/var)");
279557
+ if (ts.Node.isVariableStatement(node)) {
279558
+ const kindMap = {
279559
+ const: ts.VariableDeclarationKind.Const,
279560
+ let: ts.VariableDeclarationKind.Let,
279561
+ var: ts.VariableDeclarationKind.Var
279562
+ };
279563
+ const dk = kindMap[op.value];
279564
+ if (!dk)
279565
+ throw new Error(`Invalid declaration kind "${op.value}" \u2014 use const, let, or var`);
279566
+ node.setDeclarationKind(dk);
279567
+ return `Set declaration kind of "${op.name}" \u2192 ${op.value}`;
279568
+ }
279569
+ throw new Error(`Cannot set declaration kind on ${op.target} "${op.name}"`);
279570
+ }
279571
+ case "add_decorator": {
279572
+ if (!op.value)
279573
+ throw new Error("add_decorator requires value (decorator text, e.g. @Injectable())");
279574
+ if ("addDecorator" in node && typeof node.addDecorator === "function") {
279575
+ const decoratorName = op.value.startsWith("@") ? op.value.slice(1) : op.value;
279576
+ const parenIdx = decoratorName.indexOf("(");
279577
+ if (parenIdx >= 0) {
279578
+ const name2 = decoratorName.slice(0, parenIdx);
279579
+ const args2 = decoratorName.slice(parenIdx + 1, -1);
279580
+ node.addDecorator({
279581
+ name: name2,
279582
+ arguments: args2 ? [args2] : []
279583
+ });
279584
+ } else {
279585
+ node.addDecorator({
279586
+ name: decoratorName
279587
+ });
279588
+ }
279589
+ return `Added decorator @${decoratorName} to ${op.target} "${op.name}"`;
279590
+ }
279591
+ throw new Error(`Cannot add decorator to ${op.target} "${op.name}"`);
279592
+ }
279593
+ case "remove_decorator": {
279594
+ if (!op.value)
279595
+ throw new Error("remove_decorator requires value (decorator name)");
279596
+ if ("getDecorators" in node && typeof node.getDecorators === "function") {
279597
+ const decorators = node.getDecorators();
279598
+ const dec = decorators.find((d2) => d2.getName() === op.value);
279599
+ if (!dec)
279600
+ throw new Error(`Decorator "${op.value}" not found on ${op.target} "${op.name}"`);
279601
+ dec.remove();
279602
+ return `Removed decorator @${op.value} from ${op.target} "${op.name}"`;
279603
+ }
279604
+ throw new Error(`Cannot remove decorator from ${op.target} "${op.name}"`);
279605
+ }
279606
+ case "add_type_parameter": {
279607
+ if (!op.value)
279608
+ throw new Error("add_type_parameter requires value (e.g. T, T extends Base, T = Default)");
279609
+ if ("addTypeParameter" in node && typeof node.addTypeParameter === "function") {
279610
+ const parts2 = op.value.split(/\s+extends\s+/);
279611
+ const name2 = (parts2[0] ?? op.value).trim();
279612
+ let constraint;
279613
+ let defaultType;
279614
+ if (parts2[1]) {
279615
+ const defParts = parts2[1].split(/\s*=\s*/);
279616
+ constraint = defParts[0]?.trim();
279617
+ defaultType = defParts[1]?.trim();
279618
+ }
279619
+ node.addTypeParameter({
279620
+ name: name2,
279621
+ constraint,
279622
+ default: defaultType
279623
+ });
279624
+ return `Added type parameter <${op.value}> to ${op.target} "${op.name}"`;
279625
+ }
279626
+ throw new Error(`Cannot add type parameter to ${op.target} "${op.name}"`);
279627
+ }
279628
+ case "add_extends": {
279629
+ if (!op.value)
279630
+ throw new Error("add_extends requires value (interface/type name)");
279631
+ if (ts.Node.isInterfaceDeclaration(node)) {
279632
+ node.addExtends(op.value);
279633
+ return `Added extends ${op.value} to interface "${op.name}"`;
279634
+ }
279635
+ throw new Error(`Cannot add extends to ${op.target} "${op.name}" \u2014 use set_extends for classes`);
279636
+ }
279637
+ case "remove_method": {
279638
+ if (!op.value)
279639
+ throw new Error("remove_method requires value (method name)");
279640
+ if (ts.Node.isClassDeclaration(node)) {
279641
+ const method = node.getMethod(op.value);
279642
+ if (!method)
279643
+ throw new Error(`Method "${op.value}" not found in class "${op.name}"`);
279644
+ method.remove();
279645
+ return `Removed method "${op.value}" from class "${op.name}"`;
279646
+ }
279647
+ if (ts.Node.isInterfaceDeclaration(node)) {
279648
+ const method = node.getMethod(op.value);
279649
+ if (!method)
279650
+ throw new Error(`Method "${op.value}" not found in interface "${op.name}"`);
279651
+ method.remove();
279652
+ return `Removed method "${op.value}" from interface "${op.name}"`;
279653
+ }
279654
+ throw new Error(`Cannot remove method from ${op.target} "${op.name}"`);
279655
+ }
279656
+ case "add_overload": {
279657
+ if (!op.newCode)
279658
+ throw new Error("add_overload requires newCode (overload signature)");
279659
+ if ("addOverload" in node && typeof node.addOverload === "function") {
279660
+ node.addOverload({});
279661
+ if (ts.Node.isFunctionDeclaration(node)) {
279662
+ const overloads = node.getOverloads();
279663
+ const last = overloads[overloads.length - 1];
279664
+ if (last)
279665
+ last.replaceWithText(op.newCode);
279666
+ }
279667
+ return `Added overload to ${op.target} "${op.name}"`;
279668
+ }
279669
+ throw new Error(`Cannot add overload to ${op.target} "${op.name}"`);
279670
+ }
279671
+ case "add_jsdoc": {
279672
+ if (!op.newCode)
279673
+ throw new Error("add_jsdoc requires newCode (description text)");
279674
+ if ("addJsDoc" in node && typeof node.addJsDoc === "function") {
279675
+ node.addJsDoc({
279676
+ description: op.newCode
279677
+ });
279678
+ return `Added JSDoc to ${op.target} "${op.name}"`;
279679
+ }
279680
+ throw new Error(`Cannot add JSDoc to ${op.target} "${op.name}"`);
279681
+ }
279682
+ case "remove_jsdoc": {
279683
+ if ("getJsDocs" in node && typeof node.getJsDocs === "function") {
279684
+ const docs = node.getJsDocs();
279685
+ if (docs.length === 0)
279686
+ throw new Error(`No JSDoc found on ${op.target} "${op.name}"`);
279687
+ if (op.value === "all") {
279688
+ for (const doc of docs)
279689
+ doc.remove();
279690
+ return `Removed all ${String(docs.length)} JSDoc(s) from ${op.target} "${op.name}"`;
279691
+ }
279692
+ const last = docs[docs.length - 1];
279693
+ if (last)
279694
+ last.remove();
279695
+ return `Removed JSDoc from ${op.target} "${op.name}"`;
279696
+ }
279697
+ throw new Error(`Cannot remove JSDoc from ${op.target} "${op.name}"`);
279698
+ }
279699
+ case "unwrap": {
279700
+ if ("unwrap" in node && typeof node.unwrap === "function") {
279701
+ node.unwrap();
279702
+ return `Unwrapped ${op.target} "${op.name}" (replaced with its body)`;
279703
+ }
279704
+ throw new Error(`Cannot unwrap ${op.target} "${op.name}" \u2014 only functions and namespaces support unwrap`);
279705
+ }
279706
+ case "set_structure": {
279707
+ if (!op.newCode)
279708
+ throw new Error("set_structure requires newCode (JSON structure object)");
279709
+ if ("set" in node && typeof node.set === "function") {
279710
+ try {
279711
+ const structure = JSON.parse(op.newCode);
279712
+ node.set(structure);
279713
+ return `Applied structure to ${op.target} "${op.name}"`;
279714
+ } catch (e) {
279715
+ const msg = e instanceof Error ? e.message : String(e);
279716
+ throw new Error(`Invalid structure JSON: ${msg}`);
279717
+ }
279718
+ }
279719
+ throw new Error(`Cannot set structure on ${op.target} "${op.name}"`);
279720
+ }
279721
+ case "extract_interface": {
279722
+ if (!ts.Node.isClassDeclaration(node)) {
279723
+ throw new Error(`extract_interface only works on classes, not ${op.target}`);
279724
+ }
279725
+ const ifaceName = op.value ?? `I${op.name}`;
279726
+ const structure = node.extractInterface(ifaceName);
279727
+ sf.addInterface(structure);
279728
+ return `Extracted interface "${ifaceName}" from class "${op.name}"`;
279729
+ }
279730
+ case "replace": {
279731
+ if (!op.newCode)
279732
+ throw new Error("replace requires newCode");
279733
+ const startLine = node.getStartLineNumber();
279734
+ const endLine = node.getEndLineNumber();
279735
+ node.replaceWithText(op.newCode);
279736
+ const newLineCount = op.newCode.split(`
279737
+ `).length;
279738
+ return `Replaced ${op.target} "${op.name}" (lines ${String(startLine)}-${String(endLine)} \u2192 ${String(startLine)}-${String(startLine + newLineCount - 1)})`;
279739
+ }
279740
+ case "replace_in_body": {
279741
+ if (!op.value)
279742
+ throw new Error("replace_in_body requires value (substring/anchor to find)");
279743
+ if (op.newCode === undefined)
279744
+ throw new Error("replace_in_body requires newCode (replacement text)");
279745
+ const nodeText = node.getFullText();
279746
+ const absFullStart = node.getFullStart();
279747
+ const findOneIn = (hay, raw2, label) => {
279748
+ const passes = [{
279749
+ needle: raw2,
279750
+ matcher: "exact"
279751
+ }, {
279752
+ needle: stripCommonIndent(raw2),
279753
+ matcher: "exact"
279754
+ }, {
279755
+ needle: raw2,
279756
+ matcher: "fuzzy"
279757
+ }];
279758
+ for (const {
279759
+ needle,
279760
+ matcher
279761
+ } of passes) {
279762
+ if (!needle)
279763
+ continue;
279764
+ if (matcher === "exact") {
279765
+ const first = hay.indexOf(needle);
279766
+ if (first === -1)
279767
+ continue;
279768
+ const second = hay.indexOf(needle, first + needle.length);
279769
+ if (second !== -1) {
279770
+ throw new Error(`replace_in_body: ${label} is ambiguous (found \u22652 exact matches) inside ${op.target} "${op.name}". Add more surrounding context to make the anchor unique, or use an anchor pair (value + valueEnd).`);
279771
+ }
279772
+ return {
279773
+ index: first,
279774
+ length: needle.length
279775
+ };
279776
+ }
279777
+ if (!needle.includes(`
279778
+ `))
279779
+ continue;
279780
+ const hit = fuzzyMatchMultilineInText(hay, needle);
279781
+ if (hit)
279782
+ return hit;
279783
+ }
279784
+ const bodyPreview = hay.length > 1200 ? `${hay.slice(0, 1200)}
279785
+ \u2026(truncated \u2014 ${String(hay.length)} chars total)` : hay;
279786
+ const valPreview = raw2.length > 300 ? `${raw2.slice(0, 300)}\u2026(${label} has ${String(raw2.length)} chars)` : raw2;
279787
+ const hint = raw2.length > 200 ? `
279788
+ HINT: ${label} is long \u2014 use an anchor pair (value + valueEnd, each 1-2 unique lines) to replace a large range, or \`replace\` on the whole symbol for full rewrites.` : "";
279789
+ throw new Error(`replace_in_body: ${label} not found inside ${op.target} "${op.name}".${hint}
279790
+ Searched for:
279791
+ ${valPreview}
279792
+
279793
+ Actual body:
279794
+ ${bodyPreview}`);
279795
+ };
279796
+ let fromRel;
279797
+ let toRel;
279798
+ let modeDesc;
279799
+ if (op.valueEnd) {
279800
+ const startHit = findOneIn(nodeText, op.value, "value (start anchor)");
279801
+ const searchAfter = nodeText.slice(startHit.index + startHit.length);
279802
+ const endHit = findOneIn(searchAfter, op.valueEnd, "valueEnd (end anchor)");
279803
+ fromRel = startHit.index;
279804
+ toRel = startHit.index + startHit.length + endHit.index + endHit.length;
279805
+ modeDesc = `anchors (${String(toRel - fromRel)} chars)`;
279806
+ } else {
279807
+ const hit = findOneIn(nodeText, op.value, "value");
279808
+ fromRel = hit.index;
279809
+ toRel = hit.index + hit.length;
279810
+ modeDesc = `substring (${String(hit.length)} chars)`;
279811
+ }
279812
+ const from = absFullStart + fromRel;
279813
+ const to = absFullStart + toRel;
279814
+ if (from < absFullStart || to > absFullStart + nodeText.length) {
279815
+ throw new Error("replace_in_body: computed range outside node \u2014 aborting");
279816
+ }
279817
+ sf.replaceText([from, to], op.newCode);
279818
+ return `Replaced ${modeDesc} in ${op.target} "${op.name}" \u2192 ${String(op.newCode.length)} chars`;
279819
+ }
279820
+ default:
279821
+ throw new Error(`Unknown action: ${action}`);
278370
279822
  }
278371
- } else {
278372
- return null;
278373
279823
  }
278374
- return {
278375
- item,
278376
- supertypes,
278377
- subtypes
278378
- };
278379
- }
278380
- async findUnused(file) {
278381
- const sourceFile = await this.getSourceFile(file);
278382
- if (!sourceFile)
278383
- return null;
278384
- const ts = await getTsMorph();
278385
- const unused = [];
278386
- for (const imp of sourceFile.getImportDeclarations()) {
278387
- const defaultImport = imp.getDefaultImport();
278388
- if (defaultImport) {
278389
- const refs = defaultImport.findReferencesAsNodes();
278390
- if (refs.length <= 1) {
278391
- unused.push({
278392
- name: defaultImport.getText(),
278393
- kind: "import",
278394
- file: resolve6(file),
278395
- line: imp.getStartLineNumber()
278396
- });
279824
+ findSymbolNode(sf, target, name2, _ts) {
279825
+ if (target === "function") {
279826
+ const fn = sf.getFunction(name2);
279827
+ if (fn)
279828
+ return fn;
279829
+ for (const cls of sf.getClasses()) {
279830
+ const method = cls.getMethod(name2);
279831
+ if (method)
279832
+ return method;
278397
279833
  }
279834
+ return null;
278398
279835
  }
278399
- for (const named of imp.getNamedImports()) {
278400
- const nameNode = named.getAliasNode();
278401
- const effectiveNode = nameNode ?? named.getNameNode();
278402
- const refs = "findReferencesAsNodes" in effectiveNode ? effectiveNode.findReferencesAsNodes() : [];
278403
- if (refs.length <= 1) {
278404
- unused.push({
278405
- name: named.getName(),
278406
- kind: "import",
278407
- file: resolve6(file),
278408
- line: imp.getStartLineNumber()
278409
- });
279836
+ if (target === "method") {
279837
+ if (name2.includes(".")) {
279838
+ const [className, methodName] = name2.split(".");
279839
+ const cls = sf.getClass(className ?? "");
279840
+ if (!cls)
279841
+ return null;
279842
+ if (methodName === "constructor") {
279843
+ return cls.getConstructors()[0] ?? null;
279844
+ }
279845
+ return cls.getMethod(methodName ?? "") ?? cls.getProperty(methodName ?? "") ?? null;
279846
+ }
279847
+ if (name2 === "constructor") {
279848
+ for (const cls of sf.getClasses()) {
279849
+ const ctor = cls.getConstructors()[0];
279850
+ if (ctor)
279851
+ return ctor;
279852
+ }
279853
+ return null;
279854
+ }
279855
+ for (const cls of sf.getClasses()) {
279856
+ const method = cls.getMethod(name2);
279857
+ if (method)
279858
+ return method;
279859
+ }
279860
+ return null;
279861
+ }
279862
+ if (target === "constructor") {
279863
+ const cls = sf.getClass(name2);
279864
+ if (!cls)
279865
+ return null;
279866
+ const ctors = cls.getConstructors();
279867
+ return ctors[0] ?? null;
279868
+ }
279869
+ if (target === "property") {
279870
+ if (name2.includes(".")) {
279871
+ const [ownerName, propName] = name2.split(".");
279872
+ const cls = sf.getClass(ownerName ?? "");
279873
+ if (cls)
279874
+ return cls.getProperty(propName ?? "") ?? null;
279875
+ const iface = sf.getInterface(ownerName ?? "");
279876
+ if (iface)
279877
+ return iface.getProperty(propName ?? "") ?? null;
279878
+ return null;
279879
+ }
279880
+ for (const cls of sf.getClasses()) {
279881
+ const prop = cls.getProperty(name2);
279882
+ if (prop)
279883
+ return prop;
279884
+ }
279885
+ for (const iface of sf.getInterfaces()) {
279886
+ const prop = iface.getProperty(name2);
279887
+ if (prop)
279888
+ return prop;
279889
+ }
279890
+ return null;
279891
+ }
279892
+ if (target === "class") {
279893
+ return sf.getClass(name2) ?? null;
279894
+ }
279895
+ if (target === "interface") {
279896
+ return sf.getInterface(name2) ?? null;
279897
+ }
279898
+ if (target === "type") {
279899
+ return sf.getTypeAlias(name2) ?? null;
279900
+ }
279901
+ if (target === "enum") {
279902
+ return sf.getEnum(name2) ?? null;
279903
+ }
279904
+ if (target === "variable" || target === "constant") {
279905
+ for (const stmt of sf.getVariableStatements()) {
279906
+ for (const decl of stmt.getDeclarations()) {
279907
+ if (decl.getName() === name2)
279908
+ return stmt;
279909
+ }
279910
+ }
279911
+ }
279912
+ if (target === "arrow_function") {
279913
+ for (const stmt of sf.getVariableStatements()) {
279914
+ for (const decl of stmt.getDeclarations()) {
279915
+ if (decl.getName() !== name2)
279916
+ continue;
279917
+ const init2 = decl.getInitializer();
279918
+ if (!init2)
279919
+ return null;
279920
+ const kind = init2.getKindName();
279921
+ if (kind === "ArrowFunction" || kind === "FunctionExpression")
279922
+ return init2;
279923
+ return null;
279924
+ }
278410
279925
  }
279926
+ return null;
278411
279927
  }
279928
+ return null;
278412
279929
  }
278413
- const project = this.getProject();
278414
- if (project) {
278415
- for (const [name2, decls] of sourceFile.getExportedDeclarations()) {
278416
- if (name2 === "default")
278417
- continue;
278418
- const decl = decls[0];
278419
- if (!decl)
278420
- continue;
278421
- const nameNode = ts.Node.isIdentifier(decl) ? decl : ("getNameNode" in decl) && typeof decl.getNameNode === "function" ? decl.getNameNode() : null;
278422
- if (!nameNode || !ts.Node.isIdentifier(nameNode))
278423
- continue;
278424
- const refs = nameNode.findReferencesAsNodes();
278425
- const externalRefs = refs.filter((r4) => r4.getSourceFile() !== sourceFile);
278426
- if (externalRefs.length === 0) {
278427
- unused.push({
278428
- name: name2,
278429
- kind: "export",
278430
- file: resolve6(file),
278431
- line: decl.getStartLineNumber()
278432
- });
279930
+ listSymbolsOfKind(sourceFile, kind, ts) {
279931
+ const names = [];
279932
+ if (kind === "function") {
279933
+ for (const f3 of sourceFile.getFunctions()) {
279934
+ const n = f3.getName();
279935
+ if (n)
279936
+ names.push(`${kind} "${n}" (line ${String(f3.getStartLineNumber())})`);
279937
+ }
279938
+ for (const cls of sourceFile.getClasses()) {
279939
+ const className = cls.getName() ?? "anonymous";
279940
+ for (const m3 of cls.getMethods()) {
279941
+ names.push(`method "${className}.${m3.getName()}" (line ${String(m3.getStartLineNumber())})`);
279942
+ }
279943
+ }
279944
+ }
279945
+ if (kind === "method") {
279946
+ for (const cls of sourceFile.getClasses()) {
279947
+ const className = cls.getName() ?? "anonymous";
279948
+ for (const m3 of cls.getMethods()) {
279949
+ names.push(`method "${className}.${m3.getName()}" (line ${String(m3.getStartLineNumber())})`);
279950
+ }
279951
+ }
279952
+ }
279953
+ if (kind === "property") {
279954
+ for (const cls of sourceFile.getClasses()) {
279955
+ const className = cls.getName() ?? "anonymous";
279956
+ for (const p2 of cls.getProperties()) {
279957
+ names.push(`property "${className}.${p2.getName()}" (line ${String(p2.getStartLineNumber())})`);
279958
+ }
279959
+ }
279960
+ for (const iface of sourceFile.getInterfaces()) {
279961
+ for (const p2 of iface.getProperties()) {
279962
+ names.push(`property "${iface.getName()}.${p2.getName()}" (line ${String(p2.getStartLineNumber())})`);
279963
+ }
279964
+ }
279965
+ }
279966
+ if (kind === "class") {
279967
+ for (const c of sourceFile.getClasses()) {
279968
+ const n = c.getName();
279969
+ if (n)
279970
+ names.push(`${kind} "${n}" (line ${String(c.getStartLineNumber())})`);
279971
+ }
279972
+ }
279973
+ if (kind === "constructor") {
279974
+ for (const cls of sourceFile.getClasses()) {
279975
+ const className = cls.getName() ?? "anonymous";
279976
+ const ctors = cls.getConstructors();
279977
+ if (ctors.length > 0) {
279978
+ const ctor = ctors[0];
279979
+ if (ctor) {
279980
+ names.push(`constructor "${className}" (line ${String(ctor.getStartLineNumber())})`);
279981
+ }
279982
+ } else {
279983
+ names.push(`class "${className}" (no constructor \u2014 add_constructor will create one)`);
279984
+ }
279985
+ }
279986
+ }
279987
+ if (kind === "interface") {
279988
+ for (const i4 of sourceFile.getInterfaces()) {
279989
+ names.push(`${kind} "${i4.getName()}" (line ${String(i4.getStartLineNumber())})`);
279990
+ }
279991
+ }
279992
+ if (kind === "type") {
279993
+ for (const t of sourceFile.getTypeAliases()) {
279994
+ names.push(`${kind} "${t.getName()}" (line ${String(t.getStartLineNumber())})`);
279995
+ }
279996
+ }
279997
+ if (kind === "enum") {
279998
+ for (const e of sourceFile.getEnums()) {
279999
+ names.push(`${kind} "${e.getName()}" (line ${String(e.getStartLineNumber())})`);
280000
+ }
280001
+ }
280002
+ if (kind === "variable" || kind === "constant") {
280003
+ for (const stmt of sourceFile.getVariableStatements()) {
280004
+ const isConst = stmt.getDeclarationKind() === ts.VariableDeclarationKind.Const;
280005
+ for (const decl of stmt.getDeclarations()) {
280006
+ names.push(`${isConst ? "constant" : "variable"} "${decl.getName()}" (line ${String(stmt.getStartLineNumber())})`);
280007
+ }
278433
280008
  }
278434
280009
  }
280010
+ return names;
278435
280011
  }
278436
- return unused.length > 0 ? unused : null;
278437
- }
278438
- getProject() {
278439
- return this.project;
278440
- }
278441
- async ensureProject() {
278442
- if (this.project)
280012
+ getProject() {
278443
280013
  return this.project;
278444
- const ts = await getTsMorph();
278445
- const tsconfigPath = resolve6(this.cwd, "tsconfig.json");
278446
- try {
278447
- this.project = new ts.Project({
278448
- tsConfigFilePath: tsconfigPath,
278449
- skipAddingFilesFromTsConfig: false
278450
- });
278451
- } catch {
278452
- this.project = new ts.Project({
278453
- compilerOptions: {
278454
- target: ts.ScriptTarget.ESNext,
278455
- module: ts.ModuleKind.ESNext,
278456
- moduleResolution: ts.ModuleResolutionKind.Bundler,
278457
- strict: true,
278458
- jsx: ts.ts.JsxEmit.ReactJSX,
278459
- esModuleInterop: true,
278460
- skipLibCheck: true,
278461
- allowJs: true
278462
- }
278463
- });
278464
280014
  }
278465
- return this.project;
278466
- }
278467
- async getSourceFile(file) {
278468
- const project = await this.ensureProject();
278469
- const absPath = resolve6(file);
278470
- let sourceFile = project.getSourceFile(absPath);
278471
- if (!sourceFile) {
280015
+ async ensureProject() {
280016
+ if (this.project)
280017
+ return this.project;
280018
+ const ts = await getTsMorph();
280019
+ const tsconfigPath = resolve6(this.cwd, "tsconfig.json");
278472
280020
  try {
278473
- sourceFile = project.addSourceFileAtPath(absPath);
280021
+ this.project = new ts.Project({
280022
+ tsConfigFilePath: tsconfigPath,
280023
+ skipAddingFilesFromTsConfig: true,
280024
+ skipFileDependencyResolution: true
280025
+ });
278474
280026
  } catch {
278475
- return null;
280027
+ this.project = new ts.Project({
280028
+ compilerOptions: {
280029
+ target: ts.ScriptTarget.ESNext,
280030
+ module: ts.ModuleKind.ESNext,
280031
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
280032
+ strict: true,
280033
+ jsx: ts.ts.JsxEmit.ReactJSX,
280034
+ esModuleInterop: true,
280035
+ skipLibCheck: true,
280036
+ allowJs: true
280037
+ }
280038
+ });
278476
280039
  }
280040
+ return this.project;
278477
280041
  }
278478
- return sourceFile;
278479
- }
278480
- findNode(sourceFile, symbol, line, column) {
278481
- if (line && column) {
278482
- const pos = sourceFile.compilerNode.getPositionOfLineAndCharacter(line - 1, column - 1);
278483
- return sourceFile.getDescendantAtPos(pos) ?? null;
280042
+ async getSourceFile(file, opts) {
280043
+ const project = await this.ensureProject();
280044
+ const absPath = resolve6(file);
280045
+ let sourceFile = project.getSourceFile(absPath);
280046
+ if (!sourceFile) {
280047
+ try {
280048
+ sourceFile = project.addSourceFileAtPath(absPath);
280049
+ } catch {
280050
+ return null;
280051
+ }
280052
+ } else if (opts?.refresh !== false) {
280053
+ try {
280054
+ sourceFile.refreshFromFileSystemSync();
280055
+ } catch {
280056
+ try {
280057
+ project.removeSourceFile(sourceFile);
280058
+ this.sourceFileLru.delete(absPath);
280059
+ sourceFile = project.addSourceFileAtPath(absPath);
280060
+ } catch {
280061
+ return null;
280062
+ }
280063
+ }
280064
+ }
280065
+ this.sourceFileLru.delete(absPath);
280066
+ this.sourceFileLru.set(absPath, Date.now());
280067
+ this.evictSourceFilesIfNeeded(project);
280068
+ return sourceFile;
280069
+ }
280070
+ evictSourceFilesIfNeeded(project) {
280071
+ const overflow = this.sourceFileLru.size - TsMorphBackend.SOURCE_FILE_CAP;
280072
+ if (overflow <= 0)
280073
+ return;
280074
+ let evicted = 0;
280075
+ for (const absPath of this.sourceFileLru.keys()) {
280076
+ if (evicted >= overflow)
280077
+ break;
280078
+ const sf = project.getSourceFile(absPath);
280079
+ if (sf) {
280080
+ try {
280081
+ project.removeSourceFile(sf);
280082
+ } catch {}
280083
+ }
280084
+ this.sourceFileLru.delete(absPath);
280085
+ evicted++;
280086
+ }
280087
+ }
280088
+ findNode(sourceFile, symbol, line, column) {
280089
+ if (line && column) {
280090
+ const pos = sourceFile.compilerNode.getPositionOfLineAndCharacter(line - 1, column - 1);
280091
+ return sourceFile.getDescendantAtPos(pos) ?? null;
280092
+ }
280093
+ const found = sourceFile.forEachDescendant((node) => {
280094
+ const ts = tsMorphModule;
280095
+ if (!ts)
280096
+ return;
280097
+ if (ts.Node.isIdentifier(node) && node.getText() === symbol) {
280098
+ return node;
280099
+ }
280100
+ return;
280101
+ });
280102
+ return found ?? null;
278484
280103
  }
278485
- const found = sourceFile.forEachDescendant((node) => {
280104
+ getNodeDocumentation(node) {
278486
280105
  const ts = tsMorphModule;
278487
280106
  if (!ts)
278488
280107
  return;
278489
- if (ts.Node.isIdentifier(node) && node.getText() === symbol) {
278490
- return node;
278491
- }
278492
- return;
278493
- });
278494
- return found ?? null;
278495
- }
278496
- getNodeDocumentation(node) {
278497
- const ts = tsMorphModule;
278498
- if (!ts)
278499
- return;
278500
- const target = ts.Node.isIdentifier(node) ? node.getParent() : node;
278501
- if (!target)
278502
- return;
278503
- if ("getJsDocs" in target && typeof target.getJsDocs === "function") {
278504
- const jsDocs = target.getJsDocs();
278505
- if (jsDocs.length > 0) {
278506
- return jsDocs.map((d2) => d2.getDescription()).join(`
280108
+ const target = ts.Node.isIdentifier(node) ? node.getParent() : node;
280109
+ if (!target)
280110
+ return;
280111
+ if ("getJsDocs" in target && typeof target.getJsDocs === "function") {
280112
+ const jsDocs = target.getJsDocs();
280113
+ if (jsDocs.length > 0) {
280114
+ return jsDocs.map((d2) => d2.getDescription()).join(`
278507
280115
  `);
280116
+ }
278508
280117
  }
280118
+ return;
278509
280119
  }
278510
- return;
278511
- }
278512
- detectLang(file) {
278513
- const dot = file.lastIndexOf(".");
278514
- if (dot === -1)
280120
+ detectLang(file) {
280121
+ const dot = file.lastIndexOf(".");
280122
+ if (dot === -1)
280123
+ return "unknown";
280124
+ const ext = file.slice(dot);
280125
+ if ([".ts", ".tsx", ".mts", ".cts"].includes(ext))
280126
+ return "typescript";
280127
+ if ([".js", ".jsx", ".mjs", ".cjs"].includes(ext))
280128
+ return "javascript";
278515
280129
  return "unknown";
278516
- const ext = file.slice(dot);
278517
- if ([".ts", ".tsx", ".mts", ".cts"].includes(ext))
278518
- return "typescript";
278519
- if ([".js", ".jsx", ".mjs", ".cjs"].includes(ext))
278520
- return "javascript";
278521
- return "unknown";
278522
- }
278523
- }
278524
- var tsMorphModule = null;
278525
- var init_ts_morph = () => {};
280130
+ }
280131
+ listInheritedMembers(sf, target, name2, ts) {
280132
+ if (!name2.includes("."))
280133
+ return [];
280134
+ if (target !== "method" && target !== "property")
280135
+ return [];
280136
+ const [ownerName] = name2.split(".");
280137
+ if (!ownerName)
280138
+ return [];
280139
+ const out2 = [];
280140
+ const seen = new Set;
280141
+ const push = (kind, owner, memberName) => {
280142
+ const key2 = `${kind}:${owner}.${memberName}`;
280143
+ if (seen.has(key2))
280144
+ return;
280145
+ seen.add(key2);
280146
+ out2.push(`${kind} "${owner}.${memberName}" (inherited from ${owner})`);
280147
+ };
280148
+ const cls = sf.getClass(ownerName);
280149
+ if (cls) {
280150
+ let base = cls.getBaseClass();
280151
+ while (base) {
280152
+ const baseName = base.getName() ?? "anonymous";
280153
+ if (target === "method") {
280154
+ for (const m3 of base.getMethods())
280155
+ push("method", baseName, m3.getName());
280156
+ }
280157
+ if (target === "property") {
280158
+ for (const p2 of base.getProperties())
280159
+ push("property", baseName, p2.getName());
280160
+ }
280161
+ base = base.getBaseClass();
280162
+ }
280163
+ for (const impl of cls.getImplements()) {
280164
+ const exprText = impl.getExpression().getText();
280165
+ const iface2 = sf.getInterface(exprText);
280166
+ if (!iface2)
280167
+ continue;
280168
+ if (target === "method") {
280169
+ for (const m3 of iface2.getMethods())
280170
+ push("method", iface2.getName(), m3.getName());
280171
+ }
280172
+ if (target === "property") {
280173
+ for (const p2 of iface2.getProperties())
280174
+ push("property", iface2.getName(), p2.getName());
280175
+ }
280176
+ }
280177
+ return out2;
280178
+ }
280179
+ const iface = sf.getInterface(ownerName);
280180
+ if (iface) {
280181
+ for (const ext of iface.getBaseDeclarations()) {
280182
+ if (!ts.Node.isInterfaceDeclaration(ext))
280183
+ continue;
280184
+ const extName = ext.getName();
280185
+ if (target === "method") {
280186
+ for (const m3 of ext.getMethods())
280187
+ push("method", extName, m3.getName());
280188
+ }
280189
+ if (target === "property") {
280190
+ for (const p2 of ext.getProperties())
280191
+ push("property", extName, p2.getName());
280192
+ }
280193
+ }
280194
+ }
280195
+ return out2;
280196
+ }
280197
+ };
280198
+ });
278526
280199
 
278527
280200
  // src/core/intelligence/backends/regex.ts
278528
280201
  var exports_regex = {};
@@ -278991,10 +280664,17 @@ async function ensureHighlighter() {
278991
280664
  } = await Promise.resolve().then(() => (init_dist7(), exports_dist2));
278992
280665
  highlighter = await createHighlighter2({
278993
280666
  themes: ["catppuccin-mocha"],
278994
- langs: [...SHIKI_LANGS]
280667
+ langs: []
278995
280668
  });
278996
280669
  return highlighter;
278997
280670
  }
280671
+ async function loadLanguage(hl, lang254) {
280672
+ if (hl.getLoadedLanguages().includes(lang254))
280673
+ return;
280674
+ try {
280675
+ await hl.loadLanguage(lang254);
280676
+ } catch {}
280677
+ }
278998
280678
  function requireRepoMap() {
278999
280679
  if (!repoMap)
279000
280680
  throw new Error("RepoMap not initialized \u2014 send init first");
@@ -279214,6 +280894,8 @@ var handlers = {
279214
280894
  codeToAnsi: async (code, lang254) => {
279215
280895
  const hl = await ensureHighlighter();
279216
280896
  const normalized = lang254 ? normalizeLang(lang254) : "text";
280897
+ if (normalized !== "text")
280898
+ await loadLanguage(hl, normalized);
279217
280899
  const langId = hl.getLoadedLanguages().includes(normalized) ? normalized : "text";
279218
280900
  try {
279219
280901
  const RST = "\x1B[0m";
@@ -279246,6 +280928,8 @@ var handlers = {
279246
280928
  codeToStyledTokens: async (code, lang254) => {
279247
280929
  const hl = await ensureHighlighter();
279248
280930
  const normalized = lang254 ? normalizeLang(lang254) : "text";
280931
+ if (normalized !== "text")
280932
+ await loadLanguage(hl, normalized);
279249
280933
  const langId = hl.getLoadedLanguages().includes(normalized) ? normalized : "text";
279250
280934
  try {
279251
280935
  const result = hl.codeToTokens(code, {
@@ -279264,9 +280948,8 @@ var handlers = {
279264
280948
  }
279265
280949
  },
279266
280950
  isShikiLanguage: async (lang254) => {
279267
- const hl = await ensureHighlighter();
279268
280951
  const normalized = normalizeLang(lang254);
279269
- return hl.getLoadedLanguages().includes(normalized);
280952
+ return SHIKI_LANGS.includes(normalized);
279270
280953
  }
279271
280954
  };
279272
280955
  ctx = createWorkerHandler(handlers, async (config) => {