repowisestage 0.0.45 → 0.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin/repowise.js +364 -256
  2. package/package.json +1 -1
@@ -721,6 +721,200 @@ var init_registry = __esm({
721
721
  }
722
722
  });
723
723
 
724
+ // ../listener/dist/lsp/installer.js
725
+ import { promises as fs } from "fs";
726
+ import { join as join14, dirname as dirname5 } from "path";
727
+ import { spawn as spawn3 } from "child_process";
728
+ function getLspInstallDir() {
729
+ return join14(getConfigDir(), "lsp-servers");
730
+ }
731
+ function getLspBinDir() {
732
+ return join14(getLspInstallDir(), "node_modules", ".bin");
733
+ }
734
+ async function ensureNpmLspInstalled(config2) {
735
+ if (!config2.npmPackage)
736
+ return { installed: false, skipped: "no-npm-package" };
737
+ const installDir = getLspInstallDir();
738
+ const binPath = join14(installDir, "node_modules", ".bin", config2.command);
739
+ if (await pathExists(binPath)) {
740
+ return { installed: false, skipped: "already-present" };
741
+ }
742
+ try {
743
+ await fs.mkdir(installDir, { recursive: true });
744
+ const pkgJsonPath = join14(installDir, "package.json");
745
+ if (!await pathExists(pkgJsonPath)) {
746
+ await fs.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
747
+ }
748
+ await runNpmInstall(installDir, config2.npmPackage);
749
+ if (!await pathExists(binPath)) {
750
+ return {
751
+ installed: false,
752
+ error: `npm install completed but ${config2.command} still not at ${binPath}`
753
+ };
754
+ }
755
+ return { installed: true };
756
+ } catch (err) {
757
+ return {
758
+ installed: false,
759
+ error: err instanceof Error ? err.message : String(err)
760
+ };
761
+ }
762
+ }
763
+ async function resolveNpmCommand() {
764
+ const nodeDir = dirname5(process.execPath);
765
+ for (const candidate of [join14(nodeDir, "npm"), join14(nodeDir, "npm.cmd")]) {
766
+ try {
767
+ await fs.access(candidate);
768
+ return candidate;
769
+ } catch {
770
+ }
771
+ }
772
+ for (const candidate of ["/opt/homebrew/bin/npm", "/usr/local/bin/npm", "/usr/bin/npm"]) {
773
+ try {
774
+ await fs.access(candidate);
775
+ return candidate;
776
+ } catch {
777
+ }
778
+ }
779
+ return "npm";
780
+ }
781
+ async function runNpmInstall(cwd, pkg2) {
782
+ const npmCmd = await resolveNpmCommand();
783
+ const nodeDir = dirname5(process.execPath);
784
+ const augmentedPath = process.env.PATH ? `${nodeDir}:${process.env.PATH}` : nodeDir;
785
+ return new Promise((resolve4, reject) => {
786
+ const child = spawn3(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
787
+ cwd,
788
+ stdio: ["ignore", "pipe", "pipe"],
789
+ env: { ...process.env, PATH: augmentedPath }
790
+ });
791
+ let stderr = "";
792
+ child.stderr.on("data", (chunk) => {
793
+ stderr += chunk.toString();
794
+ });
795
+ child.on("error", (err) => reject(err));
796
+ child.on("close", (code) => {
797
+ if (code === 0) {
798
+ resolve4();
799
+ } else {
800
+ reject(new Error(`npm install ${pkg2} exited ${(code ?? -1).toString()}: ${sanitizeNpmStderr(stderr)}`));
801
+ }
802
+ });
803
+ });
804
+ }
805
+ function sanitizeNpmStderr(raw) {
806
+ const truncated = raw.split("\n").slice(0, 3).join(" ").trim();
807
+ return truncated.replace(/(https?:\/\/)[^@\s/]*@/g, (_match, scheme) => `${scheme}<redacted>@`);
808
+ }
809
+ async function pathExists(p) {
810
+ try {
811
+ await fs.access(p);
812
+ return true;
813
+ } catch {
814
+ return false;
815
+ }
816
+ }
817
+ async function detectRepoLanguages(repoRoot) {
818
+ const found = /* @__PURE__ */ new Set();
819
+ let inspected = 0;
820
+ const MAX_INSPECT = 200;
821
+ async function inspect(dir) {
822
+ if (inspected >= MAX_INSPECT)
823
+ return;
824
+ let entries;
825
+ try {
826
+ entries = await fs.readdir(dir);
827
+ } catch {
828
+ return;
829
+ }
830
+ for (const name of entries) {
831
+ if (inspected >= MAX_INSPECT)
832
+ return;
833
+ if (name.startsWith("."))
834
+ continue;
835
+ if (name === "node_modules" || name === "dist" || name === "build")
836
+ continue;
837
+ const lang = detectLanguage(name);
838
+ if (lang) {
839
+ found.add(lang);
840
+ inspected += 1;
841
+ }
842
+ }
843
+ }
844
+ await inspect(repoRoot);
845
+ let topEntries = [];
846
+ try {
847
+ topEntries = await fs.readdir(repoRoot);
848
+ } catch {
849
+ return found;
850
+ }
851
+ for (const name of topEntries) {
852
+ if (name.startsWith("."))
853
+ continue;
854
+ if (name === "node_modules" || name === "dist" || name === "build")
855
+ continue;
856
+ const sub = join14(repoRoot, name);
857
+ try {
858
+ const stat7 = await fs.stat(sub);
859
+ if (stat7.isDirectory())
860
+ await inspect(sub);
861
+ } catch {
862
+ }
863
+ }
864
+ return found;
865
+ }
866
+ async function prepareLspServersForRepos(repos) {
867
+ const detected = /* @__PURE__ */ new Set();
868
+ for (const r of repos) {
869
+ if (!r.localPath)
870
+ continue;
871
+ try {
872
+ const stat7 = await fs.stat(r.localPath);
873
+ if (!stat7.isDirectory())
874
+ continue;
875
+ } catch {
876
+ continue;
877
+ }
878
+ try {
879
+ const langs = await detectRepoLanguages(r.localPath);
880
+ for (const l of langs)
881
+ detected.add(l);
882
+ } catch {
883
+ }
884
+ }
885
+ const results = [];
886
+ for (const language of detected) {
887
+ const configs = LSP_REGISTRY[language];
888
+ const npmConfig = configs.find((c) => c.npmPackage);
889
+ if (!npmConfig) {
890
+ results.push({
891
+ language,
892
+ installed: false,
893
+ alreadyPresent: false,
894
+ skippedNoNpmPackage: true,
895
+ hint: configs[0]?.installHint
896
+ });
897
+ continue;
898
+ }
899
+ const outcome = await ensureNpmLspInstalled(npmConfig);
900
+ results.push({
901
+ language,
902
+ installed: outcome.installed,
903
+ alreadyPresent: outcome.skipped === "already-present",
904
+ skippedNoNpmPackage: false,
905
+ error: outcome.error
906
+ });
907
+ }
908
+ return results;
909
+ }
910
+ var init_installer = __esm({
911
+ "../listener/dist/lsp/installer.js"() {
912
+ "use strict";
913
+ init_config_dir();
914
+ init_registry();
915
+ }
916
+ });
917
+
724
918
  // ../../packages/shared/src/types/typed-resolution.ts
