repowise 0.1.95 → 0.1.96
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.
- package/dist/bin/repowise.js +629 -284
- package/package.json +1 -1
package/dist/bin/repowise.js
CHANGED
|
@@ -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
|
|
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
|
|
8010
|
+
const fs31 = await import("fs/promises");
|
|
8011
8011
|
const path = await import("path");
|
|
8012
|
-
await
|
|
8013
|
-
await
|
|
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
|
-
|
|
10942
|
+
let credentials = await getValidCredentials();
|
|
10913
10943
|
if (!credentials) {
|
|
10914
|
-
console.
|
|
10915
|
-
await
|
|
10916
|
-
|
|
10917
|
-
|
|
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);
|
|
@@ -11207,6 +11241,16 @@ async function startListener() {
|
|
|
11207
11241
|
console.warn("[mcp-config] front-load for newly-registered repo(s) failed:", err instanceof Error ? err.message : String(err));
|
|
11208
11242
|
});
|
|
11209
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
|
+
});
|
|
11253
|
+
}
|
|
11210
11254
|
} catch {
|
|
11211
11255
|
}
|
|
11212
11256
|
for (const group of groups) {
|
|
@@ -11456,8 +11500,8 @@ async function startListener() {
|
|
|
11456
11500
|
await sleep(5e3);
|
|
11457
11501
|
if (!running)
|
|
11458
11502
|
break;
|
|
11459
|
-
const
|
|
11460
|
-
if (
|
|
11503
|
+
const fresh2 = await getValidCredentials({ forceRefresh: true });
|
|
11504
|
+
if (fresh2) {
|
|
11461
11505
|
console.log("Credentials recovered \u2014 resuming listener");
|
|
11462
11506
|
recovered = true;
|
|
11463
11507
|
break;
|
|
@@ -11478,29 +11522,9 @@ async function startListener() {
|
|
|
11478
11522
|
} catch {
|
|
11479
11523
|
}
|
|
11480
11524
|
}
|
|
11481
|
-
const
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
try {
|
|
11485
|
-
const fs30 = await import("fs");
|
|
11486
|
-
watcher = fs30.watch(credentialsPath, () => {
|
|
11487
|
-
credentialsChanged = true;
|
|
11488
|
-
});
|
|
11489
|
-
} catch {
|
|
11490
|
-
}
|
|
11491
|
-
while (running) {
|
|
11492
|
-
await sleep(credentialsChanged ? 0 : 1e4);
|
|
11493
|
-
credentialsChanged = false;
|
|
11494
|
-
if (!running)
|
|
11495
|
-
break;
|
|
11496
|
-
const fresh = await getValidCredentials({ forceRefresh: true });
|
|
11497
|
-
if (fresh) {
|
|
11498
|
-
console.log("Re-authenticated \u2014 resuming listener");
|
|
11499
|
-
break;
|
|
11500
|
-
}
|
|
11501
|
-
}
|
|
11502
|
-
if (watcher)
|
|
11503
|
-
watcher.close();
|
|
11525
|
+
const fresh = await waitForCredentials(() => running);
|
|
11526
|
+
if (fresh)
|
|
11527
|
+
console.log("Re-authenticated \u2014 resuming listener");
|
|
11504
11528
|
continue;
|
|
11505
11529
|
}
|
|
11506
11530
|
pollIntervalMs = minPollInterval;
|
|
@@ -11654,7 +11678,7 @@ async function showWelcome(currentVersion) {
|
|
|
11654
11678
|
|
|
11655
11679
|
// src/commands/create.ts
|
|
11656
11680
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
11657
|
-
import { dirname as dirname19, join as
|
|
11681
|
+
import { dirname as dirname19, join as join50 } from "path";
|
|
11658
11682
|
init_src();
|
|
11659
11683
|
import chalk8 from "chalk";
|
|
11660
11684
|
import ora from "ora";
|
|
@@ -12013,25 +12037,26 @@ async function apiRequest(path, options) {
|
|
|
12013
12037
|
// src/lib/prompts.ts
|
|
12014
12038
|
import { checkbox, confirm, Separator } from "@inquirer/prompts";
|
|
12015
12039
|
import chalk2 from "chalk";
|
|
12016
|
-
async function selectAiTools() {
|
|
12040
|
+
async function selectAiTools(opts) {
|
|
12041
|
+
const checkedSet = new Set(opts?.defaults ?? []);
|
|
12017
12042
|
const choices = [
|
|
12018
12043
|
new Separator(chalk2.dim("\u2500\u2500 Popular \u2500\u2500")),
|
|
12019
|
-
{ name: "Cursor", value: "cursor" },
|
|
12020
|
-
{ name: "Claude Code", value: "claude-code" },
|
|
12021
|
-
{ name: "GitHub Copilot", value: "copilot" },
|
|
12022
|
-
{ 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") },
|
|
12023
12048
|
new Separator(chalk2.dim("\u2500\u2500 More Tools \u2500\u2500")),
|
|
12024
|
-
{ name: "Cline", value: "cline" },
|
|
12025
|
-
{ name: "Codex", value: "codex" },
|
|
12049
|
+
{ name: "Cline", value: "cline", checked: checkedSet.has("cline") },
|
|
12050
|
+
{ name: "Codex", value: "codex", checked: checkedSet.has("codex") },
|
|
12026
12051
|
// Roo Code shut down 2026-05-15; Kilo Code is the community successor.
|
|
12027
|
-
{ name: "Kilo Code", value: "kilo" },
|
|
12028
|
-
{ 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") },
|
|
12029
12054
|
new Separator(chalk2.dim("\u2500\u2500 Cloud Agents \u2500\u2500")),
|
|
12030
|
-
{ name: "Warp", value: "warp" },
|
|
12031
|
-
{ name: "JetBrains Junie", value: "junie" },
|
|
12032
|
-
{ name: "Google Jules", value: "jules" },
|
|
12033
|
-
{ name: "Amp", value: "amp" },
|
|
12034
|
-
{ 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") },
|
|
12035
12060
|
new Separator(chalk2.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")),
|
|
12036
12061
|
{ name: "Other (manual setup)", value: "other" }
|
|
12037
12062
|
];
|
|
@@ -12054,11 +12079,126 @@ async function selectAiTools() {
|
|
|
12054
12079
|
}
|
|
12055
12080
|
}
|
|
12056
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
|
+
|
|
12057
12197
|
// src/lib/gitignore.ts
|
|
12058
12198
|
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2 } from "fs";
|
|
12059
|
-
import { join as
|
|
12199
|
+
import { join as join46 } from "path";
|
|
12060
12200
|
function ensureGitignore(repoRoot, entry) {
|
|
12061
|
-
const gitignorePath =
|
|
12201
|
+
const gitignorePath = join46(repoRoot, ".gitignore");
|
|
12062
12202
|
if (existsSync2(gitignorePath)) {
|
|
12063
12203
|
const content = readFileSync2(gitignorePath, "utf-8");
|
|
12064
12204
|
const lines = content.split("\n").map((l) => l.trim());
|
|
@@ -12075,7 +12215,7 @@ function ensureGitignore(repoRoot, entry) {
|
|
|
12075
12215
|
// src/lib/graph-cache.ts
|
|
12076
12216
|
init_config_dir();
|
|
12077
12217
|
import { mkdirSync, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
|
|
12078
|
-
import { dirname as dirname18, join as
|
|
12218
|
+
import { dirname as dirname18, join as join47 } from "path";
|
|
12079
12219
|
var SAFE_REPO_ID = /^[A-Za-z0-9_.-]{1,128}$/;
|
|
12080
12220
|
function assertSafeRepoId2(repoId) {
|
|
12081
12221
|
if (!repoId || typeof repoId !== "string" || !SAFE_REPO_ID.test(repoId) || repoId === "." || repoId === ".." || repoId.startsWith(".")) {
|
|
@@ -12084,7 +12224,7 @@ function assertSafeRepoId2(repoId) {
|
|
|
12084
12224
|
}
|
|
12085
12225
|
function graphCachePath(repoId) {
|
|
12086
12226
|
assertSafeRepoId2(repoId);
|
|
12087
|
-
return
|
|
12227
|
+
return join47(getConfigDir(), "graphs", `${repoId}.json`);
|
|
12088
12228
|
}
|
|
12089
12229
|
function isUsableGraph(parsed) {
|
|
12090
12230
|
if (typeof parsed !== "object" || parsed === null) return false;
|
|
@@ -12700,17 +12840,27 @@ var ProgressRenderer = class {
|
|
|
12700
12840
|
if (generating) {
|
|
12701
12841
|
const genBaseName = generating.fileName.split("/").pop() ?? generating.fileName;
|
|
12702
12842
|
const isCore = CORE_FILES.has(genBaseName);
|
|
12703
|
-
const
|
|
12843
|
+
const sectionFiles2 = gp.fileStatuses.filter((f) => {
|
|
12704
12844
|
const bn = f.fileName.split("/").pop() ?? f.fileName;
|
|
12705
12845
|
return isCore ? CORE_FILES.has(bn) : !CORE_FILES.has(bn);
|
|
12706
12846
|
});
|
|
12707
|
-
const sectionCompleted =
|
|
12708
|
-
return `${generating.fileName} (${sectionCompleted}/${
|
|
12847
|
+
const sectionCompleted = sectionFiles2.filter((f) => f.status === "completed").length;
|
|
12848
|
+
return `${generating.fileName} (${sectionCompleted}/${sectionFiles2.length}) ${chalk4.dim(`(${overallPct}%)`)}`;
|
|
12709
12849
|
}
|
|
12710
12850
|
const allDone = gp.fileStatuses.every((f) => f.status === "completed");
|
|
12711
12851
|
if (allDone) {
|
|
12712
12852
|
return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
|
|
12713
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
|
+
}
|
|
12714
12864
|
}
|
|
12715
12865
|
}
|
|
12716
12866
|
return `${stepLabel}... ${chalk4.dim(`(${overallPct}%)`)}`;
|
|
@@ -12801,8 +12951,8 @@ async function promptDepInstallConsent(missing, opts) {
|
|
|
12801
12951
|
import chalk6 from "chalk";
|
|
12802
12952
|
|
|
12803
12953
|
// src/lib/dep-installer.ts
|
|
12804
|
-
import { promises as
|
|
12805
|
-
import { join as
|
|
12954
|
+
import { promises as fs22 } from "fs";
|
|
12955
|
+
import { join as join48 } from "path";
|
|
12806
12956
|
var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
|
|
12807
12957
|
"typescript",
|
|
12808
12958
|
"javascript",
|
|
@@ -12812,14 +12962,14 @@ var SUPPORTED_DEP_LANGUAGES = /* @__PURE__ */ new Set([
|
|
|
12812
12962
|
]);
|
|
12813
12963
|
var exists = async (p) => {
|
|
12814
12964
|
try {
|
|
12815
|
-
await
|
|
12965
|
+
await fs22.access(p);
|
|
12816
12966
|
return true;
|
|
12817
12967
|
} catch {
|
|
12818
12968
|
return false;
|
|
12819
12969
|
}
|
|
12820
12970
|
};
|
|
12821
12971
|
async function fileExists16(repoRoot, name) {
|
|
12822
|
-
return exists(
|
|
12972
|
+
return exists(join48(repoRoot, name));
|
|
12823
12973
|
}
|
|
12824
12974
|
async function detectNodePackageManager(repoRoot) {
|
|
12825
12975
|
if (await fileExists16(repoRoot, "pnpm-lock.yaml")) {
|
|
@@ -12896,13 +13046,13 @@ async function detectMissingDeps(repoRoot, scopedLanguages) {
|
|
|
12896
13046
|
// src/lib/dep-installer-runner.ts
|
|
12897
13047
|
import { spawn as spawn11 } from "child_process";
|
|
12898
13048
|
import { createWriteStream as createWriteStream3 } from "fs";
|
|
12899
|
-
import { promises as
|
|
12900
|
-
import { join as
|
|
13049
|
+
import { promises as fs23 } from "fs";
|
|
13050
|
+
import { join as join49 } from "path";
|
|
12901
13051
|
var DEFAULT_INSTALL_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
12902
13052
|
async function runMissingDepInstalls(opts) {
|
|
12903
13053
|
const safeRepoId = opts.repoId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
12904
|
-
const logPath2 =
|
|
12905
|
-
await
|
|
13054
|
+
const logPath2 = join49(getConfigDir2(), `install-log.${safeRepoId}.txt`);
|
|
13055
|
+
await fs23.mkdir(getConfigDir2(), { recursive: true });
|
|
12906
13056
|
const stream = opts.logStream ?? createWriteStream3(logPath2, { flags: "a" });
|
|
12907
13057
|
stream.on("error", () => {
|
|
12908
13058
|
});
|
|
@@ -12948,7 +13098,7 @@ async function runOne2(dep, repoRoot, stream, timeoutMs) {
|
|
|
12948
13098
|
}
|
|
12949
13099
|
async function runPipFlow(repoRoot, stream, timeoutMs) {
|
|
12950
13100
|
await runSimple(["python3", "-m", "venv", ".venv"], repoRoot, stream, timeoutMs);
|
|
12951
|
-
const venvPip = process.platform === "win32" ?
|
|
13101
|
+
const venvPip = process.platform === "win32" ? join49(".venv", "Scripts", "pip.exe") : join49(".venv", "bin", "pip");
|
|
12952
13102
|
await runSimple([venvPip, "install", "-r", "requirements.txt"], repoRoot, stream, timeoutMs);
|
|
12953
13103
|
}
|
|
12954
13104
|
function runSimple(cmd, repoRoot, stream, timeoutMs) {
|
|
@@ -13286,9 +13436,7 @@ async function create() {
|
|
|
13286
13436
|
}
|
|
13287
13437
|
if (tools.length === 0 && !hasOther) {
|
|
13288
13438
|
console.log(
|
|
13289
|
-
chalk8.yellow(
|
|
13290
|
-
"\nNo AI tools selected. You can configure them later with `repowise config`."
|
|
13291
|
-
)
|
|
13439
|
+
chalk8.yellow("\nNo AI tools selected. You can set them up later with `repowise tools`.")
|
|
13292
13440
|
);
|
|
13293
13441
|
}
|
|
13294
13442
|
if (repoRoot) {
|
|
@@ -13521,7 +13669,7 @@ async function create() {
|
|
|
13521
13669
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
13522
13670
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
13523
13671
|
if (files.length > 0) {
|
|
13524
|
-
const contextDir =
|
|
13672
|
+
const contextDir = join50(repoRoot, DEFAULT_CONTEXT_FOLDER);
|
|
13525
13673
|
mkdirSync2(contextDir, { recursive: true });
|
|
13526
13674
|
let downloadedCount = 0;
|
|
13527
13675
|
let failedCount = 0;
|
|
@@ -13535,7 +13683,7 @@ async function create() {
|
|
|
13535
13683
|
const response = await fetch(presignedUrl);
|
|
13536
13684
|
if (response.ok) {
|
|
13537
13685
|
const content = await response.text();
|
|
13538
|
-
const filePath =
|
|
13686
|
+
const filePath = join50(contextDir, file.fileName);
|
|
13539
13687
|
mkdirSync2(dirname19(filePath), { recursive: true });
|
|
13540
13688
|
writeFileSync3(filePath, content, "utf-8");
|
|
13541
13689
|
downloadedCount++;
|
|
@@ -13619,51 +13767,17 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
13619
13767
|
}
|
|
13620
13768
|
if (repoRoot) {
|
|
13621
13769
|
spinner.start("Configuring AI tools...");
|
|
13622
|
-
const results =
|
|
13623
|
-
|
|
13624
|
-
|
|
13625
|
-
|
|
13626
|
-
|
|
13627
|
-
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
repoRoot,
|
|
13632
|
-
tool,
|
|
13633
|
-
repoName,
|
|
13634
|
-
contextFolder,
|
|
13635
|
-
contextFiles,
|
|
13636
|
-
toolVariant
|
|
13637
|
-
);
|
|
13638
|
-
}
|
|
13639
|
-
const { created: wasCreated } = await updateToolConfig(
|
|
13640
|
-
repoRoot,
|
|
13641
|
-
tool,
|
|
13642
|
-
repoName,
|
|
13643
|
-
contextFolder,
|
|
13644
|
-
contextFiles,
|
|
13645
|
-
toolVariant
|
|
13646
|
-
);
|
|
13647
|
-
const action = wasCreated ? "Created" : "Updated";
|
|
13648
|
-
results.push(` ${action} ${config2.filePath}`);
|
|
13649
|
-
}
|
|
13650
|
-
if (!written.has("AGENTS.md")) {
|
|
13651
|
-
const { created: wasCreated } = await updateToolConfig(
|
|
13652
|
-
repoRoot,
|
|
13653
|
-
"codex",
|
|
13654
|
-
repoName,
|
|
13655
|
-
contextFolder,
|
|
13656
|
-
contextFiles,
|
|
13657
|
-
toolVariant
|
|
13658
|
-
);
|
|
13659
|
-
results.push(` ${wasCreated ? "Created" : "Updated"} AGENTS.md`);
|
|
13660
|
-
}
|
|
13661
|
-
if (tools.includes("claude-code")) {
|
|
13662
|
-
const { relPath } = await writeClaudeHooksToRepo(repoRoot, contextFolder, toolVariant);
|
|
13663
|
-
results.push(` Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
|
|
13664
|
-
}
|
|
13770
|
+
const results = await writeToolConfigsForRepo({
|
|
13771
|
+
repoRoot,
|
|
13772
|
+
tools,
|
|
13773
|
+
repoName,
|
|
13774
|
+
contextFolder,
|
|
13775
|
+
contextFiles,
|
|
13776
|
+
variant: graphOnly ? "graph" : "full",
|
|
13777
|
+
migrateLegacy: true
|
|
13778
|
+
});
|
|
13665
13779
|
spinner.succeed("AI tools configured");
|
|
13666
|
-
console.log(chalk8.dim(results.join("\n")));
|
|
13780
|
+
console.log(chalk8.dim(results.map((r) => ` ${r}`).join("\n")));
|
|
13667
13781
|
}
|
|
13668
13782
|
const priorAutoInstall = await getPriorConsent(repoId);
|
|
13669
13783
|
const updatedRepos = [];
|
|
@@ -13772,6 +13886,11 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
13772
13886
|
console.log(
|
|
13773
13887
|
` ${chalk8.cyan("\u2022")} Open Claude Code / Cursor and ask: "What does this repo do?"`
|
|
13774
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
|
+
}
|
|
13775
13894
|
console.log(
|
|
13776
13895
|
` ${chalk8.cyan("\u2022")} Head to the dashboard \u2192 "Complete Onboarding" to explore quality scores and gaps.`
|
|
13777
13896
|
);
|
|
@@ -13786,7 +13905,7 @@ Files are stored on our servers (not in git). Retry when online.`
|
|
|
13786
13905
|
|
|
13787
13906
|
// src/commands/member.ts
|
|
13788
13907
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
13789
|
-
import { dirname as dirname20, join as
|
|
13908
|
+
import { dirname as dirname20, join as join51, resolve, sep } from "path";
|
|
13790
13909
|
import chalk9 from "chalk";
|
|
13791
13910
|
import ora2 from "ora";
|
|
13792
13911
|
var DEFAULT_CONTEXT_FOLDER2 = "repowise-context";
|
|
@@ -13940,7 +14059,7 @@ async function member() {
|
|
|
13940
14059
|
spinner.succeed(`Found ${chalk9.bold(files.length)} context files on server`);
|
|
13941
14060
|
const { tools } = await selectAiTools();
|
|
13942
14061
|
spinner.start("Downloading context files...");
|
|
13943
|
-
const contextDir =
|
|
14062
|
+
const contextDir = join51(repoRoot, DEFAULT_CONTEXT_FOLDER2);
|
|
13944
14063
|
mkdirSync3(contextDir, { recursive: true });
|
|
13945
14064
|
let downloadedCount = 0;
|
|
13946
14065
|
let failedCount = 0;
|
|
@@ -13985,35 +14104,14 @@ async function member() {
|
|
|
13985
14104
|
{
|
|
13986
14105
|
spinner.start("Configuring AI tools...");
|
|
13987
14106
|
const contextFiles = await scanLocalContextFiles(repoRoot, DEFAULT_CONTEXT_FOLDER2);
|
|
13988
|
-
const configured =
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
13996
|
-
tool,
|
|
13997
|
-
repoName,
|
|
13998
|
-
DEFAULT_CONTEXT_FOLDER2,
|
|
13999
|
-
contextFiles
|
|
14000
|
-
);
|
|
14001
|
-
configured.push(`${created ? "Created" : "Updated"} ${config2.filePath}`);
|
|
14002
|
-
}
|
|
14003
|
-
if (!written.has("AGENTS.md")) {
|
|
14004
|
-
const { created } = await updateToolConfig(
|
|
14005
|
-
repoRoot,
|
|
14006
|
-
"codex",
|
|
14007
|
-
repoName,
|
|
14008
|
-
DEFAULT_CONTEXT_FOLDER2,
|
|
14009
|
-
contextFiles
|
|
14010
|
-
);
|
|
14011
|
-
configured.push(`${created ? "Created" : "Updated"} AGENTS.md`);
|
|
14012
|
-
}
|
|
14013
|
-
if (tools.includes("claude-code")) {
|
|
14014
|
-
const { relPath } = await writeClaudeHooksToRepo(repoRoot, DEFAULT_CONTEXT_FOLDER2);
|
|
14015
|
-
configured.push(`Configured ${relPath} (RepoWise-first + SubagentStart hooks, local-only)`);
|
|
14016
|
-
}
|
|
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
|
+
});
|
|
14017
14115
|
spinner.succeed("AI tools configured");
|
|
14018
14116
|
for (const msg of configured) {
|
|
14019
14117
|
console.log(chalk9.dim(` ${msg}`));
|
|
@@ -14134,19 +14232,20 @@ async function member() {
|
|
|
14134
14232
|
}
|
|
14135
14233
|
|
|
14136
14234
|
// src/commands/login.ts
|
|
14235
|
+
import { homedir as homedir9 } from "os";
|
|
14137
14236
|
import chalk10 from "chalk";
|
|
14138
14237
|
import ora3 from "ora";
|
|
14139
14238
|
|
|
14140
14239
|
// src/lib/tenant-graph-purge.ts
|
|
14141
|
-
import { promises as
|
|
14240
|
+
import { promises as fs24 } from "fs";
|
|
14142
14241
|
import { homedir as homedir8 } from "os";
|
|
14143
|
-
import { join as
|
|
14242
|
+
import { join as join52 } from "path";
|
|
14144
14243
|
async function purgeForeignGraphs(validRepoIds, home = homedir8()) {
|
|
14145
|
-
const graphsDir =
|
|
14244
|
+
const graphsDir = join52(home, ".repowise", "graphs");
|
|
14146
14245
|
const result = { kept: [], removed: [] };
|
|
14147
14246
|
let entries;
|
|
14148
14247
|
try {
|
|
14149
|
-
entries = await
|
|
14248
|
+
entries = await fs24.readdir(graphsDir);
|
|
14150
14249
|
} catch (err) {
|
|
14151
14250
|
if (err.code === "ENOENT") return result;
|
|
14152
14251
|
throw err;
|
|
@@ -14160,15 +14259,15 @@ async function purgeForeignGraphs(validRepoIds, home = homedir8()) {
|
|
|
14160
14259
|
result.kept.push(entry);
|
|
14161
14260
|
continue;
|
|
14162
14261
|
}
|
|
14163
|
-
const path =
|
|
14262
|
+
const path = join52(graphsDir, entry);
|
|
14164
14263
|
try {
|
|
14165
|
-
const stat8 = await
|
|
14264
|
+
const stat8 = await fs24.lstat(path);
|
|
14166
14265
|
if (stat8.isSymbolicLink()) {
|
|
14167
|
-
await
|
|
14266
|
+
await fs24.unlink(path);
|
|
14168
14267
|
result.removed.push(entry);
|
|
14169
14268
|
continue;
|
|
14170
14269
|
}
|
|
14171
|
-
await
|
|
14270
|
+
await fs24.rm(path, { recursive: true, force: true });
|
|
14172
14271
|
result.removed.push(entry);
|
|
14173
14272
|
} catch {
|
|
14174
14273
|
}
|
|
@@ -14232,6 +14331,23 @@ Waiting for authentication...`);
|
|
|
14232
14331
|
}
|
|
14233
14332
|
} catch {
|
|
14234
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
|
+
}
|
|
14235
14351
|
} catch (err) {
|
|
14236
14352
|
const message = err instanceof Error ? err.message : "Login failed";
|
|
14237
14353
|
spinner.fail(chalk10.red(message));
|
|
@@ -14253,11 +14369,21 @@ async function logout() {
|
|
|
14253
14369
|
|
|
14254
14370
|
// src/commands/status.ts
|
|
14255
14371
|
import { readFile as readFile16 } from "fs/promises";
|
|
14256
|
-
import { basename as
|
|
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
|
|
14257
14383
|
async function status() {
|
|
14258
14384
|
const configDir = getConfigDir2();
|
|
14259
|
-
const STATE_PATH =
|
|
14260
|
-
const CONFIG_PATH =
|
|
14385
|
+
const STATE_PATH = join53(configDir, "listener-state.json");
|
|
14386
|
+
const CONFIG_PATH = join53(configDir, "config.json");
|
|
14261
14387
|
let state = null;
|
|
14262
14388
|
try {
|
|
14263
14389
|
const data = await readFile16(STATE_PATH, "utf-8");
|
|
@@ -14279,33 +14405,74 @@ async function status() {
|
|
|
14279
14405
|
} else {
|
|
14280
14406
|
console.log("Auto-start: disabled");
|
|
14281
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
|
+
}
|
|
14282
14424
|
console.log("");
|
|
14283
14425
|
if (!state || Object.keys(state.repos).length === 0) {
|
|
14284
14426
|
console.log("No sync history. Run `repowise listen` to start syncing.");
|
|
14285
14427
|
return;
|
|
14286
14428
|
}
|
|
14287
14429
|
const repoNames = /* @__PURE__ */ new Map();
|
|
14430
|
+
const repoPaths = /* @__PURE__ */ new Map();
|
|
14431
|
+
let aiTools = [];
|
|
14288
14432
|
try {
|
|
14289
14433
|
const configData = await readFile16(CONFIG_PATH, "utf-8");
|
|
14290
14434
|
const config2 = JSON.parse(configData);
|
|
14291
14435
|
for (const repo of config2.repos ?? []) {
|
|
14292
|
-
repoNames.set(repo.repoId,
|
|
14436
|
+
repoNames.set(repo.repoId, basename5(repo.localPath));
|
|
14437
|
+
repoPaths.set(repo.repoId, repo.localPath);
|
|
14293
14438
|
}
|
|
14439
|
+
aiTools = config2.aiTools ?? [];
|
|
14294
14440
|
} catch {
|
|
14295
14441
|
}
|
|
14296
14442
|
console.log("Watched Repos:");
|
|
14443
|
+
const home = homedir10();
|
|
14297
14444
|
for (const [repoId, repoState] of Object.entries(state.repos)) {
|
|
14298
14445
|
const name = repoNames.get(repoId);
|
|
14299
14446
|
const label = name ? `${name} (${repoId.slice(0, 8)})` : repoId;
|
|
14300
14447
|
const syncTime = repoState.lastSyncTimestamp ? new Date(repoState.lastSyncTimestamp).toLocaleString() : "never";
|
|
14301
14448
|
const commit = repoState.lastSyncCommitSha ? repoState.lastSyncCommitSha.slice(0, 7) : "none";
|
|
14302
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
|
+
}
|
|
14303
14470
|
}
|
|
14304
14471
|
}
|
|
14305
14472
|
|
|
14306
14473
|
// src/commands/sync.ts
|
|
14307
14474
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
14308
|
-
import { dirname as dirname21, join as
|
|
14475
|
+
import { dirname as dirname21, join as join54 } from "path";
|
|
14309
14476
|
import chalk12 from "chalk";
|
|
14310
14477
|
import ora4 from "ora";
|
|
14311
14478
|
var POLL_INTERVAL_MS2 = 3e3;
|
|
@@ -14454,7 +14621,7 @@ async function sync() {
|
|
|
14454
14621
|
const listResult = await apiRequest(`/v1/repos/${repoId}/context`);
|
|
14455
14622
|
const files = listResult.data?.files ?? listResult.files ?? [];
|
|
14456
14623
|
if (files.length > 0) {
|
|
14457
|
-
const contextDir =
|
|
14624
|
+
const contextDir = join54(repoRoot, DEFAULT_CONTEXT_FOLDER3);
|
|
14458
14625
|
mkdirSync4(contextDir, { recursive: true });
|
|
14459
14626
|
let downloadedCount = 0;
|
|
14460
14627
|
let failedCount = 0;
|
|
@@ -14468,7 +14635,7 @@ async function sync() {
|
|
|
14468
14635
|
const response = await fetch(presignedUrl);
|
|
14469
14636
|
if (response.ok) {
|
|
14470
14637
|
const content = await response.text();
|
|
14471
|
-
const filePath =
|
|
14638
|
+
const filePath = join54(contextDir, file.fileName);
|
|
14472
14639
|
mkdirSync4(dirname21(filePath), { recursive: true });
|
|
14473
14640
|
writeFileSync5(filePath, content, "utf-8");
|
|
14474
14641
|
downloadedCount++;
|
|
@@ -14721,10 +14888,158 @@ async function config() {
|
|
|
14721
14888
|
}
|
|
14722
14889
|
}
|
|
14723
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
|
+
|
|
14724
15039
|
// src/commands/mcp-log.ts
|
|
14725
15040
|
import { createDecipheriv as createDecipheriv2 } from "crypto";
|
|
14726
15041
|
import { mkdir as mkdir19, readFile as readFile17, stat as stat7, writeFile as writeFile18 } from "fs/promises";
|
|
14727
|
-
import { dirname as dirname22, join as
|
|
15042
|
+
import { dirname as dirname22, join as join55 } from "path";
|
|
14728
15043
|
var FLAG_FILE = "mcp-log.flag";
|
|
14729
15044
|
var LOG_FILE = "mcp-log.jsonl.enc";
|
|
14730
15045
|
var KEY_FILE = "mcp-log.key";
|
|
@@ -14732,10 +15047,10 @@ var ENDPOINT_FILE = "listener.endpoint";
|
|
|
14732
15047
|
var IV_BYTES2 = 12;
|
|
14733
15048
|
var TAG_BYTES2 = 16;
|
|
14734
15049
|
function flagPath() {
|
|
14735
|
-
return
|
|
15050
|
+
return join55(getConfigDir2(), FLAG_FILE);
|
|
14736
15051
|
}
|
|
14737
15052
|
function logPath() {
|
|
14738
|
-
return
|
|
15053
|
+
return join55(getConfigDir2(), LOG_FILE);
|
|
14739
15054
|
}
|
|
14740
15055
|
async function writeFlag(flag) {
|
|
14741
15056
|
const path = flagPath();
|
|
@@ -14776,14 +15091,14 @@ async function trySendConsentToServer() {
|
|
|
14776
15091
|
let apiUrl = null;
|
|
14777
15092
|
let token = null;
|
|
14778
15093
|
try {
|
|
14779
|
-
const body = await readFile17(
|
|
15094
|
+
const body = await readFile17(join55(getConfigDir2(), "config.json"), "utf-8");
|
|
14780
15095
|
const parsed = JSON.parse(body);
|
|
14781
15096
|
apiUrl = parsed.repos?.find((r) => Boolean(r.apiUrl))?.apiUrl ?? parsed.defaultApiUrl ?? null;
|
|
14782
15097
|
} catch {
|
|
14783
15098
|
return false;
|
|
14784
15099
|
}
|
|
14785
15100
|
try {
|
|
14786
|
-
const body = await readFile17(
|
|
15101
|
+
const body = await readFile17(join55(getConfigDir2(), "credentials.json"), "utf-8");
|
|
14787
15102
|
const parsed = JSON.parse(body);
|
|
14788
15103
|
token = parsed.idToken ?? null;
|
|
14789
15104
|
} catch {
|
|
@@ -14853,7 +15168,7 @@ async function mcpLogStatus() {
|
|
|
14853
15168
|
process.stderr.write("Log size: no file yet\n");
|
|
14854
15169
|
}
|
|
14855
15170
|
try {
|
|
14856
|
-
const endpointBody = await readFile17(
|
|
15171
|
+
const endpointBody = await readFile17(join55(getConfigDir2(), ENDPOINT_FILE), "utf-8");
|
|
14857
15172
|
const match = /endpoint=([^\n]+)/.exec(endpointBody);
|
|
14858
15173
|
process.stderr.write(`MCP endpoint: ${match?.[1] ?? "(malformed endpoint file)"}
|
|
14859
15174
|
`);
|
|
@@ -14884,7 +15199,7 @@ async function mcpLogViewingFlags(flags = {}) {
|
|
|
14884
15199
|
const key = await readKey();
|
|
14885
15200
|
if (!key) {
|
|
14886
15201
|
process.stderr.write(
|
|
14887
|
-
`No encryption key at ${
|
|
15202
|
+
`No encryption key at ${join55(getConfigDir2(), KEY_FILE)} \u2014 listener may not have started yet.
|
|
14888
15203
|
`
|
|
14889
15204
|
);
|
|
14890
15205
|
return;
|
|
@@ -14960,7 +15275,7 @@ async function mcpLogViewingFlags(flags = {}) {
|
|
|
14960
15275
|
}
|
|
14961
15276
|
async function readKey() {
|
|
14962
15277
|
try {
|
|
14963
|
-
const body = await readFile17(
|
|
15278
|
+
const body = await readFile17(join55(getConfigDir2(), KEY_FILE), "utf-8");
|
|
14964
15279
|
const parsed = Buffer.from(body.trim(), "base64");
|
|
14965
15280
|
if (parsed.length !== 32) return null;
|
|
14966
15281
|
return parsed;
|
|
@@ -15000,11 +15315,11 @@ async function mcpLog(subcommand, flags = {}) {
|
|
|
15000
15315
|
}
|
|
15001
15316
|
|
|
15002
15317
|
// src/commands/query/_shared.ts
|
|
15003
|
-
import
|
|
15318
|
+
import chalk15 from "chalk";
|
|
15004
15319
|
|
|
15005
15320
|
// src/lib/graph-loader.ts
|
|
15006
|
-
import { promises as
|
|
15007
|
-
import { join as
|
|
15321
|
+
import { promises as fs25 } from "fs";
|
|
15322
|
+
import { join as join56, resolve as resolve2 } from "path";
|
|
15008
15323
|
import { gunzipSync } from "zlib";
|
|
15009
15324
|
var RELATIVE_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json";
|
|
15010
15325
|
var GZIPPED_GRAPH_PATH = "repowise-context/.meta/dependency-graph.json.gz";
|
|
@@ -15021,8 +15336,8 @@ var GraphNotFoundError = class extends Error {
|
|
|
15021
15336
|
var cache = /* @__PURE__ */ new Map();
|
|
15022
15337
|
async function loadGraph(repoRoot = process.cwd()) {
|
|
15023
15338
|
const root = resolve2(repoRoot);
|
|
15024
|
-
const gzPath =
|
|
15025
|
-
const plainPath =
|
|
15339
|
+
const gzPath = join56(root, GZIPPED_GRAPH_PATH);
|
|
15340
|
+
const plainPath = join56(root, RELATIVE_GRAPH_PATH);
|
|
15026
15341
|
const cached = cache.get(root);
|
|
15027
15342
|
if (cached) {
|
|
15028
15343
|
return { graph: cached, path: plainPath, bytes: 0, parseMs: 0, fromCache: true };
|
|
@@ -15030,14 +15345,14 @@ async function loadGraph(repoRoot = process.cwd()) {
|
|
|
15030
15345
|
let graphPath = null;
|
|
15031
15346
|
let raw = null;
|
|
15032
15347
|
try {
|
|
15033
|
-
raw = await
|
|
15348
|
+
raw = await fs25.readFile(gzPath);
|
|
15034
15349
|
graphPath = gzPath;
|
|
15035
15350
|
} catch (err) {
|
|
15036
15351
|
if (err.code !== "ENOENT") throw err;
|
|
15037
15352
|
}
|
|
15038
15353
|
if (!raw) {
|
|
15039
15354
|
try {
|
|
15040
|
-
raw = await
|
|
15355
|
+
raw = await fs25.readFile(plainPath);
|
|
15041
15356
|
graphPath = plainPath;
|
|
15042
15357
|
} catch (err) {
|
|
15043
15358
|
if (err.code === "ENOENT") {
|
|
@@ -15240,7 +15555,7 @@ async function loadService() {
|
|
|
15240
15555
|
return createGraphQueryService(graph);
|
|
15241
15556
|
} catch (err) {
|
|
15242
15557
|
if (err instanceof GraphNotFoundError) {
|
|
15243
|
-
process.stderr.write(
|
|
15558
|
+
process.stderr.write(chalk15.red(`\u2717 ${err.message}
|
|
15244
15559
|
`));
|
|
15245
15560
|
process.exit(err.exitCode);
|
|
15246
15561
|
}
|
|
@@ -15436,14 +15751,14 @@ function registerQueryCommand(program2) {
|
|
|
15436
15751
|
}
|
|
15437
15752
|
|
|
15438
15753
|
// src/commands/uninstall.ts
|
|
15439
|
-
import { promises as
|
|
15440
|
-
import { homedir as
|
|
15441
|
-
import { join as
|
|
15442
|
-
import
|
|
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";
|
|
15443
15758
|
|
|
15444
15759
|
// src/lib/cleanup/marker-blocks.ts
|
|
15445
|
-
import { promises as
|
|
15446
|
-
import { join as
|
|
15760
|
+
import { promises as fs26 } from "fs";
|
|
15761
|
+
import { join as join57 } from "path";
|
|
15447
15762
|
var MARKER_START = "<!-- repowise-start -->";
|
|
15448
15763
|
var MARKER_END = "<!-- repowise-end -->";
|
|
15449
15764
|
var CONTEXT_FILES = [
|
|
@@ -15459,7 +15774,7 @@ var CONTEXT_FILES = [
|
|
|
15459
15774
|
async function stripMarkerBlock(filePath) {
|
|
15460
15775
|
let raw;
|
|
15461
15776
|
try {
|
|
15462
|
-
raw = await
|
|
15777
|
+
raw = await fs26.readFile(filePath, "utf-8");
|
|
15463
15778
|
} catch (err) {
|
|
15464
15779
|
if (err.code === "ENOENT")
|
|
15465
15780
|
return { path: filePath, status: "missing" };
|
|
@@ -15474,16 +15789,16 @@ async function stripMarkerBlock(filePath) {
|
|
|
15474
15789
|
const after = raw.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
|
|
15475
15790
|
const stripped = (before + (before && after ? "\n\n" : "") + after).trim();
|
|
15476
15791
|
if (stripped.length === 0) {
|
|
15477
|
-
await
|
|
15792
|
+
await fs26.unlink(filePath);
|
|
15478
15793
|
return { path: filePath, status: "deleted" };
|
|
15479
15794
|
}
|
|
15480
|
-
await
|
|
15795
|
+
await fs26.writeFile(filePath, stripped + "\n", "utf-8");
|
|
15481
15796
|
return { path: filePath, status: "stripped" };
|
|
15482
15797
|
}
|
|
15483
15798
|
async function stripAllMarkerBlocks(repoRoot) {
|
|
15484
15799
|
const out = [];
|
|
15485
15800
|
for (const relative of CONTEXT_FILES) {
|
|
15486
|
-
const full =
|
|
15801
|
+
const full = join57(repoRoot, relative);
|
|
15487
15802
|
const result = await stripMarkerBlock(full).catch((err) => ({
|
|
15488
15803
|
path: full,
|
|
15489
15804
|
status: "untouched",
|
|
@@ -15495,25 +15810,25 @@ async function stripAllMarkerBlocks(repoRoot) {
|
|
|
15495
15810
|
}
|
|
15496
15811
|
|
|
15497
15812
|
// src/lib/cleanup/mcp-configs.ts
|
|
15498
|
-
import { promises as
|
|
15499
|
-
import { join as
|
|
15813
|
+
import { promises as fs27 } from "fs";
|
|
15814
|
+
import { join as join58 } from "path";
|
|
15500
15815
|
function mcpConfigPaths(repoRoot, home) {
|
|
15501
15816
|
return [
|
|
15502
|
-
|
|
15503
|
-
|
|
15504
|
-
|
|
15505
|
-
|
|
15506
|
-
|
|
15507
|
-
|
|
15508
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
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")
|
|
15511
15826
|
];
|
|
15512
15827
|
}
|
|
15513
15828
|
async function removeRepowiseFromConfig(path, serverName) {
|
|
15514
15829
|
let raw;
|
|
15515
15830
|
try {
|
|
15516
|
-
raw = await
|
|
15831
|
+
raw = await fs27.readFile(path, "utf-8");
|
|
15517
15832
|
} catch (err) {
|
|
15518
15833
|
if (err.code === "ENOENT") return { path, status: "not-found" };
|
|
15519
15834
|
return { path, status: "error", error: err.message };
|
|
@@ -15533,7 +15848,7 @@ async function removeRepowiseFromConfig(path, serverName) {
|
|
|
15533
15848
|
} else {
|
|
15534
15849
|
next.mcpServers = servers;
|
|
15535
15850
|
}
|
|
15536
|
-
await
|
|
15851
|
+
await fs27.writeFile(path, JSON.stringify(next, null, 2) + "\n", "utf-8");
|
|
15537
15852
|
return { path, status: "removed" };
|
|
15538
15853
|
}
|
|
15539
15854
|
async function removeAllMcpEntries(repoRoot, home, repoId) {
|
|
@@ -15546,17 +15861,17 @@ async function removeAllMcpEntries(repoRoot, home, repoId) {
|
|
|
15546
15861
|
}
|
|
15547
15862
|
|
|
15548
15863
|
// src/lib/cleanup/local-state.ts
|
|
15549
|
-
import { promises as
|
|
15550
|
-
import { homedir as
|
|
15551
|
-
import { join as
|
|
15864
|
+
import { promises as fs28 } from "fs";
|
|
15865
|
+
import { homedir as homedir11 } from "os";
|
|
15866
|
+
import { join as join59, resolve as resolve3 } from "path";
|
|
15552
15867
|
async function clearLocalState(homeOverride) {
|
|
15553
|
-
const home = homeOverride ??
|
|
15554
|
-
const target = resolve3(
|
|
15868
|
+
const home = homeOverride ?? homedir11();
|
|
15869
|
+
const target = resolve3(join59(home, ".repowise"));
|
|
15555
15870
|
if (target === resolve3(home) || !target.startsWith(resolve3(home))) {
|
|
15556
15871
|
return { path: target, status: "error", error: "refused: not under home" };
|
|
15557
15872
|
}
|
|
15558
15873
|
try {
|
|
15559
|
-
await
|
|
15874
|
+
await fs28.rm(target, { recursive: true, force: false });
|
|
15560
15875
|
return { path: target, status: "removed" };
|
|
15561
15876
|
} catch (err) {
|
|
15562
15877
|
if (err.code === "ENOENT")
|
|
@@ -15586,7 +15901,7 @@ async function stopAndUninstallService(uninstaller) {
|
|
|
15586
15901
|
// src/commands/uninstall.ts
|
|
15587
15902
|
async function uninstall2(opts = {}) {
|
|
15588
15903
|
const tier = opts.tier ?? "uninstall";
|
|
15589
|
-
const home = opts.home ??
|
|
15904
|
+
const home = opts.home ?? homedir12();
|
|
15590
15905
|
const repoRoot = opts.repoRoot ?? process.cwd();
|
|
15591
15906
|
const loadRepoIds = opts.loadRepoIds ?? defaultLoadRepoIds;
|
|
15592
15907
|
const report = { tier, removed: [], preserved: [], skipped: [] };
|
|
@@ -15595,7 +15910,7 @@ async function uninstall2(opts = {}) {
|
|
|
15595
15910
|
else if (svc.error) report.skipped.push({ path: "listener service", reason: svc.error });
|
|
15596
15911
|
if (tier === "stop") return report;
|
|
15597
15912
|
try {
|
|
15598
|
-
await
|
|
15913
|
+
await fs29.unlink(join60(home, ".repowise", "credentials.json"));
|
|
15599
15914
|
report.removed.push("credentials");
|
|
15600
15915
|
} catch (err) {
|
|
15601
15916
|
if (err.code !== "ENOENT") {
|
|
@@ -15622,7 +15937,7 @@ async function uninstall2(opts = {}) {
|
|
|
15622
15937
|
const allPaths = mcpConfigPaths(repoRoot, home);
|
|
15623
15938
|
for (const p of allPaths) {
|
|
15624
15939
|
try {
|
|
15625
|
-
await
|
|
15940
|
+
await fs29.access(p);
|
|
15626
15941
|
} catch {
|
|
15627
15942
|
}
|
|
15628
15943
|
}
|
|
@@ -15634,7 +15949,7 @@ async function uninstall2(opts = {}) {
|
|
|
15634
15949
|
}
|
|
15635
15950
|
async function defaultLoadRepoIds(home) {
|
|
15636
15951
|
try {
|
|
15637
|
-
const raw = await
|
|
15952
|
+
const raw = await fs29.readFile(join60(home, ".repowise", "config.json"), "utf-8");
|
|
15638
15953
|
const parsed = JSON.parse(raw);
|
|
15639
15954
|
return (parsed.repos ?? []).map((r) => r.repoId);
|
|
15640
15955
|
} catch {
|
|
@@ -15651,29 +15966,29 @@ async function uninstallCommand(opts = {}) {
|
|
|
15651
15966
|
default: false
|
|
15652
15967
|
});
|
|
15653
15968
|
if (!proceed) {
|
|
15654
|
-
process.stderr.write(
|
|
15969
|
+
process.stderr.write(chalk16.yellow("Uninstall cancelled.\n"));
|
|
15655
15970
|
process.exitCode = 1;
|
|
15656
15971
|
return;
|
|
15657
15972
|
}
|
|
15658
15973
|
}
|
|
15659
15974
|
}
|
|
15660
|
-
process.stderr.write(
|
|
15975
|
+
process.stderr.write(chalk16.bold(`RepoWise ${tier}
|
|
15661
15976
|
`));
|
|
15662
15977
|
const report = await uninstall2({ ...opts, tier });
|
|
15663
15978
|
for (const p of report.removed) {
|
|
15664
|
-
process.stderr.write(
|
|
15979
|
+
process.stderr.write(chalk16.green(` \u2713 removed: ${p}
|
|
15665
15980
|
`));
|
|
15666
15981
|
}
|
|
15667
15982
|
for (const p of report.preserved) {
|
|
15668
|
-
process.stderr.write(
|
|
15983
|
+
process.stderr.write(chalk16.gray(` \xB7 preserved: ${p}
|
|
15669
15984
|
`));
|
|
15670
15985
|
}
|
|
15671
15986
|
for (const s of report.skipped) {
|
|
15672
|
-
process.stderr.write(
|
|
15987
|
+
process.stderr.write(chalk16.yellow(` ! skipped: ${s.path} (${s.reason})
|
|
15673
15988
|
`));
|
|
15674
15989
|
}
|
|
15675
15990
|
process.stderr.write(
|
|
15676
|
-
|
|
15991
|
+
chalk16.bold(`
|
|
15677
15992
|
Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
|
|
15678
15993
|
`)
|
|
15679
15994
|
);
|
|
@@ -15681,12 +15996,13 @@ Done \u2014 ${report.removed.length} removed, ${report.skipped.length} skipped.
|
|
|
15681
15996
|
|
|
15682
15997
|
// src/commands/mcp-shim.ts
|
|
15683
15998
|
init_config_dir();
|
|
15684
|
-
import { promises as
|
|
15999
|
+
import { promises as fs30 } from "fs";
|
|
15685
16000
|
import { createInterface as createInterface2 } from "readline";
|
|
15686
|
-
import { join as
|
|
16001
|
+
import { join as join61 } from "path";
|
|
15687
16002
|
var DEFAULT_MAX = 200 * 1024;
|
|
15688
16003
|
async function mcpShim(opts) {
|
|
15689
|
-
const endpointPath = opts.endpointFile ??
|
|
16004
|
+
const endpointPath = opts.endpointFile ?? join61(getConfigDir(), "listener.endpoint");
|
|
16005
|
+
const credsPath = opts.credentialsFile ?? join61(getConfigDir(), "credentials.json");
|
|
15690
16006
|
const stdin = opts.stdin ?? process.stdin;
|
|
15691
16007
|
const stdout = opts.stdout ?? process.stdout;
|
|
15692
16008
|
const stderr = opts.stderr ?? process.stderr;
|
|
@@ -15721,12 +16037,16 @@ async function mcpShim(opts) {
|
|
|
15721
16037
|
}
|
|
15722
16038
|
const info = await readEndpoint(endpointPath);
|
|
15723
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?";
|
|
15724
16047
|
writeJson(stdout, {
|
|
15725
16048
|
jsonrpc: "2.0",
|
|
15726
|
-
error: {
|
|
15727
|
-
code: -32e3,
|
|
15728
|
-
message: "listener endpoint unavailable \u2014 is the listener running?"
|
|
15729
|
-
}
|
|
16049
|
+
error: { code: -32e3, message }
|
|
15730
16050
|
});
|
|
15731
16051
|
continue;
|
|
15732
16052
|
}
|
|
@@ -15759,7 +16079,7 @@ async function mcpShim(opts) {
|
|
|
15759
16079
|
}
|
|
15760
16080
|
async function readEndpoint(path) {
|
|
15761
16081
|
try {
|
|
15762
|
-
const raw = (await
|
|
16082
|
+
const raw = (await fs30.readFile(path, "utf-8")).trim();
|
|
15763
16083
|
if (!raw) return null;
|
|
15764
16084
|
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
15765
16085
|
return { endpoint: raw.split("\n")[0].trim(), secret: null };
|
|
@@ -15777,6 +16097,22 @@ async function readEndpoint(path) {
|
|
|
15777
16097
|
throw err;
|
|
15778
16098
|
}
|
|
15779
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
|
+
}
|
|
15780
16116
|
function writeJson(stream, value) {
|
|
15781
16117
|
stream.write(`${JSON.stringify(value)}
|
|
15782
16118
|
`);
|
|
@@ -16001,7 +16337,7 @@ init_coursier_installer();
|
|
|
16001
16337
|
init_toolchain_installer();
|
|
16002
16338
|
import { spawn as spawn12 } from "child_process";
|
|
16003
16339
|
import { resolve as resolve4 } from "path";
|
|
16004
|
-
import
|
|
16340
|
+
import chalk17 from "chalk";
|
|
16005
16341
|
async function isOnPath(command) {
|
|
16006
16342
|
if (/[^\w./+-]/.test(command)) return false;
|
|
16007
16343
|
const isWin = process.platform === "win32";
|
|
@@ -16019,14 +16355,14 @@ async function isOnPath(command) {
|
|
|
16019
16355
|
async function lspDoctor() {
|
|
16020
16356
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16021
16357
|
if (noColor) {
|
|
16022
|
-
|
|
16358
|
+
chalk17.level = 0;
|
|
16023
16359
|
}
|
|
16024
|
-
const okGlyph = noColor ? "[OK]" :
|
|
16025
|
-
const missingGlyph = noColor ? "[MISSING]" :
|
|
16026
|
-
console.log(
|
|
16027
|
-
console.log(
|
|
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."));
|
|
16028
16364
|
console.log(
|
|
16029
|
-
|
|
16365
|
+
chalk17.dim(
|
|
16030
16366
|
"Type-aware resolution covers 9 languages: Go, Python, Rust, Java, Ruby, Dart, Kotlin, PHP, Scala (Phase 7L + 7L++ + 7L+)."
|
|
16031
16367
|
)
|
|
16032
16368
|
);
|
|
@@ -16058,21 +16394,21 @@ async function lspDoctor() {
|
|
|
16058
16394
|
const missing = reports.filter((r) => r.status === "missing").length;
|
|
16059
16395
|
for (const r of reports) {
|
|
16060
16396
|
const head = r.status === "found" ? okGlyph : missingGlyph;
|
|
16061
|
-
const cmd = r.command ?
|
|
16397
|
+
const cmd = r.command ? chalk17.cyan(r.command) : chalk17.dim(r.candidates.join(" / "));
|
|
16062
16398
|
const effectiveConfigs = getEffectiveConfigList(r.language, process.env, lspOverrides);
|
|
16063
16399
|
const method = describeInstallMethod(effectiveConfigs);
|
|
16064
|
-
console.log(` ${head} ${r.language.padEnd(12)} ${cmd} ${
|
|
16400
|
+
console.log(` ${head} ${r.language.padEnd(12)} ${cmd} ${chalk17.dim(`[${method}]`)}`);
|
|
16065
16401
|
if (r.status === "missing" && r.hint) {
|
|
16066
|
-
console.log(` ${
|
|
16402
|
+
console.log(` ${chalk17.dim("install:")} ${r.hint}`);
|
|
16067
16403
|
}
|
|
16068
16404
|
}
|
|
16069
16405
|
console.log();
|
|
16070
16406
|
console.log(
|
|
16071
|
-
`${
|
|
16407
|
+
`${chalk17.green(found.toString())} found, ${chalk17.yellow(missing.toString())} missing of ${reports.length.toString()} languages.`
|
|
16072
16408
|
);
|
|
16073
16409
|
if (missing > 0) {
|
|
16074
16410
|
console.log(
|
|
16075
|
-
|
|
16411
|
+
chalk17.dim(
|
|
16076
16412
|
"Missing servers degrade LSP-backed MCP tools to AST-only fallback. Run `repowise lsp install` to auto-install."
|
|
16077
16413
|
)
|
|
16078
16414
|
);
|
|
@@ -16089,15 +16425,15 @@ function describeInstallMethod(configs) {
|
|
|
16089
16425
|
}
|
|
16090
16426
|
async function lspInstall(language) {
|
|
16091
16427
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16092
|
-
if (noColor)
|
|
16093
|
-
console.log(
|
|
16428
|
+
if (noColor) chalk17.level = 0;
|
|
16429
|
+
console.log(chalk17.bold("RepoWise LSP install"));
|
|
16094
16430
|
const cliConfig = await getConfig();
|
|
16095
16431
|
const lspOverrides = cliConfig.lspOverrides;
|
|
16096
16432
|
const targets = language ? [language] : Object.keys(LSP_REGISTRY);
|
|
16097
16433
|
for (const lang of targets) {
|
|
16098
16434
|
const configs = getEffectiveConfigList(lang, process.env, lspOverrides);
|
|
16099
16435
|
if (!configs || configs.length === 0) {
|
|
16100
|
-
console.log(` ${
|
|
16436
|
+
console.log(` ${chalk17.red("\u2717")} ${lang} ${chalk17.dim("(unknown language)")}`);
|
|
16101
16437
|
continue;
|
|
16102
16438
|
}
|
|
16103
16439
|
const result = await installOneLanguage(configs);
|
|
@@ -16165,11 +16501,11 @@ async function installOneLanguage(configs) {
|
|
|
16165
16501
|
};
|
|
16166
16502
|
}
|
|
16167
16503
|
function formatInstallLine(lang, outcome) {
|
|
16168
|
-
const glyph = outcome.ok ?
|
|
16169
|
-
const tag =
|
|
16504
|
+
const glyph = outcome.ok ? chalk17.green("\u2713") : chalk17.yellow("\u2717");
|
|
16505
|
+
const tag = chalk17.dim(`[${outcome.method}]`);
|
|
16170
16506
|
console.log(` ${glyph} ${lang.padEnd(12)} ${tag} ${outcome.detail}`);
|
|
16171
16507
|
if (!outcome.ok && outcome.hint) {
|
|
16172
|
-
console.log(` ${
|
|
16508
|
+
console.log(` ${chalk17.dim("install:")} ${outcome.hint}`);
|
|
16173
16509
|
}
|
|
16174
16510
|
}
|
|
16175
16511
|
var MAX_KEEP_WARM_MINUTES = 240;
|
|
@@ -16207,42 +16543,42 @@ async function autoDetectLanguages(cwd) {
|
|
|
16207
16543
|
async function lspWarm(opts = {}) {
|
|
16208
16544
|
const cwd = resolve4(opts.cwd ?? process.cwd());
|
|
16209
16545
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16210
|
-
if (noColor)
|
|
16546
|
+
if (noColor) chalk17.level = 0;
|
|
16211
16547
|
let languages;
|
|
16212
16548
|
if (opts.lang && opts.lang.length > 0) {
|
|
16213
16549
|
const valid = Object.keys(LSP_REGISTRY);
|
|
16214
16550
|
languages = opts.lang.filter((l) => valid.includes(l));
|
|
16215
16551
|
const invalid = opts.lang.filter((l) => !valid.includes(l));
|
|
16216
16552
|
if (invalid.length > 0) {
|
|
16217
|
-
console.log(
|
|
16553
|
+
console.log(chalk17.yellow(`[lsp warm] unknown languages skipped: ${invalid.join(", ")}`));
|
|
16218
16554
|
}
|
|
16219
16555
|
} else {
|
|
16220
16556
|
languages = await autoDetectLanguages(cwd);
|
|
16221
16557
|
if (languages.length === 0) {
|
|
16222
16558
|
console.log(
|
|
16223
|
-
|
|
16559
|
+
chalk17.yellow(
|
|
16224
16560
|
"[lsp warm] no recognised source files in this repo \u2014 pass --lang <id> to force a server."
|
|
16225
16561
|
)
|
|
16226
16562
|
);
|
|
16227
16563
|
return;
|
|
16228
16564
|
}
|
|
16229
|
-
console.log(
|
|
16565
|
+
console.log(chalk17.dim(`[lsp warm] auto-detected languages: ${languages.join(", ")}`));
|
|
16230
16566
|
}
|
|
16231
16567
|
let keepWarmMinutes = 0;
|
|
16232
16568
|
if (typeof opts.keepWarm === "number" && opts.keepWarm > 0) {
|
|
16233
16569
|
keepWarmMinutes = Math.min(opts.keepWarm, MAX_KEEP_WARM_MINUTES);
|
|
16234
16570
|
if (opts.keepWarm > MAX_KEEP_WARM_MINUTES) {
|
|
16235
16571
|
console.log(
|
|
16236
|
-
|
|
16572
|
+
chalk17.yellow(
|
|
16237
16573
|
`[lsp warm] --keep-warm clipped to ${MAX_KEEP_WARM_MINUTES.toString()} minutes (hard cap per Phase 7L plan).`
|
|
16238
16574
|
)
|
|
16239
16575
|
);
|
|
16240
16576
|
}
|
|
16241
16577
|
}
|
|
16242
|
-
console.log(
|
|
16243
|
-
console.log(
|
|
16578
|
+
console.log(chalk17.bold("RepoWise LSP warm \u2014 preview"));
|
|
16579
|
+
console.log(chalk17.dim(`Workspace: ${cwd}`));
|
|
16244
16580
|
console.log(
|
|
16245
|
-
|
|
16581
|
+
chalk17.dim(
|
|
16246
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."
|
|
16247
16583
|
)
|
|
16248
16584
|
);
|
|
@@ -16258,23 +16594,23 @@ async function lspWarm(opts = {}) {
|
|
|
16258
16594
|
if (onPath) {
|
|
16259
16595
|
foundCount += 1;
|
|
16260
16596
|
console.log(
|
|
16261
|
-
|
|
16597
|
+
chalk17.green(` \u2713 ${lang.padEnd(12)} ${config2.displayName} (${config2.command}) on PATH`)
|
|
16262
16598
|
);
|
|
16263
16599
|
} else {
|
|
16264
16600
|
missingCount += 1;
|
|
16265
|
-
console.log(
|
|
16601
|
+
console.log(chalk17.yellow(` \u2717 ${lang.padEnd(12)} ${config2.displayName} not on PATH`));
|
|
16266
16602
|
if (config2.installHint) {
|
|
16267
|
-
console.log(
|
|
16603
|
+
console.log(chalk17.dim(` install: ${config2.installHint}`));
|
|
16268
16604
|
}
|
|
16269
16605
|
}
|
|
16270
16606
|
}
|
|
16271
16607
|
console.log();
|
|
16272
16608
|
console.log(
|
|
16273
|
-
`${
|
|
16609
|
+
`${chalk17.green(foundCount.toString())} ready, ${chalk17.yellow(missingCount.toString())} missing.`
|
|
16274
16610
|
);
|
|
16275
16611
|
if (keepWarmMinutes > 0) {
|
|
16276
16612
|
console.log(
|
|
16277
|
-
|
|
16613
|
+
chalk17.dim(
|
|
16278
16614
|
`Holding for ${keepWarmMinutes.toString()} minute(s) so the listener has time to spawn on its next sync.completed; Ctrl-C to exit early.`
|
|
16279
16615
|
)
|
|
16280
16616
|
);
|
|
@@ -16291,20 +16627,20 @@ async function lspWarm(opts = {}) {
|
|
|
16291
16627
|
}
|
|
16292
16628
|
function lspStatus() {
|
|
16293
16629
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16294
|
-
if (noColor)
|
|
16295
|
-
console.log(
|
|
16630
|
+
if (noColor) chalk17.level = 0;
|
|
16631
|
+
console.log(chalk17.bold("RepoWise LSP status"));
|
|
16296
16632
|
console.log(
|
|
16297
|
-
|
|
16633
|
+
chalk17.dim(
|
|
16298
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+)."
|
|
16299
16635
|
)
|
|
16300
16636
|
);
|
|
16301
16637
|
console.log();
|
|
16302
|
-
console.log(
|
|
16638
|
+
console.log(chalk17.dim("See `repowise lsp doctor` for PATH probe + install hints."));
|
|
16303
16639
|
}
|
|
16304
16640
|
function lspStop() {
|
|
16305
|
-
console.log(
|
|
16641
|
+
console.log(chalk17.bold("RepoWise LSP stop \u2014 preview"));
|
|
16306
16642
|
console.log(
|
|
16307
|
-
|
|
16643
|
+
chalk17.dim(
|
|
16308
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."
|
|
16309
16645
|
)
|
|
16310
16646
|
);
|
|
@@ -16327,29 +16663,29 @@ var ENABLE_TARGETS = {
|
|
|
16327
16663
|
};
|
|
16328
16664
|
async function lspEnable(target) {
|
|
16329
16665
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16330
|
-
if (noColor)
|
|
16666
|
+
if (noColor) chalk17.level = 0;
|
|
16331
16667
|
if (!target) {
|
|
16332
|
-
console.error(
|
|
16333
|
-
console.error(
|
|
16668
|
+
console.error(chalk17.red("Usage: repowise lsp enable <target>"));
|
|
16669
|
+
console.error(chalk17.dim(` Supported targets: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
|
|
16334
16670
|
process.exitCode = 1;
|
|
16335
16671
|
return;
|
|
16336
16672
|
}
|
|
16337
16673
|
const spec = ENABLE_TARGETS[target];
|
|
16338
16674
|
if (!spec) {
|
|
16339
|
-
console.error(
|
|
16340
|
-
console.error(
|
|
16675
|
+
console.error(chalk17.red(`\u2716 Unknown LSP override target: ${target}`));
|
|
16676
|
+
console.error(chalk17.dim(` Supported: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
|
|
16341
16677
|
process.exitCode = 1;
|
|
16342
16678
|
return;
|
|
16343
16679
|
}
|
|
16344
16680
|
const config2 = await getConfig();
|
|
16345
16681
|
const existing = config2.lspOverrides?.[spec.language];
|
|
16346
16682
|
if (existing === spec.name) {
|
|
16347
|
-
console.log(
|
|
16348
|
-
console.log(
|
|
16683
|
+
console.log(chalk17.green(`\u2714 ${spec.displayName} is already enabled.`));
|
|
16684
|
+
console.log(chalk17.dim(` ${spec.postAcceptTip}`));
|
|
16349
16685
|
return;
|
|
16350
16686
|
}
|
|
16351
16687
|
console.log();
|
|
16352
|
-
console.log(
|
|
16688
|
+
console.log(chalk17.cyan.bold(` \u2500\u2500 Enable ${spec.displayName} \u2500\u2500`));
|
|
16353
16689
|
for (const line of spec.consentLines) {
|
|
16354
16690
|
console.log(` ${line}`);
|
|
16355
16691
|
}
|
|
@@ -16361,7 +16697,7 @@ async function lspEnable(target) {
|
|
|
16361
16697
|
});
|
|
16362
16698
|
if (!proceed) {
|
|
16363
16699
|
console.log(
|
|
16364
|
-
|
|
16700
|
+
chalk17.dim(
|
|
16365
16701
|
` Cancelled. ${spec.language.toUpperCase()} will continue using ${spec.revertDisplay}.`
|
|
16366
16702
|
)
|
|
16367
16703
|
);
|
|
@@ -16370,32 +16706,32 @@ async function lspEnable(target) {
|
|
|
16370
16706
|
const nextOverrides = { ...config2.lspOverrides ?? {}, [spec.language]: spec.name };
|
|
16371
16707
|
await mergeAndSaveConfig({ lspOverrides: nextOverrides });
|
|
16372
16708
|
console.log(
|
|
16373
|
-
|
|
16709
|
+
chalk17.green(
|
|
16374
16710
|
` \u2714 ${spec.displayName} enabled \u2014 listener will use it within ~5 min (reconcile cycle) or on next session restart.`
|
|
16375
16711
|
)
|
|
16376
16712
|
);
|
|
16377
|
-
console.log(
|
|
16713
|
+
console.log(chalk17.dim(` \u2139 ${spec.postAcceptTip}`));
|
|
16378
16714
|
}
|
|
16379
16715
|
async function lspDisable(target) {
|
|
16380
16716
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16381
|
-
if (noColor)
|
|
16717
|
+
if (noColor) chalk17.level = 0;
|
|
16382
16718
|
if (!target) {
|
|
16383
|
-
console.error(
|
|
16384
|
-
console.error(
|
|
16719
|
+
console.error(chalk17.red("Usage: repowise lsp disable <target>"));
|
|
16720
|
+
console.error(chalk17.dim(` Supported targets: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
|
|
16385
16721
|
process.exitCode = 1;
|
|
16386
16722
|
return;
|
|
16387
16723
|
}
|
|
16388
16724
|
const spec = ENABLE_TARGETS[target];
|
|
16389
16725
|
if (!spec) {
|
|
16390
|
-
console.error(
|
|
16391
|
-
console.error(
|
|
16726
|
+
console.error(chalk17.red(`\u2716 Unknown LSP override target: ${target}`));
|
|
16727
|
+
console.error(chalk17.dim(` Supported: ${Object.keys(ENABLE_TARGETS).join(", ")}`));
|
|
16392
16728
|
process.exitCode = 1;
|
|
16393
16729
|
return;
|
|
16394
16730
|
}
|
|
16395
16731
|
const config2 = await getConfig();
|
|
16396
16732
|
const current = config2.lspOverrides?.[spec.language];
|
|
16397
16733
|
if (current !== spec.name) {
|
|
16398
|
-
console.log(
|
|
16734
|
+
console.log(chalk17.dim(` ${spec.displayName} is not enabled \u2014 nothing to do.`));
|
|
16399
16735
|
return;
|
|
16400
16736
|
}
|
|
16401
16737
|
const { [spec.language]: _removed, ...rest } = config2.lspOverrides ?? {};
|
|
@@ -16405,28 +16741,28 @@ async function lspDisable(target) {
|
|
|
16405
16741
|
...nextOverrides ? { lspOverrides: nextOverrides } : { lspOverrides: void 0 }
|
|
16406
16742
|
});
|
|
16407
16743
|
console.log(
|
|
16408
|
-
|
|
16744
|
+
chalk17.green(
|
|
16409
16745
|
` \u2714 ${spec.displayName} disabled \u2014 listener will revert to ${spec.revertDisplay} within ~5 min or on next session restart.`
|
|
16410
16746
|
)
|
|
16411
16747
|
);
|
|
16412
16748
|
}
|
|
16413
16749
|
async function lspOff() {
|
|
16414
16750
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16415
|
-
if (noColor)
|
|
16751
|
+
if (noColor) chalk17.level = 0;
|
|
16416
16752
|
await mergeAndSaveConfig({ lspEnabled: false });
|
|
16417
16753
|
console.log(
|
|
16418
|
-
|
|
16754
|
+
chalk17.green(
|
|
16419
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."
|
|
16420
16756
|
)
|
|
16421
16757
|
);
|
|
16422
|
-
console.log(
|
|
16758
|
+
console.log(chalk17.dim(" Re-enable with: repowise lsp on"));
|
|
16423
16759
|
}
|
|
16424
16760
|
async function lspOn() {
|
|
16425
16761
|
const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY;
|
|
16426
|
-
if (noColor)
|
|
16762
|
+
if (noColor) chalk17.level = 0;
|
|
16427
16763
|
await mergeAndSaveConfig({ lspEnabled: true });
|
|
16428
16764
|
console.log(
|
|
16429
|
-
|
|
16765
|
+
chalk17.green(
|
|
16430
16766
|
" \u2714 LSP subsystem enabled. The listener will install + warm language servers for your repos within ~5 min (reconcile) or on next restart."
|
|
16431
16767
|
)
|
|
16432
16768
|
);
|
|
@@ -16435,7 +16771,7 @@ async function lspOn() {
|
|
|
16435
16771
|
// bin/repowise.ts
|
|
16436
16772
|
var __filename = fileURLToPath4(import.meta.url);
|
|
16437
16773
|
var __dirname = dirname23(__filename);
|
|
16438
|
-
var pkg = JSON.parse(readFileSync3(
|
|
16774
|
+
var pkg = JSON.parse(readFileSync3(join62(__dirname, "..", "..", "package.json"), "utf-8"));
|
|
16439
16775
|
var program = new Command();
|
|
16440
16776
|
program.name(getPackageName()).description("AI-optimized codebase context generator").version(pkg.version).hook("preAction", async () => {
|
|
16441
16777
|
await showWelcome(pkg.version);
|
|
@@ -16511,6 +16847,15 @@ lspCommand.command("off").description("Disable the LSP subsystem entirely (kill-
|
|
|
16511
16847
|
lspCommand.command("on").description("Re-enable the LSP subsystem after `lsp off`").action(async () => {
|
|
16512
16848
|
await lspOn();
|
|
16513
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
|
+
});
|
|
16514
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) => {
|
|
16515
16860
|
const tier = options.tier ?? "uninstall";
|
|
16516
16861
|
await uninstallCommand({ tier, yes: options.yes });
|