mcpill 1.9.0 → 1.10.0
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/CHANGELOG.md +11 -0
- package/dist/cli.js +537 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.10.0
|
|
4
|
+
|
|
5
|
+
- `mcpill compile` emits `.mcpill/dist/<pill>/` — a standalone npm package containing `install.js`, `index.js`, `tools.js`, `package.json`, and (for blocking pills) `hooks/pre-tool-gate.js` + `hooks/post-analysis-flag.js`
|
|
6
|
+
- `<pill> install --agent <target>` wires the MCP server and blocking hooks into the target agent's config; supports `claude-code`, `cursor`, `windsurf`, `github-copilot`, `codex`; idempotent
|
|
7
|
+
- `<pill> uninstall --agent <target>` reverses the install cleanly
|
|
8
|
+
- `hooks/pre-tool-gate.js` normalises stdin from all four agent input shapes (Claude Code, Cursor, Windsurf, GitHub Copilot) and branches exit code / output per agent (exit 1, exit 2, or JSON deny)
|
|
9
|
+
- `index.js` in the standalone package wraps `required_tool` handlers to write the flag file, enabling enforcement on agents with no `PostToolUse` hook (Windsurf)
|
|
10
|
+
- Opt-in `postinstall: true` and `version` fields in `PILL.md ## Server` block
|
|
11
|
+
- `<pill> install` writes `~/.{pillName}/state.json { enabled: true }` so gate enforcement is active immediately after install; `uninstall` removes it
|
|
12
|
+
- `mcpill init` scaffolds `README.md` with a quickstart guide covering project layout and tool-editing workflow
|
|
13
|
+
|
|
3
14
|
## 1.9.0
|
|
4
15
|
|
|
5
16
|
- `blocking: true` field on `PILL.md` `## Hook:` blocks; `mcpill compile` generates a `.mjs` `PreToolUse` gate script (exits 1 until a required tool has run) and a `PostToolUse` flag script
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
-
import { dirname, join as
|
|
6
|
+
import { dirname, join as join7 } from "path";
|
|
7
7
|
import { readFileSync as readFileSync5 } from "fs";
|
|
8
8
|
|
|
9
9
|
// src/commands/init.ts
|
|
@@ -1375,8 +1375,8 @@ async function runServer(opts) {
|
|
|
1375
1375
|
}
|
|
1376
1376
|
|
|
1377
1377
|
// src/commands/compile.ts
|
|
1378
|
-
import { resolve, join as
|
|
1379
|
-
import { readFileSync as readFileSync4, writeFileSync as
|
|
1378
|
+
import { resolve, join as join6 } from "path";
|
|
1379
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
|
|
1380
1380
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1381
1381
|
|
|
1382
1382
|
// src/compiler/serialize.ts
|
|
@@ -1548,6 +1548,514 @@ function mergeHandlers(doc, existing) {
|
|
|
1548
1548
|
return { doc: { ...doc, tools }, stubbed };
|
|
1549
1549
|
}
|
|
1550
1550
|
|
|
1551
|
+
// src/compiler/standalone.ts
|
|
1552
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
1553
|
+
import { join as join5 } from "path";
|
|
1554
|
+
function generateStandaloneIndexJs(pillName, version, prompts, resources, blockingHookTools) {
|
|
1555
|
+
const promptsJson = JSON.stringify(prompts, null, 2);
|
|
1556
|
+
const resourcesJson = JSON.stringify(resources, null, 2);
|
|
1557
|
+
const lines = [
|
|
1558
|
+
`#!/usr/bin/env node`,
|
|
1559
|
+
`import { createServer } from 'mcpster';`,
|
|
1560
|
+
`import { z } from 'mcpill-runtime';`,
|
|
1561
|
+
`import { writeFileSync, mkdirSync } from 'fs';`,
|
|
1562
|
+
`import { homedir } from 'os';`,
|
|
1563
|
+
`import { join } from 'path';`,
|
|
1564
|
+
`import tools from './tools.js';`,
|
|
1565
|
+
``,
|
|
1566
|
+
`const PILL_NAME = ${JSON.stringify(pillName)};`,
|
|
1567
|
+
`const PILL_VERSION = ${JSON.stringify(version)};`,
|
|
1568
|
+
`const REQUIRED_TOOLS = ${JSON.stringify(blockingHookTools)};`,
|
|
1569
|
+
``,
|
|
1570
|
+
`const prompts = ${promptsJson};`,
|
|
1571
|
+
`const resources = ${resourcesJson};`
|
|
1572
|
+
];
|
|
1573
|
+
if (blockingHookTools.length > 0) {
|
|
1574
|
+
lines.push(
|
|
1575
|
+
``,
|
|
1576
|
+
`const _writeFlagFile = (pillName, toolName) => {`,
|
|
1577
|
+
` const dir = join(homedir(), '.' + pillName);`,
|
|
1578
|
+
` try {`,
|
|
1579
|
+
` mkdirSync(dir, { recursive: true });`,
|
|
1580
|
+
` writeFileSync(join(dir, 'last_' + toolName + '_run.json'), JSON.stringify({ timestamp: new Date().toISOString() }));`,
|
|
1581
|
+
` } catch {}`,
|
|
1582
|
+
`};`
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
lines.push(
|
|
1586
|
+
``,
|
|
1587
|
+
`const server = createServer({ name: PILL_NAME, version: PILL_VERSION, transport: 'stdio' });`,
|
|
1588
|
+
``,
|
|
1589
|
+
`for (const { name, description, schema, handler } of tools) {`,
|
|
1590
|
+
` server.defineTool({`,
|
|
1591
|
+
` name,`,
|
|
1592
|
+
` description,`,
|
|
1593
|
+
` schema: z.object(schema),`
|
|
1594
|
+
);
|
|
1595
|
+
if (blockingHookTools.length > 0) {
|
|
1596
|
+
lines.push(
|
|
1597
|
+
` handler: REQUIRED_TOOLS.includes(name)`,
|
|
1598
|
+
` ? async (args) => { const r = await handler(args); _writeFlagFile(PILL_NAME, name); return r; }`,
|
|
1599
|
+
` : handler,`
|
|
1600
|
+
);
|
|
1601
|
+
} else {
|
|
1602
|
+
lines.push(` handler,`);
|
|
1603
|
+
}
|
|
1604
|
+
lines.push(
|
|
1605
|
+
` });`,
|
|
1606
|
+
`}`,
|
|
1607
|
+
``,
|
|
1608
|
+
`for (const { name: promptName, description, messages } of prompts) {`,
|
|
1609
|
+
` server.definePrompt({`,
|
|
1610
|
+
` name: promptName,`,
|
|
1611
|
+
` description,`,
|
|
1612
|
+
` handler: async (args) =>`,
|
|
1613
|
+
` messages.map(m => m.content.replace(/{{(\\w+)}}/g, (_, k) => String(args[k] ?? ''))).join('\\n'),`,
|
|
1614
|
+
` });`,
|
|
1615
|
+
`}`,
|
|
1616
|
+
``,
|
|
1617
|
+
`for (const { uri, name: resourceName, content } of resources) {`,
|
|
1618
|
+
` server.defineResource({`,
|
|
1619
|
+
` uri,`,
|
|
1620
|
+
` description: resourceName ?? uri,`,
|
|
1621
|
+
` resolver: async () => content,`,
|
|
1622
|
+
` });`,
|
|
1623
|
+
`}`,
|
|
1624
|
+
``,
|
|
1625
|
+
`await server.start();`,
|
|
1626
|
+
``
|
|
1627
|
+
);
|
|
1628
|
+
return lines.join("\n");
|
|
1629
|
+
}
|
|
1630
|
+
function generateStandaloneGateScript(pillName, blockingHooks) {
|
|
1631
|
+
const hooksData = blockingHooks.map((h) => ({
|
|
1632
|
+
required_tool: h.required_tool,
|
|
1633
|
+
gate_window_seconds: h.gate_window_seconds ?? 120,
|
|
1634
|
+
instructions: h.instructions ?? `[mcpill:${pillName}] Run mcp__${pillName}__${h.required_tool} before calling other tools.`
|
|
1635
|
+
}));
|
|
1636
|
+
return [
|
|
1637
|
+
`#!/usr/bin/env node`,
|
|
1638
|
+
`import { readFileSync, existsSync } from 'fs';`,
|
|
1639
|
+
`import { homedir } from 'os';`,
|
|
1640
|
+
`import { join } from 'path';`,
|
|
1641
|
+
``,
|
|
1642
|
+
`const PILL_NAME = ${JSON.stringify(pillName)};`,
|
|
1643
|
+
`const HOOKS = ${JSON.stringify(hooksData, null, 2)};`,
|
|
1644
|
+
``,
|
|
1645
|
+
`const raw = readFileSync(0, 'utf-8');`,
|
|
1646
|
+
`const input = JSON.parse(raw);`,
|
|
1647
|
+
``,
|
|
1648
|
+
`// Agent detection + tool name normalization`,
|
|
1649
|
+
`let toolName;`,
|
|
1650
|
+
`if (typeof input.tool_info === 'object' && input.tool_info !== null && typeof input.tool_info.mcp_tool_name === 'string') {`,
|
|
1651
|
+
` toolName = input.tool_info.mcp_tool_name; // Windsurf`,
|
|
1652
|
+
`} else if (typeof input.toolName === 'string') {`,
|
|
1653
|
+
` toolName = input.toolName; // GitHub Copilot`,
|
|
1654
|
+
`} else {`,
|
|
1655
|
+
` toolName = input.tool_name ?? ''; // Claude Code, Cursor, Codex`,
|
|
1656
|
+
`}`,
|
|
1657
|
+
``,
|
|
1658
|
+
`if (toolName.startsWith('mcp__' + PILL_NAME + '__')) process.exit(0);`,
|
|
1659
|
+
``,
|
|
1660
|
+
`const unsatisfied = [];`,
|
|
1661
|
+
`for (const hook of HOOKS) {`,
|
|
1662
|
+
` const stateFile = join(homedir(), '.' + PILL_NAME, 'state.json');`,
|
|
1663
|
+
` if (!existsSync(stateFile)) continue;`,
|
|
1664
|
+
` const state = JSON.parse(readFileSync(stateFile, 'utf-8'));`,
|
|
1665
|
+
` if (state.enabled === false) continue;`,
|
|
1666
|
+
` const flagFile = join(homedir(), '.' + PILL_NAME, 'last_' + hook.required_tool + '_run.json');`,
|
|
1667
|
+
` if (existsSync(flagFile)) {`,
|
|
1668
|
+
` const flag = JSON.parse(readFileSync(flagFile, 'utf-8'));`,
|
|
1669
|
+
` const elapsedMs = Date.now() - new Date(flag.timestamp).getTime();`,
|
|
1670
|
+
` if (elapsedMs <= hook.gate_window_seconds * 1000) continue;`,
|
|
1671
|
+
` }`,
|
|
1672
|
+
` unsatisfied.push(hook.instructions);`,
|
|
1673
|
+
`}`,
|
|
1674
|
+
``,
|
|
1675
|
+
`if (unsatisfied.length === 0) process.exit(0);`,
|
|
1676
|
+
``,
|
|
1677
|
+
`const blockMsg = unsatisfied.join('\\n');`,
|
|
1678
|
+
``,
|
|
1679
|
+
`// Per-agent output format`,
|
|
1680
|
+
`if (typeof input.tool_info === 'object' && input.tool_info !== null && typeof input.tool_info.mcp_tool_name === 'string') {`,
|
|
1681
|
+
` // Windsurf: exit 2`,
|
|
1682
|
+
` process.stderr.write(blockMsg + '\\n');`,
|
|
1683
|
+
` process.exit(2);`,
|
|
1684
|
+
`} else if (typeof input.toolName === 'string') {`,
|
|
1685
|
+
` // GitHub Copilot: JSON deny`,
|
|
1686
|
+
` process.stdout.write(JSON.stringify({ permissionDecision: 'deny', permissionDecisionReason: blockMsg }));`,
|
|
1687
|
+
` process.exit(0);`,
|
|
1688
|
+
`} else if (input.workspace_roots !== undefined || typeof input.conversation_id === 'string') {`,
|
|
1689
|
+
` // Cursor: JSON deny`,
|
|
1690
|
+
` process.stdout.write(JSON.stringify({ permission: 'deny', agentMessage: blockMsg }));`,
|
|
1691
|
+
` process.exit(0);`,
|
|
1692
|
+
`} else {`,
|
|
1693
|
+
` // Claude Code / Codex: exit 1`,
|
|
1694
|
+
` process.stderr.write(blockMsg + '\\n');`,
|
|
1695
|
+
` process.exit(1);`,
|
|
1696
|
+
`}`,
|
|
1697
|
+
``
|
|
1698
|
+
].join("\n");
|
|
1699
|
+
}
|
|
1700
|
+
function generateStandaloneFlagScript(pillName) {
|
|
1701
|
+
return [
|
|
1702
|
+
`#!/usr/bin/env node`,
|
|
1703
|
+
`import { writeFileSync, mkdirSync } from 'fs';`,
|
|
1704
|
+
`import { homedir } from 'os';`,
|
|
1705
|
+
`import { join } from 'path';`,
|
|
1706
|
+
``,
|
|
1707
|
+
`const PILL_NAME = ${JSON.stringify(pillName)};`,
|
|
1708
|
+
``,
|
|
1709
|
+
`const toolIdx = process.argv.indexOf('--tool');`,
|
|
1710
|
+
`const toolName = toolIdx !== -1 ? process.argv[toolIdx + 1] : null;`,
|
|
1711
|
+
``,
|
|
1712
|
+
`if (!toolName) {`,
|
|
1713
|
+
` process.stderr.write('[' + PILL_NAME + '] error: --tool <name> required\\n');`,
|
|
1714
|
+
` process.exit(1);`,
|
|
1715
|
+
`}`,
|
|
1716
|
+
``,
|
|
1717
|
+
`const dir = join(homedir(), '.' + PILL_NAME);`,
|
|
1718
|
+
`const flagFile = join(dir, 'last_' + toolName + '_run.json');`,
|
|
1719
|
+
``,
|
|
1720
|
+
`try {`,
|
|
1721
|
+
` mkdirSync(dir, { recursive: true });`,
|
|
1722
|
+
` writeFileSync(flagFile, JSON.stringify({ timestamp: new Date().toISOString() }));`,
|
|
1723
|
+
`} catch (e) {`,
|
|
1724
|
+
` process.stderr.write('[' + PILL_NAME + '] warning: could not write flag file: ' + e.message + '\\n');`,
|
|
1725
|
+
`}`,
|
|
1726
|
+
``
|
|
1727
|
+
].join("\n");
|
|
1728
|
+
}
|
|
1729
|
+
var INSTALL_JS_BODY = `
|
|
1730
|
+
function readJson(filePath) {
|
|
1731
|
+
if (!existsSync(filePath)) return {};
|
|
1732
|
+
try {
|
|
1733
|
+
return JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
1734
|
+
} catch {
|
|
1735
|
+
const bakPath = filePath + '.bak';
|
|
1736
|
+
try { writeFileSync(bakPath, readFileSync(filePath)); } catch {}
|
|
1737
|
+
process.stderr.write('[mcpill] warning: ' + filePath + ' was not valid JSON \u2014 backed up to ' + bakPath + ' and recreated\\n');
|
|
1738
|
+
return {};
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
function writeJson(filePath, obj) {
|
|
1743
|
+
const dir = dirname(filePath);
|
|
1744
|
+
mkdirSync(dir, { recursive: true });
|
|
1745
|
+
writeFileSync(filePath, JSON.stringify(obj, null, 2) + '\\n');
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
function activatePill() {
|
|
1749
|
+
mkdirSync(dirname(STATE_FILE), { recursive: true });
|
|
1750
|
+
writeJson(STATE_FILE, { enabled: true });
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
function deactivatePill() {
|
|
1754
|
+
try { rmSync(STATE_FILE, { force: true }); } catch {}
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
function installClaudeCode() {
|
|
1758
|
+
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
1759
|
+
const wasInstalled = existsSync(settingsPath) && (readJson(settingsPath).mcpServers || {})[PILL_NAME] !== undefined;
|
|
1760
|
+
const s = readJson(settingsPath);
|
|
1761
|
+
if (!s.mcpServers) s.mcpServers = {};
|
|
1762
|
+
s.mcpServers[PILL_NAME] = MCP_ENTRY;
|
|
1763
|
+
if (HAS_BLOCKING_HOOKS) {
|
|
1764
|
+
if (!s.hooks) s.hooks = {};
|
|
1765
|
+
if (!s.hooks.PreToolUse) s.hooks.PreToolUse = [];
|
|
1766
|
+
if (!s.hooks.PostToolUse) s.hooks.PostToolUse = [];
|
|
1767
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1768
|
+
s.hooks.PreToolUse = s.hooks.PreToolUse.filter(e => !e.hooks?.[0]?.command?.includes(gateMarker));
|
|
1769
|
+
s.hooks.PostToolUse = s.hooks.PostToolUse.filter(e => !e.matcher?.startsWith('mcp__' + PILL_NAME + '__'));
|
|
1770
|
+
s.hooks.PreToolUse.push({ matcher: '.*', hooks: [{ type: 'command', command: 'node "' + GATE_PATH + '"' }] });
|
|
1771
|
+
for (const hook of HOOKS) {
|
|
1772
|
+
s.hooks.PostToolUse.push({
|
|
1773
|
+
matcher: 'mcp__' + PILL_NAME + '__' + hook.required_tool,
|
|
1774
|
+
hooks: [{ type: 'command', command: 'node "' + FLAG_PATH + '" --tool ' + hook.required_tool }],
|
|
1775
|
+
});
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
writeJson(settingsPath, s);
|
|
1779
|
+
activatePill();
|
|
1780
|
+
if (!silent) process.stdout.write('[mcpill] info: ' + (wasInstalled ? 'reinstalled' : 'installed') + ' ' + PILL_NAME + (HAS_BLOCKING_HOOKS ? '' : ' (no blocking hooks \u2014 MCP server only)') + ' for claude-code\\n');
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
function uninstallClaudeCode() {
|
|
1784
|
+
const settingsPath = join(homedir(), '.claude', 'settings.json');
|
|
1785
|
+
if (!existsSync(settingsPath)) return;
|
|
1786
|
+
const s = readJson(settingsPath);
|
|
1787
|
+
if (s.mcpServers) delete s.mcpServers[PILL_NAME];
|
|
1788
|
+
if (s.hooks) {
|
|
1789
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1790
|
+
if (s.hooks.PreToolUse) s.hooks.PreToolUse = s.hooks.PreToolUse.filter(e => !e.hooks?.[0]?.command?.includes(gateMarker));
|
|
1791
|
+
if (s.hooks.PostToolUse) s.hooks.PostToolUse = s.hooks.PostToolUse.filter(e => !e.matcher?.startsWith('mcp__' + PILL_NAME + '__'));
|
|
1792
|
+
}
|
|
1793
|
+
writeJson(settingsPath, s);
|
|
1794
|
+
deactivatePill();
|
|
1795
|
+
if (!silent) process.stdout.write('[mcpill] info: uninstalled ' + PILL_NAME + ' from claude-code\\n');
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
function installCursor() {
|
|
1799
|
+
const mcpPath = join(homedir(), '.cursor', 'mcp.json');
|
|
1800
|
+
const mcp = readJson(mcpPath);
|
|
1801
|
+
if (!mcp.mcpServers) mcp.mcpServers = {};
|
|
1802
|
+
mcp.mcpServers[PILL_NAME] = MCP_ENTRY;
|
|
1803
|
+
writeJson(mcpPath, mcp);
|
|
1804
|
+
if (HAS_BLOCKING_HOOKS) {
|
|
1805
|
+
const hooksPath = join(homedir(), '.cursor', 'hooks.json');
|
|
1806
|
+
const h = readJson(hooksPath);
|
|
1807
|
+
if (!h.hooks) h.hooks = {};
|
|
1808
|
+
if (!h.hooks.beforeMCPExecution) h.hooks.beforeMCPExecution = [];
|
|
1809
|
+
if (!h.hooks.afterMCPExecution) h.hooks.afterMCPExecution = [];
|
|
1810
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1811
|
+
h.hooks.beforeMCPExecution = h.hooks.beforeMCPExecution.filter(e => !e.command?.includes(gateMarker));
|
|
1812
|
+
h.hooks.afterMCPExecution = h.hooks.afterMCPExecution.filter(e => !e.command?.includes(gateMarker));
|
|
1813
|
+
h.hooks.beforeMCPExecution.push({ command: 'node "' + GATE_PATH + '"', type: 'command', matcher: '.*' });
|
|
1814
|
+
for (const hook of HOOKS) {
|
|
1815
|
+
h.hooks.afterMCPExecution.push({
|
|
1816
|
+
command: 'node "' + FLAG_PATH + '" --tool ' + hook.required_tool,
|
|
1817
|
+
type: 'command',
|
|
1818
|
+
matcher: 'mcp__' + PILL_NAME + '__' + hook.required_tool,
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
writeJson(hooksPath, h);
|
|
1822
|
+
}
|
|
1823
|
+
activatePill();
|
|
1824
|
+
if (!silent) process.stdout.write('[mcpill] info: installed ' + PILL_NAME + ' for cursor\\n');
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
function uninstallCursor() {
|
|
1828
|
+
const mcpPath = join(homedir(), '.cursor', 'mcp.json');
|
|
1829
|
+
if (existsSync(mcpPath)) {
|
|
1830
|
+
const mcp = readJson(mcpPath);
|
|
1831
|
+
if (mcp.mcpServers) delete mcp.mcpServers[PILL_NAME];
|
|
1832
|
+
writeJson(mcpPath, mcp);
|
|
1833
|
+
}
|
|
1834
|
+
const hooksPath = join(homedir(), '.cursor', 'hooks.json');
|
|
1835
|
+
if (existsSync(hooksPath)) {
|
|
1836
|
+
const h = readJson(hooksPath);
|
|
1837
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1838
|
+
if (h.hooks?.beforeMCPExecution) h.hooks.beforeMCPExecution = h.hooks.beforeMCPExecution.filter(e => !e.command?.includes(gateMarker));
|
|
1839
|
+
if (h.hooks?.afterMCPExecution) h.hooks.afterMCPExecution = h.hooks.afterMCPExecution.filter(e => !e.command?.includes(gateMarker));
|
|
1840
|
+
writeJson(hooksPath, h);
|
|
1841
|
+
}
|
|
1842
|
+
deactivatePill();
|
|
1843
|
+
if (!silent) process.stdout.write('[mcpill] info: uninstalled ' + PILL_NAME + ' from cursor\\n');
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
function installWindsurf() {
|
|
1847
|
+
const mcpPath = join(homedir(), '.codeium', 'windsurf', 'mcp_config.json');
|
|
1848
|
+
const mcp = readJson(mcpPath);
|
|
1849
|
+
if (!mcp.mcpServers) mcp.mcpServers = {};
|
|
1850
|
+
mcp.mcpServers[PILL_NAME] = MCP_ENTRY;
|
|
1851
|
+
writeJson(mcpPath, mcp);
|
|
1852
|
+
if (HAS_BLOCKING_HOOKS) {
|
|
1853
|
+
const hooksPath = join(homedir(), '.codeium', 'windsurf', 'hooks.json');
|
|
1854
|
+
const h = readJson(hooksPath);
|
|
1855
|
+
if (!h.hooks) h.hooks = {};
|
|
1856
|
+
if (!h.hooks.pre_mcp_tool_use) h.hooks.pre_mcp_tool_use = [];
|
|
1857
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1858
|
+
h.hooks.pre_mcp_tool_use = h.hooks.pre_mcp_tool_use.filter(e => !e.command?.includes(gateMarker));
|
|
1859
|
+
h.hooks.pre_mcp_tool_use.push({ command: 'node "' + GATE_PATH + '"' });
|
|
1860
|
+
writeJson(hooksPath, h);
|
|
1861
|
+
}
|
|
1862
|
+
activatePill();
|
|
1863
|
+
if (!silent) process.stdout.write('[mcpill] info: installed ' + PILL_NAME + ' for windsurf\\n');
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
function uninstallWindsurf() {
|
|
1867
|
+
const mcpPath = join(homedir(), '.codeium', 'windsurf', 'mcp_config.json');
|
|
1868
|
+
if (existsSync(mcpPath)) {
|
|
1869
|
+
const mcp = readJson(mcpPath);
|
|
1870
|
+
if (mcp.mcpServers) delete mcp.mcpServers[PILL_NAME];
|
|
1871
|
+
writeJson(mcpPath, mcp);
|
|
1872
|
+
}
|
|
1873
|
+
const hooksPath = join(homedir(), '.codeium', 'windsurf', 'hooks.json');
|
|
1874
|
+
if (existsSync(hooksPath)) {
|
|
1875
|
+
const h = readJson(hooksPath);
|
|
1876
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1877
|
+
if (h.hooks?.pre_mcp_tool_use) h.hooks.pre_mcp_tool_use = h.hooks.pre_mcp_tool_use.filter(e => !e.command?.includes(gateMarker));
|
|
1878
|
+
writeJson(hooksPath, h);
|
|
1879
|
+
}
|
|
1880
|
+
deactivatePill();
|
|
1881
|
+
if (!silent) process.stdout.write('[mcpill] info: uninstalled ' + PILL_NAME + ' from windsurf\\n');
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
function installCopilot() {
|
|
1885
|
+
const mcpPath = join(homedir(), '.vscode', 'mcp.json');
|
|
1886
|
+
const mcp = readJson(mcpPath);
|
|
1887
|
+
if (!mcp.mcpServers) mcp.mcpServers = {};
|
|
1888
|
+
mcp.mcpServers[PILL_NAME] = MCP_ENTRY;
|
|
1889
|
+
writeJson(mcpPath, mcp);
|
|
1890
|
+
if (HAS_BLOCKING_HOOKS) {
|
|
1891
|
+
const hooksPath = join(homedir(), '.copilot', 'settings.json');
|
|
1892
|
+
const h = readJson(hooksPath);
|
|
1893
|
+
if (!h.hooks) h.hooks = {};
|
|
1894
|
+
if (!h.hooks.preToolUse) h.hooks.preToolUse = [];
|
|
1895
|
+
if (!h.hooks.postToolUse) h.hooks.postToolUse = [];
|
|
1896
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1897
|
+
h.hooks.preToolUse = h.hooks.preToolUse.filter(e => !e.bash?.includes(gateMarker));
|
|
1898
|
+
h.hooks.postToolUse = h.hooks.postToolUse.filter(e => !e.bash?.includes(gateMarker));
|
|
1899
|
+
h.hooks.preToolUse.push({ type: 'command', bash: 'node "' + GATE_PATH + '"', timeoutSec: 30 });
|
|
1900
|
+
for (const hook of HOOKS) {
|
|
1901
|
+
h.hooks.postToolUse.push({
|
|
1902
|
+
type: 'command',
|
|
1903
|
+
bash: 'node "' + FLAG_PATH + '" --tool ' + hook.required_tool,
|
|
1904
|
+
timeoutSec: 30,
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
writeJson(hooksPath, h);
|
|
1908
|
+
}
|
|
1909
|
+
activatePill();
|
|
1910
|
+
if (!silent) process.stdout.write('[mcpill] info: installed ' + PILL_NAME + ' for github-copilot\\n');
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
function uninstallCopilot() {
|
|
1914
|
+
const mcpPath = join(homedir(), '.vscode', 'mcp.json');
|
|
1915
|
+
if (existsSync(mcpPath)) {
|
|
1916
|
+
const mcp = readJson(mcpPath);
|
|
1917
|
+
if (mcp.mcpServers) delete mcp.mcpServers[PILL_NAME];
|
|
1918
|
+
writeJson(mcpPath, mcp);
|
|
1919
|
+
}
|
|
1920
|
+
const hooksPath = join(homedir(), '.copilot', 'settings.json');
|
|
1921
|
+
if (existsSync(hooksPath)) {
|
|
1922
|
+
const h = readJson(hooksPath);
|
|
1923
|
+
const gateMarker = '/' + PILL_NAME + '/hooks/';
|
|
1924
|
+
if (h.hooks?.preToolUse) h.hooks.preToolUse = h.hooks.preToolUse.filter(e => !e.bash?.includes(gateMarker));
|
|
1925
|
+
if (h.hooks?.postToolUse) h.hooks.postToolUse = h.hooks.postToolUse.filter(e => !e.bash?.includes(gateMarker));
|
|
1926
|
+
writeJson(hooksPath, h);
|
|
1927
|
+
}
|
|
1928
|
+
deactivatePill();
|
|
1929
|
+
if (!silent) process.stdout.write('[mcpill] info: uninstalled ' + PILL_NAME + ' from github-copilot\\n');
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
function installCodex() {
|
|
1933
|
+
const configPath = join(homedir(), '.codex', 'config.toml');
|
|
1934
|
+
mkdirSync(join(homedir(), '.codex'), { recursive: true });
|
|
1935
|
+
let existing = '';
|
|
1936
|
+
if (existsSync(configPath)) {
|
|
1937
|
+
try { existing = readFileSync(configPath, 'utf-8'); } catch {}
|
|
1938
|
+
}
|
|
1939
|
+
// Remove existing entry for this pill
|
|
1940
|
+
const pat = new RegExp('\\\\[mcp_servers\\\\.' + PILL_NAME.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&') + '\\\\][\\\\s\\\\S]*?(?=\\\\[|$)', 'g');
|
|
1941
|
+
existing = existing.replace(pat, '').trimEnd();
|
|
1942
|
+
if (!existing.includes('[mcp_servers]')) existing += '\\n[mcp_servers]';
|
|
1943
|
+
existing += '\\n\\n[mcp_servers.' + PILL_NAME + ']\\ncommand = "node"\\nargs = [' + JSON.stringify(INDEX_PATH) + ']\\n';
|
|
1944
|
+
writeFileSync(configPath, existing.trim() + '\\n');
|
|
1945
|
+
activatePill();
|
|
1946
|
+
if (!silent) process.stdout.write('[mcpill] info: installed ' + PILL_NAME + ' for codex (MCP server only \u2014 hooks not yet supported for codex)\\n');
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
function uninstallCodex() {
|
|
1950
|
+
const configPath = join(homedir(), '.codex', 'config.toml');
|
|
1951
|
+
if (!existsSync(configPath)) return;
|
|
1952
|
+
let content = readFileSync(configPath, 'utf-8');
|
|
1953
|
+
const pat = new RegExp('\\\\[mcp_servers\\\\.' + PILL_NAME.replace(/[.*+?^\${}()|[\\]\\\\]/g, '\\\\$&') + '\\\\][\\\\s\\\\S]*?(?=\\\\[|$)', 'g');
|
|
1954
|
+
content = content.replace(pat, '').trim() + '\\n';
|
|
1955
|
+
writeFileSync(configPath, content);
|
|
1956
|
+
deactivatePill();
|
|
1957
|
+
if (!silent) process.stdout.write('[mcpill] info: uninstalled ' + PILL_NAME + ' from codex\\n');
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
if (command === 'install') {
|
|
1961
|
+
switch (agent) {
|
|
1962
|
+
case 'claude-code': installClaudeCode(); break;
|
|
1963
|
+
case 'cursor': installCursor(); break;
|
|
1964
|
+
case 'windsurf': installWindsurf(); break;
|
|
1965
|
+
case 'github-copilot': installCopilot(); break;
|
|
1966
|
+
case 'codex': installCodex(); break;
|
|
1967
|
+
}
|
|
1968
|
+
} else {
|
|
1969
|
+
switch (agent) {
|
|
1970
|
+
case 'claude-code': uninstallClaudeCode(); break;
|
|
1971
|
+
case 'cursor': uninstallCursor(); break;
|
|
1972
|
+
case 'windsurf': uninstallWindsurf(); break;
|
|
1973
|
+
case 'github-copilot': uninstallCopilot(); break;
|
|
1974
|
+
case 'codex': uninstallCodex(); break;
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
`;
|
|
1978
|
+
function generateStandaloneInstallJs(pillName, version, blockingHooks) {
|
|
1979
|
+
const hooksData = blockingHooks.map((h) => ({
|
|
1980
|
+
required_tool: h.required_tool
|
|
1981
|
+
}));
|
|
1982
|
+
const constants = [
|
|
1983
|
+
`#!/usr/bin/env node`,
|
|
1984
|
+
`import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync } from 'fs';`,
|
|
1985
|
+
`import { homedir } from 'os';`,
|
|
1986
|
+
`import { join, dirname, resolve } from 'path';`,
|
|
1987
|
+
`import { fileURLToPath } from 'url';`,
|
|
1988
|
+
``,
|
|
1989
|
+
`const __filename = fileURLToPath(import.meta.url);`,
|
|
1990
|
+
`const __dirname = dirname(__filename);`,
|
|
1991
|
+
``,
|
|
1992
|
+
`const PILL_NAME = ${JSON.stringify(pillName)};`,
|
|
1993
|
+
`const PILL_VERSION = ${JSON.stringify(version)};`,
|
|
1994
|
+
`const HAS_BLOCKING_HOOKS = ${blockingHooks.length > 0};`,
|
|
1995
|
+
`const HOOKS = ${JSON.stringify(hooksData, null, 2)};`,
|
|
1996
|
+
`const VALID_AGENTS = ['claude-code', 'cursor', 'windsurf', 'github-copilot', 'codex'];`,
|
|
1997
|
+
``,
|
|
1998
|
+
`const argv = process.argv.slice(2);`,
|
|
1999
|
+
`const command = argv[0];`,
|
|
2000
|
+
`const agentIdx = argv.indexOf('--agent');`,
|
|
2001
|
+
`const agent = agentIdx !== -1 ? argv[agentIdx + 1] : null;`,
|
|
2002
|
+
`const silent = argv.includes('--silent');`,
|
|
2003
|
+
``,
|
|
2004
|
+
`if (!['install', 'uninstall'].includes(command)) {`,
|
|
2005
|
+
` process.stderr.write('[mcpill] error: expected \\'install\\' or \\'uninstall\\', got \\'' + command + '\\'\\n');`,
|
|
2006
|
+
` process.exit(1);`,
|
|
2007
|
+
`}`,
|
|
2008
|
+
`if (!agent) {`,
|
|
2009
|
+
` process.stderr.write('[mcpill] error: --agent <target> is required\\n');`,
|
|
2010
|
+
` process.exit(1);`,
|
|
2011
|
+
`}`,
|
|
2012
|
+
`if (!VALID_AGENTS.includes(agent)) {`,
|
|
2013
|
+
` process.stderr.write('[mcpill] error: unknown agent \\'' + agent + '\\'. Valid targets: ' + VALID_AGENTS.join(', ') + '\\n');`,
|
|
2014
|
+
` process.exit(1);`,
|
|
2015
|
+
`}`,
|
|
2016
|
+
``,
|
|
2017
|
+
`const INDEX_PATH = resolve(__dirname, 'index.js');`,
|
|
2018
|
+
`const GATE_PATH = resolve(__dirname, 'hooks', 'pre-tool-gate.js');`,
|
|
2019
|
+
`const FLAG_PATH = resolve(__dirname, 'hooks', 'post-analysis-flag.js');`,
|
|
2020
|
+
`const MCP_ENTRY = { command: 'node', args: [INDEX_PATH] };`,
|
|
2021
|
+
`const STATE_FILE = join(homedir(), '.' + PILL_NAME, 'state.json');`
|
|
2022
|
+
].join("\n");
|
|
2023
|
+
return constants + "\n" + INSTALL_JS_BODY;
|
|
2024
|
+
}
|
|
2025
|
+
function generateDistPackageJson(pillName, version, opts) {
|
|
2026
|
+
const pkg2 = {
|
|
2027
|
+
name: pillName,
|
|
2028
|
+
version,
|
|
2029
|
+
type: "module",
|
|
2030
|
+
bin: { [pillName]: "install.js" },
|
|
2031
|
+
dependencies: {
|
|
2032
|
+
"mcpster": "*",
|
|
2033
|
+
"mcpill-runtime": "*"
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
if (opts.postinstall) {
|
|
2037
|
+
pkg2.scripts = { postinstall: `node install.js --agent claude-code --silent` };
|
|
2038
|
+
}
|
|
2039
|
+
return JSON.stringify(pkg2, null, 2) + "\n";
|
|
2040
|
+
}
|
|
2041
|
+
function emitStandalonePackage(baseDir, pillName, version, toolsJsContent, doc, blockingHooks, opts = {}) {
|
|
2042
|
+
const distDir = join5(baseDir, ".mcpill", "dist", pillName);
|
|
2043
|
+
const hooksDir = join5(distDir, "hooks");
|
|
2044
|
+
mkdirSync3(hooksDir, { recursive: true });
|
|
2045
|
+
const blockingHookTools = blockingHooks.filter((h) => h.required_tool).map((h) => h.required_tool);
|
|
2046
|
+
writeFileSync3(join5(distDir, "package.json"), generateDistPackageJson(pillName, version, opts));
|
|
2047
|
+
writeFileSync3(join5(distDir, "tools.js"), toolsJsContent);
|
|
2048
|
+
writeFileSync3(
|
|
2049
|
+
join5(distDir, "index.js"),
|
|
2050
|
+
generateStandaloneIndexJs(pillName, version, doc.prompts, doc.resources, blockingHookTools)
|
|
2051
|
+
);
|
|
2052
|
+
writeFileSync3(join5(distDir, "install.js"), generateStandaloneInstallJs(pillName, version, blockingHooks));
|
|
2053
|
+
if (blockingHooks.length > 0) {
|
|
2054
|
+
writeFileSync3(join5(hooksDir, "pre-tool-gate.js"), generateStandaloneGateScript(pillName, blockingHooks));
|
|
2055
|
+
writeFileSync3(join5(hooksDir, "post-analysis-flag.js"), generateStandaloneFlagScript(pillName));
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
|
|
1551
2059
|
// src/commands/compile.ts
|
|
1552
2060
|
var zodTypeNameMap = {
|
|
1553
2061
|
ZodString: "string",
|
|
@@ -1597,18 +2105,18 @@ ${r.content}`;
|
|
|
1597
2105
|
}
|
|
1598
2106
|
async function runCompile(opts) {
|
|
1599
2107
|
const baseDir = resolve(opts.dir ?? process.cwd());
|
|
1600
|
-
const mcpillDir =
|
|
1601
|
-
const serverDir =
|
|
2108
|
+
const mcpillDir = join6(baseDir, ".mcpill");
|
|
2109
|
+
const serverDir = join6(mcpillDir, "server");
|
|
1602
2110
|
if (opts.toMd) {
|
|
1603
|
-
const configPath =
|
|
2111
|
+
const configPath = join6(serverDir, "mcpill.config.json");
|
|
1604
2112
|
if (!existsSync3(configPath)) {
|
|
1605
2113
|
throw new Error("No pill found \u2014 run mcpill compile first");
|
|
1606
2114
|
}
|
|
1607
2115
|
const config = await loadConfig(serverDir);
|
|
1608
2116
|
const resources = await loadResources(serverDir);
|
|
1609
|
-
const promptsPath =
|
|
2117
|
+
const promptsPath = join6(serverDir, "prompts.json");
|
|
1610
2118
|
const prompts = JSON.parse(readFileSync4(promptsPath, "utf-8"));
|
|
1611
|
-
const toolsPath =
|
|
2119
|
+
const toolsPath = join6(serverDir, "tools.js");
|
|
1612
2120
|
const handlers = existsSync3(toolsPath) ? extractHandlers(readFileSync4(toolsPath, "utf-8")) : /* @__PURE__ */ new Map();
|
|
1613
2121
|
let tools = [];
|
|
1614
2122
|
if (existsSync3(toolsPath)) {
|
|
@@ -1642,11 +2150,13 @@ async function runCompile(opts) {
|
|
|
1642
2150
|
return;
|
|
1643
2151
|
}
|
|
1644
2152
|
const doc = parseServerDir(mcpillDir);
|
|
1645
|
-
const toolsJsPath =
|
|
2153
|
+
const toolsJsPath = join6(serverDir, "tools.js");
|
|
1646
2154
|
const existing = existsSync3(toolsJsPath) ? extractHandlers(readFileSync4(toolsJsPath, "utf-8")) : /* @__PURE__ */ new Map();
|
|
1647
2155
|
const { doc: mergedDoc, stubbed } = mergeHandlers(doc, existing);
|
|
1648
|
-
const pillMdPath =
|
|
2156
|
+
const pillMdPath = join6(baseDir, "PILL.md");
|
|
1649
2157
|
let pillContent = null;
|
|
2158
|
+
let pillVersion = "0.1.0";
|
|
2159
|
+
let pillPostinstall = false;
|
|
1650
2160
|
if (existsSync3(pillMdPath)) {
|
|
1651
2161
|
pillContent = readFileSync4(pillMdPath, "utf-8");
|
|
1652
2162
|
const serverSection = ("\n" + pillContent).split("\n## ").find((s) => s.startsWith("Server\n"));
|
|
@@ -1656,6 +2166,8 @@ async function runCompile(opts) {
|
|
|
1656
2166
|
if (kv["transport"] === "stdio" || kv["transport"] === "http") {
|
|
1657
2167
|
mergedDoc.config.transport = kv["transport"];
|
|
1658
2168
|
}
|
|
2169
|
+
if (kv["version"]) pillVersion = kv["version"].trim();
|
|
2170
|
+
if (kv["postinstall"]?.trim().toLowerCase() === "true") pillPostinstall = true;
|
|
1659
2171
|
}
|
|
1660
2172
|
}
|
|
1661
2173
|
if (opts.strict && stubbed.length > 0) {
|
|
@@ -1664,18 +2176,21 @@ async function runCompile(opts) {
|
|
|
1664
2176
|
for (const name of stubbed) {
|
|
1665
2177
|
console.warn(`\u26A0 Stub generated for tool: ${name}`);
|
|
1666
2178
|
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
2179
|
+
mkdirSync4(serverDir, { recursive: true });
|
|
2180
|
+
writeFileSync4(join6(serverDir, "mcpill.config.json"), JSON.stringify(mergedDoc.config, null, 2));
|
|
2181
|
+
writeFileSync4(join6(serverDir, "prompts.json"), JSON.stringify(mergedDoc.prompts, null, 2));
|
|
2182
|
+
writeFileSync4(join6(serverDir, "resources.md"), serializeResourcesMd(mergedDoc.resources));
|
|
2183
|
+
const toolsJsContent = generateToolsJs(mergedDoc.tools);
|
|
2184
|
+
writeFileSync4(toolsJsPath, toolsJsContent);
|
|
1672
2185
|
ensureDeps(serverDir);
|
|
1673
2186
|
console.log(`\u2713 .mcpill/server/ updated from .mcpill/server.md, .mcpill/server/tools/, .mcpill/server/prompts/`);
|
|
1674
2187
|
if (opts.noHooks) {
|
|
1675
|
-
|
|
2188
|
+
writeFileSync4(join6(mcpillDir, ".no-hooks"), "");
|
|
2189
|
+
emitStandalonePackage(baseDir, mergedDoc.config.name, pillVersion, toolsJsContent, mergedDoc, [], { postinstall: false });
|
|
2190
|
+
console.log(`\u2713 .mcpill/dist/${mergedDoc.config.name}/ updated`);
|
|
1676
2191
|
return;
|
|
1677
2192
|
}
|
|
1678
|
-
const agentMdPath = existsSync3(
|
|
2193
|
+
const agentMdPath = existsSync3(join6(baseDir, "AGENT.md")) ? join6(baseDir, "AGENT.md") : existsSync3(join6(mcpillDir, "AGENT.md")) ? join6(mcpillDir, "AGENT.md") : null;
|
|
1679
2194
|
const pillTools = pillContent ? parsePillMdTools(pillContent) : [];
|
|
1680
2195
|
const agentTools = agentMdPath ? parseAgentMdTools(readFileSync4(agentMdPath, "utf-8")) : [];
|
|
1681
2196
|
const toolMap = new Map([...pillTools, ...agentTools].map((t) => [t.name, t]));
|
|
@@ -1686,7 +2201,7 @@ async function runCompile(opts) {
|
|
|
1686
2201
|
console.log(`\u2713 .claude/settings.json updated with PreToolUse hook`);
|
|
1687
2202
|
}
|
|
1688
2203
|
const pillHooks = pillContent ? parsePillMdHooks(pillContent) : [];
|
|
1689
|
-
const hooksDir =
|
|
2204
|
+
const hooksDir = join6(mcpillDir, "server", "hooks");
|
|
1690
2205
|
const fileHooks = [];
|
|
1691
2206
|
if (existsSync3(hooksDir)) {
|
|
1692
2207
|
let hookFiles = [];
|
|
@@ -1695,7 +2210,7 @@ async function runCompile(opts) {
|
|
|
1695
2210
|
} catch {
|
|
1696
2211
|
}
|
|
1697
2212
|
for (const file of hookFiles) {
|
|
1698
|
-
const entry = parseHookFile(readFileSync4(
|
|
2213
|
+
const entry = parseHookFile(readFileSync4(join6(hooksDir, file), "utf-8"));
|
|
1699
2214
|
if (entry) fileHooks.push(entry);
|
|
1700
2215
|
}
|
|
1701
2216
|
}
|
|
@@ -1721,6 +2236,8 @@ async function runCompile(opts) {
|
|
|
1721
2236
|
mergeUserHooksIntoSettings(baseDir, generatedEntries);
|
|
1722
2237
|
console.log(`\u2713 .claude/settings.json updated with ${blockingHooks.length} blocking hook(s)`);
|
|
1723
2238
|
}
|
|
2239
|
+
emitStandalonePackage(baseDir, mergedDoc.config.name, pillVersion, toolsJsContent, mergedDoc, blockingHooks, { postinstall: pillPostinstall });
|
|
2240
|
+
console.log(`\u2713 .mcpill/dist/${mergedDoc.config.name}/ updated`);
|
|
1724
2241
|
}
|
|
1725
2242
|
|
|
1726
2243
|
// src/commands/pack.ts
|
|
@@ -1769,7 +2286,7 @@ async function runPublish(dir, access) {
|
|
|
1769
2286
|
// src/cli.ts
|
|
1770
2287
|
var pkgDir = dirname(fileURLToPath(import.meta.url));
|
|
1771
2288
|
var pkg = JSON.parse(
|
|
1772
|
-
readFileSync5(
|
|
2289
|
+
readFileSync5(join7(pkgDir, "../package.json"), "utf-8")
|
|
1773
2290
|
);
|
|
1774
2291
|
var program = new Command();
|
|
1775
2292
|
program.name("mcpill").version(pkg.version);
|