725
919
  function typedResolutionKey(filePath, propertyName, line, column) {
726
920
  return `${filePath}\0${propertyName}\0${line.toString()}\0${column.toString()}`;
@@ -1132,7 +1326,8 @@ __export(lsp_tools_exports, {
1132
1326
  lspWorkspaceSymbol: () => lspWorkspaceSymbol
1133
1327
  });
1134
1328
  import { fileURLToPath as fileURLToPath2 } from "url";
1135
- import { relative as pathRelative, resolve as pathResolve3 } from "path";
1329
+ import { relative as pathRelative, resolve as pathResolve3, join as pathJoin } from "path";
1330
+ import { existsSync } from "fs";
1136
1331
  import { execSync } from "child_process";
1137
1332
  function probeBinary(command) {
1138
1333
  if (process.env["VITEST"])
@@ -1142,8 +1337,12 @@ function probeBinary(command) {
1142
1337
  return cached;
1143
1338
  let found = false;
1144
1339
  try {
1145
- execSync(`which ${command}`, { stdio: "ignore", timeout: 200 });
1146
- found = true;
1340
+ if (existsSync(pathJoin(getLspBinDir(), command))) {
1341
+ found = true;
1342
+ } else {
1343
+ execSync(`which ${command}`, { stdio: "ignore", timeout: 200 });
1344
+ found = true;
1345
+ }
1147
1346
  } catch {
1148
1347
  found = false;
1149
1348
  }
@@ -1465,6 +1664,7 @@ var init_lsp_tools = __esm({
1465
1664
  "../listener/dist/lsp/lsp-tools.js"() {
1466
1665
  "use strict";
1467
1666
  init_registry();
1667
+ init_installer();
1468
1668
  binaryProbeCache = /* @__PURE__ */ new Map();
1469
1669
  }
1470
1670
  });
@@ -3042,196 +3242,8 @@ var LspClient = class extends EventEmitter {
3042
3242
  }
3043
3243
  };
3044
3244
 
3045
- // ../listener/dist/lsp/installer.js
3046
- init_config_dir();
3047
- init_registry();
3048
- import { promises as fs } from "fs";
3049
- import { join as join14, dirname as dirname5 } from "path";
3050
- import { spawn as spawn3 } from "child_process";
3051
- function getLspInstallDir() {
3052
- return join14(getConfigDir(), "lsp-servers");
3053
- }
3054
- function getLspBinDir() {
3055
- return join14(getLspInstallDir(), "node_modules", ".bin");
3056
- }
3057
- async function ensureNpmLspInstalled(config2) {
3058
- if (!config2.npmPackage)
3059
- return { installed: false, skipped: "no-npm-package" };
3060
- const installDir = getLspInstallDir();
3061
- const binPath = join14(installDir, "node_modules", ".bin", config2.command);
3062
- if (await pathExists(binPath)) {
3063
- return { installed: false, skipped: "already-present" };
3064
- }
3065
- try {
3066
- await fs.mkdir(installDir, { recursive: true });
3067
- const pkgJsonPath = join14(installDir, "package.json");
3068
- if (!await pathExists(pkgJsonPath)) {
3069
- await fs.writeFile(pkgJsonPath, JSON.stringify({ name: "repowise-lsp-servers", private: true, version: "0.0.0" }, null, 2), "utf-8");
3070
- }
3071
- await runNpmInstall(installDir, config2.npmPackage);
3072
- if (!await pathExists(binPath)) {
3073
- return {
3074
- installed: false,
3075
- error: `npm install completed but ${config2.command} still not at ${binPath}`
3076
- };
3077
- }
3078
- return { installed: true };
3079
- } catch (err) {
3080
- return {
3081
- installed: false,
3082
- error: err instanceof Error ? err.message : String(err)
3083
- };
3084
- }
3085
- }
3086
- async function resolveNpmCommand() {
3087
- const nodeDir = dirname5(process.execPath);
3088
- for (const candidate of [join14(nodeDir, "npm"), join14(nodeDir, "npm.cmd")]) {
3089
- try {
3090
- await fs.access(candidate);
3091
- return candidate;
3092
- } catch {
3093
- }
3094
- }
3095
- for (const candidate of ["/opt/homebrew/bin/npm", "/usr/local/bin/npm", "/usr/bin/npm"]) {
3096
- try {
3097
- await fs.access(candidate);
3098
- return candidate;
3099
- } catch {
3100
- }
3101
- }
3102
- return "npm";
3103
- }
3104
- async function runNpmInstall(cwd, pkg2) {
3105
- const npmCmd = await resolveNpmCommand();
3106
- const nodeDir = dirname5(process.execPath);
3107
- const augmentedPath = process.env.PATH ? `${nodeDir}:${process.env.PATH}` : nodeDir;
3108
- return new Promise((resolve4, reject) => {
3109
- const child = spawn3(npmCmd, ["install", "--no-audit", "--no-fund", "--silent", pkg2], {
3110
- cwd,
3111
- stdio: ["ignore", "pipe", "pipe"],
3112
- env: { ...process.env, PATH: augmentedPath }
3113
- });
3114
- let stderr = "";
3115
- child.stderr.on("data", (chunk) => {
3116
- stderr += chunk.toString();
3117
- });
3118
- child.on("error", (err) => reject(err));
3119
- child.on("close", (code) => {
3120
- if (code === 0) {
3121
- resolve4();
3122
- } else {
3123
- reject(new Error(`npm install ${pkg2} exited ${(code ?? -1).toString()}: ${sanitizeNpmStderr(stderr)}`));
3124
- }
3125
- });
3126
- });
3127
- }
3128
- function sanitizeNpmStderr(raw) {
3129
- const truncated = raw.split("\n").slice(0, 3).join(" ").trim();
3130
- return truncated.replace(/(https?:\/\/)[^@\s/]*@/g, (_match, scheme) => `${scheme}<redacted>@`);
3131
- }
3132
- async function pathExists(p) {
3133
- try {
3134
- await fs.access(p);
3135
- return true;
3136
- } catch {
3137
- return false;
3138
- }
3139
- }
3140
- async function detectRepoLanguages(repoRoot) {
3141
- const found = /* @__PURE__ */ new Set();
3142
- let inspected = 0;
3143
- const MAX_INSPECT = 200;
3144
- async function inspect(dir) {
3145
- if (inspected >= MAX_INSPECT)
3146
- return;
3147
- let entries;
3148
- try {
3149
- entries = await fs.readdir(dir);
3150
- } catch {
3151
- return;
3152
- }
3153
- for (const name of entries) {
3154
- if (inspected >= MAX_INSPECT)
3155
- return;
3156
- if (name.startsWith("."))
3157
- continue;
3158
- if (name === "node_modules" || name === "dist" || name === "build")
3159
- continue;
3160
- const lang = detectLanguage(name);
3161
- if (lang) {
3162
- found.add(lang);
3163
- inspected += 1;
3164
- }
3165
- }
3166
- }
3167
- await inspect(repoRoot);
3168
- let topEntries = [];
3169
- try {
3170
- topEntries = await fs.readdir(repoRoot);
3171
- } catch {
3172
- return found;
3173
- }
3174
- for (const name of topEntries) {
3175
- if (name.startsWith("."))
3176
- continue;
3177
- if (name === "node_modules" || name === "dist" || name === "build")
3178
- continue;
3179
- const sub = join14(repoRoot, name);
3180
- try {
3181
- const stat7 = await fs.stat(sub);
3182
- if (stat7.isDirectory())
3183
- await inspect(sub);
3184
- } catch {
3185
- }
3186
- }
3187
- return found;
3188
- }
3189
- async function prepareLspServersForRepos(repos) {
3190
- const detected = /* @__PURE__ */ new Set();
3191
- for (const r of repos) {
3192
- if (!r.localPath)
3193
- continue;
3194
- try {
3195
- const stat7 = await fs.stat(r.localPath);
3196
- if (!stat7.isDirectory())
3197
- continue;
3198
- } catch {
3199
- continue;
3200
- }
3201
- try {
3202
- const langs = await detectRepoLanguages(r.localPath);
3203
- for (const l of langs)
3204
- detected.add(l);
3205
- } catch {
3206
- }
3207
- }
3208
- const results = [];
3209
- for (const language of detected) {
3210
- const configs = LSP_REGISTRY[language];
3211
- const npmConfig = configs.find((c) => c.npmPackage);
3212
- if (!npmConfig) {
3213
- results.push({
3214
- language,
3215
- installed: false,
3216
- alreadyPresent: false,
3217
- skippedNoNpmPackage: true,
3218
- hint: configs[0]?.installHint
3219
- });
3220
- continue;
3221
- }
3222
- const outcome = await ensureNpmLspInstalled(npmConfig);
3223
- results.push({
3224
- language,
3225
- installed: outcome.installed,
3226
- alreadyPresent: outcome.skipped === "already-present",
3227
- skippedNoNpmPackage: false,
3228
- error: outcome.error
3229
- });
3230
- }
3231
- return results;
3232
- }
3233
-
3234
3245
  // ../listener/dist/lsp/workspace-session.js
3246
+ init_installer();
3235
3247
  function keyOf(args) {
3236
3248
  return `${args.repoRoot}\0${args.language}`;
3237
3249
  }
@@ -4866,24 +4878,49 @@ function batchQuery(graphJson, req) {
4866
4878
  const allQueries = req.queries ?? [];
4867
4879
  const queries = allQueries.slice(0, BATCH_CAP);
4868
4880
  const droppedCount = Math.max(0, allQueries.length - queries.length);
4869
- const results = queries.map((q) => {
4881
+ const results = queries.map((rawQ) => {
4882
+ const q = rawQ;
4883
+ const toolName = q.tool ?? "";
4884
+ const params = q.params ?? q.arguments;
4885
+ if (!params || typeof params !== "object" || Array.isArray(params)) {
4886
+ return {
4887
+ ok: false,
4888
+ tool: toolName,
4889
+ error: "params required (object) \u2014 pass `params` or `arguments` per sub-query"
4890
+ };
4891
+ }
4892
+ const args = params;
4870
4893
  try {
4871
- switch (q.tool) {
4894
+ switch (toolName) {
4872
4895
  case "find_symbol":
4873
- return { ok: true, tool: q.tool, result: findSymbol(graph, q.params) };
4896
+ return {
4897
+ ok: true,
4898
+ tool: toolName,
4899
+ result: findSymbol(graph, args)
4900
+ };
4874
4901
  case "get_symbol":
4875
- return { ok: true, tool: q.tool, result: getSymbol(graph, q.params) };
4902
+ return { ok: true, tool: toolName, result: getSymbol(graph, args) };
4876
4903
  case "get_impact":
4877
- return { ok: true, tool: q.tool, result: getImpact(graph, q.params) };
4904
+ return { ok: true, tool: toolName, result: getImpact(graph, args) };
4878
4905
  case "list_edges":
4879
- return { ok: true, tool: q.tool, result: listEdges(graph, q.params) };
4906
+ return { ok: true, tool: toolName, result: listEdges(graph, args) };
4880
4907
  case "search_pattern":
4881
- return { ok: true, tool: q.tool, result: searchPattern(graph, q.params) };
4908
+ return {
4909
+ ok: true,
4910
+ tool: toolName,
4911
+ result: searchPattern(graph, args)
4912
+ };
4913
+ default:
4914
+ return {
4915
+ ok: false,
4916
+ tool: toolName,
4917
+ error: `unsupported sub-query tool: ${String(toolName)}`
4918
+ };
4882
4919
  }
4883
4920
  } catch (err) {
4884
4921
  return {
4885
4922
  ok: false,
4886
- tool: q.tool,
4923
+ tool: toolName,
4887
4924
  error: err instanceof Error ? err.message : String(err)
4888
4925
  };
4889
4926
  }
@@ -5121,7 +5158,14 @@ function handleRequest(req, res, options, sessions, secretCtx) {
5121
5158
  sessions.set(sessionId, { repoId: body.repoId });
5122
5159
  return sendJson(res, 200, { sessionId });
5123
5160
  });
5124
- }).catch((err) => sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) }));
5161
+ }).catch((err) => {
5162
+ const rawMsg = err instanceof Error ? err.message : String(err);
5163
+ const safeMsg = stripAbsolutePaths(rawMsg);
5164
+ if (err?.code === "ENOENT") {
5165
+ return sendJson(res, 404, { error: "unknown_repo" });
5166
+ }
5167
+ sendJson(res, 500, { error: safeMsg });
5168
+ });
5125
5169
  return;
