@polterware/polter 0.2.4 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +30 -4
- package/dist/{chunk-2B2UWOVQ.js → chunk-7MIUDIAI.js} +711 -46
- package/dist/index.js +2112 -566
- package/dist/mcp.js +394 -9
- package/package.json +1 -1
- /package/dist/{chunk-EB76TTZL.js → chunk-AGVTFYXU.js} +0 -0
|
@@ -673,12 +673,42 @@ var gitCommands = [
|
|
|
673
673
|
{ id: "git:merge", tool: "git", base: ["merge"], label: "merge", hint: "Merge branches" }
|
|
674
674
|
];
|
|
675
675
|
|
|
676
|
+
// src/data/commands/pkg.ts
|
|
677
|
+
var pkgCommands = [
|
|
678
|
+
// Build & Publish
|
|
679
|
+
{ id: "pkg:build", tool: "pkg", base: ["run", "build"], label: "build", hint: "Run build script" },
|
|
680
|
+
{ id: "pkg:publish", tool: "pkg", base: ["publish"], label: "publish", hint: "Publish package to registry" },
|
|
681
|
+
{ id: "pkg:pack", tool: "pkg", base: ["pack"], label: "pack", hint: "Create tarball from package" },
|
|
682
|
+
{ id: "pkg:version:patch", tool: "pkg", base: ["version", "patch"], label: "version patch", hint: "Bump patch version" },
|
|
683
|
+
{ id: "pkg:version:minor", tool: "pkg", base: ["version", "minor"], label: "version minor", hint: "Bump minor version" },
|
|
684
|
+
{ id: "pkg:version:major", tool: "pkg", base: ["version", "major"], label: "version major", hint: "Bump major version" },
|
|
685
|
+
{ id: "pkg:login", tool: "pkg", base: ["login"], label: "login", hint: "Log in to registry" },
|
|
686
|
+
{ id: "pkg:logout", tool: "pkg", base: ["logout"], label: "logout", hint: "Log out from registry" },
|
|
687
|
+
// Dependency Management
|
|
688
|
+
{ id: "pkg:install", tool: "pkg", base: ["install"], label: "install", hint: "Install all dependencies" },
|
|
689
|
+
{ id: "pkg:add", tool: "pkg", base: ["add"], label: "add", hint: "Add a dependency" },
|
|
690
|
+
{ id: "pkg:remove", tool: "pkg", base: ["remove"], label: "remove", hint: "Remove a dependency" },
|
|
691
|
+
{ id: "pkg:update", tool: "pkg", base: ["update"], label: "update", hint: "Update dependencies" },
|
|
692
|
+
{ id: "pkg:outdated", tool: "pkg", base: ["outdated"], label: "outdated", hint: "List outdated packages" },
|
|
693
|
+
{ id: "pkg:audit", tool: "pkg", base: ["audit"], label: "audit", hint: "Run security audit" },
|
|
694
|
+
{ id: "pkg:ls", tool: "pkg", base: ["ls"], label: "ls", hint: "List installed packages" },
|
|
695
|
+
// Registry & Config
|
|
696
|
+
{ id: "pkg:config:list", tool: "pkg", base: ["config", "list"], label: "config list", hint: "Show config" },
|
|
697
|
+
{ id: "pkg:whoami", tool: "pkg", base: ["whoami"], label: "whoami", hint: "Show logged-in user" },
|
|
698
|
+
// Scripts & Info
|
|
699
|
+
{ id: "pkg:run", tool: "pkg", base: ["run"], label: "run", hint: "Run a package script" },
|
|
700
|
+
{ id: "pkg:info", tool: "pkg", base: ["info"], label: "info", hint: "Show package info" },
|
|
701
|
+
{ id: "pkg:search", tool: "pkg", base: ["search"], label: "search", hint: "Search packages in registry" },
|
|
702
|
+
{ id: "pkg:init", tool: "pkg", base: ["init"], label: "init", hint: "Initialize a new package.json" }
|
|
703
|
+
];
|
|
704
|
+
|
|
676
705
|
// src/data/commands/index.ts
|
|
677
706
|
var allCommands = [
|
|
678
707
|
...supabaseCommands,
|
|
679
708
|
...ghCommands,
|
|
680
709
|
...vercelCommands,
|
|
681
|
-
...gitCommands
|
|
710
|
+
...gitCommands,
|
|
711
|
+
...pkgCommands
|
|
682
712
|
];
|
|
683
713
|
var commandById = new Map(allCommands.map((cmd) => [cmd.id, cmd]));
|
|
684
714
|
var commandByValue = new Map(
|
|
@@ -703,11 +733,10 @@ function findCommandByValue(value) {
|
|
|
703
733
|
}
|
|
704
734
|
|
|
705
735
|
// src/data/features.ts
|
|
736
|
+
var allSources = [...supabaseCommands, ...ghCommands, ...vercelCommands, ...gitCommands, ...pkgCommands];
|
|
706
737
|
function pick(ids) {
|
|
707
738
|
const idSet = new Set(ids);
|
|
708
|
-
return
|
|
709
|
-
(cmd) => idSet.has(cmd.id)
|
|
710
|
-
);
|
|
739
|
+
return allSources.filter((cmd) => idSet.has(cmd.id));
|
|
711
740
|
}
|
|
712
741
|
var features = [
|
|
713
742
|
{
|
|
@@ -746,7 +775,7 @@ var features = [
|
|
|
746
775
|
},
|
|
747
776
|
{
|
|
748
777
|
id: "repo",
|
|
749
|
-
icon: "\u{
|
|
778
|
+
icon: "\u{1F5C3}\uFE0F",
|
|
750
779
|
label: "Repo",
|
|
751
780
|
commands: pick([
|
|
752
781
|
"git:status",
|
|
@@ -822,6 +851,34 @@ var features = [
|
|
|
822
851
|
"vercel:domains:rm"
|
|
823
852
|
])
|
|
824
853
|
},
|
|
854
|
+
{
|
|
855
|
+
id: "packages",
|
|
856
|
+
icon: "\u{1F4CB}",
|
|
857
|
+
label: "Packages",
|
|
858
|
+
commands: pick([
|
|
859
|
+
"pkg:install",
|
|
860
|
+
"pkg:add",
|
|
861
|
+
"pkg:remove",
|
|
862
|
+
"pkg:update",
|
|
863
|
+
"pkg:outdated",
|
|
864
|
+
"pkg:audit",
|
|
865
|
+
"pkg:ls",
|
|
866
|
+
"pkg:build",
|
|
867
|
+
"pkg:run",
|
|
868
|
+
"pkg:publish",
|
|
869
|
+
"pkg:pack",
|
|
870
|
+
"pkg:version:patch",
|
|
871
|
+
"pkg:version:minor",
|
|
872
|
+
"pkg:version:major",
|
|
873
|
+
"pkg:login",
|
|
874
|
+
"pkg:logout",
|
|
875
|
+
"pkg:config:list",
|
|
876
|
+
"pkg:whoami",
|
|
877
|
+
"pkg:info",
|
|
878
|
+
"pkg:search",
|
|
879
|
+
"pkg:init"
|
|
880
|
+
])
|
|
881
|
+
},
|
|
825
882
|
{
|
|
826
883
|
id: "setup",
|
|
827
884
|
icon: "\u2699\uFE0F",
|
|
@@ -864,7 +921,12 @@ var toolFlags = {
|
|
|
864
921
|
{ value: "--yes", label: "--yes", hint: "Skip confirmation prompts" },
|
|
865
922
|
{ value: "--debug", label: "--debug", hint: "Debug mode" }
|
|
866
923
|
],
|
|
867
|
-
git: []
|
|
924
|
+
git: [],
|
|
925
|
+
pkg: [
|
|
926
|
+
{ value: "--save-dev", label: "--save-dev", hint: "Save as dev dependency" },
|
|
927
|
+
{ value: "--global", label: "--global", hint: "Install globally" },
|
|
928
|
+
{ value: "--dry-run", label: "--dry-run", hint: "Show what would happen" }
|
|
929
|
+
]
|
|
868
930
|
};
|
|
869
931
|
function getFlagsForTool(toolId) {
|
|
870
932
|
return toolFlags[toolId] ?? globalFlags;
|
|
@@ -923,20 +985,20 @@ function resolveSupabaseCommand(startDir = process.cwd(), env = process.env) {
|
|
|
923
985
|
localBinDir
|
|
924
986
|
};
|
|
925
987
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
988
|
+
function runCommand(execution, args, cwd = process.cwd(), options) {
|
|
989
|
+
let stdout = "";
|
|
990
|
+
let stderr = "";
|
|
991
|
+
const resolvedExecution = typeof execution === "string" ? { command: execution } : execution;
|
|
992
|
+
const child = spawn(resolvedExecution.command, args, {
|
|
993
|
+
cwd,
|
|
994
|
+
env: resolvedExecution.env,
|
|
995
|
+
shell: true,
|
|
996
|
+
stdio: [options?.quiet ? "pipe" : "inherit", "pipe", "pipe"]
|
|
997
|
+
});
|
|
998
|
+
if (options?.quiet) {
|
|
999
|
+
child.stdin?.end();
|
|
1000
|
+
}
|
|
1001
|
+
const promise = new Promise((resolve3) => {
|
|
940
1002
|
child.stdout?.on("data", (data) => {
|
|
941
1003
|
const text = data.toString();
|
|
942
1004
|
stdout += text;
|
|
@@ -960,6 +1022,10 @@ async function runCommand(execution, args, cwd = process.cwd(), options) {
|
|
|
960
1022
|
resolve3({ exitCode: code, signal, stdout, stderr });
|
|
961
1023
|
});
|
|
962
1024
|
});
|
|
1025
|
+
return {
|
|
1026
|
+
promise,
|
|
1027
|
+
abort: () => child.kill("SIGTERM")
|
|
1028
|
+
};
|
|
963
1029
|
}
|
|
964
1030
|
function runInteractiveCommand(execution, args, cwd = process.cwd()) {
|
|
965
1031
|
const resolved = typeof execution === "string" ? { command: execution } : execution;
|
|
@@ -978,12 +1044,126 @@ function runInteractiveCommand(execution, args, cwd = process.cwd()) {
|
|
|
978
1044
|
};
|
|
979
1045
|
}
|
|
980
1046
|
async function runSupabaseCommand(args, cwd = process.cwd()) {
|
|
981
|
-
return runCommand(resolveSupabaseCommand(cwd), args, cwd);
|
|
1047
|
+
return runCommand(resolveSupabaseCommand(cwd), args, cwd).promise;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// src/lib/pkgManager.ts
|
|
1051
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1052
|
+
import { join as join2, dirname as dirname2 } from "path";
|
|
1053
|
+
var LOCK_FILES = [
|
|
1054
|
+
{ file: "bun.lockb", id: "bun" },
|
|
1055
|
+
{ file: "bun.lock", id: "bun" },
|
|
1056
|
+
{ file: "pnpm-lock.yaml", id: "pnpm" },
|
|
1057
|
+
{ file: "yarn.lock", id: "yarn" },
|
|
1058
|
+
{ file: "package-lock.json", id: "npm" }
|
|
1059
|
+
];
|
|
1060
|
+
var MANAGER_META = {
|
|
1061
|
+
npm: { lockFile: "package-lock.json", command: "npm" },
|
|
1062
|
+
pnpm: { lockFile: "pnpm-lock.yaml", command: "pnpm" },
|
|
1063
|
+
yarn: { lockFile: "yarn.lock", command: "yarn" },
|
|
1064
|
+
bun: { lockFile: "bun.lockb", command: "bun" }
|
|
1065
|
+
};
|
|
1066
|
+
function detectPkgManager(cwd = process.cwd()) {
|
|
1067
|
+
let dir = cwd;
|
|
1068
|
+
const root = dirname2(dir) === dir ? dir : void 0;
|
|
1069
|
+
while (true) {
|
|
1070
|
+
for (const { file, id } of LOCK_FILES) {
|
|
1071
|
+
if (existsSync2(join2(dir, file))) {
|
|
1072
|
+
const meta = MANAGER_META[id];
|
|
1073
|
+
return { id, lockFile: meta.lockFile, command: meta.command };
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
const parent = dirname2(dir);
|
|
1077
|
+
if (parent === dir || parent === root) break;
|
|
1078
|
+
dir = parent;
|
|
1079
|
+
}
|
|
1080
|
+
return { id: "npm", lockFile: "package-lock.json", command: "npm" };
|
|
1081
|
+
}
|
|
1082
|
+
var TRANSLATIONS = {
|
|
1083
|
+
"install": {},
|
|
1084
|
+
// same for all
|
|
1085
|
+
"add": {
|
|
1086
|
+
npm: ["install"]
|
|
1087
|
+
},
|
|
1088
|
+
"remove": {
|
|
1089
|
+
npm: ["uninstall"]
|
|
1090
|
+
},
|
|
1091
|
+
"run": {},
|
|
1092
|
+
// same for all
|
|
1093
|
+
"publish": {
|
|
1094
|
+
bun: null
|
|
1095
|
+
},
|
|
1096
|
+
"audit": {
|
|
1097
|
+
bun: null
|
|
1098
|
+
},
|
|
1099
|
+
"outdated": {
|
|
1100
|
+
bun: null
|
|
1101
|
+
},
|
|
1102
|
+
"ls": {
|
|
1103
|
+
yarn: ["list"],
|
|
1104
|
+
bun: null
|
|
1105
|
+
},
|
|
1106
|
+
"pack": {
|
|
1107
|
+
bun: null
|
|
1108
|
+
},
|
|
1109
|
+
"version": {},
|
|
1110
|
+
// same for all
|
|
1111
|
+
"login": {
|
|
1112
|
+
bun: null
|
|
1113
|
+
},
|
|
1114
|
+
"logout": {
|
|
1115
|
+
bun: null
|
|
1116
|
+
},
|
|
1117
|
+
"config": {},
|
|
1118
|
+
// same for all
|
|
1119
|
+
"whoami": {
|
|
1120
|
+
bun: null
|
|
1121
|
+
},
|
|
1122
|
+
"info": {
|
|
1123
|
+
npm: ["info"],
|
|
1124
|
+
pnpm: ["info"],
|
|
1125
|
+
yarn: ["info"],
|
|
1126
|
+
bun: null
|
|
1127
|
+
},
|
|
1128
|
+
"search": {
|
|
1129
|
+
pnpm: null,
|
|
1130
|
+
yarn: null,
|
|
1131
|
+
bun: null
|
|
1132
|
+
},
|
|
1133
|
+
"init": {}
|
|
1134
|
+
// same for all
|
|
1135
|
+
};
|
|
1136
|
+
var UnsupportedCommandError = class extends Error {
|
|
1137
|
+
constructor(command, manager) {
|
|
1138
|
+
super(`"${command}" is not supported by ${manager}`);
|
|
1139
|
+
this.name = "UnsupportedCommandError";
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
function translateCommand(base, manager) {
|
|
1143
|
+
const [subcommand, ...rest] = base;
|
|
1144
|
+
if (!subcommand) {
|
|
1145
|
+
return { command: manager, args: [] };
|
|
1146
|
+
}
|
|
1147
|
+
const translation = TRANSLATIONS[subcommand];
|
|
1148
|
+
if (translation) {
|
|
1149
|
+
const managerEntry = translation[manager];
|
|
1150
|
+
if (managerEntry === null) {
|
|
1151
|
+
throw new UnsupportedCommandError(subcommand, manager);
|
|
1152
|
+
}
|
|
1153
|
+
if (managerEntry !== void 0) {
|
|
1154
|
+
return { command: manager, args: [...managerEntry, ...rest] };
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
return { command: manager, args: [subcommand, ...rest] };
|
|
1158
|
+
}
|
|
1159
|
+
function resolvePkgArgs(base, cwd = process.cwd()) {
|
|
1160
|
+
const mgr = detectPkgManager(cwd);
|
|
1161
|
+
return translateCommand(base, mgr.id);
|
|
982
1162
|
}
|
|
983
1163
|
|
|
984
1164
|
// src/lib/toolResolver.ts
|
|
985
|
-
import { existsSync as
|
|
986
|
-
import { join as
|
|
1165
|
+
import { existsSync as existsSync3, readFileSync } from "fs";
|
|
1166
|
+
import { join as join3 } from "path";
|
|
987
1167
|
|
|
988
1168
|
// src/lib/system.ts
|
|
989
1169
|
import { execSync } from "child_process";
|
|
@@ -1012,6 +1192,13 @@ function resolveToolCommand(toolId, cwd = process.cwd()) {
|
|
|
1012
1192
|
source: resolved.source
|
|
1013
1193
|
};
|
|
1014
1194
|
}
|
|
1195
|
+
if (toolId === "pkg") {
|
|
1196
|
+
const mgr = detectPkgManager(cwd);
|
|
1197
|
+
if (!commandExists(mgr.command)) {
|
|
1198
|
+
return { command: mgr.command, source: "not-found" };
|
|
1199
|
+
}
|
|
1200
|
+
return { command: mgr.command, env: { ...process.env }, source: "path" };
|
|
1201
|
+
}
|
|
1015
1202
|
const command = toolId;
|
|
1016
1203
|
if (!commandExists(command)) {
|
|
1017
1204
|
return { command, source: "not-found" };
|
|
@@ -1029,6 +1216,10 @@ function getToolVersion(toolId) {
|
|
|
1029
1216
|
return execCapture("vercel --version").split("\n")[0]?.trim();
|
|
1030
1217
|
case "git":
|
|
1031
1218
|
return execCapture("git --version").replace(/^git version\s+/i, "").trim();
|
|
1219
|
+
case "pkg": {
|
|
1220
|
+
const mgr = detectPkgManager();
|
|
1221
|
+
return execCapture(`${mgr.command} --version`).split("\n")[0]?.trim();
|
|
1222
|
+
}
|
|
1032
1223
|
default:
|
|
1033
1224
|
return void 0;
|
|
1034
1225
|
}
|
|
@@ -1044,9 +1235,11 @@ function getToolInfo(toolId) {
|
|
|
1044
1235
|
supabase: "Supabase CLI",
|
|
1045
1236
|
gh: "GitHub CLI",
|
|
1046
1237
|
vercel: "Vercel CLI",
|
|
1047
|
-
git: "Git"
|
|
1238
|
+
git: "Git",
|
|
1239
|
+
pkg: "Package Manager"
|
|
1048
1240
|
};
|
|
1049
|
-
const
|
|
1241
|
+
const commandName = toolId === "supabase" ? "supabase" : toolId === "pkg" ? detectPkgManager().command : toolId;
|
|
1242
|
+
const installed = commandExists(commandName);
|
|
1050
1243
|
const version = installed ? getToolVersion(toolId) : void 0;
|
|
1051
1244
|
const info = {
|
|
1052
1245
|
id: toolId,
|
|
@@ -1059,8 +1252,8 @@ function getToolInfo(toolId) {
|
|
|
1059
1252
|
}
|
|
1060
1253
|
function detectSupabaseLink(cwd) {
|
|
1061
1254
|
try {
|
|
1062
|
-
const configPath =
|
|
1063
|
-
if (
|
|
1255
|
+
const configPath = join3(cwd, ".supabase", "config.toml");
|
|
1256
|
+
if (existsSync3(configPath)) {
|
|
1064
1257
|
const content = readFileSync(configPath, "utf-8");
|
|
1065
1258
|
const match = content.match(/project_id\s*=\s*"([^"]+)"/);
|
|
1066
1259
|
return { linked: true, project: match?.[1] };
|
|
@@ -1071,8 +1264,8 @@ function detectSupabaseLink(cwd) {
|
|
|
1071
1264
|
}
|
|
1072
1265
|
function detectVercelLink(cwd) {
|
|
1073
1266
|
try {
|
|
1074
|
-
const projectPath =
|
|
1075
|
-
if (
|
|
1267
|
+
const projectPath = join3(cwd, ".vercel", "project.json");
|
|
1268
|
+
if (existsSync3(projectPath)) {
|
|
1076
1269
|
const data = JSON.parse(readFileSync(projectPath, "utf-8"));
|
|
1077
1270
|
return { linked: true, project: data.projectId };
|
|
1078
1271
|
}
|
|
@@ -1080,6 +1273,18 @@ function detectVercelLink(cwd) {
|
|
|
1080
1273
|
}
|
|
1081
1274
|
return { linked: false };
|
|
1082
1275
|
}
|
|
1276
|
+
function detectPkgLink(cwd) {
|
|
1277
|
+
try {
|
|
1278
|
+
const pkgPath = join3(cwd, "package.json");
|
|
1279
|
+
if (existsSync3(pkgPath)) {
|
|
1280
|
+
const data = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
1281
|
+
const mgr = detectPkgManager(cwd);
|
|
1282
|
+
return { linked: true, project: data.name ? `${data.name} (${mgr.id})` : mgr.id };
|
|
1283
|
+
}
|
|
1284
|
+
} catch {
|
|
1285
|
+
}
|
|
1286
|
+
return { linked: false };
|
|
1287
|
+
}
|
|
1083
1288
|
function detectGhLink(cwd) {
|
|
1084
1289
|
try {
|
|
1085
1290
|
const remote = execCapture(`git -C "${cwd}" remote get-url origin 2>/dev/null`);
|
|
@@ -1108,6 +1313,9 @@ function getToolLinkInfo(toolId, cwd = process.cwd()) {
|
|
|
1108
1313
|
case "gh":
|
|
1109
1314
|
linkStatus = detectGhLink(cwd);
|
|
1110
1315
|
break;
|
|
1316
|
+
case "pkg":
|
|
1317
|
+
linkStatus = detectPkgLink(cwd);
|
|
1318
|
+
break;
|
|
1111
1319
|
}
|
|
1112
1320
|
}
|
|
1113
1321
|
const info = { ...base, ...linkStatus };
|
|
@@ -1115,6 +1323,194 @@ function getToolLinkInfo(toolId, cwd = process.cwd()) {
|
|
|
1115
1323
|
return info;
|
|
1116
1324
|
}
|
|
1117
1325
|
|
|
1326
|
+
// src/lib/processManager.ts
|
|
1327
|
+
import { spawn as spawn2 } from "child_process";
|
|
1328
|
+
var DEFAULT_BUFFER_CAP = 1e3;
|
|
1329
|
+
function createRingBuffer(cap = DEFAULT_BUFFER_CAP) {
|
|
1330
|
+
return { lines: [], cap, totalLines: 0 };
|
|
1331
|
+
}
|
|
1332
|
+
function appendToBuffer(buf, data) {
|
|
1333
|
+
const newLines = data.split("\n");
|
|
1334
|
+
if (newLines.length > 0 && newLines[newLines.length - 1] === "") {
|
|
1335
|
+
newLines.pop();
|
|
1336
|
+
}
|
|
1337
|
+
if (newLines.length === 0) return;
|
|
1338
|
+
buf.lines.push(...newLines);
|
|
1339
|
+
buf.totalLines += newLines.length;
|
|
1340
|
+
if (buf.lines.length > buf.cap) {
|
|
1341
|
+
buf.lines.splice(0, buf.lines.length - buf.cap);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
function tailBuffer(buf, n) {
|
|
1345
|
+
if (n === void 0 || n >= buf.lines.length) return [...buf.lines];
|
|
1346
|
+
return buf.lines.slice(-n);
|
|
1347
|
+
}
|
|
1348
|
+
var registry = /* @__PURE__ */ new Map();
|
|
1349
|
+
var cleanupRegistered = false;
|
|
1350
|
+
function registerCleanup() {
|
|
1351
|
+
if (cleanupRegistered) return;
|
|
1352
|
+
cleanupRegistered = true;
|
|
1353
|
+
const cleanup = () => {
|
|
1354
|
+
for (const proc of registry.values()) {
|
|
1355
|
+
if (proc.status === "running" && proc.child.pid) {
|
|
1356
|
+
try {
|
|
1357
|
+
process.kill(-proc.child.pid, "SIGKILL");
|
|
1358
|
+
} catch {
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
process.on("exit", cleanup);
|
|
1364
|
+
process.on("SIGINT", () => {
|
|
1365
|
+
cleanup();
|
|
1366
|
+
process.exit(130);
|
|
1367
|
+
});
|
|
1368
|
+
process.on("SIGTERM", () => {
|
|
1369
|
+
cleanup();
|
|
1370
|
+
process.exit(143);
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
function generateProcessId(command, args) {
|
|
1374
|
+
const slug = [command, ...args].join("-").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
1375
|
+
if (!registry.has(slug)) return slug;
|
|
1376
|
+
let i = 2;
|
|
1377
|
+
while (registry.has(`${slug}-${i}`)) i++;
|
|
1378
|
+
return `${slug}-${i}`;
|
|
1379
|
+
}
|
|
1380
|
+
function startProcess(id, command, args = [], cwd = process.cwd(), env) {
|
|
1381
|
+
const existing = registry.get(id);
|
|
1382
|
+
if (existing && existing.status === "running") {
|
|
1383
|
+
throw new Error(`Process "${id}" is already running (pid ${existing.child.pid})`);
|
|
1384
|
+
}
|
|
1385
|
+
registerCleanup();
|
|
1386
|
+
const child = spawn2(command, args, {
|
|
1387
|
+
cwd,
|
|
1388
|
+
env: env ?? process.env,
|
|
1389
|
+
detached: true,
|
|
1390
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1391
|
+
shell: true
|
|
1392
|
+
});
|
|
1393
|
+
const tracked = {
|
|
1394
|
+
id,
|
|
1395
|
+
command,
|
|
1396
|
+
args,
|
|
1397
|
+
cwd,
|
|
1398
|
+
child,
|
|
1399
|
+
status: "running",
|
|
1400
|
+
exitCode: null,
|
|
1401
|
+
signal: null,
|
|
1402
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
1403
|
+
exitedAt: null,
|
|
1404
|
+
stdout: createRingBuffer(),
|
|
1405
|
+
stderr: createRingBuffer()
|
|
1406
|
+
};
|
|
1407
|
+
child.stdout?.on("data", (data) => {
|
|
1408
|
+
appendToBuffer(tracked.stdout, data.toString());
|
|
1409
|
+
});
|
|
1410
|
+
child.stderr?.on("data", (data) => {
|
|
1411
|
+
appendToBuffer(tracked.stderr, data.toString());
|
|
1412
|
+
});
|
|
1413
|
+
child.on("exit", (code, signal) => {
|
|
1414
|
+
tracked.status = "exited";
|
|
1415
|
+
tracked.exitCode = code;
|
|
1416
|
+
tracked.signal = signal;
|
|
1417
|
+
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1418
|
+
});
|
|
1419
|
+
child.on("error", (err) => {
|
|
1420
|
+
tracked.status = "errored";
|
|
1421
|
+
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1422
|
+
appendToBuffer(tracked.stderr, `spawn error: ${err.message}
|
|
1423
|
+
`);
|
|
1424
|
+
});
|
|
1425
|
+
registry.set(id, tracked);
|
|
1426
|
+
return toProcessInfo(tracked);
|
|
1427
|
+
}
|
|
1428
|
+
function stopProcess(id) {
|
|
1429
|
+
const proc = registry.get(id);
|
|
1430
|
+
if (!proc) throw new Error(`No tracked process with id "${id}"`);
|
|
1431
|
+
if (proc.status !== "running") return Promise.resolve(toProcessInfo(proc));
|
|
1432
|
+
return new Promise((resolve3) => {
|
|
1433
|
+
const escalateTimer = setTimeout(() => {
|
|
1434
|
+
if (proc.status === "running" && proc.child.pid) {
|
|
1435
|
+
try {
|
|
1436
|
+
process.kill(-proc.child.pid, "SIGKILL");
|
|
1437
|
+
} catch {
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}, 5e3);
|
|
1441
|
+
const onDone = () => {
|
|
1442
|
+
clearTimeout(escalateTimer);
|
|
1443
|
+
resolve3(toProcessInfo(proc));
|
|
1444
|
+
};
|
|
1445
|
+
proc.child.once("exit", onDone);
|
|
1446
|
+
if (proc.child.pid) {
|
|
1447
|
+
try {
|
|
1448
|
+
process.kill(-proc.child.pid, "SIGTERM");
|
|
1449
|
+
} catch {
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (proc.status !== "running") {
|
|
1453
|
+
clearTimeout(escalateTimer);
|
|
1454
|
+
proc.child.removeListener("exit", onDone);
|
|
1455
|
+
resolve3(toProcessInfo(proc));
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
function listProcesses() {
|
|
1460
|
+
return Array.from(registry.values()).map(toProcessInfo);
|
|
1461
|
+
}
|
|
1462
|
+
function getProcessOutput(id, tail, stream) {
|
|
1463
|
+
const proc = registry.get(id);
|
|
1464
|
+
if (!proc) throw new Error(`No tracked process with id "${id}"`);
|
|
1465
|
+
const which = stream ?? "both";
|
|
1466
|
+
return {
|
|
1467
|
+
stdout: which === "stderr" ? [] : tailBuffer(proc.stdout, tail),
|
|
1468
|
+
stderr: which === "stdout" ? [] : tailBuffer(proc.stderr, tail),
|
|
1469
|
+
stdoutLineCount: proc.stdout.totalLines,
|
|
1470
|
+
stderrLineCount: proc.stderr.totalLines
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function isProcessRunning(id) {
|
|
1474
|
+
const proc = registry.get(id);
|
|
1475
|
+
if (!proc) return false;
|
|
1476
|
+
if (proc.status !== "running") return false;
|
|
1477
|
+
if (proc.child.pid) {
|
|
1478
|
+
try {
|
|
1479
|
+
process.kill(proc.child.pid, 0);
|
|
1480
|
+
return true;
|
|
1481
|
+
} catch {
|
|
1482
|
+
return false;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return false;
|
|
1486
|
+
}
|
|
1487
|
+
function removeProcess(id) {
|
|
1488
|
+
const proc = registry.get(id);
|
|
1489
|
+
if (!proc) throw new Error(`No tracked process with id "${id}"`);
|
|
1490
|
+
if (proc.status === "running") {
|
|
1491
|
+
throw new Error(`Cannot remove running process "${id}". Stop it first.`);
|
|
1492
|
+
}
|
|
1493
|
+
registry.delete(id);
|
|
1494
|
+
}
|
|
1495
|
+
function toProcessInfo(proc) {
|
|
1496
|
+
const now = Date.now();
|
|
1497
|
+
const start = proc.startedAt.getTime();
|
|
1498
|
+
const end = proc.exitedAt ? proc.exitedAt.getTime() : now;
|
|
1499
|
+
return {
|
|
1500
|
+
id: proc.id,
|
|
1501
|
+
command: proc.command,
|
|
1502
|
+
args: proc.args,
|
|
1503
|
+
cwd: proc.cwd,
|
|
1504
|
+
pid: proc.child.pid,
|
|
1505
|
+
status: proc.status,
|
|
1506
|
+
exitCode: proc.exitCode,
|
|
1507
|
+
signal: proc.signal,
|
|
1508
|
+
startedAt: proc.startedAt.toISOString(),
|
|
1509
|
+
exitedAt: proc.exitedAt?.toISOString() ?? null,
|
|
1510
|
+
uptime: end - start
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1118
1514
|
// src/pipeline/engine.ts
|
|
1119
1515
|
async function executePipeline(pipeline, onProgress, cwd = process.cwd()) {
|
|
1120
1516
|
const stepResults = pipeline.steps.map((step) => ({
|
|
@@ -1134,13 +1530,20 @@ async function executePipeline(pipeline, onProgress, cwd = process.cwd()) {
|
|
|
1134
1530
|
const cmdDef = getCommandById(step.commandId);
|
|
1135
1531
|
const toolId = cmdDef?.tool ?? "supabase";
|
|
1136
1532
|
const resolved = resolveToolCommand(toolId, cwd);
|
|
1137
|
-
|
|
1533
|
+
let baseArgs = cmdDef?.base ?? [];
|
|
1534
|
+
if (toolId === "pkg" && cmdDef) {
|
|
1535
|
+
try {
|
|
1536
|
+
const translated = resolvePkgArgs(cmdDef.base, cwd);
|
|
1537
|
+
baseArgs = translated.args;
|
|
1538
|
+
} catch {
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1138
1541
|
const allArgs = [...baseArgs, ...step.args, ...step.flags];
|
|
1139
1542
|
const result = await runCommand(
|
|
1140
1543
|
{ command: resolved.command, env: resolved.env },
|
|
1141
1544
|
allArgs,
|
|
1142
1545
|
cwd
|
|
1143
|
-
);
|
|
1546
|
+
).promise;
|
|
1144
1547
|
const success = !result.spawnError && result.exitCode === 0;
|
|
1145
1548
|
stepResults[i] = {
|
|
1146
1549
|
step,
|
|
@@ -1161,23 +1564,23 @@ async function executePipeline(pipeline, onProgress, cwd = process.cwd()) {
|
|
|
1161
1564
|
}
|
|
1162
1565
|
|
|
1163
1566
|
// src/config/projectConfig.ts
|
|
1164
|
-
import { existsSync as
|
|
1165
|
-
import { join as
|
|
1567
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
|
|
1568
|
+
import { join as join5 } from "path";
|
|
1166
1569
|
|
|
1167
1570
|
// src/lib/packageRoot.ts
|
|
1168
|
-
import { existsSync as
|
|
1169
|
-
import { dirname as
|
|
1571
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1572
|
+
import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
|
|
1170
1573
|
var rootCache = /* @__PURE__ */ new Map();
|
|
1171
1574
|
function findNearestPackageRoot(startDir = process.cwd()) {
|
|
1172
1575
|
const resolvedStart = resolve2(startDir);
|
|
1173
1576
|
if (rootCache.has(resolvedStart)) return rootCache.get(resolvedStart);
|
|
1174
1577
|
let currentDir = resolvedStart;
|
|
1175
1578
|
while (true) {
|
|
1176
|
-
if (
|
|
1579
|
+
if (existsSync4(join4(currentDir, "package.json"))) {
|
|
1177
1580
|
rootCache.set(resolvedStart, currentDir);
|
|
1178
1581
|
return currentDir;
|
|
1179
1582
|
}
|
|
1180
|
-
const parentDir =
|
|
1583
|
+
const parentDir = dirname3(currentDir);
|
|
1181
1584
|
if (parentDir === currentDir) {
|
|
1182
1585
|
rootCache.set(resolvedStart, void 0);
|
|
1183
1586
|
return void 0;
|
|
@@ -1199,13 +1602,13 @@ function defaultConfig() {
|
|
|
1199
1602
|
function getProjectConfigPath(startDir) {
|
|
1200
1603
|
const root = findNearestPackageRoot(startDir);
|
|
1201
1604
|
if (!root) return void 0;
|
|
1202
|
-
const dir =
|
|
1203
|
-
return { dir, file:
|
|
1605
|
+
const dir = join5(root, CONFIG_DIR);
|
|
1606
|
+
return { dir, file: join5(dir, CONFIG_FILE) };
|
|
1204
1607
|
}
|
|
1205
1608
|
function readProjectConfig(startDir) {
|
|
1206
1609
|
const paths = getProjectConfigPath(startDir);
|
|
1207
1610
|
if (!paths) return void 0;
|
|
1208
|
-
if (!
|
|
1611
|
+
if (!existsSync5(paths.file)) return void 0;
|
|
1209
1612
|
try {
|
|
1210
1613
|
const raw = readFileSync2(paths.file, "utf-8");
|
|
1211
1614
|
return JSON.parse(raw);
|
|
@@ -1306,8 +1709,8 @@ function findPipelineByName(name, startDir) {
|
|
|
1306
1709
|
}
|
|
1307
1710
|
|
|
1308
1711
|
// src/declarative/parser.ts
|
|
1309
|
-
import { existsSync as
|
|
1310
|
-
import { join as
|
|
1712
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
|
|
1713
|
+
import { join as join6 } from "path";
|
|
1311
1714
|
var YAML_FILE = "polter.yaml";
|
|
1312
1715
|
function parseSimpleYaml(content) {
|
|
1313
1716
|
const lines = content.split("\n");
|
|
@@ -1379,8 +1782,8 @@ function parseValue(raw) {
|
|
|
1379
1782
|
return raw;
|
|
1380
1783
|
}
|
|
1381
1784
|
function findPolterYaml(startDir = process.cwd()) {
|
|
1382
|
-
const filePath =
|
|
1383
|
-
return
|
|
1785
|
+
const filePath = join6(startDir, YAML_FILE);
|
|
1786
|
+
return existsSync6(filePath) ? filePath : void 0;
|
|
1384
1787
|
}
|
|
1385
1788
|
function parsePolterYaml(startDir = process.cwd()) {
|
|
1386
1789
|
const filePath = findPolterYaml(startDir);
|
|
@@ -1517,13 +1920,258 @@ async function applyActions(actions, cwd = process.cwd(), onProgress) {
|
|
|
1517
1920
|
{ command: resolved.command, env: resolved.env },
|
|
1518
1921
|
action.args,
|
|
1519
1922
|
cwd
|
|
1520
|
-
);
|
|
1923
|
+
).promise;
|
|
1521
1924
|
const success = !result.spawnError && result.exitCode === 0;
|
|
1522
1925
|
results.push({ action, success, result });
|
|
1523
1926
|
}
|
|
1524
1927
|
return results;
|
|
1525
1928
|
}
|
|
1526
1929
|
|
|
1930
|
+
// src/lib/mcpInstaller.ts
|
|
1931
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
1932
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync7 } from "fs";
|
|
1933
|
+
import { join as join7, dirname as dirname4 } from "path";
|
|
1934
|
+
import { fileURLToPath } from "url";
|
|
1935
|
+
import { homedir } from "os";
|
|
1936
|
+
import pc from "picocolors";
|
|
1937
|
+
var __dirname = dirname4(fileURLToPath(import.meta.url));
|
|
1938
|
+
function readPkgVersion() {
|
|
1939
|
+
for (const rel of ["../../package.json", "../package.json"]) {
|
|
1940
|
+
const p = join7(__dirname, rel);
|
|
1941
|
+
if (existsSync7(p)) {
|
|
1942
|
+
const pkg = JSON.parse(readFileSync4(p, "utf-8"));
|
|
1943
|
+
return pkg.version;
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
return "0.0.0";
|
|
1947
|
+
}
|
|
1948
|
+
var MCP_ARGS = ["npx", "-y", "-p", "@polterware/polter@latest", "polter-mcp"];
|
|
1949
|
+
var SCOPE_LABELS = {
|
|
1950
|
+
local: "local (this machine)",
|
|
1951
|
+
project: "project (shared via repo)",
|
|
1952
|
+
user: "global (all projects)"
|
|
1953
|
+
};
|
|
1954
|
+
function tryClaudeCli(scope) {
|
|
1955
|
+
if (!commandExists("claude")) return false;
|
|
1956
|
+
const result = spawnSync2("claude", ["mcp", "add", "-s", scope, "polter", "--", ...MCP_ARGS], {
|
|
1957
|
+
stdio: "inherit",
|
|
1958
|
+
shell: true
|
|
1959
|
+
});
|
|
1960
|
+
return result.status === 0;
|
|
1961
|
+
}
|
|
1962
|
+
function getSettingsPath(scope) {
|
|
1963
|
+
if (scope === "project") {
|
|
1964
|
+
return join7(process.cwd(), ".mcp.json");
|
|
1965
|
+
}
|
|
1966
|
+
return join7(homedir(), ".claude", "settings.json");
|
|
1967
|
+
}
|
|
1968
|
+
function tryManualInstall(scope) {
|
|
1969
|
+
const settingsPath = getSettingsPath(scope);
|
|
1970
|
+
const dir = join7(settingsPath, "..");
|
|
1971
|
+
let settings = {};
|
|
1972
|
+
if (existsSync7(settingsPath)) {
|
|
1973
|
+
try {
|
|
1974
|
+
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
1975
|
+
} catch {
|
|
1976
|
+
process.stderr.write(pc.red(`Failed to parse ${settingsPath}
|
|
1977
|
+
`));
|
|
1978
|
+
return false;
|
|
1979
|
+
}
|
|
1980
|
+
} else {
|
|
1981
|
+
mkdirSync2(dir, { recursive: true });
|
|
1982
|
+
}
|
|
1983
|
+
const mcpServers = settings.mcpServers ?? {};
|
|
1984
|
+
mcpServers.polter = {
|
|
1985
|
+
command: "npx",
|
|
1986
|
+
args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"]
|
|
1987
|
+
};
|
|
1988
|
+
settings.mcpServers = mcpServers;
|
|
1989
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1990
|
+
return true;
|
|
1991
|
+
}
|
|
1992
|
+
async function installMcpServer(scope) {
|
|
1993
|
+
process.stdout.write(pc.bold(`Installing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
|
|
1994
|
+
|
|
1995
|
+
`));
|
|
1996
|
+
if (commandExists("claude")) {
|
|
1997
|
+
process.stdout.write(` Using 'claude mcp add -s ${scope}'...
|
|
1998
|
+
`);
|
|
1999
|
+
if (tryClaudeCli(scope)) {
|
|
2000
|
+
process.stdout.write(pc.green("\n Done! Restart Claude Code to use Polter tools.\n"));
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
process.stdout.write(pc.yellow(" 'claude mcp add' failed, falling back to manual install...\n\n"));
|
|
2004
|
+
}
|
|
2005
|
+
const settingsPath = getSettingsPath(scope);
|
|
2006
|
+
process.stdout.write(` Writing to ${settingsPath}...
|
|
2007
|
+
`);
|
|
2008
|
+
if (tryManualInstall(scope)) {
|
|
2009
|
+
process.stdout.write(pc.green("\n Done! Restart Claude Code to use Polter tools.\n"));
|
|
2010
|
+
} else {
|
|
2011
|
+
process.stderr.write(pc.red("\n Failed to install. Add manually:\n\n"));
|
|
2012
|
+
process.stderr.write(` ${pc.dim(JSON.stringify({ mcpServers: { polter: { command: "npx", args: ["-y", "-p", "@polterware/polter@latest", "polter-mcp"] } } }, null, 2))}
|
|
2013
|
+
`);
|
|
2014
|
+
process.exit(1);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
async function removeMcpServer(scope) {
|
|
2018
|
+
process.stdout.write(pc.bold(`Removing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
|
|
2019
|
+
|
|
2020
|
+
`));
|
|
2021
|
+
if (commandExists("claude")) {
|
|
2022
|
+
const result = spawnSync2("claude", ["mcp", "remove", "-s", scope, "polter"], {
|
|
2023
|
+
stdio: "inherit",
|
|
2024
|
+
shell: true
|
|
2025
|
+
});
|
|
2026
|
+
if (result.status === 0) {
|
|
2027
|
+
process.stdout.write(pc.green("\n Done! Polter MCP server removed.\n"));
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
process.stdout.write(pc.yellow(" 'claude mcp remove' failed, falling back to manual removal...\n\n"));
|
|
2031
|
+
}
|
|
2032
|
+
const settingsPath = getSettingsPath(scope);
|
|
2033
|
+
if (!existsSync7(settingsPath)) {
|
|
2034
|
+
process.stdout.write(pc.yellow(" No settings file found. Nothing to remove.\n"));
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
let settings;
|
|
2038
|
+
try {
|
|
2039
|
+
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
2040
|
+
} catch {
|
|
2041
|
+
process.stderr.write(pc.red(` Failed to parse ${settingsPath}
|
|
2042
|
+
`));
|
|
2043
|
+
process.exit(1);
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
const mcpServers = settings.mcpServers;
|
|
2047
|
+
if (!mcpServers?.polter) {
|
|
2048
|
+
process.stdout.write(pc.yellow(" Polter MCP server not found in settings. Nothing to remove.\n"));
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
delete mcpServers.polter;
|
|
2052
|
+
settings.mcpServers = mcpServers;
|
|
2053
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
2054
|
+
process.stdout.write(pc.green(" Done! Polter MCP server removed.\n"));
|
|
2055
|
+
}
|
|
2056
|
+
function getMcpStatusInfo() {
|
|
2057
|
+
const version = readPkgVersion();
|
|
2058
|
+
const scopeDefs = [
|
|
2059
|
+
{ label: "Project (.mcp.json)", path: join7(process.cwd(), ".mcp.json"), scope: "project" },
|
|
2060
|
+
{ label: "User (~/.claude/settings.json)", path: join7(homedir(), ".claude", "settings.json"), scope: "user" }
|
|
2061
|
+
];
|
|
2062
|
+
const scopes = scopeDefs.map((s) => {
|
|
2063
|
+
if (!existsSync7(s.path)) {
|
|
2064
|
+
return { label: s.label, scope: s.scope, registered: false };
|
|
2065
|
+
}
|
|
2066
|
+
try {
|
|
2067
|
+
const settings = JSON.parse(readFileSync4(s.path, "utf-8"));
|
|
2068
|
+
const mcpServers = settings.mcpServers;
|
|
2069
|
+
return { label: s.label, scope: s.scope, registered: !!mcpServers?.polter };
|
|
2070
|
+
} catch {
|
|
2071
|
+
return { label: s.label, scope: s.scope, registered: false, error: "error reading file" };
|
|
2072
|
+
}
|
|
2073
|
+
});
|
|
2074
|
+
return { installedVersion: version, latestVersion: null, scopes };
|
|
2075
|
+
}
|
|
2076
|
+
async function installMcpServerSilent(scope) {
|
|
2077
|
+
const messages = [];
|
|
2078
|
+
messages.push(`Installing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}`);
|
|
2079
|
+
if (commandExists("claude")) {
|
|
2080
|
+
messages.push(`Using 'claude mcp add -s ${scope}'...`);
|
|
2081
|
+
if (tryClaudeCli(scope)) {
|
|
2082
|
+
messages.push("Done! Restart Claude Code to use Polter tools.");
|
|
2083
|
+
return { success: true, message: messages.join("\n") };
|
|
2084
|
+
}
|
|
2085
|
+
messages.push("'claude mcp add' failed, falling back to manual install...");
|
|
2086
|
+
}
|
|
2087
|
+
const settingsPath = getSettingsPath(scope);
|
|
2088
|
+
messages.push(`Writing to ${settingsPath}...`);
|
|
2089
|
+
if (tryManualInstall(scope)) {
|
|
2090
|
+
messages.push("Done! Restart Claude Code to use Polter tools.");
|
|
2091
|
+
return { success: true, message: messages.join("\n") };
|
|
2092
|
+
}
|
|
2093
|
+
return { success: false, message: "Failed to install. Try manual installation." };
|
|
2094
|
+
}
|
|
2095
|
+
async function removeMcpServerSilent(scope) {
|
|
2096
|
+
const messages = [];
|
|
2097
|
+
messages.push(`Removing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}`);
|
|
2098
|
+
if (commandExists("claude")) {
|
|
2099
|
+
const result = spawnSync2("claude", ["mcp", "remove", "-s", scope, "polter"], {
|
|
2100
|
+
encoding: "utf-8",
|
|
2101
|
+
shell: true
|
|
2102
|
+
});
|
|
2103
|
+
if (result.status === 0) {
|
|
2104
|
+
messages.push("Done! Polter MCP server removed.");
|
|
2105
|
+
return { success: true, message: messages.join("\n") };
|
|
2106
|
+
}
|
|
2107
|
+
messages.push("'claude mcp remove' failed, falling back to manual removal...");
|
|
2108
|
+
}
|
|
2109
|
+
const settingsPath = getSettingsPath(scope);
|
|
2110
|
+
if (!existsSync7(settingsPath)) {
|
|
2111
|
+
messages.push("No settings file found. Nothing to remove.");
|
|
2112
|
+
return { success: true, message: messages.join("\n") };
|
|
2113
|
+
}
|
|
2114
|
+
let settings;
|
|
2115
|
+
try {
|
|
2116
|
+
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
2117
|
+
} catch {
|
|
2118
|
+
return { success: false, message: `Failed to parse ${settingsPath}` };
|
|
2119
|
+
}
|
|
2120
|
+
const mcpServers = settings.mcpServers;
|
|
2121
|
+
if (!mcpServers?.polter) {
|
|
2122
|
+
messages.push("Polter MCP server not found in settings. Nothing to remove.");
|
|
2123
|
+
return { success: true, message: messages.join("\n") };
|
|
2124
|
+
}
|
|
2125
|
+
delete mcpServers.polter;
|
|
2126
|
+
settings.mcpServers = mcpServers;
|
|
2127
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
2128
|
+
messages.push("Done! Polter MCP server removed.");
|
|
2129
|
+
return { success: true, message: messages.join("\n") };
|
|
2130
|
+
}
|
|
2131
|
+
async function mcpStatus() {
|
|
2132
|
+
process.stdout.write(pc.bold("Polter MCP Server Status\n\n"));
|
|
2133
|
+
const pkgVersion = readPkgVersion();
|
|
2134
|
+
process.stdout.write(` Installed version: ${pc.cyan(pkgVersion)}
|
|
2135
|
+
`);
|
|
2136
|
+
const latestResult = spawnSync2("npm", ["view", "@polterware/polter", "version"], {
|
|
2137
|
+
encoding: "utf-8",
|
|
2138
|
+
shell: true,
|
|
2139
|
+
timeout: 1e4
|
|
2140
|
+
});
|
|
2141
|
+
const latest = latestResult.stdout?.trim();
|
|
2142
|
+
if (latest) {
|
|
2143
|
+
const upToDate = latest === pkgVersion;
|
|
2144
|
+
process.stdout.write(` Latest version: ${upToDate ? pc.green(latest) : pc.yellow(`${latest} (update available)`)}
|
|
2145
|
+
`);
|
|
2146
|
+
}
|
|
2147
|
+
process.stdout.write("\n");
|
|
2148
|
+
const scopes = [
|
|
2149
|
+
{ label: "Project (.mcp.json)", path: join7(process.cwd(), ".mcp.json"), key: "project" },
|
|
2150
|
+
{ label: "User (~/.claude/settings.json)", path: join7(homedir(), ".claude", "settings.json"), key: "user" }
|
|
2151
|
+
];
|
|
2152
|
+
for (const scope of scopes) {
|
|
2153
|
+
if (!existsSync7(scope.path)) {
|
|
2154
|
+
process.stdout.write(` ${scope.label}: ${pc.dim("not found")}
|
|
2155
|
+
`);
|
|
2156
|
+
continue;
|
|
2157
|
+
}
|
|
2158
|
+
try {
|
|
2159
|
+
const settings = JSON.parse(readFileSync4(scope.path, "utf-8"));
|
|
2160
|
+
const mcpServers = settings.mcpServers;
|
|
2161
|
+
if (mcpServers?.polter) {
|
|
2162
|
+
process.stdout.write(` ${scope.label}: ${pc.green("registered")}
|
|
2163
|
+
`);
|
|
2164
|
+
} else {
|
|
2165
|
+
process.stdout.write(` ${scope.label}: ${pc.dim("not registered")}
|
|
2166
|
+
`);
|
|
2167
|
+
}
|
|
2168
|
+
} catch {
|
|
2169
|
+
process.stdout.write(` ${scope.label}: ${pc.red("error reading file")}
|
|
2170
|
+
`);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
|
|
1527
2175
|
export {
|
|
1528
2176
|
__export,
|
|
1529
2177
|
allCommands,
|
|
@@ -1539,9 +2187,19 @@ export {
|
|
|
1539
2187
|
runInteractiveCommand,
|
|
1540
2188
|
runSupabaseCommand,
|
|
1541
2189
|
commandExists,
|
|
2190
|
+
detectPkgManager,
|
|
2191
|
+
translateCommand,
|
|
2192
|
+
resolvePkgArgs,
|
|
1542
2193
|
resolveToolCommand,
|
|
1543
2194
|
getToolInfo,
|
|
1544
2195
|
getToolLinkInfo,
|
|
2196
|
+
generateProcessId,
|
|
2197
|
+
startProcess,
|
|
2198
|
+
stopProcess,
|
|
2199
|
+
listProcesses,
|
|
2200
|
+
getProcessOutput,
|
|
2201
|
+
isProcessRunning,
|
|
2202
|
+
removeProcess,
|
|
1545
2203
|
executePipeline,
|
|
1546
2204
|
findNearestPackageRoot,
|
|
1547
2205
|
getProjectConfigPath,
|
|
@@ -1551,8 +2209,15 @@ export {
|
|
|
1551
2209
|
savePipeline,
|
|
1552
2210
|
deletePipeline,
|
|
1553
2211
|
findPipelineByName,
|
|
2212
|
+
findPolterYaml,
|
|
1554
2213
|
parsePolterYaml,
|
|
1555
2214
|
getCurrentStatus,
|
|
1556
2215
|
planChanges,
|
|
1557
|
-
applyActions
|
|
2216
|
+
applyActions,
|
|
2217
|
+
installMcpServer,
|
|
2218
|
+
removeMcpServer,
|
|
2219
|
+
getMcpStatusInfo,
|
|
2220
|
+
installMcpServerSilent,
|
|
2221
|
+
removeMcpServerSilent,
|
|
2222
|
+
mcpStatus
|
|
1558
2223
|
};
|