@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.
Files changed (2) hide show
  1. package/dist/index.js +104 -15
  2. 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/index.tsx
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(pc4.bold(`Running pipeline: ${pipeline.name}
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(pc4.red(`
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(pc4.green("\nPipeline completed successfully!\n"));
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(pc4.bold("Tool Status\n\n"));
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 ? pc4.green(`\u2713 ${info.label} ${info.version ?? ""}`) : pc4.red(`\u2717 ${info.label} not found`);
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(pc4.bold("\nProject Status\n\n"));
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(pc4.green("No changes needed. State is up to date.\n"));
4902
+ process.stdout.write(pc5.green("No changes needed. State is up to date.\n"));
4814
4903
  return;
4815
4904
  }
4816
- process.stdout.write(pc4.bold(`Plan: ${plan.actions.length} action(s)
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" ? pc4.green : action.action === "delete" ? pc4.red : pc4.yellow;
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(pc4.green("No changes needed. State is up to date.\n"));
4924
+ process.stdout.write(pc5.green("No changes needed. State is up to date.\n"));
4836
4925
  return;
4837
4926
  }
4838
- process.stdout.write(pc4.bold(`Applying ${plan.actions.length} action(s)...
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(pc4.red(`
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(pc4.red(` \u2717 ${err.action.description}
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(pc4.green("\nAll changes applied successfully!\n"));
4945
+ process.stdout.write(pc5.green("\nAll changes applied successfully!\n"));
4857
4946
  return;
4858
4947
  }
4859
4948
  if (parsed.mode === "config") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polterware/polter",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "An interactive CLI for managing Supabase CLI workflows.",
5
5
  "type": "module",
6
6
  "bin": {