5126
5170
  }
5127
5171
  if (method === "POST" && url === "/mcp/disconnect") {
@@ -5305,6 +5349,7 @@ function dispatchSessionTool(req, res, url, sessions, logger, handler) {
5305
5349
  latencyMs: Date.now() - startedAt,
5306
5350
  error: "unknown_session"
5307
5351
  });
5352
+ logToStdout("rejected", tool, null, Date.now() - startedAt, "unknown_session");
5308
5353
  return sendJson(res, 404, { error: "unknown_session" });
5309
5354
  }
5310
5355
  try {
@@ -5319,8 +5364,10 @@ function dispatchSessionTool(req, res, url, sessions, logger, handler) {
5319
5364
  status: "ok",
5320
5365
  latencyMs: Date.now() - startedAt
5321
5366
  });
5367
+ logToStdout("ok", tool, session.repoId, Date.now() - startedAt);
5322
5368
  return sendJson(res, 200, result.value);
5323
5369
  }
5370
+ const rejectErr = typeof result.body["error"] === "string" ? result.body["error"] : "tool_rejected";
5324
5371
  recordLog(logger, {
5325
5372
  ts: new Date(startedAt).toISOString(),
5326
5373
  sessionId: body.sessionId,
@@ -5329,8 +5376,9 @@ function dispatchSessionTool(req, res, url, sessions, logger, handler) {
5329
5376
  args: redactArgs(body),
5330
5377
  status: "rejected",
5331
5378
  latencyMs: Date.now() - startedAt,
5332
- error: typeof result.body["error"] === "string" ? result.body["error"] : "tool_rejected"
5379
+ error: rejectErr
5333
5380
  });
5381
+ logToStdout("rejected", tool, session.repoId, Date.now() - startedAt, rejectErr);
5334
5382
  return sendJson(res, result.status, result.body);
5335
5383
  } catch (err) {
5336
5384
  const isTimeout = err instanceof ToolTimeoutError;
@@ -5346,6 +5394,7 @@ function dispatchSessionTool(req, res, url, sessions, logger, handler) {
5346
5394
  latencyMs: Date.now() - startedAt,
5347
5395
  error: safeMsg
5348
5396
  });
