repowisestage 0.0.69 → 0.0.71

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 +632 -284
  2. package/package.json +1 -1
@@ -3133,7 +3133,7 @@ var init_telemetry = __esm({
3133
3133
  // bin/repowise.ts
3134
3134
  import { readFileSync as readFileSync3 } from "fs";
3135
3135
  import { fileURLToPath as fileURLToPath4 } from "url";
3136
- import { dirname as dirname23, join as join61 } from "path";
3136
+ import { dirname as dirname23, join as join62 } from "path";
3137
3137
  import { Command } from "commander";
3138
3138
 
3139
3139
  // ../listener/dist/main.js
@@ -8007,10 +8007,10 @@ async function ensureServerConsent(opts) {
8007
8007
  });
8008
8008
  if (!res.ok)
8009
8009
  return;
8010
- const fs30 = await import("fs/promises");
8010
+ const fs31 = await import("fs/promises");
8011
8011
  const path = await import("path");
8012
- await fs30.mkdir(path.dirname(opts.markerPath), { recursive: true });
8013
- await fs30.writeFile(opts.markerPath, (/* @__PURE__ */ new Date()).toISOString() + "\n", {
8012
+ await fs31.mkdir(path.dirname(opts.markerPath), { recursive: true });
8013
+ await fs31.writeFile(opts.markerPath, (/* @__PURE__ */ new Date()).toISOString() + "\n", {
8014
8014
  encoding: "utf-8",
8015
8015
  mode: 384
8016
8016
  });
@@ -10510,6 +10510,33 @@ function sleep(ms) {
10510
10510
  }, ms);
10511
10511
  });
10512
10512
  }
