@piut/cli 3.4.0 → 3.5.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.
Files changed (2) hide show
  1. package/dist/cli.js +151 -37
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -141,6 +141,16 @@ async function publishServer(key) {
141
141
  }
142
142
  return res.json();
143
143
  }
144
+ async function getBrain(key) {
145
+ const res = await fetch(`${API_BASE}/api/cli/brain`, {
146
+ headers: { Authorization: `Bearer ${key}` }
147
+ });
148
+ if (!res.ok) {
149
+ const body = await res.json().catch(() => ({ error: "Unknown error" }));
150
+ throw new Error(body.error || `Failed to fetch brain (HTTP ${res.status})`);
151
+ }
152
+ return res.json();
153
+ }
144
154
  function getMachineId() {
145
155
  const hostname = os.hostname();
146
156
  return crypto.createHash("sha256").update(hostname).digest("hex").slice(0, 16);
@@ -603,19 +613,14 @@ var Spinner = class {
603
613
  interval = null;
604
614
  startTime = Date.now();
605
615
  message = "";
606
- tokens = 0;
607
616
  sections = [];
608
617
  start(message) {
609
618
  this.message = message;
610
619
  this.startTime = Date.now();
611
- this.tokens = 0;
612
620
  this.sections = [];
613
621
  this.render();
614
622
  this.interval = setInterval(() => this.render(), 80);
615
623
  }
616
- updateTokens(count) {
617
- this.tokens = count;
618
- }
619
624
  addSection(name) {
620
625
  this.clearLine();
621
626
  const elapsed = this.elapsed();
@@ -639,9 +644,8 @@ var Spinner = class {
639
644
  this.frame = (this.frame + 1) % SPINNER_FRAMES.length;
640
645
  const spinner = brand(SPINNER_FRAMES[this.frame]);
641
646
  const elapsed = dim(this.elapsed());
642
- const tokenStr = this.tokens > 0 ? dim(` ${this.tokens.toLocaleString()} tokens`) : "";
643
647
  this.clearLine();
644
- process.stdout.write(` ${spinner} ${this.message} ${elapsed}${tokenStr}`);
648
+ process.stdout.write(` ${spinner} ${this.message} ${elapsed}`);
645
649
  }
646
650
  elapsed() {
647
651
  const ms = Date.now() - this.startTime;
@@ -1430,7 +1434,7 @@ async function removeCommand() {
1430
1434
  }
1431
1435
 
1432
1436
  // src/commands/build.ts
1433
- import { select, checkbox as checkbox3, input } from "@inquirer/prompts";
1437
+ import { select, checkbox as checkbox3, input, confirm as confirm3 } from "@inquirer/prompts";
1434
1438
  import chalk5 from "chalk";
1435
1439
  import os6 from "os";
1436
1440
 
@@ -1578,7 +1582,6 @@ async function buildCommand(options) {
1578
1582
  spinner.updateMessage(String(event.data.message || "Processing..."));
1579
1583
  break;
1580
1584
  case "progress":
1581
- spinner.updateTokens(event.data.tokens);
1582
1585
  break;
1583
1586
  case "section":
1584
1587
  spinner.addSection(String(event.data.name));
@@ -1600,28 +1603,66 @@ async function buildCommand(options) {
1600
1603
  console.log();
1601
1604
  console.log(success(" Brain built!"));
1602
1605
  console.log();
1603
- const sectionSummary = (content, label) => {
1604
- if (!content || !content.trim()) {
1606
+ const SECTION_LABELS = {
1607
+ about: "About",
1608
+ soul: "Soul",
1609
+ areas: "Areas of Responsibility",
1610
+ projects: "Projects",
1611
+ memory: "Memory"
1612
+ };
1613
+ for (const [key, label] of Object.entries(SECTION_LABELS)) {
1614
+ const content = sections[key] || "";
1615
+ if (!content.trim()) {
1605
1616
  console.log(dim(` ${label} \u2014 (empty)`));
1606
1617
  } else {
1607
- const firstLine = content.split("\n").find((l) => l.trim() && !l.startsWith("#"))?.trim() || "";
1608
- const preview = firstLine.length > 60 ? firstLine.slice(0, 60) + "..." : firstLine;
1609
- console.log(success(` ${label}`) + dim(` \u2014 ${preview}`));
1618
+ console.log(success(` ${label}`));
1619
+ const lines = content.split("\n").filter((l) => l.trim()).slice(0, 5);
1620
+ for (const line of lines) {
1621
+ console.log(dim(` ${line.length > 80 ? line.slice(0, 80) + "..." : line}`));
1622
+ }
1623
+ const totalLines = content.split("\n").filter((l) => l.trim()).length;
1624
+ if (totalLines > 5) {
1625
+ console.log(dim(` ... (${totalLines - 5} more lines)`));
1626
+ }
1610
1627
  }
1611
- };
1612
- sectionSummary(sections.about || "", "About");
1613
- sectionSummary(sections.soul || "", "Soul");
1614
- sectionSummary(sections.areas || "", "Areas of Responsibility");
1615
- sectionSummary(sections.projects || "", "Projects");
1616
- sectionSummary(sections.memory || "", "Memory");
1617
- console.log();
1628
+ console.log();
1629
+ }
1618
1630
  console.log(dim(` Review and edit at ${brand("piut.com/dashboard")}`));
1619
1631
  console.log();
1632
+ const wantPublish = options.publish === false ? false : options.yes ? true : await confirm3({
1633
+ message: "Publish your brain now?",
1634
+ default: true
1635
+ });
1636
+ if (wantPublish) {
1637
+ try {
1638
+ await publishServer(apiKey);
1639
+ console.log();
1640
+ console.log(success(" \u2713 Brain published. MCP server is live."));
1641
+ console.log();
1642
+ } catch (err) {
1643
+ console.log();
1644
+ const msg = err.message;
1645
+ if (msg === "REQUIRES_SUBSCRIPTION") {
1646
+ console.log(chalk5.yellow(" Deploy requires an active subscription ($10/mo)."));
1647
+ console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
1648
+ console.log(dim(" 14-day free trial included."));
1649
+ } else {
1650
+ console.log(chalk5.red(` \u2717 ${msg}`));
1651
+ }
1652
+ console.log();
1653
+ }
1654
+ } else {
1655
+ console.log();
1656
+ console.log(dim(" You can publish anytime with: ") + brand("piut deploy"));
1657
+ console.log();
1658
+ }
1620
1659
  } catch (err) {
1621
1660
  spinner.stop();
1622
1661
  if (err instanceof CliError) throw err;
1623
- console.log(chalk5.red(` \u2717 ${err.message}`));
1624
- throw new CliError(err.message);
1662
+ const msg = err.message || "Unknown error";
1663
+ const hint = msg === "terminated" || msg.includes("network") || msg.includes("fetch") ? "The build was interrupted. This can happen if your scan data is very large. Try using --folders to limit which directories are scanned." : msg;
1664
+ console.log(chalk5.red(` \u2717 ${hint}`));
1665
+ throw new CliError(hint);
1625
1666
  }
1626
1667
  }
1627
1668
 
@@ -1808,8 +1849,10 @@ async function connectCommand(options) {
1808
1849
  }
1809
1850
  }
1810
1851
  }
1852
+ const projectsWithActions = new Set(actions.map((a) => a.project.path));
1853
+ const alreadyConnectedCount = projects.filter((p) => !projectsWithActions.has(p.path)).length;
1811
1854
  if (actions.length === 0) {
1812
- console.log(dim(" All projects are already connected."));
1855
+ console.log(success(` All ${projects.length} project(s) are already connected.`));
1813
1856
  console.log();
1814
1857
  return;
1815
1858
  }
@@ -1820,7 +1863,10 @@ async function connectCommand(options) {
1820
1863
  byProject.get(key).push(action);
1821
1864
  }
1822
1865
  console.log();
1823
- console.log(` Found ${brand.bold(String(byProject.size))} project(s) to connect:`);
1866
+ if (alreadyConnectedCount > 0) {
1867
+ console.log(dim(` ${alreadyConnectedCount} project(s) already connected.`));
1868
+ }
1869
+ console.log(` Found ${brand.bold(String(byProject.size))} project(s) with new connections available:`);
1824
1870
  console.log();
1825
1871
  const projectChoices = [];
1826
1872
  for (const [projectPath, projectActions] of byProject) {
@@ -1831,7 +1877,8 @@ async function connectCommand(options) {
1831
1877
  }).join(", ");
1832
1878
  projectChoices.push({
1833
1879
  name: `${projectName} ${dim(`(${desc})`)}`,
1834
- value: projectPath
1880
+ value: projectPath,
1881
+ checked: true
1835
1882
  });
1836
1883
  }
1837
1884
  let selectedPaths;
@@ -1903,7 +1950,7 @@ async function connectCommand(options) {
1903
1950
  // src/commands/disconnect.ts
1904
1951
  import fs10 from "fs";
1905
1952
  import path11 from "path";
1906
- import { checkbox as checkbox5, confirm as confirm4 } from "@inquirer/prompts";
1953
+ import { checkbox as checkbox5, confirm as confirm5 } from "@inquirer/prompts";
1907
1954
  var DEDICATED_FILES = /* @__PURE__ */ new Set([
1908
1955
  ".cursor/rules/piut.mdc",
1909
1956
  ".windsurf/rules/piut.md",
@@ -2031,7 +2078,7 @@ async function disconnectCommand(options) {
2031
2078
  console.log(dim(" No projects selected."));
2032
2079
  return;
2033
2080
  }
2034
- const proceed = await confirm4({
2081
+ const proceed = await confirm5({
2035
2082
  message: `Disconnect ${selectedPaths.length} project(s)?`,
2036
2083
  default: false
2037
2084
  });
@@ -2110,7 +2157,7 @@ import chalk8 from "chalk";
2110
2157
  // src/lib/update-check.ts
2111
2158
  import { execFile } from "child_process";
2112
2159
  import chalk7 from "chalk";
2113
- import { confirm as confirm5 } from "@inquirer/prompts";
2160
+ import { confirm as confirm6 } from "@inquirer/prompts";
2114
2161
  var PACKAGE_NAME = "@piut/cli";
2115
2162
  function isNpx() {
2116
2163
  return process.env.npm_command === "exec" || (process.env._?.includes("npx") ?? false);
@@ -2150,7 +2197,7 @@ async function checkForUpdate(currentVersion) {
2150
2197
  console.log();
2151
2198
  if (npx) return;
2152
2199
  try {
2153
- const shouldUpdate = await confirm5({
2200
+ const shouldUpdate = await confirm6({
2154
2201
  message: `Update to v${latest} now?`,
2155
2202
  default: true
2156
2203
  });
@@ -2374,7 +2421,7 @@ async function doctorCommand(options) {
2374
2421
  }
2375
2422
 
2376
2423
  // src/commands/interactive.ts
2377
- import { select as select2, confirm as confirm6, checkbox as checkbox6, password as password3 } from "@inquirer/prompts";
2424
+ import { select as select2, confirm as confirm7, checkbox as checkbox6, password as password3 } from "@inquirer/prompts";
2378
2425
  import fs12 from "fs";
2379
2426
  import path12 from "path";
2380
2427
  import chalk10 from "chalk";
@@ -2428,7 +2475,7 @@ async function interactiveMenu() {
2428
2475
  console.log(warning(" You haven\u2019t built a brain yet."));
2429
2476
  console.log(dim(" Your brain is how AI tools learn about you \u2014 your projects, preferences, and context."));
2430
2477
  console.log();
2431
- const wantBuild = await confirm6({
2478
+ const wantBuild = await confirm7({
2432
2479
  message: "Build your brain now?",
2433
2480
  default: true
2434
2481
  });
@@ -2444,7 +2491,7 @@ async function interactiveMenu() {
2444
2491
  console.log(warning(" Your brain is built but not deployed yet."));
2445
2492
  console.log(dim(" Deploy it to make your MCP server live so AI tools can read your brain."));
2446
2493
  console.log();
2447
- const wantDeploy = await confirm6({
2494
+ const wantDeploy = await confirm7({
2448
2495
  message: "Deploy your brain now?",
2449
2496
  default: true
2450
2497
  });
@@ -2498,6 +2545,12 @@ async function interactiveMenu() {
2498
2545
  description: "Remove brain references from project configs",
2499
2546
  disabled: !isDeployed && "(deploy brain first)"
2500
2547
  },
2548
+ {
2549
+ name: "View Brain",
2550
+ value: "view-brain",
2551
+ description: "View all 5 brain sections",
2552
+ disabled: !hasBrain && "(build brain first)"
2553
+ },
2501
2554
  {
2502
2555
  name: "Status",
2503
2556
  value: "status",
@@ -2543,6 +2596,9 @@ async function interactiveMenu() {
2543
2596
  case "disconnect-projects":
2544
2597
  await disconnectCommand({});
2545
2598
  break;
2599
+ case "view-brain":
2600
+ await handleViewBrain(apiKey);
2601
+ break;
2546
2602
  case "status":
2547
2603
  statusCommand();
2548
2604
  break;
@@ -2575,7 +2631,7 @@ async function interactiveMenu() {
2575
2631
  }
2576
2632
  }
2577
2633
  async function handleUndeploy(apiKey) {
2578
- const confirmed = await confirm6({
2634
+ const confirmed = await confirm7({
2579
2635
  message: "Undeploy your brain? AI tools will lose access to your MCP server.",
2580
2636
  default: false
2581
2637
  });
@@ -2642,6 +2698,11 @@ async function handleConnectTools(apiKey, validation) {
2642
2698
  mergeConfig(configPath, tool.configKey, serverConfig);
2643
2699
  toolLine(tool.name, success("connected"), "\u2714");
2644
2700
  }
2701
+ if (validation.serverUrl) {
2702
+ await Promise.all(
2703
+ selected.map(({ tool }) => pingMcp(validation.serverUrl, apiKey, tool.name))
2704
+ );
2705
+ }
2645
2706
  console.log();
2646
2707
  console.log(dim(" Restart your AI tools for changes to take effect."));
2647
2708
  console.log();
@@ -2674,7 +2735,7 @@ async function handleDisconnectTools() {
2674
2735
  console.log(dim(" No tools selected."));
2675
2736
  return;
2676
2737
  }
2677
- const proceed = await confirm6({
2738
+ const proceed = await confirm7({
2678
2739
  message: `Disconnect p\u0131ut from ${selected.length} tool(s)?`,
2679
2740
  default: false
2680
2741
  });
@@ -2692,9 +2753,62 @@ async function handleDisconnectTools() {
2692
2753
  console.log(dim(" Restart your AI tools for changes to take effect."));
2693
2754
  console.log();
2694
2755
  }
2756
+ async function handleViewBrain(apiKey) {
2757
+ console.log(dim(" Loading brain..."));
2758
+ const { sections, hasUnpublishedChanges } = await getBrain(apiKey);
2759
+ const SECTION_LABELS = {
2760
+ about: "About",
2761
+ soul: "Soul",
2762
+ areas: "Areas of Responsibility",
2763
+ projects: "Projects",
2764
+ memory: "Memory"
2765
+ };
2766
+ console.log();
2767
+ for (const [key, label] of Object.entries(SECTION_LABELS)) {
2768
+ const content = sections[key] || "";
2769
+ if (!content.trim()) {
2770
+ console.log(dim(` ${label} \u2014 (empty)`));
2771
+ } else {
2772
+ console.log(success(` ${label}`));
2773
+ for (const line of content.split("\n")) {
2774
+ console.log(` ${line}`);
2775
+ }
2776
+ }
2777
+ console.log();
2778
+ }
2779
+ console.log(dim(` Edit at ${brand("piut.com/dashboard")}`));
2780
+ console.log();
2781
+ if (hasUnpublishedChanges) {
2782
+ console.log(warning(" You have unpublished changes."));
2783
+ console.log();
2784
+ const wantPublish = await confirm7({
2785
+ message: "Publish now?",
2786
+ default: true
2787
+ });
2788
+ if (wantPublish) {
2789
+ try {
2790
+ await publishServer(apiKey);
2791
+ console.log();
2792
+ console.log(success(" \u2713 Brain published."));
2793
+ console.log();
2794
+ } catch (err) {
2795
+ console.log();
2796
+ const msg = err.message;
2797
+ if (msg === "REQUIRES_SUBSCRIPTION") {
2798
+ console.log(chalk10.yellow(" Deploy requires an active subscription ($10/mo)."));
2799
+ console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
2800
+ console.log(dim(" 14-day free trial included."));
2801
+ } else {
2802
+ console.log(chalk10.red(` \u2717 ${msg}`));
2803
+ }
2804
+ console.log();
2805
+ }
2806
+ }
2807
+ }
2808
+ }
2695
2809
 
2696
2810
  // src/cli.ts
2697
- var VERSION = "3.4.0";
2811
+ var VERSION = "3.5.0";
2698
2812
  function withExit(fn) {
2699
2813
  return async (...args2) => {
2700
2814
  try {
@@ -2710,7 +2824,7 @@ program.name("piut").description("Build your AI brain instantly. Deploy it as an
2710
2824
  if (actionCommand.name() === "update") return;
2711
2825
  return checkForUpdate(VERSION);
2712
2826
  }).action(interactiveMenu);
2713
- program.command("build").description("Build or rebuild your brain from your files").option("-k, --key <key>", "API key").option("--folders <paths>", "Comma-separated folder paths to scan").action(withExit(buildCommand));
2827
+ program.command("build").description("Build or rebuild your brain from your files").option("-k, --key <key>", "API key").option("--folders <paths>", "Comma-separated folder paths to scan").option("-y, --yes", "Auto-publish after build").option("--no-publish", "Skip publish prompt after build").action(withExit(buildCommand));
2714
2828
  program.command("deploy").description("Publish your MCP server (requires paid account)").option("-k, --key <key>", "API key").action(withExit(deployCommand));
2715
2829
  program.command("connect").description("Add brain references to project config files").option("-k, --key <key>", "API key").option("-y, --yes", "Skip interactive prompts").option("--folders <paths>", "Comma-separated folder paths to scan").action(withExit(connectCommand));
2716
2830
  program.command("disconnect").description("Remove brain references from project config files").option("-y, --yes", "Skip interactive prompts").option("--folders <paths>", "Comma-separated folder paths to scan").action(withExit(disconnectCommand));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piut/cli",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Build your AI brain instantly. Deploy it as an MCP server. Connect it to every project.",
5
5
  "type": "module",
6
6
  "bin": {