@polterware/polter 0.1.0 → 0.1.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/index.js +104 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3832,6 +3832,12 @@ function parseCliArgs(argv) {
|
|
|
3832
3832
|
if (argv_[0] === "pipeline" && argv_[1] === "run" && argv_[2]) {
|
|
3833
3833
|
return { mode: "pipeline-run", options: {}, pipelineName: argv_[2] };
|
|
3834
3834
|
}
|
|
3835
|
+
if (argv_[0] === "mcp" && argv_[1] === "install") {
|
|
3836
|
+
let mcpScope = "local";
|
|
3837
|
+
if (argv_.includes("--project")) mcpScope = "project";
|
|
3838
|
+
else if (argv_.includes("--global")) mcpScope = "user";
|
|
3839
|
+
return { mode: "mcp-install", options: {}, mcpScope };
|
|
3840
|
+
}
|
|
3835
3841
|
if (argv_[0] === "config") {
|
|
3836
3842
|
const edit = argv_.includes("--edit");
|
|
3837
3843
|
return { mode: "config", options: {}, configEdit: edit, classic };
|
|
@@ -3916,6 +3922,9 @@ function printCliHelp() {
|
|
|
3916
3922
|
" polter plan Show declarative state diff",
|
|
3917
3923
|
" polter apply Apply declarative state changes",
|
|
3918
3924
|
" polter status Show current tool status",
|
|
3925
|
+
" polter mcp install Install Polter MCP server into Claude Code (local scope)",
|
|
3926
|
+
" polter mcp install --project Install for this project only (shared via repo)",
|
|
3927
|
+
" polter mcp install --global Install globally for all projects",
|
|
3919
3928
|
"",
|
|
3920
3929
|
" polter app setup ops [--path <dir>] [--create-project|--use-existing-project] [--yes]",
|
|
3921
3930
|
" polter app link ops [--path <dir>] [--relink]",
|
|
@@ -4736,14 +4745,94 @@ async function runAppCli(options) {
|
|
|
4736
4745
|
});
|
|
4737
4746
|
}
|
|
4738
4747
|
|
|
4739
|
-
// src/
|
|
4748
|
+
// src/lib/mcpInstaller.ts
|
|
4749
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
4750
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
4751
|
+
import { join as join3 } from "path";
|
|
4752
|
+
import { homedir as homedir2 } from "os";
|
|
4740
4753
|
import pc4 from "picocolors";
|
|
4754
|
+
var MCP_ARGS = ["npx", "-y", "-p", "@polterware/polter", "polter-mcp"];
|
|
4755
|
+
var SCOPE_LABELS = {
|
|
4756
|
+
local: "local (this machine)",
|
|
4757
|
+
project: "project (shared via repo)",
|
|
4758
|
+
user: "global (all projects)"
|
|
4759
|
+
};
|
|
4760
|
+
function tryClaudeCli(scope) {
|
|
4761
|
+
if (!commandExists("claude")) return false;
|
|
4762
|
+
const result = spawnSync2("claude", ["mcp", "add", "-s", scope, "polter", "--", ...MCP_ARGS], {
|
|
4763
|
+
stdio: "inherit",
|
|
4764
|
+
shell: true
|
|
4765
|
+
});
|
|
4766
|
+
return result.status === 0;
|
|
4767
|
+
}
|
|
4768
|
+
function getSettingsPath(scope) {
|
|
4769
|
+
if (scope === "project") {
|
|
4770
|
+
return join3(process.cwd(), ".mcp.json");
|
|
4771
|
+
}
|
|
4772
|
+
return join3(homedir2(), ".claude", "settings.json");
|
|
4773
|
+
}
|
|
4774
|
+
function tryManualInstall(scope) {
|
|
4775
|
+
const settingsPath = getSettingsPath(scope);
|
|
4776
|
+
const dir = join3(settingsPath, "..");
|
|
4777
|
+
let settings = {};
|
|
4778
|
+
if (existsSync2(settingsPath)) {
|
|
4779
|
+
try {
|
|
4780
|
+
settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
|
|
4781
|
+
} catch {
|
|
4782
|
+
process.stderr.write(pc4.red(`Failed to parse ${settingsPath}
|
|
4783
|
+
`));
|
|
4784
|
+
return false;
|
|
4785
|
+
}
|
|
4786
|
+
} else {
|
|
4787
|
+
mkdirSync2(dir, { recursive: true });
|
|
4788
|
+
}
|
|
4789
|
+
const mcpServers = settings.mcpServers ?? {};
|
|
4790
|
+
mcpServers.polter = {
|
|
4791
|
+
command: "npx",
|
|
4792
|
+
args: ["-y", "-p", "@polterware/polter", "polter-mcp"]
|
|
4793
|
+
};
|
|
4794
|
+
settings.mcpServers = mcpServers;
|
|
4795
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
4796
|
+
return true;
|
|
4797
|
+
}
|
|
4798
|
+
async function installMcpServer(scope) {
|
|
4799
|
+
process.stdout.write(pc4.bold(`Installing Polter MCP server \u2014 ${SCOPE_LABELS[scope]}
|
|
4800
|
+
|
|
4801
|
+
`));
|
|
4802
|
+
if (commandExists("claude")) {
|
|
4803
|
+
process.stdout.write(` Using 'claude mcp add -s ${scope}'...
|
|
4804
|
+
`);
|
|
4805
|
+
if (tryClaudeCli(scope)) {
|
|
4806
|
+
process.stdout.write(pc4.green("\n Done! Restart Claude Code to use Polter tools.\n"));
|
|
4807
|
+
return;
|
|
4808
|
+
}
|
|
4809
|
+
process.stdout.write(pc4.yellow(" 'claude mcp add' failed, falling back to manual install...\n\n"));
|
|
4810
|
+
}
|
|
4811
|
+
const settingsPath = getSettingsPath(scope);
|
|
4812
|
+
process.stdout.write(` Writing to ${settingsPath}...
|
|
4813
|
+
`);
|
|
4814
|
+
if (tryManualInstall(scope)) {
|
|
4815
|
+
process.stdout.write(pc4.green("\n Done! Restart Claude Code to use Polter tools.\n"));
|
|
4816
|
+
} else {
|
|
4817
|
+
process.stderr.write(pc4.red("\n Failed to install. Add manually:\n\n"));
|
|
4818
|
+
process.stderr.write(` ${pc4.dim(JSON.stringify({ mcpServers: { polter: { command: "npx", args: ["-y", "-p", "@polterware/polter", "polter-mcp"] } } }, null, 2))}
|
|
4819
|
+
`);
|
|
4820
|
+
process.exit(1);
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
|
|
4824
|
+
// src/index.tsx
|
|
4825
|
+
import pc5 from "picocolors";
|
|
4741
4826
|
async function main() {
|
|
4742
4827
|
const parsed = parseCliArgs(process.argv.slice(2));
|
|
4743
4828
|
if (parsed.mode === "help") {
|
|
4744
4829
|
printCliHelp();
|
|
4745
4830
|
return;
|
|
4746
4831
|
}
|
|
4832
|
+
if (parsed.mode === "mcp-install") {
|
|
4833
|
+
await installMcpServer(parsed.mcpScope ?? "local");
|
|
4834
|
+
return;
|
|
4835
|
+
}
|
|
4747
4836
|
if (parsed.mode === "app") {
|
|
4748
4837
|
const exitCode = await runAppCli(parsed.options);
|
|
4749
4838
|
process.exit(exitCode);
|
|
@@ -4755,7 +4844,7 @@ async function main() {
|
|
|
4755
4844
|
`);
|
|
4756
4845
|
process.exit(1);
|
|
4757
4846
|
}
|
|
4758
|
-
process.stdout.write(
|
|
4847
|
+
process.stdout.write(pc5.bold(`Running pipeline: ${pipeline.name}
|
|
4759
4848
|
`));
|
|
4760
4849
|
const results = await executePipeline(pipeline, (progress) => {
|
|
4761
4850
|
if (progress.done) return;
|
|
@@ -4769,24 +4858,24 @@ async function main() {
|
|
|
4769
4858
|
});
|
|
4770
4859
|
const errors = results.filter((r) => r.status === "error");
|
|
4771
4860
|
if (errors.length > 0) {
|
|
4772
|
-
process.stderr.write(
|
|
4861
|
+
process.stderr.write(pc5.red(`
|
|
4773
4862
|
Pipeline completed with ${errors.length} error(s)
|
|
4774
4863
|
`));
|
|
4775
4864
|
process.exit(1);
|
|
4776
4865
|
}
|
|
4777
|
-
process.stdout.write(
|
|
4866
|
+
process.stdout.write(pc5.green("\nPipeline completed successfully!\n"));
|
|
4778
4867
|
return;
|
|
4779
4868
|
}
|
|
4780
4869
|
if (parsed.mode === "status") {
|
|
4781
4870
|
const tools = ["supabase", "gh", "vercel"];
|
|
4782
|
-
process.stdout.write(
|
|
4871
|
+
process.stdout.write(pc5.bold("Tool Status\n\n"));
|
|
4783
4872
|
for (const toolId of tools) {
|
|
4784
4873
|
const info = getToolInfo(toolId);
|
|
4785
|
-
const status = info.installed ?
|
|
4874
|
+
const status = info.installed ? pc5.green(`\u2713 ${info.label} ${info.version ?? ""}`) : pc5.red(`\u2717 ${info.label} not found`);
|
|
4786
4875
|
process.stdout.write(` ${status}
|
|
4787
4876
|
`);
|
|
4788
4877
|
}
|
|
4789
|
-
process.stdout.write(
|
|
4878
|
+
process.stdout.write(pc5.bold("\nProject Status\n\n"));
|
|
4790
4879
|
const projectStatus = getCurrentStatus();
|
|
4791
4880
|
if (projectStatus.supabase) {
|
|
4792
4881
|
process.stdout.write(` Supabase: ${projectStatus.supabase.linked ? "linked" : "not linked"}
|
|
@@ -4810,15 +4899,15 @@ Pipeline completed with ${errors.length} error(s)
|
|
|
4810
4899
|
}
|
|
4811
4900
|
const plan = planChanges(yaml);
|
|
4812
4901
|
if (plan.noChanges) {
|
|
4813
|
-
process.stdout.write(
|
|
4902
|
+
process.stdout.write(pc5.green("No changes needed. State is up to date.\n"));
|
|
4814
4903
|
return;
|
|
4815
4904
|
}
|
|
4816
|
-
process.stdout.write(
|
|
4905
|
+
process.stdout.write(pc5.bold(`Plan: ${plan.actions.length} action(s)
|
|
4817
4906
|
|
|
4818
4907
|
`));
|
|
4819
4908
|
for (const action of plan.actions) {
|
|
4820
4909
|
const prefix = action.action === "create" ? "+" : action.action === "delete" ? "-" : "~";
|
|
4821
|
-
const color = action.action === "create" ?
|
|
4910
|
+
const color = action.action === "create" ? pc5.green : action.action === "delete" ? pc5.red : pc5.yellow;
|
|
4822
4911
|
process.stdout.write(` ${color(`${prefix} [${action.tool}] ${action.description}`)}
|
|
4823
4912
|
`);
|
|
4824
4913
|
}
|
|
@@ -4832,10 +4921,10 @@ Pipeline completed with ${errors.length} error(s)
|
|
|
4832
4921
|
}
|
|
4833
4922
|
const plan = planChanges(yaml);
|
|
4834
4923
|
if (plan.noChanges) {
|
|
4835
|
-
process.stdout.write(
|
|
4924
|
+
process.stdout.write(pc5.green("No changes needed. State is up to date.\n"));
|
|
4836
4925
|
return;
|
|
4837
4926
|
}
|
|
4838
|
-
process.stdout.write(
|
|
4927
|
+
process.stdout.write(pc5.bold(`Applying ${plan.actions.length} action(s)...
|
|
4839
4928
|
|
|
4840
4929
|
`));
|
|
4841
4930
|
const results = await applyActions(plan.actions, process.cwd(), (i, total, action) => {
|
|
@@ -4844,16 +4933,16 @@ Pipeline completed with ${errors.length} error(s)
|
|
|
4844
4933
|
});
|
|
4845
4934
|
const errors = results.filter((r) => !r.success);
|
|
4846
4935
|
if (errors.length > 0) {
|
|
4847
|
-
process.stderr.write(
|
|
4936
|
+
process.stderr.write(pc5.red(`
|
|
4848
4937
|
Apply completed with ${errors.length} error(s).
|
|
4849
4938
|
`));
|
|
4850
4939
|
for (const err of errors) {
|
|
4851
|
-
process.stderr.write(
|
|
4940
|
+
process.stderr.write(pc5.red(` \u2717 ${err.action.description}
|
|
4852
4941
|
`));
|
|
4853
4942
|
}
|
|
4854
4943
|
process.exit(1);
|
|
4855
4944
|
}
|
|
4856
|
-
process.stdout.write(
|
|
4945
|
+
process.stdout.write(pc5.green("\nAll changes applied successfully!\n"));
|
|
4857
4946
|
return;
|
|
4858
4947
|
}
|
|
4859
4948
|
if (parsed.mode === "config") {
|