10513
+ async function waitForCredentials(isRunning2) {
10514
+ let changed = false;
10515
+ let watcher = null;
10516
+ try {
10517
+ const fs31 = await import("fs");
10518
+ watcher = fs31.watch(getConfigDir(), (_event, filename) => {
10519
+ if (!filename || filename === "credentials.json")
10520
+ changed = true;
10521
+ });
10522
+ } catch {
10523
+ }
10524
+ try {
10525
+ while (isRunning2()) {
10526
+ await sleep(changed ? 0 : 1e4);
10527
+ changed = false;
10528
+ if (!isRunning2())
10529
+ break;
10530
+ const fresh = await getValidCredentials({ forceRefresh: true });
10531
+ if (fresh)
10532
+ return fresh;
10533
+ }
10534
+ return null;
10535
+ } finally {
10536
+ if (watcher)
10537
+ watcher.close();
10538
+ }
10539
+ }
10513
10540
  function decodeEmailFromIdToken(idToken) {
10514
10541
  try {
10515
10542
  const payload = JSON.parse(Buffer.from(idToken.split(".")[1], "base64url").toString());
@@ -10741,6 +10768,9 @@ async function checkStaleContext(repos, state, groups) {
10741
10768
  }
10742
10769
  return dirty;
10743
10770
  }
10771
+ function mcpAiToolsKey(aiTools) {
10772
+ return JSON.stringify([...aiTools ?? []].sort());
10773
+ }
10744
10774
  async function reconcileMcpConfigs(repos, packageName) {
10745
10775
  const shimCmd = packageName;
10746
10776
  const { contextFolder, aiTools, graphOnly } = await readRawToolConfig();
@@ -10909,12 +10939,15 @@ async function startListener() {
10909
10939
  process.exitCode = 1;
10910
10940
  return;
10911
10941
  }
10912
- const credentials = await getValidCredentials();
10942
+ let credentials = await getValidCredentials();
10913
10943
  if (!credentials) {
10914
- console.error("Not logged in. Run `repowise login` first.");
10915
- await releaseLockAndExit();
10916
- process.exitCode = 1;
10917
- return;
10944
+ console.warn("Not logged in. Run `repowise login` \u2014 the listener will start automatically once you sign in.");
10945
+ credentials = await waitForCredentials(() => running);
10946
+ if (!credentials) {
10947
+ await releaseLockAndExit();
10948
+ return;
10949
+ }
10950
+ console.log("Logged in \u2014 starting RepoWise listener.");
10918
10951
  }
10919
10952
  let state;
10920
10953
  try {
@@ -10981,6 +11014,7 @@ async function startListener() {
10981
11014
  let pollIntervalMs = 5e3;
10982
11015
  let pollCycleCount = 0;
10983
11016
  const RECONCILE_EVERY_N_CYCLES = 60;
11017
+ let lastMcpAiToolsKey = null;
10984
11018
  const origLog = console.log.bind(console);
10985
11019
  const origError = console.error.bind(console);
10986
11020
  const origWarn = console.warn.bind(console);
@@ -11203,6 +11237,19 @@ async function startListener() {
11203
11237
  void warmReposLsp(newRepos, mcpRuntime.lspWorkspaces, config2.lspOverrides).catch(() => {
11204
11238
  });
11205
11239
  }
11240
+ void reconcileMcpConfigs(newRepos, packageName).catch((err) => {
11241
+ console.warn("[mcp-config] front-load for newly-registered repo(s) failed:", err instanceof Error ? err.message : String(err));
11242
+ });
11243
+ }
11244
+ const aiToolsKey = mcpAiToolsKey((await readRawToolConfig()).aiTools);
11245
+ if (lastMcpAiToolsKey === null) {
11246
+ lastMcpAiToolsKey = aiToolsKey;
11247
+ } else if (aiToolsKey !== lastMcpAiToolsKey) {
11248
+ lastMcpAiToolsKey = aiToolsKey;
11249
+ console.log("[config-sync] AI-tool selection changed \u2014 front-loading MCP config");
11250
+ void reconcileMcpConfigs(freshRepos, packageName).catch((err) => {
11251
+ console.warn("[mcp-config] front-load for aiTools change failed:", err instanceof Error ? err.message : String(err));
11252
+ });
11206
11253
  }
11207
11254
  } catch {
11208
11255
  }
@@ -11453,8 +11500,8 @@ async function startListener() {
11453
11500
  await sleep(5e3);
11454
11501
  if (!running)
11455
11502
  break;
11456
- const fresh = await getValidCredentials({ forceRefresh: true });
11457
- if (fresh) {
11503
+ const fresh2 = await getValidCredentials({ forceRefresh: true });
11504
+ if (fresh2) {
11458
11505
  console.log("Credentials recovered \u2014 resuming listener");
11459
11506
  recovered = true;
11460
11507
  break;
@@ -11475,29 +11522,9 @@ async function startListener() {
11475
11522
  } catch {
11476
11523
  }
11477
11524
  }
11478
- const credentialsPath = join41(getConfigDir(), "credentials.json");
11479
- let credentialsChanged = false;
11480
- let watcher = null;
11481
- try {
11482
- const fs30 = await import("fs");
11483
- watcher = fs30.watch(credentialsPath, () => {
11484
- credentialsChanged = true;
11485
- });
11486
- } catch {
11487
- }
11488
- while (running) {
11489
- await sleep(credentialsChanged ? 0 : 1e4);
11490
- credentialsChanged = false;
11491
- if (!running)
11492
- break;
11493
- const fresh = await getValidCredentials({ forceRefresh: true });
11494
- if (fresh) {
11495
- console.log("Re-authenticated \u2014 resuming listener");
11496
- break;
11497
- }
11498
- }
11499
- if (watcher)
11500
- watcher.close();
11525
+ const fresh = await waitForCredentials(() => running);
11526
+ if (fresh)
11527
+ console.log("Re-authenticated \u2014 resuming listener");
11501
11528
  continue;
11502
11529
  }
11503
11530
  pollIntervalMs = minPollInterval;
@@ -11651,7 +11678,7 @@ async function showWelcome(currentVersion) {
11651
11678
 
11652
11679
  // src/commands/create.ts
11653
11680
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
11654
- import { dirname as dirname19, join as join49 } from "path";
11681
+ import { dirname as dirname19, join as join50 } from "path";
11655
11682
  init_src();
11656
11683
  import chalk8 from "chalk";
11657
11684
  import ora from "ora";
@@ -12010,25 +12037,26 @@ async function apiRequest(path, options) {
12010
12037
  // src/lib/prompts.ts
12011
12038
  import { checkbox, confirm, Separator } from "@inquirer/prompts";
12012
12039
  import chalk2 from "chalk";
12013
- async function selectAiTools() {
12040
+ async function selectAiTools(opts) {
12041
+ const checkedSet = new Set(opts?.defaults ?? []);
12014
12042
  const choices = [
12015
12043
  new Separator(chalk2.dim("\u2500\u2500 Popular \u2500\u2500")),
12016
- { name: "Cursor", value: "cursor" },
12017
- { name: "Claude Code", value: "claude-code" },
12018
- { name: "GitHub Copilot", value: "copilot" },
12019
- { name: "Windsurf", value: "windsurf" },
12044
+ { name: "Cursor", value: "cursor", checked: checkedSet.has("cursor") },
12045
+ { name: "Claude Code", value: "claude-code", checked: checkedSet.has("claude-code") },
12046
+ { name: "GitHub Copilot", value: "copilot", checked: checkedSet.has("copilot") },
12047
+ { name: "Windsurf", value: "windsurf", checked: checkedSet.has("windsurf") },
12020
12048
  new Separator(chalk2.dim("\u2500\u2500 More Tools \u2500\u2500")),
12021
- { name: "Cline", value: "cline" },
12022
- { name: "Codex", value: "codex" },
12049
+ { name: "Cline", value: "cline", checked: checkedSet.has("cline") },
12050
+ { name: "Codex", value: "codex", checked: checkedSet.has("codex") },
12023
12051
  // Roo Code shut down 2026-05-15; Kilo Code is the community successor.
12024
- { name: "Kilo Code", value: "kilo" },
12025
- { name: "Gemini CLI", value: "gemini" },
12052
+ { name: "Kilo Code", value: "kilo", checked: checkedSet.has("kilo") },
12053
+ { name: "Gemini CLI", value: "gemini", checked: checkedSet.has("gemini") },
12026
12054
  new Separator(chalk2.dim("\u2500\u2500 Cloud Agents \u2500\u2500")),
12027
- { name: "Warp", value: "warp" },
12028
- { name: "JetBrains Junie", value: "junie" },
12029
- { name: "Google Jules", value: "jules" },
12030
- { name: "Amp", value: "amp" },
12031
- { name: "Devin", value: "devin" },
12055
+ { name: "Warp", value: "warp", checked: checkedSet.has("warp") },
12056
+ { name: "JetBrains Junie", value: "junie", checked: checkedSet.has("junie") },
12057
+ { name: "Google Jules", value: "jules", checked: checkedSet.has("jules") },
12058
+ { name: "Amp", value: "amp", checked: checkedSet.has("amp") },
12059
+ { name: "Devin", value: "devin", checked: checkedSet.has("devin") },
12032
12060
  new Separator(chalk2.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),
12033
12061
  { name: "Other (manual setup)", value: "other" }
12034
12062
  ];
@@ -12051,11 +12079,126 @@ async function selectAiTools() {
12051
12079
  }
12052
12080
  }
12053
12081
 
12082
+ // src/lib/setup-tools.ts
12083
+ async function writeToolConfigsForRepo(opts) {
12084
+ const { repoRoot, tools, repoName, contextFolder, contextFiles, variant, migrateLegacy } = opts;
12085
+ const results = [];
12086
+ const written = /* @__PURE__ */ new Set();
12087
+ for (const tool of tools) {
12088
+ const config2 = AI_TOOL_CONFIG[tool];
12089
+ if (written.has(config2.filePath)) continue;
12090
+ written.add(config2.filePath);
12091
+ if (migrateLegacy && config2.legacyFilePath) {
12092
+ await migrateToolConfig(repoRoot, tool, repoName, contextFolder, contextFiles, variant);
12093
+ }
12094
+ const { created } = await updateToolConfig(
12095
+ repoRoot,
12096
+ tool,
12097
+ repoName,
12098
+ contextFolder,
12099
+ contextFiles,
12100
+ variant
12101
+ );
12102
+ results.push(`${created ? "Created" : "Updated"} ${config2.filePath}`);
12103
+ }
12104
+ if (!written.has("AGENTS.md")) {
12105
+ const { created } = await updateToolConfig(
12106
+ repoRoot,
12107
+ "codex",
12108
+ repoName,
12109
+ contextFolder,
12110
+ contextFiles,
12111
+ variant
12112
+ );
12113
+ results.push(`${created ? "Created" : "Updated"} AGENTS.md`);
12114
+ }
12115
+ if (tools.includes("claude-code")) {
12116
+ const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder, variant);
12117
+ results.push(`Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
12118
+ }
12119
+ return results;
12120
+ }
12121
+
12122
+ // src/lib/mcp-resolver.ts
12123
+ import { basename as basename4, join as join45 } from "path";
12124
+ import { promises as fs21 } from "fs";
12125
+ var MCP_WRITER_TOOLS = /* @__PURE__ */ new Set([
12126
+ "claude-code",
12127
+ "cursor",
12128
+ "copilot",
12129
+ "codex",
12130
+ "windsurf",
12131
+ "cline",
12132
+ "gemini"
12133
+ ]);
12134
+ function mcpPathFor(tool, repoRoot, home) {
12135
+ switch (tool) {
12136
+ case "claude-code":
12137
+ return { path: join45(repoRoot, ".mcp.json"), scope: "repo" };
12138
+ case "cursor":
12139
+ return { path: join45(repoRoot, ".cursor", "mcp.json"), scope: "repo" };
12140
+ case "copilot":
12141
+ return { path: join45(repoRoot, ".vscode", "mcp.json"), scope: "repo" };
12142
+ case "codex":
12143
+ return { path: join45(home, ".codex", "mcp.json"), scope: "global" };
12144
+ case "windsurf":
12145
+ return { path: join45(home, ".codeium", "windsurf", "mcp_config.json"), scope: "global" };
12146
+ case "cline":
12147
+ return { path: join45(home, ".cline", "mcp.json"), scope: "global" };
12148
+ case "gemini":
12149
+ return { path: join45(home, ".gemini", "settings.json"), scope: "global" };
12150
+ default:
12151
+ return null;
12152
+ }
12153
+ }
12154
+ function mcpServerKeyFor(repoRoot) {
12155
+ return `RepoWise MCP for ${basename4(repoRoot)}`;
12156
+ }
12157
+ function mcpConfigTargetForTool(tool, opts) {
12158
+ const loc = mcpPathFor(tool, opts.repoRoot, opts.home);
12159
+ if (!loc) return null;
12160
+ return { tool, path: loc.path, scope: loc.scope, serverKey: mcpServerKeyFor(opts.repoRoot) };
12161
+ }
12162
+ async function mcpConfigState(target) {
12163
+ let raw;
12164
+ try {
12165
+ raw = await fs21.readFile(target.path, "utf-8");
12166
+ } catch {
12167
+ return "absent";
12168
+ }
12169
+ try {
12170
+ const parsed = JSON.parse(raw);
12171
+ return parsed.mcpServers && target.serverKey in parsed.mcpServers ? "configured" : "pending";
12172
+ } catch {
12173
+ return "absent";
12174
+ }
12175
+ }
12176
+ function mcpActivationHint(tool, repoName) {
12177
+ if (!MCP_WRITER_TOOLS.has(tool)) return null;
12178
+ if (tool === "claude-code") {
12179
+ return `In Claude Code, run \`/mcp\` and approve the "RepoWise MCP for ${repoName}" server.`;
12180
+ }
12181
+ return `Reload your AI tool and approve the "RepoWise MCP for ${repoName}" server when prompted.`;
12182
+ }
12183
+ async function mcpStatusForRepo(opts) {
12184
+ const repoName = basename4(opts.repoRoot);
12185
+ const out = [];
12186
+ for (const tool of opts.aiTools) {
12187
+ const target = mcpConfigTargetForTool(tool, { repoRoot: opts.repoRoot, home: opts.home });
12188
+ if (!target) continue;
12189
+ const state = await mcpConfigState(target);
12190
+ if (state === "absent") continue;
12191
+ const configured = state === "configured";
12192
+ out.push({ tool, configured, hint: configured ? null : mcpActivationHint(tool, repoName) });
12193
+ }
12194
+ return out;
12195
+ }
12196
+
12054
12197
  // src/lib/gitignore.ts
12055
12198
  import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
12056
- import { join as join45 } from "path";
12199
+ import { join as join46 } from "path";
12057
12200
  function ensureGitignore(repoRoot, entry) {
12058
- const gitignorePath = join45(repoRoot, ".gitignore");
12201
+ const gitignorePath = join46(repoRoot, ".gitignore");
12059
12202
  if (existsSync2(gitignorePath)) {
12060
12203
  const content = readFileSync2(gitignorePath, "utf-8");
12061
12204
  const lines = content.split("\n").map((l) => l.trim());
@@ -12072,7 +12215,7 @@ function ensureGitignore(repoRoot, entry) {
12072
12215
  // src/lib/graph-cache.ts
12073
12216
  init_config_dir();
12074
12217
  import { mkdirSync, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
12075
- import { dirname as dirname18, join as join46 } from "path";
12218
+ import { dirname as dirname18, join as join47 } from "path";
12076
12219
  var SAFE_REPO_ID = /^[A-Za-z0-9_.-]{1,128}$/;
12077
12220
  function assertSafeRepoId2(repoId) {
12078
12221
  if (!repoId || typeof repoId !== "string" || !SAFE_REPO_ID.test(repoId) || repoId === "." || repoId === ".." || repoId.startsWith(".")) {
@@ -12081,7 +12224,7 @@ function assertSafeRepoId2(repoId) {
12081
12224
  }
12082
12225
  function graphCachePath(repoId) {
12083
12226
  assertSafeRepoId2(repoId);
12084
- return join46(getConfigDir(), "graphs", `${repoId}.json`);
12227
+ return join47(getConfigDir(), "graphs", `${repoId}.json`);
12085
12228
  }
12086
12229
  function isUsableGraph(parsed) {
12087
12230
  if (typeof parsed !== "object" || parsed === null) return false;
@@ -12697,17 +12840,27 @@ var ProgressRenderer = class {
12697
12840
  if (generating) {
12698
12841
  const genBaseName = generating.fileName.split("/").pop() ?? generating.fileName;
12699
12842
  const isCore = CORE_FILES.has(genBaseName);
12700
- const sectionFiles = gp.fileStatuses.filter((f) => {
12843
+ const sectionFiles2 = gp.fileStatuses.filter((f) => {
12701
12844
  const bn = f.fileName.split("/").pop() ?? f.fileName;
12702
12845
  return isCore ? CORE_FILES.has(bn) : !CORE_FILES.has(bn);
12703
12846
  });
12704
- const sectionCompleted = sectionFiles.filter((f) => f.status === "completed").length;
12705
- return `${generating.fileName} (${sectionCompleted}/${sectionFiles.length}) ${chalk4.dim(`(${overallPct}%)`)}`;
12847
+ const sectionCompleted = sectionFiles2.filter((f) => f.status === "completed").length;
12848
+ return `${generating.fileName} (${sectionCompleted}/${sectionFiles2.length}) ${chalk4.dim(`(${overallPct}%)`)}`;
12706
12849
  }
12707
12850
  const allDone = gp.fileStatuses.every((f) => f.status === "completed");
12708
12851
  if (allDone) {
12709
12852
  return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
12710
12853
  }
12854
+ const baseNameOf = (f) => f.fileName.split("/").pop() ?? f.fileName;
12855
+ const coreFiles = gp.fileStatuses.filter((f) => CORE_FILES.has(baseNameOf(f)));
12856
+ const optionalFiles = gp.fileStatuses.filter((f) => !CORE_FILES.has(baseNameOf(f)));
12857
+ const coreDone = coreFiles.length > 0 && coreFiles.every((f) => f.status === "completed");
12858
+ const sectionFiles = coreDone && optionalFiles.length > 0 ? optionalFiles : coreFiles;
12859
+ if (sectionFiles.length > 0) {
12860
+ const sectionCompleted = sectionFiles.filter((f) => f.status === "completed").length;
12861
+ const name = gp.currentFileName || stepLabel;
12862
+ return `${name} (${sectionCompleted}/${sectionFiles.length}) ${chalk4.dim(`(${overallPct}%)`)}`;
12863
+ }
12711
12864
  }
12712
12865
  }
12713
12866
  return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
@@ -12798,8 +12951,8 @@ async function promptDepInstallConsent(missing, opts) {
12798
12951
  import chalk6 from "chalk";
12799
12952
 
12800
12953
  // src/lib/dep-installer.ts
12801
- import { promises as fs21 } from "fs";
12802
- import { join as join47 } from "path";
12954
+ import { promises as fs22 } from "fs";
12955
+ import { join as join48 } from "path";
12803
12956
  var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
12804
12957
  "typescript",
12805
12958
  "javascript",
@@ -12809,14 +12962,14 @@ var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
12809
12962
  ]);
12810
12963
  var exists = async (p) => {
12811
12964
  try {
12812
- await fs21.access(p);
12965
+ await fs22.access(p);
12813
12966
  return true;
12814
12967
  } catch {
12815
12968
  return false;
12816
12969
  }
12817
12970
  };
12818
12971
  async function fileExists16(repoRoot, name) {
12819
- return exists(join47(repoRoot, name));
12972
+ return exists(join48(repoRoot, name));
12820
12973
  }
12821
12974
  async function detectNodePackageManager(repoRoot) {
12822
12975
  if (await fileExists16(repoRoot, "pnpm-lock.yaml")) {
@@ -12893,13 +13046,13 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
12893
13046
  // src/lib/dep-installer-runner.ts
12894
13047
  import { spawn as spawn11 } from "child_process";
12895
13048
  import { createWriteStream as createWriteStream3 } from "fs";
12896
- import { promises as fs22 } from "fs";
12897
- import { join as join48 } from "path";
13049
+ import { promises as fs23 } from "fs";
13050
+ import { join as join49 } from "path";
12898
13051
  var DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
12899
13052
  async function runMissingDepInstalls(opts) {
12900
13053
  const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
12901
- const logPath2 = join48(getConfigDir2(), `install-log.${safeRepoId}.txt`);
12902
- await fs22.mkdir(getConfigDir2(), { recursive: true });
13054
+ const logPath2 = join49(getConfigDir2(), `install-log.${safeRepoId}.txt`);
13055
+ await fs23.mkdir(getConfigDir2(), { recursive: true });
12903
13056
  const stream = opts.logStream ?? createWriteStream3(logPath2, { flags: "a" });
12904
13057
  stream.on("error", () => {
12905
13058
  });
@@ -12945,7 +13098,7 @@ async function runOne2(dep, repoRoot, stream, timeoutMs) {
12945
13098
  }
12946
13099
  async function runPipFlow(repoRoot, stream, timeoutMs) {
12947
13100
  await runSimple(["python3", "-m", "venv", ".venv"], repoRoot, stream, timeoutMs);
12948
- const venvPip = process.platform === "win32" ? join48(".venv", "Scripts", "pip.exe") : join48(".venv", "bin", "pip");
13101
+ const venvPip = process.platform === "win32" ? join49(".venv", "Scripts", "pip.exe") : join49(".venv", "bin", "pip");
12949
13102
  await runSimple([venvPip, "install", "-r", "requirements.txt"], repoRoot, stream, timeoutMs);
12950
13103
  }
12951
13104
  function runSimple(cmd, repoRoot, stream, timeoutMs) {
@@ -13283,9 +13436,7 @@ async function create() {
13283
13436
  }
13284
13437
  if (tools.length === 0 && !hasOther) {
13285
13438
  console.log(
13286
- chalk8.yellow(
13287
- "\nNo AI tools selected. You can configure them later with `repowise config`."
13288
- )
13439
+ chalk8.yellow("\nNo AI tools selected. You can set them up later with `repowise tools`.")
13289
13440
  );
13290
13441
  }
13291
13442
  if (repoRoot) {
@@ -13518,7 +13669,7 @@ async function create() {
13518
13669
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
13519
13670
  const files = listResult.data?.files ?? listResult.files ?? [];
13520
13671
  if (files.length > 0) {
13521
- const contextDir = join49(repoRoot, DEFAULT_CONTEXT_FOLDER);
13672
+ const contextDir = join50(repoRoot, DEFAULT_CONTEXT_FOLDER);
13522
13673
  mkdirSync2(contextDir, { recursive: true });
13523
13674
  let downloadedCount = 0;
13524
13675
  let failedCount = 0;
@@ -13532,7 +13683,7 @@ async function create() {
13532
13683
  const response = await fetch(presignedUrl);
13533
13684
  if (response.ok) {
13534
13685
  const content = await response.text();
13535
- const filePath = join49(contextDir, file.fileName);
13686
+ const filePath = join50(contextDir, file.fileName);
13536
13687
  mkdirSync2(dirname19(filePath), { recursive: true });
13537
13688
  writeFileSync3(filePath, content, "utf-8");
13538
13689
  downloadedCount++;
@@ -13616,51 +13767,17 @@ Files are stored on our servers (not in git). Retry when online.`
13616
13767
  }
13617
13768
  if (repoRoot) {
13618
13769
  spinner.start("Configuring AI tools...");
13619
- const results = [];
13620
- const written = /* @__PURE__ */ new Set();
13621
- const toolVariant = graphOnly ? "graph" : "full";
13622
- for (const tool of tools) {
13623
- const config2 = AI_TOOL_CONFIG[tool];
13624
- if (written.has(config2.filePath)) continue;
13625
- written.add(config2.filePath);
13626
- if (config2.legacyFilePath) {
13627
- await migrateToolConfig(
13628
- repoRoot,
13629
- tool,
13630
- repoName,
13631
- contextFolder,
13632
- contextFiles,
13633
- toolVariant
13634
- );
13635
- }
13636
- const { created: wasCreated } = await updateToolConfig(
13637
- repoRoot,
13638
- tool,
13639
- repoName,
13640
- contextFolder,
13641
- contextFiles,
13642
- toolVariant
13643
- );
13644
- const action = wasCreated ? "Created" : "Updated";
13645
- results.push(` ${action} ${config2.filePath}`);
13646
- }
13647
- if (!written.has("AGENTS.md")) {
13648
- const { created: wasCreated } = await updateToolConfig(
13649
- repoRoot,
13650
- "codex",
13651
- repoName,
13652
- contextFolder,
13653
- contextFiles,
13654
- toolVariant
13655
- );
13656
- results.push(` ${wasCreated ? "Created" : "Updated"} AGENTS.md`);
13657
- }
13658
- if (tools.includes("claude-code")) {
13659
- const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder, toolVariant);
13660
- results.push(` Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
13661
- }
13770
+ const results = await writeToolConfigsForRepo({
13771
+ repoRoot,
13772
+ tools,
13773
+ repoName,
13774
+ contextFolder,
13775
+ contextFiles,
13776
+ variant: graphOnly ? "graph" : "full",
13777
+ migrateLegacy: true
13778
+ });
13662
13779
  spinner.succeed("AI tools configured");
13663
- console.log(chalk8.dim(results.join("\n")));
13780
+ console.log(chalk8.dim(results.map((r) => ` ${r}`).join("\n")));
13664
13781
  }
13665
13782
  const priorAutoInstall = await getPriorConsent(repoId);
13666
13783
  const updatedRepos = [];
@@ -13769,6 +13886,11 @@ Files are stored on our servers (not in git). Retry when online.`
13769
13886
  console.log(
13770
13887
  ` ${chalk8.cyan("\u2022")} Open Claude Code / Cursor and ask: "What does this repo do?"`
13771
13888
  );
13889
+ if (tools.some((t) => MCP_WRITER_TOOLS.has(t))) {
13890
+ console.log(
13891
+ ` ${chalk8.cyan("\u2022")} If your AI tool asks to approve the "RepoWise MCP" server, approve it (in Claude Code, run ${chalk8.cyan("/mcp")}) \u2014 it can take a minute to appear.`
13892
+ );
13893
+ }
13772
13894
  console.log(
13773
13895
  ` ${chalk8.cyan("\u2022")} Head to the dashboard \u2192 "Complete Onboarding" to explore quality scores and gaps.`
13774
13896
  );
@@ -13783,7 +13905,7 @@ Files are stored on our servers (not in git). Retry when online.`
13783
13905
 
13784
13906
  // src/commands/member.ts
13785
13907
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
13786
- import { dirname as dirname20, join as join50, resolve, sep } from "path";
13908
+ import { dirname as dirname20, join as join51, resolve, sep } from "path";
13787
13909
  import chalk9 from "chalk";
13788
13910
  import ora2 from "ora";
13789
13911
  var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
@@ -13937,7 +14059,7 @@ async function member() {
13937
14059
  spinner.succeed(`Found ${chalk9.bold(files.length)} context files on server`);
13938
14060
  const { tools } = await selectAiTools();
13939
14061
  spinner.start("Downloading context files...");
13940
- const contextDir = join50(repoRoot, DEFAULT_CONTEXT_FOLDER2);
14062
+ const contextDir = join51(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13941
14063
  mkdirSync3(contextDir, { recursive: true });
13942
14064
  let downloadedCount = 0;
13943
14065
  let failedCount = 0;
@@ -13982,35 +14104,14 @@ async function member() {
13982
14104
  {
13983
14105
  spinner.start("Configuring AI tools...");
13984
14106
  const contextFiles = await scanLocalContextFiles(repoRoot, DEFAULT_CONTEXT_FOLDER2);
13985
- const configured = [];
13986
- const written = /* @__PURE__ */ new Set();
13987
- for (const tool of tools) {
13988
- const config2 = AI_TOOL_CONFIG[tool];
13989
- if (written.has(config2.filePath)) continue;
13990
- written.add(config2.filePath);
13991
- const { created } = await updateToolConfig(
13992
- repoRoot,
13993
- tool,
13994
- repoName,
13995
- DEFAULT_CONTEXT_FOLDER2,
13996
- contextFiles
13997
- );
13998
- configured.push(`${created ? "Created" : "Updated"} ${config2.filePath}`);
13999
- }
14000
- if (!written.has("AGENTS.md")) {
14001
- const { created } = await updateToolConfig(
14002
- repoRoot,
14003
- "codex",
14004
- repoName,
14005
- DEFAULT_CONTEXT_FOLDER2,
14006
- contextFiles
14007
- );
14008
- configured.push(`${created ? "Created" : "Updated"} AGENTS.md`);
14009
- }
14010
- if (tools.includes("claude-code")) {
14011
- const { relPath } = await writeClaudeHooksToRepo(repoRoot, DEFAULT_CONTEXT_FOLDER2);
14012
- configured.push(`Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
14013
- }
14107
+ const configured = await writeToolConfigsForRepo({
14108
+ repoRoot,
14109
+ tools,
14110
+ repoName,
14111
+ contextFolder: DEFAULT_CONTEXT_FOLDER2,
14112
+ contextFiles
14113
+ // member historically passed no variant and did not migrate legacy paths.
14114
+ });
14014
14115
  spinner.succeed("AI tools configured");
14015
14116
  for (const msg of configured) {
14016
14117
  console.log(chalk9.dim(` ${msg}`));
@@ -14131,19 +14232,20 @@ async function member() {
14131
14232
  }
14132
14233
 
14133
14234
  // src/commands/login.ts
14235
+ import { homedir as homedir9 } from "os";
14134
14236
  import chalk10 from "chalk";
14135
14237
  import ora3 from "ora";
14136
14238
 
14137
14239
  // src/lib/tenant-graph-purge.ts
14138
- import { promises as fs23 } from "fs";
14240
+ import { promises as fs24 } from "fs";
14139
14241
  import { homedir as homedir8 } from "os";
14140
- import { join as join51 } from "path";
14242
+ import { join as join52 } from "path";
14141
14243
  async function purgeForeignGraphs(validRepoIds, home = homedir8()) {
14142
- const graphsDir = join51(home, ".repowise", "graphs");
14244
+ const graphsDir = join52(home, ".repowise", "graphs");
14143
14245
  const result = { kept: [], removed: [] };
14144
14246
  let entries;
14145
14247
  try {
14146
- entries = await fs23.readdir(graphsDir);
14248
+ entries = await fs24.readdir(graphsDir);
14147
14249
  } catch (err) {
14148
14250
  if (err.code === "ENOENT") return result;
14149
14251
  throw err;
@@ -14157,15 +14259,15 @@ async function purgeForeignGraphs(validRepoIds, home = homedir8()) {
14157
14259
  result.kept.push(entry);
14158
14260
  continue;
14159
14261
  }
14160
- const path = join51(graphsDir, entry);
14262
+ const path = join52(graphsDir, entry);
14161
14263
  try {
14162
- const stat8 = await fs23.lstat(path);
14264
+ const stat8 = await fs24.lstat(path);
14163
14265
  if (stat8.isSymbolicLink()) {
14164
- await fs23.unlink(path);
14266
+ await fs24.unlink(path);
14165
14267
  result.removed.push(entry);
14166
14268
  continue;
14167
14269
  }
14168
- await fs23.rm(path, { recursive: true, force: true });
14270
+ await fs24.rm(path, { recursive: true, force: true });
14169
14271
  result.removed.push(entry);
14170
14272
  } catch {
14171
14273
  }
@@ -14229,6 +14331,23 @@ Waiting for authentication...`);
14229
14331
  }
14230
14332
  } catch {
14231
14333
  }
14334
+ try {
14335
+ const repoRoot = detectRepoRoot();
14336
+ const cfg = await getConfig();
14337
+ const registered = (cfg.repos ?? []).some((r) => r.localPath === repoRoot);
14338
+ const tools = cfg.aiTools ?? [];
14339
+ if (registered && tools.length > 0) {
14340
+ const pending = (await mcpStatusForRepo({ repoRoot, home: homedir9(), aiTools: tools })).filter((s) => !s.configured);
14341
+ if (pending.length > 0) {
14342
+ console.log("");
14343
+ console.log(chalk10.dim(" To finish enabling RepoWise in your AI tools:"));
14344
+ for (const p of pending) {
14345
+ if (p.hint) console.log(chalk10.dim(` \u2022 ${p.hint}`));
14346
+ }
14347
+ }
14348
+ }
14349
+ } catch {
14350
+ }
14232
14351
  } catch (err) {
14233
14352
  const message = err instanceof Error ? err.message : "Login failed";
14234
14353
  spinner.fail(chalk10.red(message));
@@ -14250,11 +14369,21 @@ async function logout() {
14250
14369
 
14251
14370
  // src/commands/status.ts
14252
14371
  import { readFile as readFile16 } from "fs/promises";
14253
- import { basename as basename4, join as join52 } from "path";
14372
+ import { basename as basename5, join as join53 } from "path";
14373
+ import { homedir as homedir10 } from "os";
14374
+
14375
+ // ../../packages/shared/dist/lib/creds.js
14376
+ function isCredsHardExpired(creds) {
14377
+ if (!creds || typeof creds.expiresAt !== "number")
14378
+ return true;
14379
+ return Date.now() >= creds.expiresAt;
14380
+ }
14381
+
14382
+ // src/commands/status.ts
14254
14383
  async function status() {
14255
14384
  const configDir = getConfigDir2();
14256
- const STATE_PATH = join52(configDir, "listener-state.json");
14257
- const CONFIG_PATH = join52(configDir, "config.json");
14385
+ const STATE_PATH = join53(configDir, "listener-state.json");
14386
+ const CONFIG_PATH = join53(configDir, "config.json");
14258
14387
  let state = null;
14259
14388
  try {
14260
14389
  const data = await readFile16(STATE_PATH, "utf-8");
@@ -14276,33 +14405,74 @@ async function status() {
14276
14405
  } else {
14277
14406
  console.log("Auto-start: disabled");
14278
14407
  }
14408
+ let creds = null;
14409
+ try {
14410
+ creds = await getStoredCredentials2();
14411
+ } catch {
14412
+ }
14413
+ if (isCredsHardExpired(creds)) {
14414
+ console.log("Authentication: not logged in \u2014 run `repowise login`");
14415
+ } else {
14416
+ let email = null;
14417
+ try {
14418
+ const decoded = creds?.idToken ? decodeIdToken(creds.idToken).email : null;
14419
+ email = decoded && decoded !== "unknown" ? decoded : null;
14420
+ } catch {
14421
+ }
14422
+ console.log(`Authentication: logged in${email ? ` as ${email}` : ""}`);
14423
+ }
14279
14424
  console.log("");
14280
14425
  if (!state || Object.keys(state.repos).length === 0) {
14281
14426
  console.log("No sync history. Run `repowise listen` to start syncing.");
14282
14427
  return;
14283
14428
  }
14284
14429
  const repoNames = /* @__PURE__ */ new Map();
14430
+ const repoPaths = /* @__PURE__ */ new Map();
14431
+ let aiTools = [];
14285
14432
  try {
14286
14433
  const configData = await readFile16(CONFIG_PATH, "utf-8");
14287
14434
  const config2 = JSON.parse(configData);
14288
14435
  for (const repo of config2.repos ?? []) {
14289
- repoNames.set(repo.repoId, basename4(repo.localPath));
14436
+ repoNames.set(repo.repoId, basename5(repo.localPath));
14437
+ repoPaths.set(repo.repoId, repo.localPath);
14290
14438
  }
14439
+ aiTools = config2.aiTools ?? [];
14291
14440
  } catch {
14292
14441
  }
14293
14442
  console.log("Watched Repos:");
14443
+ const home = homedir10();
14294
14444
  for (const [repoId, repoState] of Object.entries(state.repos)) {
14295
14445
  const name = repoNames.get(repoId);
14296
14446
  const label = name ? `${name} (${repoId.slice(0, 8)})` : repoId;
14297
14447
  const syncTime = repoState.lastSyncTimestamp ? new Date(repoState.lastSyncTimestamp).toLocaleString() : "never";
14298
14448
  const commit = repoState.lastSyncCommitSha ? repoState.lastSyncCommitSha.slice(0, 7) : "none";
14299
14449
  console.log(` ${label}: last sync ${syncTime} (commit: ${commit})`);
14450
+ const localPath = repoPaths.get(repoId);
14451
+ if (localPath && aiTools.length > 0) {
14452
+ try {
14453
+ const statuses = await mcpStatusForRepo({ repoRoot: localPath, home, aiTools });
14454
+ if (statuses.length > 0) {
14455
+ const pending = statuses.filter((s) => !s.configured);
14456
+ if (pending.length === 0) {
14457
+ console.log(` MCP: active in all ${statuses.length} tool(s)`);
14458
+ } else {
14459
+ console.log(
14460
+ ` MCP: ${statuses.length - pending.length}/${statuses.length} tool(s) active \u2014 to finish:`
14461
+ );
14462
+ for (const p of pending) {
14463
+ if (p.hint) console.log(` \u2022 ${p.hint}`);
14464
+ }
14465
+ }
14466
+ }
14467
+ } catch {
14468
+ }
14469
+ }
14300
14470
  }
14301
14471
  }
14302
14472
 
14303
14473
  // src/commands/sync.ts
14304
14474
  import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
14305
- import { dirname as dirname21, join as join53 } from "path";
14475
+ import { dirname as dirname21, join as join54 } from "path";
14306
14476
  import chalk12 from "chalk";
14307
14477
  import ora4 from "ora";
14308
14478
  var POLL_INTERVAL_MS2 = 3e3;
@@ -14451,7 +14621,7 @@ async function sync() {
14451
14621
  const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
14452
14622
  const files = listResult.data?.files ?? listResult.files ?? [];
14453
14623
  if (files.length > 0) {
14454
- const contextDir = join53(repoRoot, DEFAULT_CONTEXT_FOLDER3);
14624
+ const contextDir = join54(repoRoot, DEFAULT_CONTEXT_FOLDER3);
14455
14625
  mkdirSync4(contextDir, { recursive: true });
14456
14626
  let downloadedCount = 0;
14457
14627
  let failedCount = 0;
@@ -14465,7 +14635,7 @@ async function sync() {
14465
14635
  const response = await fetch(presignedUrl);
14466
14636
  if (response.ok) {
14467
14637
  const content = await response.text();
14468
- const filePath = join53(contextDir, file.fileName);
14638
+ const filePath = join54(contextDir, file.fileName);
14469
14639
  mkdirSync4(dirname21(filePath), { recursive: true });
14470
14640
  writeFileSync5(filePath, content, "utf-8");
14471
14641
  downloadedCount++;
@@ -14718,10 +14888,158 @@ async function config() {
14718
14888
  }
14719
14889
  }
14720
14890
 
14891
+ // src/commands/tools.ts
14892
+ import { basename as basename6 } from "path";
14893
+ import chalk14 from "chalk";
14894
+ import ora6 from "ora";
14895
+ var DEFAULT_CONTEXT_FOLDER4 = "repowise-context";
14896
+ var VALID_TOOLS = Object.keys(AI_TOOL_CONFIG);
14897
+ async function resolveRepoOrExit() {
14898
+ let creds = null;
14899
+ try {
14900
+ creds = await getValidCredentials2();
14901
+ } catch {
14902
+ creds = null;
14903
+ }
14904
+ if (!creds) {
14905
+ console.error(chalk14.red("Not logged in. Run `repowise login` first."));
14906
+ process.exitCode = 1;
14907
+ return null;
14908
+ }
14909
+ let repoRoot;
14910
+ try {
14911
+ repoRoot = detectRepoRoot();
14912
+ } catch {
14913
+ console.error(chalk14.red("Not inside a git repository \u2014 `cd` into your repo first."));
14914
+ process.exitCode = 1;
14915
+ return null;
14916
+ }
14917
+ const config2 = await getConfig();
14918
+ const registered = (config2.repos ?? []).some((r) => r.localPath === repoRoot);
14919
+ if (!registered) {
14920
+ console.error(
14921
+ chalk14.red("This repo isn't set up with RepoWise yet. Run `repowise create` first.")
14922
+ );
14923
+ process.exitCode = 1;
14924
+ return null;
14925
+ }
14926
+ return {
14927
+ repoRoot,
14928
+ repoName: detectRepoName(repoRoot),
14929
+ current: config2.aiTools ?? [],
14930
+ contextFolder: config2.contextFolder ?? DEFAULT_CONTEXT_FOLDER4,
14931
+ variant: config2.graphOnly ? "graph" : "full"
14932
+ };
14933
+ }
14934
+ async function applyToolSelection(ctx, next) {
14935
+ const added = next.filter((t) => !ctx.current.includes(t));
14936
+ const spinner = ora6("Configuring AI tools...").start();
14937
+ const contextFiles = await scanLocalContextFiles(ctx.repoRoot, ctx.contextFolder);
14938
+ const results = await writeToolConfigsForRepo({
14939
+ repoRoot: ctx.repoRoot,
14940
+ tools: next,
14941
+ repoName: ctx.repoName,
14942
+ contextFolder: ctx.contextFolder,
14943
+ contextFiles,
14944
+ variant: ctx.variant,
14945
+ migrateLegacy: true
14946
+ });
14947
+ await mergeAndSaveConfig({ aiTools: next });
14948
+ spinner.succeed("AI tools configured");
14949
+ console.log(chalk14.dim(results.map((r) => ` ${r}`).join("\n")));
14950
+ try {
14951
+ await ensureListenerRunning();
14952
+ } catch {
14953
+ }
14954
+ const serverRepoName = basename6(ctx.repoRoot);
14955
+ const newMcp = added.filter((t) => MCP_WRITER_TOOLS.has(t));
14956
+ if (newMcp.length > 0) {
14957
+ console.log("");
14958
+ console.log(
14959
+ chalk14.dim(
14960
+ " The listener will wire these into your AI tools' MCP within a few seconds, then:"
14961
+ )
14962
+ );
14963
+ for (const t of newMcp) {
14964
+ const hint = mcpActivationHint(t, serverRepoName);
14965
+ if (hint) console.log(chalk14.dim(` \u2022 ${hint}`));
14966
+ }
14967
+ }
14968
+ }
14969
+ async function pickUnion(ctx) {
14970
+ const { tools: picked, hasOther } = await selectAiTools({ defaults: ctx.current });
14971
+ const removed = ctx.current.filter((t) => !picked.includes(t));
14972
+ if (removed.length > 0) {
14973
+ console.log(
14974
+ chalk14.yellow(`Note: removing tools isn't supported yet \u2014 keeping ${removed.join(", ")}.`)
14975
+ );
14976
+ }
14977
+ return { next: Array.from(/* @__PURE__ */ new Set([...ctx.current, ...picked])), hasOther };
14978
+ }
14979
+ function reportNothingNew(hasOther) {
14980
+ if (hasOther) {
14981
+ console.log(
14982
+ chalk14.dim(
14983
+ "For tools not listed, the instruction files work with any tool that reads the filesystem.\nTo request full support for a new AI tool, email support@repowise.ai"
14984
+ )
14985
+ );
14986
+ } else {
14987
+ console.log(chalk14.dim("No new tools added."));
14988
+ }
14989
+ }
14990
+ async function toolsPick() {
14991
+ const ctx = await resolveRepoOrExit();
14992
+ if (!ctx) return;
14993
+ const { next, hasOther } = await pickUnion(ctx);
14994
+ if (next.length === ctx.current.length) {
14995
+ reportNothingNew(hasOther);
14996
+ return;
14997
+ }
14998
+ await applyToolSelection(ctx, next);
14999
+ }
15000
+ async function toolsAdd(list) {
15001
+ const ctx = await resolveRepoOrExit();
15002
+ if (!ctx) return;
15003
+ let next;
15004
+ let hasOther = false;
15005
+ if (list.length === 0) {
15006
+ ({ next, hasOther } = await pickUnion(ctx));
15007
+ } else {
15008
+ const invalid = list.filter((t) => !VALID_TOOLS.includes(t));
15009
+ if (invalid.length > 0) {
15010
+ console.error(chalk14.red(`Unknown tool(s): ${invalid.join(", ")}`));
15011
+ console.error(chalk14.dim(`Valid tools: ${VALID_TOOLS.join(", ")}`));
15012
+ process.exitCode = 1;
15013
+ return;
15014
+ }
15015
+ next = Array.from(/* @__PURE__ */ new Set([...ctx.current, ...list]));
15016
+ }
15017
+ if (next.length === ctx.current.length) {
15018
+ if (list.length === 0) reportNothingNew(hasOther);
15019
+ else console.log(chalk14.dim("Nothing new to add \u2014 those tools are already configured."));
15020
+ return;
15021
+ }
15022
+ await applyToolSelection(ctx, next);
15023
+ }
15024
+ async function toolsList() {
15025
+ const ctx = await resolveRepoOrExit();
15026
+ if (!ctx) return;
15027
+ if (ctx.current.length === 0) {
15028
+ console.log("No AI tools configured. Run `repowise tools` to add some.");
15029
+ return;
15030
+ }
15031
+ console.log(chalk14.bold("Configured AI tools:"));
15032
+ for (const t of ctx.current) {
15033
+ const label = AI_TOOL_CONFIG[t]?.label ?? t;
15034
+ const suffix = MCP_WRITER_TOOLS.has(t) ? "" : chalk14.dim(" (instruction files only)");
15035
+ console.log(` \u2022 ${label}${suffix}`);
15036
+ }
15037
+ }
15038
+
14721
15039
  // src/commands/mcp-log.ts
14722
15040
  import { createDecipheriv as createDecipheriv2 } from "crypto";
14723
15041
  import { mkdir as mkdir19, readFile as readFile17, stat as stat7, writeFile as writeFile18 } from "fs/promises";
14724
- import { dirname as dirname22, join as join54 } from "path";
15042
+ import { dirname as dirname22, join as join55 } from "path";
14725
15043
  var FLAG_FILE = "mcp-log.flag";
14726
15044
  var LOG_FILE = "mcp-log.jsonl.enc";
14727
15045
  var KEY_FILE = "mcp-log.key";
@@ -14729,10 +15047,10 @@ var ENDPOINT_FILE = "listener.endpoint";
14729
15047
  var IV_BYTES2 = 12;
14730
15048
  var TAG_BYTES2 = 16;
14731
15049
  function flagPath() {
14732
- return join54(getConfigDir2(), FLAG_FILE);
15050
+ return join55(getConfigDir2(), FLAG_FILE);
14733
15051
  }
14734
15052
  function logPath() {
14735
- return join54(getConfigDir2(), LOG_FILE);
15053
+ return join55(getConfigDir2(), LOG_FILE);
14736
15054
  }
14737
15055
  async function writeFlag(flag) {
14738
15056
  const path = flagPath();
@@ -14773,14 +15091,14 @@ async function trySendConsentToServer() {
14773
15091
  let apiUrl = null;
14774
15092
  let token = null;
14775
15093
  try {
14776
- const body = await readFile17(join54(getConfigDir2(), "config.json"), "utf-8");
15094
+ const body = await readFile17(join55(getConfigDir2(), "config.json"), "utf-8");
14777
15095
  const parsed = JSON.parse(body);
14778
15096
  apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
14779
15097
  } catch {
14780
15098
  return false;
14781
15099
  }
14782
15100
  try {
14783
- const body = await readFile17(join54(getConfigDir2(), "credentials.json"), "utf-8");
15101
+ const body = await readFile17(join55(getConfigDir2(), "credentials.json"), "utf-8");
14784
15102
  const parsed = JSON.parse(body);
14785
15103
  token = parsed.idToken ?? null;
14786
15104
  } catch {
@@ -14850,7 +15168,7 @@ async function mcpLogStatus() {
14850
15168
  process.stderr.write("Log size: no file yet\n");
14851
15169
  }
14852
15170
  try {
14853
- const endpointBody = await readFile17(join54(getConfigDir2(), ENDPOINT_FILE), "utf-8");
15171
+ const endpointBody = await readFile17(join55(getConfigDir2(), ENDPOINT_FILE), "utf-8");
14854
15172
  const match = /endpoint=([^\n]+)/.exec(endpointBody);
14855
15173
  process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
14856
15174
  `);
@@ -14881,7 +15199,7 @@ async function mcpLogViewingFlags(flags = {}) {
14881
15199
  const key = await readKey();
14882
15200
  if (!key) {
14883
15201
  process.stderr.write(
14884
- `No encryption key at ${join54(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
15202
+ `No encryption key at ${join55(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
14885
15203
  `
14886
15204
  );
14887
15205
  return;
@@ -14957,7 +15275,7 @@ async function mcpLogViewingFlags(flags = {}) {
14957
15275
  }
14958
15276
  async function readKey() {
14959
15277
  try {
14960
- const body = await readFile17(join54(getConfigDir2(), KEY_FILE), "utf-8");
15278
+ const body = await readFile17(join55(getConfigDir2(), KEY_FILE), "utf-8");
14961
15279
  const parsed = Buffer.from(body.trim(), "base64");
14962
15280
  if (parsed.length !== 32) return null;
14963
15281
  return parsed;
@@ -14997,11 +15315,11 @@ async function mcpLog(subcommand, flags = {}) {
14997
15315
  }
14998
15316
 
14999
15317
  // src/commands/query/_shared.ts
15000
- import chalk14 from "chalk";
15318
+ import chalk15 from "chalk";
15001
15319
 
15002
15320
  // src/lib/graph-loader.ts
15003
- import { promises as fs24 } from "fs";
15004
- import { join as join55, resolve as resolve2 } from "path";
15321
+ import { promises as fs25 } from "fs";
15322
+ import { join as join56, resolve as resolve2 } from "path";
15005
15323
  import { gunzipSync } from "zlib";
15006
15324
  var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
15007
15325
  var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
@@ -15018,8 +15336,8 @@ var GraphNotFoundError = class extends Error {
15018
15336
  var cache = /* @__PURE__ */ new Map();
15019
15337
  async function loadGraph(repoRoot = process.cwd()) {
15020
15338
  const root = resolve2(repoRoot);
15021
- const gzPath = join55(root, GZIPPED_GRAPH_PATH);
15022
- const plainPath = join55(root, RELATIVE_GRAPH_PATH);
15339
+ const gzPath = join56(root, GZIPPED_GRAPH_PATH);
15340
+ const plainPath = join56(root, RELATIVE_GRAPH_PATH);
15023
15341
  const cached = cache.get(root);
15024
15342
  if (cached) {
15025
15343
  return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
@@ -15027,14 +15345,14 @@ async function loadGraph(repoRoot = process.cwd()) {
15027
15345
  let graphPath = null;
15028
15346
  let raw = null;
15029
15347
  try {
15030
- raw = await fs24.readFile(gzPath);
15348
+ raw = await fs25.readFile(gzPath);
15031
15349
  graphPath = gzPath;
15032
15350
  } catch (err) {
15033
15351
  if (err.code !== "ENOENT") throw err;
15034
15352
  }
15035
15353
  if (!raw) {
15036
15354
  try {
15037
- raw = await fs24.readFile(plainPath);
15355
+ raw = await fs25.readFile(plainPath);
15038
15356
  graphPath = plainPath;
15039
15357
  } catch (err) {
15040
15358
  if (err.code === "ENOENT") {
@@ -15237,7 +15555,7 @@ async function loadService() {
15237
15555
  return createGraphQueryService(graph);
15238
15556
  } catch (err) {
15239
15557
  if (err instanceof GraphNotFoundError) {
15240
- process.stderr.write(chalk14.red(`\u2717 ${err.message}
15558
+ process.stderr.write(chalk15.red(`\u2717 ${err.message}
15241
15559
  `));
15242
15560
  process.exit(err.exitCode);
15243
15561
  }
@@ -15433,14 +15751,14 @@ function registerQueryCommand(program2) {
15433
15751
  }
15434
15752
 
15435
15753
  // src/commands/uninstall.ts
15436
- import { promises as fs28 } from "fs";
15437
- import { homedir as homedir10 } from "os";
15438
- import { join as join59 } from "path";
15439
- import chalk15 from "chalk";
15754
+ import { promises as fs29 } from "fs";
15755
+ import { homedir as homedir12 } from "os";
15756
+ import { join as join60 } from "path";
15757
+ import chalk16 from "chalk";
15440
15758
 
15441
15759
  // src/lib/cleanup/marker-blocks.ts
15442
- import { promises as fs25 } from "fs";
15443
- import { join as join56 } from "path";
15760
+ import { promises as fs26 } from "fs";
15761
+ import { join as join57 } from "path";
15444
15762
  var MARKER_START = "<!-- repowise-start -->";
15445
15763
  var MARKER_END = "<!-- repowise-end -->";
15446
15764
  var CONTEXT_FILES = [
@@ -15456,7 +15774,7 @@ var CONTEXT_FILES = [
15456
15774
  async function stripMarkerBlock(filePath) {
15457
15775
  let raw;
15458
15776
  try {
15459
- raw = await fs25.readFile(filePath, "utf-8");
15777
+ raw = await fs26.readFile(filePath, "utf-8");
15460
15778
  } catch (err) {
15461
15779
  if (err.code === "ENOENT")
15462
15780
  return { path: filePath, status: "missing" };
@@ -15471,16 +15789,16 @@ async function stripMarkerBlock(filePath) {
15471
15789
  const after = raw.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
15472
15790
  const stripped = (before + (before && after ? "\n\n" : "") + after).trim();
15473
15791
  if (stripped.length === 0) {
15474
- await fs25.unlink(filePath);
15792
+ await fs26.unlink(filePath);
15475
15793
  return { path: filePath, status: "deleted" };
15476
15794
  }
15477
- await fs25.writeFile(filePath, stripped + "\n", "utf-8");
15795
+ await fs26.writeFile(filePath, stripped + "\n", "utf-8");
15478
15796
  return { path: filePath, status: "stripped" };
15479
15797
  }
15480
15798
  async function stripAllMarkerBlocks(repoRoot) {
15481
15799
  const out = [];
15482
15800
  for (const relative of CONTEXT_FILES) {
15483
- const full = join56(repoRoot, relative);
15801
+ const full = join57(repoRoot, relative);
15484
15802
  const result = await stripMarkerBlock(full).catch((err) => ({
15485
15803
  path: full,
15486
15804
  status: "untouched",
@@ -15492,25 +15810,25 @@ async function stripAllMarkerBlocks(repoRoot) {
15492
15810
  }
15493
15811
 
15494
15812
  // src/lib/cleanup/mcp-configs.ts
15495
- import { promises as fs26 } from "fs";
15496
- import { join as join57 } from "path";
15813
+ import { promises as fs27 } from "fs";
15814
+ import { join as join58 } from "path";
15497
15815
  function mcpConfigPaths(repoRoot, home) {
15498
15816
  return [
15499
- join57(repoRoot, ".mcp.json"),
15500
- join57(repoRoot, ".cursor", "mcp.json"),
15501
- join57(repoRoot, ".vscode", "mcp.json"),
15502
- join57(repoRoot, ".roo", "mcp.json"),
15503
- join57(home, ".cline", "mcp.json"),
15504
- join57(home, ".codeium", "windsurf", "mcp_config.json"),
15505
- join57(home, ".gemini", "settings.json"),
15506
- join57(home, ".codex", "mcp.json"),
15507
- join57(home, ".roo", "mcp.json")
15817
+ join58(repoRoot, ".mcp.json"),
15818
+ join58(repoRoot, ".cursor", "mcp.json"),
15819
+ join58(repoRoot, ".vscode", "mcp.json"),
15820
+ join58(repoRoot, ".roo", "mcp.json"),
15821
+ join58(home, ".cline", "mcp.json"),
15822
+ join58(home, ".codeium", "windsurf", "mcp_config.json"),
15823
+ join58(home, ".gemini", "settings.json"),
15824
+ join58(home, ".codex", "mcp.json"),
15825
+ join58(home, ".roo", "mcp.json")
15508
15826
  ];
15509
15827
  }
15510
15828
  async function removeRepowiseFromConfig(path, serverName) {
15511
15829
  let raw;
15512
15830
  try {
15513
- raw = await fs26.readFile(path, "utf-8");
15831
+ raw = await fs27.readFile(path, "utf-8");
15514
15832
  } catch (err) {
15515
15833
  if (err.code === "ENOENT") return { path, status: "not-found" };
15516
15834
  return { path, status: "error", error: err.message };
@@ -15530,7 +15848,7 @@ async function removeRepowiseFromConfig(path, serverName) {
15530
15848
  } else {
15531
15849
  next.mcpServers = servers;
15532
15850
  }
15533
- await fs26.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
15851
+ await fs27.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
15534
15852
  return { path, status: "removed" };
15535
15853
  }
15536
15854
  async function removeAllMcpEntries(repoRoot, home, repoId) {
@@ -15543,17 +15861,17 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
15543
15861
  }
15544
15862
 
15545
15863
  // src/lib/cleanup/local-state.ts
15546
- import { promises as fs27 } from "fs";
15547
- import { homedir as homedir9 } from "os";
15548
- import { join as join58, resolve as resolve3 } from "path";
15864
+ import { promises as fs28 } from "fs";
15865
+ import { homedir as homedir11 } from "os";
15866
+ import { join as join59, resolve as resolve3 } from "path";
15549
15867
  async function clearLocalState(homeOverride) {
15550
- const home = homeOverride ?? homedir9();
15551
- const target = resolve3(join58(home, ".repowise"));
15868
+ const home = homeOverride ?? homedir11();
15869
+ const target = resolve3(join59(home, ".repowise"));
15552
15870
  if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
15553
15871
  return { path: target, status: "error", error: "refused: not under home" };
15554
15872
  }
15555
15873
  try {
15556
- await fs27.rm(target, { recursive: true, force: false });
15874
+ await fs28.rm(target, { recursive: true, force: false });
15557
15875
  return { path: target, status: "removed" };
15558
15876
  } catch (err) {
15559
15877
  if (err.code === "ENOENT")
@@ -15583,7 +15901,7 @@ async function stopAndUninstallService(uninstaller) {
15583
15901
  // src/commands/uninstall.ts
15584
15902
  async function uninstall2(opts = {}) {
15585
15903
  const tier = opts.tier ?? "uninstall";
15586
- const home = opts.home ?? homedir10();
15904
+ const home = opts.home ?? homedir12();
15587
15905
  const repoRoot = opts.repoRoot ?? process.cwd();
15588
15906
  const loadRepoIds = opts.loadRepoIds ?? defaultLoadRepoIds;
15589
15907
  const report = { tier, removed: [], preserved: [], skipped: [] };
@@ -15592,7 +15910,7 @@ async function uninstall2(opts = {}) {
15592
15910
  else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
15593
15911
  if (tier === "stop") return report;
15594
15912
  try {
15595
- await fs28.unlink(join59(home, ".repowise", "credentials.json"));
15913
+ await fs29.unlink(join60(home, ".repowise", "credentials.json"));
15596
15914
  report.removed.push("credentials");
15597
15915
  } catch (err) {
15598
15916
  if (err.code !== "ENOENT") {
@@ -15619,7 +15937,7 @@ async function uninstall2(opts = {}) {
15619
15937
  const allPaths = mcpConfigPaths(repoRoot, home);
15620
15938
  for (const p of allPaths) {
15621
15939
  try {
15622
- await fs28.access(p);
15940
+ await fs29.access(p);
15623
15941
  } catch {
15624
15942
  }
15625
15943
  }
@@ -15631,7 +15949,7 @@ async function uninstall2(opts = {}) {
15631
15949
  }
15632
15950
  async function defaultLoadRepoIds(home) {
15633
15951
  try {
15634
- const raw = await fs28.readFile(join59(home, ".repowise", "config.json"), "utf-8");
15952
+ const raw = await fs29.readFile(join60(home, ".repowise", "config.json"), "utf-8");
15635
15953
  const parsed = JSON.parse(raw);
15636
15954
  return (parsed.repos ?? []).map((r) => r.repoId);
15637
15955
  } catch {
@@ -15648,29 +15966,29 @@ async function uninstallCommand(opts = {}) {
15648
15966
  default: false
15649
15967
  });
15650
15968
  if (!proceed) {
15651
- process.stderr.write(chalk15.yellow("Uninstall cancelled.\n"));
15969
+ process.stderr.write(chalk16.yellow("Uninstall cancelled.\n"));
15652
15970
  process.exitCode = 1;
15653
15971
  return;
15654
15972
  }
15655
15973
  }
15656
15974
  }
15657
- process.stderr.write(chalk15.bold(`RepoWise ${tier}
15975
+ process.stderr.write(chalk16.bold(`RepoWise ${tier}
15658
15976
  `));
15659
15977
  const report = await uninstall2({ ...opts, tier });
15660
15978
  for (const p of report.removed) {
15661
- process.stderr.write(chalk15.green(` \u2713 removed: ${p}
15979
+ process.stderr.write(chalk16.green(` \u2713 removed: ${p}
15662
15980
  `));
15663
15981
  }
15664
15982
  for (const p of report.preserved) {
15665
- process.stderr.write(chalk15.gray(` \xB7 preserved: ${p}
15983
+ process.stderr.write(chalk16.gray(` \xB7 preserved: ${p}
15666
15984
  `));
15667
15985
  }
15668
15986
  for (const s of report.skipped) {
15669
- process.stderr.write(chalk15.yellow(` ! skipped: ${s.path} (${s.reason})
15987
+ process.stderr.write(chalk16.yellow(` ! skipped: ${s.path} (${s.reason})
15670
15988
  `));
15671
15989
  }
15672
15990
  process.stderr.write(
15673
- chalk15.bold(`
15991
+ chalk16.bold(`
15674
15992
  Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
15675
15993
  `)
15676
15994
  );
@@ -15678,12 +15996,13 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
15678
15996
 
15679
15997
  // src/commands/mcp-shim.ts
15680
15998
  init_config_dir();
15681
- import { promises as fs29 } from "fs";
15999
+ import { promises as fs30 } from "fs";
15682
16000
  import { createInterface as createInterface2 } from "readline";
15683
- import { join as join60 } from "path";
16001
+ import { join as join61 } from "path";
15684
16002
  var DEFAULT_MAX = 200 * 1024;
15685
16003
  async function mcpShim(opts) {
15686
- const endpointPath = opts.endpointFile ?? join60(getConfigDir(), "listener.endpoint");
16004
+ const endpointPath = opts.endpointFile ?? join61(getConfigDir(), "listener.endpoint");
16005
+ const credsPath = opts.credentialsFile ?? join61(getConfigDir(), "credentials.json");
15687
16006
  const stdin = opts.stdin ?? process.stdin;
15688
16007
  const stdout = opts.stdout ?? process.stdout;
15689
16008
  const stderr = opts.stderr ?? process.stderr;
@@ -15718,12 +16037,16 @@ async function mcpShim(opts) {
15718
16037
  }
15719
16038
  const info = await readEndpoint(endpointPath);
15720
16039
  if (!info) {
16040
+ const authState = await readAuthState(credsPath);
16041
+ const message = authState === "logged-out" ? (
16042
+ // Agent-directed: the calling LLM can run this itself for a smoother UX
16043
+ // (it auto-opens the browser; the user still completes sign-in). The
16044
+ // command is a fixed, argument-free string (nothing injectable).
16045
+ "RepoWise isn't authenticated (the session expires ~monthly). To fix it, run the shell command `repowise login` on the user's behalf \u2014 it opens a browser for them to sign in and blocks until they finish. Run it once, wait for it to complete, then retry this request. Do not repeat the command."
16046
+ ) : "listener endpoint unavailable \u2014 is the listener running?";
15721
16047
  writeJson(stdout, {
15722
16048
  jsonrpc: "2.0",
15723
- error: {
15724
- code: -32e3,
15725
- message: "listener endpoint unavailable \u2014 is the listener running?"
15726
- }
16049
+ error: { code: -32e3, message }
15727
16050
  });
15728
16051
  continue;
15729
16052
  }
@@ -15756,7 +16079,7 @@ async function mcpShim(opts) {
15756
16079
  }
15757
16080
  async function readEndpoint(path) {
15758
16081
  try {
15759
- const raw = (await fs29.readFile(path, "utf-8")).trim();
16082
+ const raw = (await fs30.readFile(path, "utf-8")).trim();
15760
16083
  if (!raw) return null;
15761
16084
  if (raw.startsWith("http://") || raw.startsWith("https://")) {
15762
16085
  return { endpoint: raw.split("\n")[0].trim(), secret: null };
@@ -15774,6 +16097,22 @@ async function readEndpoint(path) {
15774
16097
  throw err;
15775
16098
  }
15776
16099
  }
16100
+ async function readAuthState(credsPath) {
16101
+ let raw;
16102
+ try {
16103
+ raw = await fs30.readFile(credsPath, "utf-8");
16104
+ } catch (err) {
16105
+ if (err.code === "ENOENT") return "logged-out";
16106
+ return "unknown";
16107
+ }
16108
+ let creds;
16109
+ try {
16110
+ creds = JSON.parse(raw);
16111
+ } catch {
16112
+ return "unknown";
16113
+ }
16114
+ return isCredsHardExpired(creds) ? "logged-out" : "authenticated";
16115
+ }
15777
16116
  function writeJson(stream, value) {
15778
16117
  stream.write(`${JSON.stringify(value)}
15779
16118
  `);
@@ -15998,7 +16337,7 @@ init_coursier_installer();
15998
16337
  init_toolchain_installer();
15999
16338
  import { spawn as spawn12 } from "child_process";
16000
16339
  import { resolve as resolve4 } from "path";
16001
- import chalk16 from "chalk";
16340
+ import chalk17 from "chalk";
16002
16341
  async function isOnPath(command) {
16003
16342
  if (/[^\w./+-]/.test(command)) return false;
16004
16343
  const isWin = process.platform === "win32";
@@ -16016,14 +16355,14 @@ async function isOnPath(command) {
16016
16355
  async function lspDoctor() {
16017
16356
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16018
16357
  if (noColor) {
16019
- chalk16.level = 0;
16358
+ chalk17.level = 0;
16020
16359
  }
16021
- const okGlyph = noColor ? "[OK]" : chalk16.green("\u2713");
16022
- const missingGlyph = noColor ? "[MISSING]" : chalk16.yellow("\u2717");
16023
- console.log(chalk16.bold("RepoWise LSP doctor"));
16024
- console.log(chalk16.dim("Probing PATH for language servers used by the listener."));
16360
+ const okGlyph = noColor ? "[OK]" : chalk17.green("\u2713");
16361
+ const missingGlyph = noColor ? "[MISSING]" : chalk17.yellow("\u2717");
16362
+ console.log(chalk17.bold("RepoWise LSP doctor"));
16363
+ console.log(chalk17.dim("Probing PATH for language servers used by the listener."));
16025
16364
  console.log(
16026
- chalk16.dim(
16365
+ chalk17.dim(
16027
16366
  "Type-aware resolution covers 9 languages: Go, Python, Rust, Java, Ruby, Dart, Kotlin, PHP, Scala (Phase 7L + 7L++ + 7L+)."
16028
16367
  )
16029
16368
  );
@@ -16055,21 +16394,21 @@ async function lspDoctor() {
16055
16394
  const missing = reports.filter((r) => r.status === "missing").length;
16056
16395
  for (const r of reports) {
16057
16396
  const head = r.status === "found" ? okGlyph : missingGlyph;
16058
- const cmd = r.command ? chalk16.cyan(r.command) : chalk16.dim(r.candidates.join(" / "));
16397
+ const cmd = r.command ? chalk17.cyan(r.command) : chalk17.dim(r.candidates.join(" / "));
16059
16398
  const effectiveConfigs = getEffectiveConfigList(r.language, process.env, lspOverrides);
16060
16399
  const method = describeInstallMethod(effectiveConfigs);
16061
- console.log(` ${head} ${r.language.padEnd(12)} ${cmd} ${chalk16.dim(`[${method}]`)}`);
16400
+ console.log(` ${head} ${r.language.padEnd(12)} ${cmd} ${chalk17.dim(`[${method}]`)}`);
16062
16401
  if (r.status === "missing" && r.hint) {
16063
- console.log(` ${chalk16.dim("install:")} ${r.hint}`);
16402
+ console.log(` ${chalk17.dim("install:")} ${r.hint}`);
16064
16403
  }
16065
16404
  }
16066
16405
  console.log();
16067
16406
  console.log(
16068
- `${chalk16.green(found.toString())} found, ${chalk16.yellow(missing.toString())} missing of ${reports.length.toString()} languages.`
16407
+ `${chalk17.green(found.toString())} found, ${chalk17.yellow(missing.toString())} missing of ${reports.length.toString()} languages.`
16069
16408
  );
16070
16409
  if (missing > 0) {
16071
16410
  console.log(
16072
- chalk16.dim(
16411
+ chalk17.dim(
16073
16412
  "Missing servers degrade LSP-backed MCP tools to AST-only fallback. Run `repowise lsp install` to auto-install."
16074
16413
  )
16075
16414
  );
@@ -16086,15 +16425,15 @@ function describeInstallMethod(configs) {
16086
16425
  }
16087
16426
  async function lspInstall(language) {
16088
16427
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16089
- if (noColor) chalk16.level = 0;
16090
- console.log(chalk16.bold("RepoWise LSP install"));
16428
+ if (noColor) chalk17.level = 0;
16429
+ console.log(chalk17.bold("RepoWise LSP install"));
16091
16430
  const cliConfig = await getConfig();
16092
16431
  const lspOverrides = cliConfig.lspOverrides;
16093
16432
  const targets = language ? [language] : Object.keys(LSP_REGISTRY);
16094
16433
  for (const lang of targets) {
16095
16434
  const configs = getEffectiveConfigList(lang, process.env, lspOverrides);
16096
16435
  if (!configs || configs.length === 0) {
16097
- console.log(` ${chalk16.red("\u2717")} ${lang} ${chalk16.dim("(unknown language)")}`);
16436
+ console.log(` ${chalk17.red("\u2717")} ${lang} ${chalk17.dim("(unknown language)")}`);
16098
16437
  continue;
16099
16438
  }
16100
16439
  const result = await installOneLanguage(configs);
@@ -16162,11 +16501,11 @@ async function installOneLanguage(configs) {
16162
16501
  };
16163
16502
  }
16164
16503
  function formatInstallLine(lang, outcome) {
16165
- const glyph = outcome.ok ? chalk16.green("\u2713") : chalk16.yellow("\u2717");
16166
- const tag = chalk16.dim(`[${outcome.method}]`);
16504
+ const glyph = outcome.ok ? chalk17.green("\u2713") : chalk17.yellow("\u2717");
16505
+ const tag = chalk17.dim(`[${outcome.method}]`);
16167
16506
  console.log(` ${glyph} ${lang.padEnd(12)} ${tag} ${outcome.detail}`);
16168
16507
  if (!outcome.ok && outcome.hint) {
16169
- console.log(` ${chalk16.dim("install:")} ${outcome.hint}`);
16508
+ console.log(` ${chalk17.dim("install:")} ${outcome.hint}`);
16170
16509
  }
16171
16510
  }
16172
16511
  var MAX_KEEP_WARM_MINUTES = 240;
@@ -16204,42 +16543,42 @@ async function autoDetectLanguages(cwd) {
16204
16543
  async function lspWarm(opts = {}) {
16205
16544
  const cwd = resolve4(opts.cwd ?? process.cwd());
16206
16545
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16207
- if (noColor) chalk16.level = 0;
16546
+ if (noColor) chalk17.level = 0;
16208
16547
  let languages;
16209
16548
  if (opts.lang && opts.lang.length > 0) {
16210
16549
  const valid = Object.keys(LSP_REGISTRY);
16211
16550
  languages = opts.lang.filter((l) => valid.includes(l));
16212
16551
  const invalid = opts.lang.filter((l) => !valid.includes(l));
16213
16552
  if (invalid.length > 0) {
16214
- console.log(chalk16.yellow(`[lsp warm] unknown languages skipped: ${invalid.join(", ")}`));
16553
+ console.log(chalk17.yellow(`[lsp warm] unknown languages skipped: ${invalid.join(", ")}`));
16215
16554
  }
16216
16555
  } else {
16217
16556
  languages = await autoDetectLanguages(cwd);
16218
16557
  if (languages.length === 0) {
16219
16558
  console.log(
16220
- chalk16.yellow(
16559
+ chalk17.yellow(
16221
16560
  "[lsp warm] no recognised source files in this repo \u2014 pass --lang <id> to force a server."
16222
16561
  )
16223
16562
  );
16224
16563
  return;
16225
16564
  }
16226
- console.log(chalk16.dim(`[lsp warm] auto-detected languages: ${languages.join(", ")}`));
16565
+ console.log(chalk17.dim(`[lsp warm] auto-detected languages: ${languages.join(", ")}`));
16227
16566
  }
16228
16567
  let keepWarmMinutes = 0;
16229
16568
  if (typeof opts.keepWarm === "number" && opts.keepWarm > 0) {
16230
16569
  keepWarmMinutes = Math.min(opts.keepWarm, MAX_KEEP_WARM_MINUTES);
16231
16570
  if (opts.keepWarm > MAX_KEEP_WARM_MINUTES) {
16232
16571
  console.log(
16233
- chalk16.yellow(
16572
+ chalk17.yellow(
16234
16573
  `[lsp warm] --keep-warm clipped to ${MAX_KEEP_WARM_MINUTES.toString()} minutes (hard cap per Phase 7L plan).`
16235
16574
  )
16236
16575
  );
16237
16576
  }
16238
16577
  }
16239
- console.log(chalk16.bold("RepoWise LSP warm \u2014 preview"));
16240
- console.log(chalk16.dim(`Workspace: ${cwd}`));
16578
+ console.log(chalk17.bold("RepoWise LSP warm \u2014 preview"));
16579
+ console.log(chalk17.dim(`Workspace: ${cwd}`));
16241
16580
  console.log(
16242
- chalk16.dim(
16581
+ chalk17.dim(
16243
16582
  "LSP sessions live inside the listener (lazy-by-default, idle-evict after 5 min). The CLI cannot currently trigger a remote spawn \u2014 this preview shows which servers would be used."
16244
16583
  )
16245
16584
  );
@@ -16255,23 +16594,23 @@ async function lspWarm(opts = {}) {
16255
16594
  if (onPath) {
16256
16595
  foundCount += 1;
16257
16596
  console.log(
16258
- chalk16.green(` \u2713 ${lang.padEnd(12)} ${config2.displayName} (${config2.command}) on PATH`)
16597
+ chalk17.green(` \u2713 ${lang.padEnd(12)} ${config2.displayName} (${config2.command}) on PATH`)
16259
16598
  );
16260
16599
  } else {
16261
16600
  missingCount += 1;
16262
- console.log(chalk16.yellow(` \u2717 ${lang.padEnd(12)} ${config2.displayName} not on PATH`));
16601
+ console.log(chalk17.yellow(` \u2717 ${lang.padEnd(12)} ${config2.displayName} not on PATH`));
16263
16602
  if (config2.installHint) {
16264
- console.log(chalk16.dim(` install: ${config2.installHint}`));
16603
+ console.log(chalk17.dim(` install: ${config2.installHint}`));
16265
16604
  }
16266
16605
  }
16267
16606
  }
16268
16607
  console.log();
16269
16608
  console.log(
16270
- `${chalk16.green(foundCount.toString())} ready, ${chalk16.yellow(missingCount.toString())} missing.`
16609
+ `${chalk17.green(foundCount.toString())} ready, ${chalk17.yellow(missingCount.toString())} missing.`
16271
16610
  );
16272
16611
  if (keepWarmMinutes > 0) {
16273
16612
  console.log(
16274
- chalk16.dim(
16613
+ chalk17.dim(
16275
16614
  `Holding for ${keepWarmMinutes.toString()} minute(s) so the listener has time to spawn on its next sync.completed; Ctrl-C to exit early.`
16276
16615
  )
16277
16616
  );
@@ -16288,20 +16627,20 @@ async function lspWarm(opts = {}) {
16288
16627
  }
16289
16628
  function lspStatus() {
16290
16629
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16291
- if (noColor) chalk16.level = 0;
16292
- console.log(chalk16.bold("RepoWise LSP status"));
16630
+ if (noColor) chalk17.level = 0;
16631
+ console.log(chalk17.bold("RepoWise LSP status"));
16293
16632
  console.log(
16294
- chalk16.dim(
16633
+ chalk17.dim(
16295
16634
  "Active LSP sessions live inside the listener (not the CLI). Sessions are lazy-by-default: spawn on first MCP tool / receiver query, idle-evict after 5 minutes. Scala sessions also evict their paired Bloop daemon (Phase 7L+)."
16296
16635
  )
16297
16636
  );
16298
16637
  console.log();
16299
- console.log(chalk16.dim("See `repowise lsp doctor` for PATH probe + install hints."));
16638
+ console.log(chalk17.dim("See `repowise lsp doctor` for PATH probe + install hints."));
16300
16639
  }
16301
16640
  function lspStop() {
16302
- console.log(chalk16.bold("RepoWise LSP stop \u2014 preview"));
16641
+ console.log(chalk17.bold("RepoWise LSP stop \u2014 preview"));
16303
16642
  console.log(
16304
- chalk16.dim(
16643
+ chalk17.dim(
16305
16644
  "No CLI-owned warm sessions exist. Listener-owned LSP sessions evict automatically after 5 minutes of idle (Phase 7L) and under the RSS soft-cap LRU when memory pressure rises. Explicit listener-side stop IPC is a future follow-up."
16306
16645
  )
16307
16646
  );
@@ -16324,29 +16663,29 @@ var ENABLE_TARGETS = {
16324
16663
  };
16325
16664
  async function lspEnable(target) {
16326
16665
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16327
- if (noColor) chalk16.level = 0;
16666
+ if (noColor) chalk17.level = 0;
16328
16667
  if (!target) {
16329
- console.error(chalk16.red("Usage: repowise lsp enable <target>"));
16330
- console.error(chalk16.dim(` Supported targets: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16668
+ console.error(chalk17.red("Usage: repowise lsp enable <target>"));
16669
+ console.error(chalk17.dim(` Supported targets: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16331
16670
  process.exitCode = 1;
16332
16671
  return;
16333
16672
  }
16334
16673
  const spec = ENABLE_TARGETS[target];
16335
16674
  if (!spec) {
16336
- console.error(chalk16.red(`\u2716 Unknown LSP override target: ${target}`));
16337
- console.error(chalk16.dim(` Supported: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16675
+ console.error(chalk17.red(`\u2716 Unknown LSP override target: ${target}`));
16676
+ console.error(chalk17.dim(` Supported: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16338
16677
  process.exitCode = 1;
16339
16678
  return;
16340
16679
  }
16341
16680
  const config2 = await getConfig();
16342
16681
  const existing = config2.lspOverrides?.[spec.language];
16343
16682
  if (existing === spec.name) {
16344
- console.log(chalk16.green(`\u2714 ${spec.displayName} is already enabled.`));
16345
- console.log(chalk16.dim(` ${spec.postAcceptTip}`));
16683
+ console.log(chalk17.green(`\u2714 ${spec.displayName} is already enabled.`));
16684
+ console.log(chalk17.dim(` ${spec.postAcceptTip}`));
16346
16685
  return;
16347
16686
  }
16348
16687
  console.log();
16349
- console.log(chalk16.cyan.bold(` \u2500\u2500 Enable ${spec.displayName} \u2500\u2500`));
16688
+ console.log(chalk17.cyan.bold(` \u2500\u2500 Enable ${spec.displayName} \u2500\u2500`));
16350
16689
  for (const line of spec.consentLines) {
16351
16690
  console.log(` ${line}`);
16352
16691
  }
@@ -16358,7 +16697,7 @@ async function lspEnable(target) {
16358
16697
  });
16359
16698
  if (!proceed) {
16360
16699
  console.log(
16361
- chalk16.dim(
16700
+ chalk17.dim(
16362
16701
  ` Cancelled. ${spec.language.toUpperCase()} will continue using ${spec.revertDisplay}.`
16363
16702
  )
16364
16703
  );
@@ -16367,32 +16706,32 @@ async function lspEnable(target) {
16367
16706
  const nextOverrides = { ...config2.lspOverrides ?? {}, [spec.language]: spec.name };
16368
16707
  await mergeAndSaveConfig({ lspOverrides: nextOverrides });
16369
16708
  console.log(
16370
- chalk16.green(
16709
+ chalk17.green(
16371
16710
  ` \u2714 ${spec.displayName} enabled \u2014 listener will use it within ~5 min (reconcile cycle) or on next session restart.`
16372
16711
  )
16373
16712
  );
16374
- console.log(chalk16.dim(` \u2139 ${spec.postAcceptTip}`));
16713
+ console.log(chalk17.dim(` \u2139 ${spec.postAcceptTip}`));
16375
16714
  }
16376
16715
  async function lspDisable(target) {
16377
16716
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16378
- if (noColor) chalk16.level = 0;
16717
+ if (noColor) chalk17.level = 0;
16379
16718
  if (!target) {
16380
- console.error(chalk16.red("Usage: repowise lsp disable <target>"));
16381
- console.error(chalk16.dim(` Supported targets: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16719
+ console.error(chalk17.red("Usage: repowise lsp disable <target>"));
16720
+ console.error(chalk17.dim(` Supported targets: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16382
16721
  process.exitCode = 1;
16383
16722
  return;
16384
16723
  }
16385
16724
  const spec = ENABLE_TARGETS[target];
16386
16725
  if (!spec) {
16387
- console.error(chalk16.red(`\u2716 Unknown LSP override target: ${target}`));
16388
- console.error(chalk16.dim(` Supported: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16726
+ console.error(chalk17.red(`\u2716 Unknown LSP override target: ${target}`));
16727
+ console.error(chalk17.dim(` Supported: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
16389
16728
  process.exitCode = 1;
16390
16729
  return;
16391
16730
  }
16392
16731
  const config2 = await getConfig();
16393
16732
  const current = config2.lspOverrides?.[spec.language];
16394
16733
  if (current !== spec.name) {
16395
- console.log(chalk16.dim(` ${spec.displayName} is not enabled \u2014 nothing to do.`));
16734
+ console.log(chalk17.dim(` ${spec.displayName} is not enabled \u2014 nothing to do.`));
16396
16735
  return;
16397
16736
  }
16398
16737
  const { [spec.language]: _removed, ...rest } = config2.lspOverrides ?? {};
@@ -16402,28 +16741,28 @@ async function lspDisable(target) {
16402
16741
  ...nextOverrides ? { lspOverrides: nextOverrides } : { lspOverrides: void 0 }
16403
16742
  });
16404
16743
  console.log(
16405
- chalk16.green(
16744
+ chalk17.green(
16406
16745
  ` \u2714 ${spec.displayName} disabled \u2014 listener will revert to ${spec.revertDisplay} within ~5 min or on next session restart.`
16407
16746
  )
16408
16747
  );
16409
16748
  }
16410
16749
  async function lspOff() {
16411
16750
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16412
- if (noColor) chalk16.level = 0;
16751
+ if (noColor) chalk17.level = 0;
16413
16752
  await mergeAndSaveConfig({ lspEnabled: false });
16414
16753
  console.log(
16415
- chalk16.green(
16754
+ chalk17.green(
16416
16755
  " \u2714 LSP subsystem disabled. The listener will stop spawning language servers and `lsp_*` tools will report disabled within ~5 min (reconcile) or on next restart."
16417
16756
  )
16418
16757
  );
16419
- console.log(chalk16.dim(" Re-enable with: repowise lsp on"));
16758
+ console.log(chalk17.dim(" Re-enable with: repowise lsp on"));
16420
16759
  }
16421
16760
  async function lspOn() {
16422
16761
  const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
16423
- if (noColor) chalk16.level = 0;
16762
+ if (noColor) chalk17.level = 0;
16424
16763
  await mergeAndSaveConfig({ lspEnabled: true });
16425
16764
  console.log(
16426
- chalk16.green(
16765
+ chalk17.green(
16427
16766
  " \u2714 LSP subsystem enabled. The listener will install + warm language servers for your repos within ~5 min (reconcile) or on next restart."
16428
16767
  )
16429
16768
  );
@@ -16432,7 +16771,7 @@ async function lspOn() {
16432
16771
  // bin/repowise.ts
16433
16772
  var __filename = fileURLToPath4(import.meta.url);
16434
16773
  var __dirname = dirname23(__filename);
16435
- var pkg = JSON.parse(readFileSync3(join61(__dirname, "..", "..", "package.json"), "utf-8"));
16774
+ var pkg = JSON.parse(readFileSync3(join62(__dirname, "..", "..", "package.json"), "utf-8"));
16436
16775
  var program = new Command();
16437
16776
  program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
16438
16777
  await showWelcome(pkg.version);
@@ -16508,6 +16847,15 @@ lspCommand.command("off").description("Disable the LSP subsystem entirely (kill-
16508
16847
  lspCommand.command("on").description("Re-enable the LSP subsystem after `lsp off`").action(async () => {
16509
16848
  await lspOn();
16510
16849
  });
16850
+ var toolsCommand = program.command("tools").description("Add RepoWise to your AI tools (Claude Code, Cursor, \u2026) after install").action(async () => {
16851
+ await toolsPick();
16852
+ });
16853
+ toolsCommand.command("add [tools...]").description("Add named AI tools (or open the picker with no args)").action(async (list) => {
16854
+ await toolsAdd(list ?? []);
16855
+ });
16856
+ toolsCommand.command("list").description("List the AI tools RepoWise is configured for").action(async () => {
16857
+ await toolsList();
16858
+ });
16511
16859
  program.command("uninstall").description("Remove RepoWise from this machine (3 tiers: stop | logout | uninstall)").option("--tier <tier>", "Cleanup tier: stop | logout | uninstall", "uninstall").option("--yes", "Skip confirm prompt", false).action(async (options) => {
16512
16860
  const tier = options.tier ?? "uninstall";
16513
16861
  await uninstallCommand({ tier, yes: options.yes });