5397
+ logToStdout("error", tool, session.repoId, Date.now() - startedAt, safeMsg);
5349
5398
  if (isTimeout) {
5350
5399
  return sendJson(res, 504, { error: "tool-timeout", tool });
5351
5400
  }
@@ -5365,6 +5414,11 @@ function recordLog(logger, entry) {
5365
5414
  return;
5366
5415
  void logger.append(entry);
5367
5416
  }
5417
+ function logToStdout(status2, tool, repoId, latencyMs, error) {
5418
+ const repoFrag = repoId ? `repo=${repoId}` : "repo=\u2014";
5419
+ const errFrag = error ? ` error=${error}` : "";
5420
+ console.log(`[mcp] ${status2} ${tool} ${repoFrag} ${latencyMs}ms${errFrag}`);
5421
+ }
5368
5422
  var REDACT_FIELDS = /* @__PURE__ */ new Set([
5369
5423
  "query",
5370
5424
  "pattern",
@@ -5641,6 +5695,7 @@ function defaultMcpHome() {
5641
5695
 
5642
5696
  // ../listener/dist/mcp/auto-config/index.js
5643
5697
  import { homedir as homedir4 } from "os";
5698
+ import { basename as basename2 } from "path";
5644
5699
 
5645
5700
  // ../listener/dist/mcp/auto-config/writers/claude-code.js
5646
5701
  import { promises as fs3 } from "fs";
@@ -5728,15 +5783,18 @@ var claudeCodeWriter = {
5728
5783
  },
5729
5784
  async write(ctx) {
5730
5785
  const path = join19(ctx.repoRoot, ".mcp.json");
5786
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5731
5787
  const status2 = await writeMergedConfig({
5732
5788
  path,
5733
- serverName: `repowise-${ctx.repoId}`,
5789
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5734
5790
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5735
5791
  });
5736
5792
  return { status: status2, path };
5737
5793
  },
5738
5794
  async remove(ctx) {
5739
- await removeFromConfig(join19(ctx.repoRoot, ".mcp.json"), `repowise-${ctx.repoId}`);
5795
+ const path = join19(ctx.repoRoot, ".mcp.json");
5796
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
5797
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5740
5798
  }
5741
5799
  };
5742
5800
  async function fileExists2(path) {
@@ -5761,15 +5819,18 @@ var clineWriter = {
5761
5819
  },
5762
5820
  async write(ctx) {
5763
5821
  const path = join20(ctx.home, ".cline", "mcp.json");
5822
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5764
5823
  const status2 = await writeMergedConfig({
5765
5824
  path,
5766
- serverName: `repowise-${ctx.repoId}`,
5825
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5767
5826
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5768
5827
  });
5769
5828
  return { status: status2, path };
5770
5829
  },
5771
5830
  async remove(ctx) {
5772
- await removeFromConfig(join20(ctx.home, ".cline", "mcp.json"), `repowise-${ctx.repoId}`);
5831
+ const path = join20(ctx.home, ".cline", "mcp.json");
5832
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
5833
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5773
5834
  }
5774
5835
  };
5775
5836
  async function fileExists3(path) {
@@ -5791,15 +5852,18 @@ var codexWriter = {
5791
5852
  },
5792
5853
  async write(ctx) {
5793
5854
  const path = join21(ctx.home, ".codex", "mcp.json");
5855
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5794
5856
  const status2 = await writeMergedConfig({
5795
5857
  path,
5796
- serverName: `repowise-${ctx.repoId}`,
5858
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5797
5859
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5798
5860
  });
5799
5861
  return { status: status2, path };
5800
5862
  },
5801
5863
  async remove(ctx) {
5802
- await removeFromConfig(join21(ctx.home, ".codex", "mcp.json"), `repowise-${ctx.repoId}`);
5864
+ const path = join21(ctx.home, ".codex", "mcp.json");
5865
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
5866
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5803
5867
  }
5804
5868
  };
5805
5869
  async function fileExists4(path) {
@@ -5821,15 +5885,18 @@ var copilotWriter = {
5821
5885
  },
5822
5886
  async write(ctx) {
5823
5887
  const path = join22(ctx.repoRoot, ".vscode", "mcp.json");
5888
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5824
5889
  const status2 = await writeMergedConfig({
5825
5890
  path,
5826
- serverName: `repowise-${ctx.repoId}`,
5891
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5827
5892
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5828
5893
  });
5829
5894
  return { status: status2, path };
5830
5895
  },
5831
5896
  async remove(ctx) {
5832
- await removeFromConfig(join22(ctx.repoRoot, ".vscode", "mcp.json"), `repowise-${ctx.repoId}`);
5897
+ const path = join22(ctx.repoRoot, ".vscode", "mcp.json");
5898
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
5899
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5833
5900
  }
5834
5901
  };
5835
5902
  async function fileExists5(path) {
@@ -5851,15 +5918,18 @@ var cursorWriter = {
5851
5918
  },
5852
5919
  async write(ctx) {
5853
5920
  const path = join23(ctx.repoRoot, ".cursor", "mcp.json");
5921
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5854
5922
  const status2 = await writeMergedConfig({
5855
5923
  path,
5856
- serverName: `repowise-${ctx.repoId}`,
5924
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5857
5925
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5858
5926
  });
5859
5927
  return { status: status2, path };
5860
5928
  },
5861
5929
  async remove(ctx) {
5862
- await removeFromConfig(join23(ctx.repoRoot, ".cursor", "mcp.json"), `repowise-${ctx.repoId}`);
5930
+ const path = join23(ctx.repoRoot, ".cursor", "mcp.json");
5931
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
5932
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5863
5933
  }
5864
5934
  };
5865
5935
  async function fileExists6(path) {
@@ -5881,15 +5951,18 @@ var geminiCliWriter = {
5881
5951
  },
5882
5952
  async write(ctx) {
5883
5953
  const path = join24(ctx.home, ".gemini", "settings.json");
5954
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5884
5955
  const status2 = await writeMergedConfig({
5885
5956
  path,
5886
- serverName: `repowise-${ctx.repoId}`,
5957
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5887
5958
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5888
5959
  });
5889
5960
  return { status: status2, path };
5890
5961
  },
5891
5962
  async remove(ctx) {
5892
- await removeFromConfig(join24(ctx.home, ".gemini", "settings.json"), `repowise-${ctx.repoId}`);
5963
+ const path = join24(ctx.home, ".gemini", "settings.json");
5964
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
5965
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5893
5966
  }
5894
5967
  };
5895
5968
  async function fileExists7(path) {
@@ -5913,16 +5986,21 @@ var rooWriter = {
5913
5986
  const repoConfig = join25(ctx.repoRoot, ".roo", "mcp.json");
5914
5987
  const useRepoScope = await fileExists8(join25(ctx.repoRoot, ".roo"));
5915
5988
  const path = useRepoScope ? repoConfig : join25(ctx.home, ".roo", "mcp.json");
5989
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5916
5990
  const status2 = await writeMergedConfig({
5917
5991
  path,
5918
- serverName: `repowise-${ctx.repoId}`,
5992
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5919
5993
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5920
5994
  });
5921
5995
  return { status: status2, path };
5922
5996
  },
5923
5997
  async remove(ctx) {
5924
- await removeFromConfig(join25(ctx.repoRoot, ".roo", "mcp.json"), `repowise-${ctx.repoId}`);
5925
- await removeFromConfig(join25(ctx.home, ".roo", "mcp.json"), `repowise-${ctx.repoId}`);
5998
+ const repoConfig = join25(ctx.repoRoot, ".roo", "mcp.json");
5999
+ const homeConfig = join25(ctx.home, ".roo", "mcp.json");
6000
+ await removeFromConfig(repoConfig, `RepoWise MCP for ${ctx.repoName}`);
6001
+ await removeFromConfig(repoConfig, `repowise-${ctx.repoId}`);
6002
+ await removeFromConfig(homeConfig, `RepoWise MCP for ${ctx.repoName}`);
6003
+ await removeFromConfig(homeConfig, `repowise-${ctx.repoId}`);
5926
6004
  }
5927
6005
  };
5928
6006
  async function fileExists8(path) {
@@ -5944,15 +6022,18 @@ var windsurfWriter = {
5944
6022
  },
5945
6023
  async write(ctx) {
5946
6024
  const path = join26(ctx.home, ".codeium", "windsurf", "mcp_config.json");
6025
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5947
6026
  const status2 = await writeMergedConfig({
5948
6027
  path,
5949
- serverName: `repowise-${ctx.repoId}`,
6028
+ serverName: `RepoWise MCP for ${ctx.repoName}`,
5950
6029
  spec: { command: ctx.shimCmd, args: ["mcp-shim", "--repo-id", ctx.repoId] }
5951
6030
  });
5952
6031
  return { status: status2, path };
5953
6032
  },
5954
6033
  async remove(ctx) {
5955
- await removeFromConfig(join26(ctx.home, ".codeium", "windsurf", "mcp_config.json"), `repowise-${ctx.repoId}`);
6034
+ const path = join26(ctx.home, ".codeium", "windsurf", "mcp_config.json");
6035
+ await removeFromConfig(path, `RepoWise MCP for ${ctx.repoName}`);
6036
+ await removeFromConfig(path, `repowise-${ctx.repoId}`);
5956
6037
  }
5957
6038
  };
5958
6039
  async function fileExists9(path) {
@@ -6000,6 +6081,7 @@ async function runAutoConfig(opts) {
6000
6081
  try {
6001
6082
  const outcome = await writer.write({
6002
6083
  repoRoot: opts.repoRoot,
6084
+ repoName: basename2(opts.repoRoot),
6003
6085
  repoId: opts.repoId,
6004
6086
  shimCmd: opts.shimCmd,
6005
6087
  home
@@ -6016,6 +6098,9 @@ async function runAutoConfig(opts) {
6016
6098
  return results;
6017
6099
  }
6018
6100
 
6101
+ // ../listener/dist/main.js
6102
+ init_installer();
6103
+
6019
6104
  // ../listener/dist/typed-resolution/resolver-loop.js
6020
6105
  init_src();
6021
6106
  init_registry();
@@ -7699,11 +7784,11 @@ async function writeClaudeSubagentHook(repoRoot, contextFolder) {
7699
7784
  }
7700
7785
 
7701
7786
  // src/lib/gitignore.ts
7702
- import { readFileSync as readFileSync2, writeFileSync, existsSync } from "fs";
7787
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
7703
7788
  import { join as join32 } from "path";
7704
7789
  function ensureGitignore(repoRoot, entry) {
7705
7790
  const gitignorePath = join32(repoRoot, ".gitignore");
7706
- if (existsSync(gitignorePath)) {
7791
+ if (existsSync2(gitignorePath)) {
7707
7792
  const content = readFileSync2(gitignorePath, "utf-8");
7708
7793
  const lines = content.split("\n").map((l) => l.trim());
7709
7794
  if (lines.includes(entry) || lines.includes(entry + "/")) {
@@ -9032,7 +9117,7 @@ async function logout() {
9032
9117
 
9033
9118
  // src/commands/status.ts
9034
9119
  import { readFile as readFile16 } from "fs/promises";
9035
- import { basename as basename2, join as join36 } from "path";
9120
+ import { basename as basename3, join as join36 } from "path";
9036
9121
  async function status() {
9037
9122
  const configDir = getConfigDir2();
9038
9123
  const STATE_PATH = join36(configDir, "listener-state.json");
@@ -9068,7 +9153,7 @@ async function status() {
9068
9153
  const configData = await readFile16(CONFIG_PATH, "utf-8");
9069
9154
  const config2 = JSON.parse(configData);
9070
9155
  for (const repo of config2.repos ?? []) {
9071
- repoNames.set(repo.repoId, basename2(repo.localPath));
9156
+ repoNames.set(repo.repoId, basename3(repo.localPath));
9072
9157
  }
9073
9158
  } catch {
9074
9159
  }
@@ -10493,6 +10578,7 @@ async function mcpShim(opts) {
10493
10578
  const stderr = opts.stderr ?? process.stderr;
10494
10579
  const maxBytes = opts.maxRequestBytes ?? DEFAULT_MAX;
10495
10580
  const postJson = opts.postJson ?? defaultPostJson;
10581
+ const getJson = opts.getJson ?? defaultGetJson;
10496
10582
  const sessionState = { sessionId: null };
10497
10583
  const rl = createInterface2({ input: stdin, crlfDelay: Infinity });
10498
10584
  for await (const rawLine of rl) {
@@ -10538,6 +10624,7 @@ async function mcpShim(opts) {
10538
10624
  opts.repoId,
10539
10625
  parsed,
10540
10626
  postJson,
10627
+ getJson,
10541
10628
  headers,
10542
10629
  sessionState
10543
10630
  );
@@ -10580,7 +10667,7 @@ function writeJson(stream, value) {
10580
10667
  stream.write(`${JSON.stringify(value)}
10581
10668
  `);
10582
10669
  }
10583
- async function routeMessage(endpoint, repoId, message, postJson, headers, sessionState) {
10670
+ async function routeMessage(endpoint, repoId, message, postJson, getJson, headers, sessionState) {
10584
10671
  const msg = message;
10585
10672
  const rpcId = msg.id;
10586
10673
  const method = msg.method;
@@ -10596,41 +10683,28 @@ async function routeMessage(endpoint, repoId, message, postJson, headers, sessio
10596
10683
  };
10597
10684
  }
10598
10685
  if (method === "tools/list") {
10599
- const tools = [
10600
- "find_symbol",
10601
- "get_symbol",
10602
- "get_impact",
10603
- "list_edges",
10604
- "search_pattern",
10605
- "batch_query",
10606
- "find_callers",
10607
- "find_references",
10608
- "get_deps",
10609
- "get_call_graph",
10610
- "find_tests_for_symbol",
10611
- "get_todos",
10612
- "get_freshness",
10613
- "lsp_definition",
10614
- "lsp_references",
10615
- "lsp_hover",
10616
- "lsp_workspace_symbol",
10617
- "lsp_document_symbol",
10618
- "lsp_call_hierarchy",
10619
- "lsp_implementation",
10620
- "lsp_type_hierarchy"
10621
- ].map((name) => ({ name, description: "", inputSchema: { type: "object" } }));
10686
+ const catalog = await getJson(`${endpoint}/mcp/tools`, headers);
10687
+ const tools = (catalog.tools ?? []).map((t) => ({
10688
+ name: t.name,
10689
+ description: [t.description, t.whenToUse].filter((s) => s && s.length > 0).join("\n\n"),
10690
+ inputSchema: t.inputSchema ?? { type: "object" }
10691
+ }));
10622
10692
  return { jsonrpc: "2.0", id: rpcId, result: { tools } };
10623
10693
  }
10624
- if (!sessionState.sessionId) {
10694
+ async function connect() {
10625
10695
  const connectResp = await postJson(`${endpoint}/mcp/connect`, { repoId }, headers);
10626
- if (!connectResp.sessionId) {
10696
+ return connectResp.sessionId ?? null;
10697
+ }
10698
+ if (!sessionState.sessionId) {
10699
+ const sid = await connect();
10700
+ if (!sid) {
10627
10701
  return {
10628
10702
  jsonrpc: "2.0",
10629
10703
  id: rpcId,
10630
10704
  error: { code: -32002, message: "failed to establish MCP session" }
10631
10705
  };
10632
10706
  }
10633
- sessionState.sessionId = connectResp.sessionId;
10707
+ sessionState.sessionId = sid;
10634
10708
  }
10635
10709
  if (method === "tools/call") {
10636
10710
  const params = msg.params ?? {};
@@ -10644,12 +10718,33 @@ async function routeMessage(endpoint, repoId, message, postJson, headers, sessio
10644
10718
  error: { code: -32602, message: "tools/call missing name" }
10645
10719
  };
10646
10720
  }
10647
- const result = await postJson(
10648
- `${endpoint}/mcp/tools/${name}`,
10649
- { sessionId: sessionState.sessionId, ...args },
10650
- headers
10651
- );
10652
- return { jsonrpc: "2.0", id: rpcId, result };
10721
+ const callTool = (sid) => postJson(`${endpoint}/mcp/tools/${name}`, { sessionId: sid, ...args }, headers);
10722
+ let result;
10723
+ try {
10724
+ result = await callTool(sessionState.sessionId);
10725
+ } catch (err) {
10726
+ const detail = err.message;
10727
+ const isStaleSession = /HTTP (401|404)/.test(detail) || /session/i.test(detail) || /unknown_session/i.test(detail);
10728
+ if (!isStaleSession) throw err;
10729
+ sessionState.sessionId = null;
10730
+ const fresh = await connect();
10731
+ if (!fresh) {
10732
+ return {
10733
+ jsonrpc: "2.0",
10734
+ id: rpcId,
10735
+ error: { code: -32002, message: "failed to establish MCP session (after retry)" }
10736
+ };
10737
+ }
10738
+ sessionState.sessionId = fresh;
10739
+ result = await callTool(fresh);
10740
+ }
10741
+ return {
10742
+ jsonrpc: "2.0",
10743
+ id: rpcId,
10744
+ result: {
10745
+ content: [{ type: "text", text: JSON.stringify(result) }]
10746
+ }
10747
+ };
10653
10748
  }
10654
10749
  return {
10655
10750
  jsonrpc: "2.0",
@@ -10674,6 +10769,19 @@ async function defaultPostJson(url, body, headers = { "content-type": "applicati
10674
10769
  }
10675
10770
  return await res.json();
10676
10771
  }
10772
+ async function defaultGetJson(url, headers = {}) {
10773
+ const res = await fetch(url, { method: "GET", headers });
10774
+ if (!res.ok) {
10775
+ let detail = "";
10776
+ try {
10777
+ const errBody = await res.json();
10778
+ detail = errBody.message ?? errBody.error ?? "";
10779
+ } catch {
10780
+ }
10781
+ throw new Error(`HTTP ${res.status.toString()}${detail ? `: ${detail}` : ""}`);
10782
+ }
10783
+ return await res.json();
10784
+ }
10677
10785
 
10678
10786
  // src/commands/mcp-serve.ts
10679
10787
  import { createInterface as createInterface3 } from "readline";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "repowisestage",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
4
4
  "type": "module",
5
5
  "description": "AI-optimized codebase context generator",
6
6
  "bin": {