@polterware/polter 0.4.0 → 0.4.2
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 +19 -2
- package/dist/{chunk-YNOZDU75.js → chunk-CWBIXRZP.js} +410 -55
- package/dist/chunk-XCCKD3RZ.js +108 -0
- package/dist/index.js +552 -334
- package/dist/mcp.js +100 -24
- package/package.json +1 -1
- package/dist/chunk-AGVTFYXU.js +0 -0
package/dist/api.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
createIpcClient
|
|
3
|
+
} from "./chunk-XCCKD3RZ.js";
|
|
2
4
|
import {
|
|
3
5
|
allCommands,
|
|
4
6
|
applyActions,
|
|
7
|
+
createIpcServer,
|
|
5
8
|
deletePipeline,
|
|
6
9
|
detectPkgManager,
|
|
7
10
|
executePipeline,
|
|
@@ -19,6 +22,9 @@ import {
|
|
|
19
22
|
getFlagsForTool,
|
|
20
23
|
getMcpStatusInfo,
|
|
21
24
|
getProcessOutput,
|
|
25
|
+
getSkillContent,
|
|
26
|
+
getSkillPath,
|
|
27
|
+
getSocketPath,
|
|
22
28
|
getToolDisplayName,
|
|
23
29
|
getToolInfo,
|
|
24
30
|
installMcpServerSilent,
|
|
@@ -26,20 +32,25 @@ import {
|
|
|
26
32
|
listProcesses,
|
|
27
33
|
parsePolterYaml,
|
|
28
34
|
planChanges,
|
|
35
|
+
registerForegroundProcess,
|
|
29
36
|
removeMcpServerSilent,
|
|
30
37
|
removeProcess,
|
|
31
38
|
resolvePkgArgs,
|
|
32
39
|
resolveToolCommand,
|
|
33
40
|
runCommand,
|
|
34
41
|
savePipeline,
|
|
42
|
+
setupSkill,
|
|
43
|
+
setupSkillCli,
|
|
35
44
|
startProcess,
|
|
36
45
|
stopProcess,
|
|
37
46
|
toolFlags,
|
|
38
47
|
translateCommand
|
|
39
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-CWBIXRZP.js";
|
|
40
49
|
export {
|
|
41
50
|
allCommands,
|
|
42
51
|
applyActions,
|
|
52
|
+
createIpcClient,
|
|
53
|
+
createIpcServer,
|
|
43
54
|
deletePipeline,
|
|
44
55
|
detectPkgManager,
|
|
45
56
|
executePipeline,
|
|
@@ -57,6 +68,9 @@ export {
|
|
|
57
68
|
getFlagsForTool,
|
|
58
69
|
getMcpStatusInfo,
|
|
59
70
|
getProcessOutput,
|
|
71
|
+
getSkillContent,
|
|
72
|
+
getSkillPath,
|
|
73
|
+
getSocketPath,
|
|
60
74
|
getToolDisplayName,
|
|
61
75
|
getToolInfo,
|
|
62
76
|
installMcpServerSilent,
|
|
@@ -64,12 +78,15 @@ export {
|
|
|
64
78
|
listProcesses,
|
|
65
79
|
parsePolterYaml,
|
|
66
80
|
planChanges,
|
|
81
|
+
registerForegroundProcess,
|
|
67
82
|
removeMcpServerSilent,
|
|
68
83
|
removeProcess,
|
|
69
84
|
resolvePkgArgs,
|
|
70
85
|
resolveToolCommand,
|
|
71
86
|
runCommand,
|
|
72
87
|
savePipeline,
|
|
88
|
+
setupSkill,
|
|
89
|
+
setupSkillCli,
|
|
73
90
|
startProcess,
|
|
74
91
|
stopProcess,
|
|
75
92
|
toolFlags,
|
|
@@ -989,6 +989,7 @@ function runCommand(execution, args, cwd = process.cwd(), options) {
|
|
|
989
989
|
cwd,
|
|
990
990
|
env: resolvedExecution.env,
|
|
991
991
|
shell: true,
|
|
992
|
+
detached: true,
|
|
992
993
|
stdio: [options?.quiet ? "pipe" : "inherit", "pipe", "pipe"]
|
|
993
994
|
});
|
|
994
995
|
if (options?.quiet) {
|
|
@@ -1022,7 +1023,15 @@ function runCommand(execution, args, cwd = process.cwd(), options) {
|
|
|
1022
1023
|
});
|
|
1023
1024
|
return {
|
|
1024
1025
|
promise,
|
|
1025
|
-
abort: () =>
|
|
1026
|
+
abort: () => {
|
|
1027
|
+
if (child.pid) {
|
|
1028
|
+
try {
|
|
1029
|
+
process.kill(-child.pid, "SIGTERM");
|
|
1030
|
+
} catch {
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
child
|
|
1026
1035
|
};
|
|
1027
1036
|
}
|
|
1028
1037
|
function runInteractiveCommand(execution, args, cwd = process.cwd()) {
|
|
@@ -1337,6 +1346,31 @@ function getToolLinkInfo(toolId, cwd = process.cwd()) {
|
|
|
1337
1346
|
|
|
1338
1347
|
// src/lib/processManager.ts
|
|
1339
1348
|
import { spawn as spawn2 } from "child_process";
|
|
1349
|
+
import { join as join5 } from "path";
|
|
1350
|
+
|
|
1351
|
+
// src/lib/packageRoot.ts
|
|
1352
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1353
|
+
import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
|
|
1354
|
+
var rootCache = /* @__PURE__ */ new Map();
|
|
1355
|
+
function findNearestPackageRoot(startDir = process.cwd()) {
|
|
1356
|
+
const resolvedStart = resolve2(startDir);
|
|
1357
|
+
if (rootCache.has(resolvedStart)) return rootCache.get(resolvedStart);
|
|
1358
|
+
let currentDir = resolvedStart;
|
|
1359
|
+
while (true) {
|
|
1360
|
+
if (existsSync4(join4(currentDir, "package.json"))) {
|
|
1361
|
+
rootCache.set(resolvedStart, currentDir);
|
|
1362
|
+
return currentDir;
|
|
1363
|
+
}
|
|
1364
|
+
const parentDir = dirname3(currentDir);
|
|
1365
|
+
if (parentDir === currentDir) {
|
|
1366
|
+
rootCache.set(resolvedStart, void 0);
|
|
1367
|
+
return void 0;
|
|
1368
|
+
}
|
|
1369
|
+
currentDir = parentDir;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// src/lib/processManager.ts
|
|
1340
1374
|
var DEFAULT_BUFFER_CAP = 1e3;
|
|
1341
1375
|
function createRingBuffer(cap = DEFAULT_BUFFER_CAP) {
|
|
1342
1376
|
return { lines: [], cap, totalLines: 0 };
|
|
@@ -1437,6 +1471,47 @@ function startProcess(id, command, args = [], cwd = process.cwd(), env) {
|
|
|
1437
1471
|
registry.set(id, tracked);
|
|
1438
1472
|
return toProcessInfo(tracked);
|
|
1439
1473
|
}
|
|
1474
|
+
function registerForegroundProcess(id, command, args, cwd, child) {
|
|
1475
|
+
const existing = registry.get(id);
|
|
1476
|
+
if (existing && existing.status === "running") {
|
|
1477
|
+
throw new Error(`Process "${id}" is already running (pid ${existing.child.pid})`);
|
|
1478
|
+
}
|
|
1479
|
+
registerCleanup();
|
|
1480
|
+
const tracked = {
|
|
1481
|
+
id,
|
|
1482
|
+
command,
|
|
1483
|
+
args,
|
|
1484
|
+
cwd,
|
|
1485
|
+
child,
|
|
1486
|
+
status: "running",
|
|
1487
|
+
exitCode: null,
|
|
1488
|
+
signal: null,
|
|
1489
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
1490
|
+
exitedAt: null,
|
|
1491
|
+
stdout: createRingBuffer(),
|
|
1492
|
+
stderr: createRingBuffer()
|
|
1493
|
+
};
|
|
1494
|
+
child.stdout?.on("data", (data) => {
|
|
1495
|
+
appendToBuffer(tracked.stdout, data.toString());
|
|
1496
|
+
});
|
|
1497
|
+
child.stderr?.on("data", (data) => {
|
|
1498
|
+
appendToBuffer(tracked.stderr, data.toString());
|
|
1499
|
+
});
|
|
1500
|
+
child.on("exit", (code, signal) => {
|
|
1501
|
+
tracked.status = "exited";
|
|
1502
|
+
tracked.exitCode = code;
|
|
1503
|
+
tracked.signal = signal;
|
|
1504
|
+
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1505
|
+
});
|
|
1506
|
+
child.on("error", (err) => {
|
|
1507
|
+
tracked.status = "errored";
|
|
1508
|
+
tracked.exitedAt = /* @__PURE__ */ new Date();
|
|
1509
|
+
appendToBuffer(tracked.stderr, `spawn error: ${err.message}
|
|
1510
|
+
`);
|
|
1511
|
+
});
|
|
1512
|
+
registry.set(id, tracked);
|
|
1513
|
+
return toProcessInfo(tracked);
|
|
1514
|
+
}
|
|
1440
1515
|
function stopProcess(id) {
|
|
1441
1516
|
const proc = registry.get(id);
|
|
1442
1517
|
if (!proc) throw new Error(`No tracked process with id "${id}"`);
|
|
@@ -1536,6 +1611,188 @@ function toProcessInfo(proc) {
|
|
|
1536
1611
|
uptime: end - start
|
|
1537
1612
|
};
|
|
1538
1613
|
}
|
|
1614
|
+
function getSocketPath(cwd) {
|
|
1615
|
+
const root = findNearestPackageRoot(cwd);
|
|
1616
|
+
if (!root) return void 0;
|
|
1617
|
+
return join5(root, ".polter", "polter.sock");
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
// src/lib/ipcServer.ts
|
|
1621
|
+
import net from "net";
|
|
1622
|
+
import { mkdirSync, unlinkSync, existsSync as existsSync5 } from "fs";
|
|
1623
|
+
import { dirname as dirname4 } from "path";
|
|
1624
|
+
|
|
1625
|
+
// src/lib/ipcProtocol.ts
|
|
1626
|
+
var DELIMITER = "\n";
|
|
1627
|
+
function serializeMessage(msg) {
|
|
1628
|
+
return JSON.stringify(msg) + DELIMITER;
|
|
1629
|
+
}
|
|
1630
|
+
function parseMessages(buffer) {
|
|
1631
|
+
const messages = [];
|
|
1632
|
+
let remainder = buffer;
|
|
1633
|
+
let idx;
|
|
1634
|
+
while ((idx = remainder.indexOf(DELIMITER)) !== -1) {
|
|
1635
|
+
const line = remainder.slice(0, idx);
|
|
1636
|
+
remainder = remainder.slice(idx + 1);
|
|
1637
|
+
if (line.length === 0) continue;
|
|
1638
|
+
try {
|
|
1639
|
+
messages.push(JSON.parse(line));
|
|
1640
|
+
} catch {
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
return { messages, remainder };
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// src/lib/ipcServer.ts
|
|
1647
|
+
var handlers = {
|
|
1648
|
+
"ps.list": () => listProcesses(),
|
|
1649
|
+
"ps.start": (params) => {
|
|
1650
|
+
const { command, args, cwd, id } = params;
|
|
1651
|
+
const processArgs = args ?? [];
|
|
1652
|
+
const processId = id ?? generateProcessId(command, processArgs);
|
|
1653
|
+
const processCwd = cwd ?? process.cwd();
|
|
1654
|
+
return startProcess(processId, command, processArgs, processCwd);
|
|
1655
|
+
},
|
|
1656
|
+
"ps.stop": async (params) => {
|
|
1657
|
+
const { id } = params;
|
|
1658
|
+
return stopProcess(id);
|
|
1659
|
+
},
|
|
1660
|
+
"ps.logs": (params) => {
|
|
1661
|
+
const { id, tail, stream } = params;
|
|
1662
|
+
return getProcessOutput(id, tail, stream);
|
|
1663
|
+
},
|
|
1664
|
+
"ps.remove": (params) => {
|
|
1665
|
+
const { id } = params;
|
|
1666
|
+
removeProcess(id);
|
|
1667
|
+
return null;
|
|
1668
|
+
},
|
|
1669
|
+
"ps.find_by_cwd": (params) => {
|
|
1670
|
+
const { cwd, filter } = params;
|
|
1671
|
+
let processes = findProcessesByCwd(cwd);
|
|
1672
|
+
if (filter) {
|
|
1673
|
+
const f = filter.toLowerCase();
|
|
1674
|
+
processes = processes.filter(
|
|
1675
|
+
(p) => (p.command + " " + p.args.join(" ")).toLowerCase().includes(f)
|
|
1676
|
+
);
|
|
1677
|
+
}
|
|
1678
|
+
return processes;
|
|
1679
|
+
},
|
|
1680
|
+
"ps.find_running": (params) => {
|
|
1681
|
+
const { cwd, command, args } = params;
|
|
1682
|
+
return findRunningByCommand(cwd, command, args) ?? null;
|
|
1683
|
+
},
|
|
1684
|
+
"ps.generate_id": (params) => {
|
|
1685
|
+
const { command, args } = params;
|
|
1686
|
+
return generateProcessId(command, args);
|
|
1687
|
+
},
|
|
1688
|
+
status: () => ({ tui: true, pid: process.pid })
|
|
1689
|
+
};
|
|
1690
|
+
async function handleRequest(req) {
|
|
1691
|
+
const handler = handlers[req.method];
|
|
1692
|
+
if (!handler) {
|
|
1693
|
+
return {
|
|
1694
|
+
jsonrpc: "2.0",
|
|
1695
|
+
id: req.id,
|
|
1696
|
+
error: { code: -32601, message: `Method not found: ${req.method}` }
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
try {
|
|
1700
|
+
const result = await handler(req.params ?? {});
|
|
1701
|
+
return { jsonrpc: "2.0", id: req.id, result };
|
|
1702
|
+
} catch (err) {
|
|
1703
|
+
return {
|
|
1704
|
+
jsonrpc: "2.0",
|
|
1705
|
+
id: req.id,
|
|
1706
|
+
error: { code: -32e3, message: err.message }
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
function createIpcServer(socketPath) {
|
|
1711
|
+
let server = null;
|
|
1712
|
+
const connections = /* @__PURE__ */ new Set();
|
|
1713
|
+
function handleConnection(socket) {
|
|
1714
|
+
connections.add(socket);
|
|
1715
|
+
let buffer = "";
|
|
1716
|
+
socket.on("data", async (data) => {
|
|
1717
|
+
buffer += data.toString();
|
|
1718
|
+
const { messages, remainder } = parseMessages(buffer);
|
|
1719
|
+
buffer = remainder;
|
|
1720
|
+
for (const msg of messages) {
|
|
1721
|
+
if ("method" in msg) {
|
|
1722
|
+
const response = await handleRequest(msg);
|
|
1723
|
+
try {
|
|
1724
|
+
socket.write(serializeMessage(response));
|
|
1725
|
+
} catch {
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
});
|
|
1730
|
+
socket.on("close", () => {
|
|
1731
|
+
connections.delete(socket);
|
|
1732
|
+
});
|
|
1733
|
+
socket.on("error", () => {
|
|
1734
|
+
connections.delete(socket);
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
async function cleanStaleSocket() {
|
|
1738
|
+
if (!existsSync5(socketPath)) return;
|
|
1739
|
+
return new Promise((resolve3) => {
|
|
1740
|
+
const probe = net.createConnection(socketPath);
|
|
1741
|
+
probe.on("connect", () => {
|
|
1742
|
+
probe.destroy();
|
|
1743
|
+
resolve3();
|
|
1744
|
+
});
|
|
1745
|
+
probe.on("error", () => {
|
|
1746
|
+
try {
|
|
1747
|
+
unlinkSync(socketPath);
|
|
1748
|
+
} catch {
|
|
1749
|
+
}
|
|
1750
|
+
resolve3();
|
|
1751
|
+
});
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
return {
|
|
1755
|
+
async start() {
|
|
1756
|
+
mkdirSync(dirname4(socketPath), { recursive: true });
|
|
1757
|
+
await cleanStaleSocket();
|
|
1758
|
+
return new Promise((resolve3, reject) => {
|
|
1759
|
+
server = net.createServer(handleConnection);
|
|
1760
|
+
server.on("error", (err) => {
|
|
1761
|
+
if (err.code === "EADDRINUSE") {
|
|
1762
|
+
try {
|
|
1763
|
+
unlinkSync(socketPath);
|
|
1764
|
+
} catch {
|
|
1765
|
+
}
|
|
1766
|
+
server.listen(socketPath, () => resolve3());
|
|
1767
|
+
} else {
|
|
1768
|
+
reject(err);
|
|
1769
|
+
}
|
|
1770
|
+
});
|
|
1771
|
+
server.listen(socketPath, () => resolve3());
|
|
1772
|
+
});
|
|
1773
|
+
},
|
|
1774
|
+
async stop() {
|
|
1775
|
+
for (const conn of connections) {
|
|
1776
|
+
conn.destroy();
|
|
1777
|
+
}
|
|
1778
|
+
connections.clear();
|
|
1779
|
+
return new Promise((resolve3) => {
|
|
1780
|
+
if (!server) {
|
|
1781
|
+
resolve3();
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
server.close(() => {
|
|
1785
|
+
try {
|
|
1786
|
+
unlinkSync(socketPath);
|
|
1787
|
+
} catch {
|
|
1788
|
+
}
|
|
1789
|
+
server = null;
|
|
1790
|
+
resolve3();
|
|
1791
|
+
});
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1539
1796
|
|
|
1540
1797
|
// src/pipeline/engine.ts
|
|
1541
1798
|
async function executePipeline(pipeline, onProgress, cwd = process.cwd()) {
|
|
@@ -1590,32 +1847,8 @@ async function executePipeline(pipeline, onProgress, cwd = process.cwd()) {
|
|
|
1590
1847
|
}
|
|
1591
1848
|
|
|
1592
1849
|
// src/config/projectConfig.ts
|
|
1593
|
-
import { existsSync as
|
|
1594
|
-
import { join as
|
|
1595
|
-
|
|
1596
|
-
// src/lib/packageRoot.ts
|
|
1597
|
-
import { existsSync as existsSync4 } from "fs";
|
|
1598
|
-
import { dirname as dirname3, join as join4, resolve as resolve2 } from "path";
|
|
1599
|
-
var rootCache = /* @__PURE__ */ new Map();
|
|
1600
|
-
function findNearestPackageRoot(startDir = process.cwd()) {
|
|
1601
|
-
const resolvedStart = resolve2(startDir);
|
|
1602
|
-
if (rootCache.has(resolvedStart)) return rootCache.get(resolvedStart);
|
|
1603
|
-
let currentDir = resolvedStart;
|
|
1604
|
-
while (true) {
|
|
1605
|
-
if (existsSync4(join4(currentDir, "package.json"))) {
|
|
1606
|
-
rootCache.set(resolvedStart, currentDir);
|
|
1607
|
-
return currentDir;
|
|
1608
|
-
}
|
|
1609
|
-
const parentDir = dirname3(currentDir);
|
|
1610
|
-
if (parentDir === currentDir) {
|
|
1611
|
-
rootCache.set(resolvedStart, void 0);
|
|
1612
|
-
return void 0;
|
|
1613
|
-
}
|
|
1614
|
-
currentDir = parentDir;
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
|
|
1618
|
-
// src/config/projectConfig.ts
|
|
1850
|
+
import { existsSync as existsSync6, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
1851
|
+
import { join as join6 } from "path";
|
|
1619
1852
|
var CONFIG_DIR = ".polter";
|
|
1620
1853
|
var CONFIG_FILE = "config.json";
|
|
1621
1854
|
function defaultConfig() {
|
|
@@ -1628,13 +1861,13 @@ function defaultConfig() {
|
|
|
1628
1861
|
function getProjectConfigPath(startDir) {
|
|
1629
1862
|
const root = findNearestPackageRoot(startDir);
|
|
1630
1863
|
if (!root) return void 0;
|
|
1631
|
-
const dir =
|
|
1632
|
-
return { dir, file:
|
|
1864
|
+
const dir = join6(root, CONFIG_DIR);
|
|
1865
|
+
return { dir, file: join6(dir, CONFIG_FILE) };
|
|
1633
1866
|
}
|
|
1634
1867
|
function readProjectConfig(startDir) {
|
|
1635
1868
|
const paths = getProjectConfigPath(startDir);
|
|
1636
1869
|
if (!paths) return void 0;
|
|
1637
|
-
if (!
|
|
1870
|
+
if (!existsSync6(paths.file)) return void 0;
|
|
1638
1871
|
try {
|
|
1639
1872
|
const raw = readFileSync2(paths.file, "utf-8");
|
|
1640
1873
|
return JSON.parse(raw);
|
|
@@ -1645,7 +1878,7 @@ function readProjectConfig(startDir) {
|
|
|
1645
1878
|
function writeProjectConfig(config2, startDir) {
|
|
1646
1879
|
const paths = getProjectConfigPath(startDir);
|
|
1647
1880
|
if (!paths) return false;
|
|
1648
|
-
|
|
1881
|
+
mkdirSync2(paths.dir, { recursive: true });
|
|
1649
1882
|
writeFileSync(paths.file, JSON.stringify(config2, null, 2) + "\n", "utf-8");
|
|
1650
1883
|
return true;
|
|
1651
1884
|
}
|
|
@@ -1735,8 +1968,8 @@ function findPipelineByName(name, startDir) {
|
|
|
1735
1968
|
}
|
|
1736
1969
|
|
|
1737
1970
|
// src/declarative/parser.ts
|
|
1738
|
-
import { existsSync as
|
|
1739
|
-
import { join as
|
|
1971
|
+
import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
|
|
1972
|
+
import { join as join7 } from "path";
|
|
1740
1973
|
var YAML_FILE = "polter.yaml";
|
|
1741
1974
|
function parseSimpleYaml(content) {
|
|
1742
1975
|
const lines = content.split("\n");
|
|
@@ -1808,8 +2041,8 @@ function parseValue(raw) {
|
|
|
1808
2041
|
return raw;
|
|
1809
2042
|
}
|
|
1810
2043
|
function findPolterYaml(startDir = process.cwd()) {
|
|
1811
|
-
const filePath =
|
|
1812
|
-
return
|
|
2044
|
+
const filePath = join7(startDir, YAML_FILE);
|
|
2045
|
+
return existsSync7(filePath) ? filePath : void 0;
|
|
1813
2046
|
}
|
|
1814
2047
|
function parsePolterYaml(startDir = process.cwd()) {
|
|
1815
2048
|
const filePath = findPolterYaml(startDir);
|
|
@@ -1955,16 +2188,16 @@ async function applyActions(actions, cwd = process.cwd(), onProgress) {
|
|
|
1955
2188
|
|
|
1956
2189
|
// src/lib/mcpInstaller.ts
|
|
1957
2190
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1958
|
-
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as
|
|
1959
|
-
import { join as
|
|
2191
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
|
|
2192
|
+
import { join as join8, dirname as dirname5 } from "path";
|
|
1960
2193
|
import { fileURLToPath } from "url";
|
|
1961
2194
|
import { homedir } from "os";
|
|
1962
2195
|
import pc from "picocolors";
|
|
1963
|
-
var __dirname =
|
|
2196
|
+
var __dirname = dirname5(fileURLToPath(import.meta.url));
|
|
1964
2197
|
function readPkgVersion() {
|
|
1965
2198
|
for (const rel of ["../../package.json", "../package.json"]) {
|
|
1966
|
-
const p =
|
|
1967
|
-
if (
|
|
2199
|
+
const p = join8(__dirname, rel);
|
|
2200
|
+
if (existsSync8(p)) {
|
|
1968
2201
|
const pkg = JSON.parse(readFileSync4(p, "utf-8"));
|
|
1969
2202
|
return pkg.version;
|
|
1970
2203
|
}
|
|
@@ -1987,15 +2220,15 @@ function tryClaudeCli(scope) {
|
|
|
1987
2220
|
}
|
|
1988
2221
|
function getSettingsPath(scope) {
|
|
1989
2222
|
if (scope === "project") {
|
|
1990
|
-
return
|
|
2223
|
+
return join8(process.cwd(), ".mcp.json");
|
|
1991
2224
|
}
|
|
1992
|
-
return
|
|
2225
|
+
return join8(homedir(), ".claude", "settings.json");
|
|
1993
2226
|
}
|
|
1994
2227
|
function tryManualInstall(scope) {
|
|
1995
2228
|
const settingsPath = getSettingsPath(scope);
|
|
1996
|
-
const dir =
|
|
2229
|
+
const dir = join8(settingsPath, "..");
|
|
1997
2230
|
let settings = {};
|
|
1998
|
-
if (
|
|
2231
|
+
if (existsSync8(settingsPath)) {
|
|
1999
2232
|
try {
|
|
2000
2233
|
settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
|
|
2001
2234
|
} catch {
|
|
@@ -2004,7 +2237,7 @@ function tryManualInstall(scope) {
|
|
|
2004
2237
|
return false;
|
|
2005
2238
|
}
|
|
2006
2239
|
} else {
|
|
2007
|
-
|
|
2240
|
+
mkdirSync3(dir, { recursive: true });
|
|
2008
2241
|
}
|
|
2009
2242
|
const mcpServers = settings.mcpServers ?? {};
|
|
2010
2243
|
mcpServers.polter = {
|
|
@@ -2056,7 +2289,7 @@ async function removeMcpServer(scope) {
|
|
|
2056
2289
|
process.stdout.write(pc.yellow(" 'claude mcp remove' failed, falling back to manual removal...\n\n"));
|
|
2057
2290
|
}
|
|
2058
2291
|
const settingsPath = getSettingsPath(scope);
|
|
2059
|
-
if (!
|
|
2292
|
+
if (!existsSync8(settingsPath)) {
|
|
2060
2293
|
process.stdout.write(pc.yellow(" No settings file found. Nothing to remove.\n"));
|
|
2061
2294
|
return;
|
|
2062
2295
|
}
|
|
@@ -2082,11 +2315,11 @@ async function removeMcpServer(scope) {
|
|
|
2082
2315
|
function getMcpStatusInfo() {
|
|
2083
2316
|
const version = readPkgVersion();
|
|
2084
2317
|
const scopeDefs = [
|
|
2085
|
-
{ label: "Project (.mcp.json)", path:
|
|
2086
|
-
{ label: "User (~/.claude/settings.json)", path:
|
|
2318
|
+
{ label: "Project (.mcp.json)", path: join8(process.cwd(), ".mcp.json"), scope: "project" },
|
|
2319
|
+
{ label: "User (~/.claude/settings.json)", path: join8(homedir(), ".claude", "settings.json"), scope: "user" }
|
|
2087
2320
|
];
|
|
2088
2321
|
const scopes = scopeDefs.map((s) => {
|
|
2089
|
-
if (!
|
|
2322
|
+
if (!existsSync8(s.path)) {
|
|
2090
2323
|
return { label: s.label, scope: s.scope, registered: false };
|
|
2091
2324
|
}
|
|
2092
2325
|
try {
|
|
@@ -2133,7 +2366,7 @@ async function removeMcpServerSilent(scope) {
|
|
|
2133
2366
|
messages.push("'claude mcp remove' failed, falling back to manual removal...");
|
|
2134
2367
|
}
|
|
2135
2368
|
const settingsPath = getSettingsPath(scope);
|
|
2136
|
-
if (!
|
|
2369
|
+
if (!existsSync8(settingsPath)) {
|
|
2137
2370
|
messages.push("No settings file found. Nothing to remove.");
|
|
2138
2371
|
return { success: true, message: messages.join("\n") };
|
|
2139
2372
|
}
|
|
@@ -2172,11 +2405,11 @@ async function mcpStatus() {
|
|
|
2172
2405
|
}
|
|
2173
2406
|
process.stdout.write("\n");
|
|
2174
2407
|
const scopes = [
|
|
2175
|
-
{ label: "Project (.mcp.json)", path:
|
|
2176
|
-
{ label: "User (~/.claude/settings.json)", path:
|
|
2408
|
+
{ label: "Project (.mcp.json)", path: join8(process.cwd(), ".mcp.json"), key: "project" },
|
|
2409
|
+
{ label: "User (~/.claude/settings.json)", path: join8(homedir(), ".claude", "settings.json"), key: "user" }
|
|
2177
2410
|
];
|
|
2178
2411
|
for (const scope of scopes) {
|
|
2179
|
-
if (!
|
|
2412
|
+
if (!existsSync8(scope.path)) {
|
|
2180
2413
|
process.stdout.write(` ${scope.label}: ${pc.dim("not found")}
|
|
2181
2414
|
`);
|
|
2182
2415
|
continue;
|
|
@@ -2198,6 +2431,119 @@ async function mcpStatus() {
|
|
|
2198
2431
|
}
|
|
2199
2432
|
}
|
|
2200
2433
|
|
|
2434
|
+
// src/lib/skillSetup.ts
|
|
2435
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "fs";
|
|
2436
|
+
import { join as join9 } from "path";
|
|
2437
|
+
import { homedir as homedir2 } from "os";
|
|
2438
|
+
import pc2 from "picocolors";
|
|
2439
|
+
var SKILL_DIR = join9(homedir2(), ".claude", "skills", "polter");
|
|
2440
|
+
var SKILL_PATH = join9(SKILL_DIR, "SKILL.md");
|
|
2441
|
+
var SKILL_CONTENT = `---
|
|
2442
|
+
name: polter
|
|
2443
|
+
description: "Polter - Infrastructure Orchestrator. Use when developing in any project to: monitor dev processes and check logs for errors after code changes, manage pipelines (multi-step command sequences), run CLI commands (Supabase, GitHub, Vercel, Git), manage packages (install, build, publish, audit), and apply declarative infrastructure from polter.yaml."
|
|
2444
|
+
---
|
|
2445
|
+
|
|
2446
|
+
# Polter Skill
|
|
2447
|
+
|
|
2448
|
+
You have access to Polter MCP tools for infrastructure orchestration. Use them proactively during development.
|
|
2449
|
+
|
|
2450
|
+
## Process Monitoring
|
|
2451
|
+
|
|
2452
|
+
**Tools:** \`polter_ps\`, \`polter_logs\`, \`polter_start\`, \`polter_stop\`, \`polter_find_process\`, \`polter_smart_start\`, \`polter_run_script_bg\`
|
|
2453
|
+
|
|
2454
|
+
- At the start of a session, run \`polter_ps\` to check for active dev processes
|
|
2455
|
+
- After significant code edits, check \`polter_logs\` for compilation or runtime errors
|
|
2456
|
+
- Use \`polter_find_process\` to find processes running in the current directory
|
|
2457
|
+
- Use \`polter_smart_start\` to start package.json scripts as background processes (e.g. dev servers)
|
|
2458
|
+
- Use \`polter_run_script_bg\` to run arbitrary scripts in the background
|
|
2459
|
+
- Use \`polter_stop\` to stop processes that are no longer needed
|
|
2460
|
+
|
|
2461
|
+
## CLI Commands
|
|
2462
|
+
|
|
2463
|
+
**Tools:** \`polter_list_commands\`, \`polter_run_command\`, \`polter_status\`
|
|
2464
|
+
|
|
2465
|
+
- Execute commands for Supabase, GitHub CLI, Vercel, and Git via their command IDs
|
|
2466
|
+
- Use \`polter_list_commands\` to discover available commands, optionally filtered by tool
|
|
2467
|
+
- Use \`polter_run_command\` to execute a command by its ID with additional args/flags
|
|
2468
|
+
- Check \`polter_status\` to see which CLI tools are installed and their versions
|
|
2469
|
+
|
|
2470
|
+
## Pipelines
|
|
2471
|
+
|
|
2472
|
+
**Tools:** \`polter_list_pipelines\`, \`polter_run_pipeline\`, \`polter_create_pipeline\`, \`polter_update_pipeline\`, \`polter_delete_pipeline\`
|
|
2473
|
+
|
|
2474
|
+
- List and run saved multi-step command sequences with \`polter_list_pipelines\` and \`polter_run_pipeline\`
|
|
2475
|
+
- Create new pipelines for repetitive workflows (e.g. build + test + deploy) with \`polter_create_pipeline\`
|
|
2476
|
+
- Suggest creating pipelines when you notice the user repeating the same sequence of commands
|
|
2477
|
+
|
|
2478
|
+
## Package Management
|
|
2479
|
+
|
|
2480
|
+
**Tools:** \`polter_pkg_build\`, \`polter_pkg_install\`, \`polter_pkg_publish\`, \`polter_pkg_run_script\`, \`polter_pkg_version_bump\`, \`polter_pkg_audit\`, \`polter_pkg_info\`
|
|
2481
|
+
|
|
2482
|
+
- Auto-detects the package manager (npm, pnpm, yarn, bun) from lockfiles
|
|
2483
|
+
- Use \`polter_pkg_build\` for building, \`polter_pkg_install\` for installing dependencies
|
|
2484
|
+
- Use \`polter_pkg_run_script\` to run package.json scripts
|
|
2485
|
+
- Use \`polter_pkg_audit\` to check for vulnerabilities
|
|
2486
|
+
- Use \`polter_pkg_version_bump\` before publishing, then \`polter_pkg_publish\`
|
|
2487
|
+
|
|
2488
|
+
## Declarative Infrastructure
|
|
2489
|
+
|
|
2490
|
+
**Tools:** \`polter_plan\`, \`polter_apply\`
|
|
2491
|
+
|
|
2492
|
+
- Use \`polter_plan\` to read \`polter.yaml\` and compute a diff of desired vs current state
|
|
2493
|
+
- Use \`polter_apply\` to execute the planned infrastructure changes
|
|
2494
|
+
- Always run \`polter_plan\` first to review changes before applying
|
|
2495
|
+
|
|
2496
|
+
## Workflow Recommendations
|
|
2497
|
+
|
|
2498
|
+
1. **Starting a session:** Run \`polter_ps\` to see what's already running
|
|
2499
|
+
2. **After code changes:** Check \`polter_logs\` for errors in dev server output
|
|
2500
|
+
3. **Setting up a project:** Use \`polter_status\` to verify tools, then \`polter_smart_start\` for dev server
|
|
2501
|
+
4. **Deploying:** Create a pipeline with build + test + deploy steps
|
|
2502
|
+
5. **Infrastructure changes:** Edit \`polter.yaml\`, run \`polter_plan\`, then \`polter_apply\`
|
|
2503
|
+
`;
|
|
2504
|
+
function setupSkill() {
|
|
2505
|
+
if (existsSync9(SKILL_PATH)) {
|
|
2506
|
+
const existing = readFileSync5(SKILL_PATH, "utf-8");
|
|
2507
|
+
if (existing === SKILL_CONTENT) {
|
|
2508
|
+
return { status: "already-up-to-date", path: SKILL_PATH };
|
|
2509
|
+
}
|
|
2510
|
+
writeFileSync3(SKILL_PATH, SKILL_CONTENT, "utf-8");
|
|
2511
|
+
return { status: "updated", path: SKILL_PATH };
|
|
2512
|
+
}
|
|
2513
|
+
mkdirSync4(SKILL_DIR, { recursive: true });
|
|
2514
|
+
writeFileSync3(SKILL_PATH, SKILL_CONTENT, "utf-8");
|
|
2515
|
+
return { status: "created", path: SKILL_PATH };
|
|
2516
|
+
}
|
|
2517
|
+
function setupSkillCli() {
|
|
2518
|
+
const result = setupSkill();
|
|
2519
|
+
switch (result.status) {
|
|
2520
|
+
case "created":
|
|
2521
|
+
process.stdout.write(pc2.green(`
|
|
2522
|
+
Skill installed at ${result.path}
|
|
2523
|
+
`));
|
|
2524
|
+
process.stdout.write(pc2.dim(" Use /polter in Claude Code to activate.\n\n"));
|
|
2525
|
+
break;
|
|
2526
|
+
case "updated":
|
|
2527
|
+
process.stdout.write(pc2.green(`
|
|
2528
|
+
Skill updated at ${result.path}
|
|
2529
|
+
`));
|
|
2530
|
+
process.stdout.write(pc2.dim(" Use /polter in Claude Code to activate.\n\n"));
|
|
2531
|
+
break;
|
|
2532
|
+
case "already-up-to-date":
|
|
2533
|
+
process.stdout.write(pc2.cyan(`
|
|
2534
|
+
Skill already up to date at ${result.path}
|
|
2535
|
+
|
|
2536
|
+
`));
|
|
2537
|
+
break;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
function getSkillContent() {
|
|
2541
|
+
return SKILL_CONTENT;
|
|
2542
|
+
}
|
|
2543
|
+
function getSkillPath() {
|
|
2544
|
+
return SKILL_PATH;
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2201
2547
|
export {
|
|
2202
2548
|
__export,
|
|
2203
2549
|
allCommands,
|
|
@@ -2220,8 +2566,10 @@ export {
|
|
|
2220
2566
|
resolveToolCommand,
|
|
2221
2567
|
getToolInfo,
|
|
2222
2568
|
getToolLinkInfo,
|
|
2569
|
+
findNearestPackageRoot,
|
|
2223
2570
|
generateProcessId,
|
|
2224
2571
|
startProcess,
|
|
2572
|
+
registerForegroundProcess,
|
|
2225
2573
|
stopProcess,
|
|
2226
2574
|
listProcesses,
|
|
2227
2575
|
getProcessOutput,
|
|
@@ -2229,8 +2577,11 @@ export {
|
|
|
2229
2577
|
removeProcess,
|
|
2230
2578
|
findProcessesByCwd,
|
|
2231
2579
|
findRunningByCommand,
|
|
2580
|
+
getSocketPath,
|
|
2581
|
+
serializeMessage,
|
|
2582
|
+
parseMessages,
|
|
2583
|
+
createIpcServer,
|
|
2232
2584
|
executePipeline,
|
|
2233
|
-
findNearestPackageRoot,
|
|
2234
2585
|
getProjectConfigPath,
|
|
2235
2586
|
writeProjectConfig,
|
|
2236
2587
|
getOrCreateProjectConfig,
|
|
@@ -2248,5 +2599,9 @@ export {
|
|
|
2248
2599
|
getMcpStatusInfo,
|
|
2249
2600
|
installMcpServerSilent,
|
|
2250
2601
|
removeMcpServerSilent,
|
|
2251
|
-
mcpStatus
|
|
2602
|
+
mcpStatus,
|
|
2603
|
+
setupSkill,
|
|
2604
|
+
setupSkillCli,
|
|
2605
|
+
getSkillContent,
|
|
2606
|
+
getSkillPath
|
|
2252
2607
|
};
|