@piut/cli 3.8.0 → 3.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.
Files changed (3) hide show
  1. package/README.md +4 -4
  2. package/dist/cli.js +583 -236
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -28,9 +28,9 @@ __export(tree_prompt_exports, {
28
28
  loadChildren: () => loadChildren,
29
29
  shouldShowInTree: () => shouldShowInTree
30
30
  });
31
- import fs13 from "fs";
32
- import path14 from "path";
33
- import os8 from "os";
31
+ import fs15 from "fs";
32
+ import path16 from "path";
33
+ import os9 from "os";
34
34
  import {
35
35
  createPrompt,
36
36
  useState,
@@ -50,19 +50,19 @@ function shouldShowInTree(name) {
50
50
  }
51
51
  function shouldShowFile(name) {
52
52
  if (name.startsWith(".")) return false;
53
- const ext = path14.extname(name).toLowerCase();
53
+ const ext = path16.extname(name).toLowerCase();
54
54
  return !HIDDEN_FILE_EXTENSIONS.has(ext);
55
55
  }
56
56
  function loadChildren(parentPath, parentDepth, includeFiles = false) {
57
57
  try {
58
- const entries = fs13.readdirSync(parentPath, { withFileTypes: true });
58
+ const entries = fs15.readdirSync(parentPath, { withFileTypes: true });
59
59
  const dirs = [];
60
60
  const files = [];
61
61
  for (const entry of entries) {
62
62
  if (entry.isDirectory()) {
63
63
  if (!shouldShowInTree(entry.name)) continue;
64
64
  dirs.push({
65
- path: path14.join(parentPath, entry.name),
65
+ path: path16.join(parentPath, entry.name),
66
66
  name: entry.name,
67
67
  depth: parentDepth + 1,
68
68
  expanded: false,
@@ -72,7 +72,7 @@ function loadChildren(parentPath, parentDepth, includeFiles = false) {
72
72
  } else if (includeFiles && entry.isFile()) {
73
73
  if (!shouldShowFile(entry.name)) continue;
74
74
  files.push({
75
- path: path14.join(parentPath, entry.name),
75
+ path: path16.join(parentPath, entry.name),
76
76
  name: entry.name,
77
77
  depth: parentDepth + 1,
78
78
  expanded: false,
@@ -214,7 +214,7 @@ var init_tree_prompt = __esm({
214
214
  ".map"
215
215
  ]);
216
216
  treePrompt = createPrompt((config, done) => {
217
- const root = config.root ?? os8.homedir();
217
+ const root = config.root ?? os9.homedir();
218
218
  const pageSize = config.pageSize ?? 15;
219
219
  const mode = config.mode ?? "folders";
220
220
  const includeFiles = mode === "files";
@@ -325,9 +325,9 @@ import { Command } from "commander";
325
325
 
326
326
  // src/commands/setup.ts
327
327
  init_esm_shims();
328
- import fs4 from "fs";
329
- import path7 from "path";
330
- import { execSync } from "child_process";
328
+ import fs6 from "fs";
329
+ import path9 from "path";
330
+ import { execSync as execSync2 } from "child_process";
331
331
  import { password, confirm, checkbox } from "@inquirer/prompts";
332
332
  import chalk2 from "chalk";
333
333
 
@@ -384,29 +384,33 @@ async function* buildBrainStreaming(key, input2) {
384
384
  const reader = res.body.getReader();
385
385
  const decoder = new TextDecoder();
386
386
  let buffer = "";
387
- while (true) {
388
- const { done, value } = await reader.read();
389
- if (done) break;
390
- buffer += decoder.decode(value, { stream: true });
391
- const parts = buffer.split("\n\n");
392
- buffer = parts.pop() || "";
393
- for (const part of parts) {
394
- let eventName = "";
395
- let eventData = "";
396
- for (const line of part.split("\n")) {
397
- if (line.startsWith("event: ")) {
398
- eventName = line.slice(7).trim();
399
- } else if (line.startsWith("data: ")) {
400
- eventData = line.slice(6);
387
+ try {
388
+ while (true) {
389
+ const { done, value } = await reader.read();
390
+ if (done) break;
391
+ buffer += decoder.decode(value, { stream: true });
392
+ const parts = buffer.split("\n\n");
393
+ buffer = parts.pop() || "";
394
+ for (const part of parts) {
395
+ let eventName = "";
396
+ let eventData = "";
397
+ for (const line of part.split("\n")) {
398
+ if (line.startsWith("event: ")) {
399
+ eventName = line.slice(7).trim();
400
+ } else if (line.startsWith("data: ")) {
401
+ eventData = line.slice(6);
402
+ }
401
403
  }
402
- }
403
- if (eventName && eventData) {
404
- try {
405
- yield { event: eventName, data: JSON.parse(eventData) };
406
- } catch {
404
+ if (eventName && eventData) {
405
+ try {
406
+ yield { event: eventName, data: JSON.parse(eventData) };
407
+ } catch {
408
+ }
407
409
  }
408
410
  }
409
411
  }
412
+ } catch {
413
+ yield { event: "error", data: { message: "Connection lost. The build may still complete \u2014 run `piut status` to check." } };
410
414
  }
411
415
  }
412
416
  async function verifyMcpEndpoint(serverUrl, key) {
@@ -637,7 +641,7 @@ var TOOLS = [
637
641
  project: [".mcp.json"]
638
642
  },
639
643
  skillFilePath: "CLAUDE.md",
640
- quickCommand: (slug, key) => `claude mcp add-json piut-context '${JSON.stringify({
644
+ quickCommand: (slug, key) => `claude mcp add-json piut '${JSON.stringify({
641
645
  type: "http",
642
646
  url: MCP_URL(slug),
643
647
  headers: { ...AUTH_HEADER(key), ...machineHeaders("Claude Code") }
@@ -700,21 +704,20 @@ var TOOLS = [
700
704
  })
701
705
  },
702
706
  {
703
- id: "copilot",
704
- name: "GitHub Copilot",
707
+ id: "vscode",
708
+ name: "VS Code",
705
709
  configKey: "servers",
706
- globalConfigKey: "mcp.servers",
707
710
  configPaths: {
708
- darwin: ["~/Library/Application Support/Code/User/settings.json"],
709
- win32: [path2.join(appData(), "Code", "User", "settings.json")],
710
- linux: ["~/.config/Code/User/settings.json"],
711
+ darwin: ["~/Library/Application Support/Code/User/mcp.json"],
712
+ win32: [path2.join(appData(), "Code", "User", "mcp.json")],
713
+ linux: ["~/.config/Code/User/mcp.json"],
711
714
  project: [".vscode/mcp.json"]
712
715
  },
713
716
  skillFilePath: ".github/copilot-instructions.md",
714
717
  generateConfig: (slug, key) => ({
715
718
  type: "http",
716
719
  url: MCP_URL(slug),
717
- headers: { ...AUTH_HEADER(key), ...machineHeaders("GitHub Copilot") }
720
+ headers: { ...AUTH_HEADER(key), ...machineHeaders("VS Code") }
718
721
  })
719
722
  },
720
723
  {
@@ -773,7 +776,7 @@ var TOOLS = [
773
776
  win32: ["~/.mcporter/mcporter.json", "~/.openclaw/workspace/config/mcporter.json"],
774
777
  linux: ["~/.mcporter/mcporter.json", "~/.openclaw/workspace/config/mcporter.json"]
775
778
  },
776
- quickCommand: (slug, key) => `npx mcporter config add piut-context ${MCP_URL(slug)} --header "Authorization=Bearer ${key}"`,
779
+ quickCommand: (slug, key) => `npx mcporter config add piut ${MCP_URL(slug)} --header "Authorization=Bearer ${key}"`,
777
780
  generateConfig: (slug, key) => ({
778
781
  url: MCP_URL(slug),
779
782
  headers: { ...AUTH_HEADER(key), ...machineHeaders("OpenClaw") }
@@ -817,6 +820,8 @@ function resolveConfigPaths(tool) {
817
820
  init_esm_shims();
818
821
  import fs from "fs";
819
822
  import path4 from "path";
823
+ var SERVER_KEY = "piut";
824
+ var LEGACY_SERVER_KEY = "piut-context";
820
825
  function readConfig(filePath) {
821
826
  let raw;
822
827
  try {
@@ -864,12 +869,13 @@ function isPiutConfigured(filePath, configKey) {
864
869
  const config = readConfig(filePath);
865
870
  if (!config) return false;
866
871
  const servers = resolveKeyPath(config, configKey);
867
- return !!servers?.["piut-context"];
872
+ return !!(servers?.[SERVER_KEY] || servers?.[LEGACY_SERVER_KEY]);
868
873
  }
869
874
  function mergeConfig(filePath, configKey, serverConfig) {
870
875
  const existing = readConfig(filePath) || {};
871
876
  const servers = resolveKeyPath(existing, configKey) || {};
872
- servers["piut-context"] = serverConfig;
877
+ delete servers[LEGACY_SERVER_KEY];
878
+ servers[SERVER_KEY] = serverConfig;
873
879
  setAtKeyPath(existing, configKey, servers);
874
880
  writeConfig(filePath, existing);
875
881
  }
@@ -877,7 +883,7 @@ function getPiutConfig(filePath, configKey) {
877
883
  const config = readConfig(filePath);
878
884
  if (!config) return null;
879
885
  const servers = resolveKeyPath(config, configKey);
880
- const piut = servers?.["piut-context"];
886
+ const piut = servers?.[SERVER_KEY] || servers?.[LEGACY_SERVER_KEY];
881
887
  return piut ?? null;
882
888
  }
883
889
  function extractKeyFromConfig(piutConfig) {
@@ -929,8 +935,11 @@ function removeFromConfig(filePath, configKey) {
929
935
  const config = readConfig(filePath);
930
936
  if (!config) return false;
931
937
  const servers = resolveKeyPath(config, configKey);
932
- if (!servers?.["piut-context"]) return false;
933
- delete servers["piut-context"];
938
+ const hasNew = !!servers?.[SERVER_KEY];
939
+ const hasLegacy = !!servers?.[LEGACY_SERVER_KEY];
940
+ if (!hasNew && !hasLegacy) return false;
941
+ delete servers[SERVER_KEY];
942
+ delete servers[LEGACY_SERVER_KEY];
934
943
  if (Object.keys(servers).length === 0 && !configKey.includes(".")) {
935
944
  delete config[configKey];
936
945
  }
@@ -942,7 +951,7 @@ function removeFromConfig(filePath, configKey) {
942
951
  init_esm_shims();
943
952
  import fs2 from "fs";
944
953
  import path5 from "path";
945
- var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
954
+ var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut)
946
955
 
947
956
  This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
948
957
  p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
@@ -962,7 +971,7 @@ p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use
962
971
  4. Never read .piut/config.json directly \u2014 always use the MCP tools
963
972
 
964
973
  Skill reference: https://raw.githubusercontent.com/M-Flat-Inc/piut/main/skill.md`;
965
- var PROJECT_SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
974
+ var PROJECT_SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut)
966
975
 
967
976
  This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
968
977
  p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
@@ -1058,6 +1067,16 @@ function writePiutConfig(projectPath, config) {
1058
1067
  "utf-8"
1059
1068
  );
1060
1069
  }
1070
+ function readPiutConfig(projectPath) {
1071
+ try {
1072
+ const raw = fs3.readFileSync(path6.join(piutDir(projectPath), CONFIG_FILE), "utf-8");
1073
+ const parsed = JSON.parse(raw);
1074
+ if (parsed.slug && parsed.apiKey && parsed.serverUrl) return parsed;
1075
+ return null;
1076
+ } catch {
1077
+ return null;
1078
+ }
1079
+ }
1061
1080
  async function writePiutSkill(projectPath, slug, apiKey) {
1062
1081
  const dir = piutDir(projectPath);
1063
1082
  fs3.mkdirSync(dir, { recursive: true });
@@ -1101,6 +1120,42 @@ function hasPiutDir(projectPath) {
1101
1120
  return fs3.existsSync(path6.join(piutDir(projectPath), CONFIG_FILE));
1102
1121
  }
1103
1122
 
1123
+ // src/lib/global-install.ts
1124
+ init_esm_shims();
1125
+ import { execSync } from "child_process";
1126
+ import fs5 from "fs";
1127
+ import path8 from "path";
1128
+ import os5 from "os";
1129
+
1130
+ // src/lib/store.ts
1131
+ init_esm_shims();
1132
+ import fs4 from "fs";
1133
+ import path7 from "path";
1134
+ import os4 from "os";
1135
+ var CONFIG_DIR = path7.join(os4.homedir(), ".piut");
1136
+ var CONFIG_FILE2 = path7.join(CONFIG_DIR, "config.json");
1137
+ function readStore() {
1138
+ try {
1139
+ const raw = fs4.readFileSync(CONFIG_FILE2, "utf-8");
1140
+ return JSON.parse(raw);
1141
+ } catch {
1142
+ return {};
1143
+ }
1144
+ }
1145
+ function updateStore(updates) {
1146
+ const config = readStore();
1147
+ const updated = { ...config, ...updates };
1148
+ fs4.mkdirSync(CONFIG_DIR, { recursive: true });
1149
+ fs4.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
1150
+ return updated;
1151
+ }
1152
+ function clearStore() {
1153
+ try {
1154
+ fs4.unlinkSync(CONFIG_FILE2);
1155
+ } catch {
1156
+ }
1157
+ }
1158
+
1104
1159
  // src/lib/ui.ts
1105
1160
  init_esm_shims();
1106
1161
  import chalk from "chalk";
@@ -1187,6 +1242,65 @@ var Spinner = class {
1187
1242
  }
1188
1243
  };
1189
1244
 
1245
+ // src/lib/global-install.ts
1246
+ function isPiutInPath() {
1247
+ try {
1248
+ execSync(process.platform === "win32" ? "where piut" : "which piut", { stdio: "pipe" });
1249
+ return true;
1250
+ } catch {
1251
+ return false;
1252
+ }
1253
+ }
1254
+ function getShellProfile() {
1255
+ const shell = process.env.SHELL || "";
1256
+ const home2 = os5.homedir();
1257
+ if (shell.includes("zsh")) {
1258
+ return path8.join(home2, ".zshrc");
1259
+ }
1260
+ if (shell.includes("bash")) {
1261
+ const profile = path8.join(home2, ".bash_profile");
1262
+ if (process.platform === "darwin" && fs5.existsSync(profile)) {
1263
+ return profile;
1264
+ }
1265
+ return path8.join(home2, ".bashrc");
1266
+ }
1267
+ return null;
1268
+ }
1269
+ function addShellAlias(profilePath) {
1270
+ try {
1271
+ const content = fs5.existsSync(profilePath) ? fs5.readFileSync(profilePath, "utf-8") : "";
1272
+ if (content.includes("alias piut=") || content.includes("piut()")) {
1273
+ return true;
1274
+ }
1275
+ const alias = '\n# p\u0131ut CLI shortcut\nalias piut="npx @piut/cli"\n';
1276
+ fs5.appendFileSync(profilePath, alias);
1277
+ return true;
1278
+ } catch {
1279
+ return false;
1280
+ }
1281
+ }
1282
+ async function offerGlobalInstall() {
1283
+ if (isPiutInPath()) return;
1284
+ const store = readStore();
1285
+ if (store.globalInstallOffered) return;
1286
+ updateStore({ globalInstallOffered: true });
1287
+ try {
1288
+ execSync("npm install -g @piut/cli", { stdio: "pipe", timeout: 3e4 });
1289
+ if (isPiutInPath()) {
1290
+ console.log(dim(" Installed `piut` command for quick access"));
1291
+ return;
1292
+ }
1293
+ } catch {
1294
+ }
1295
+ const profile = getShellProfile();
1296
+ if (profile) {
1297
+ if (addShellAlias(profile)) {
1298
+ console.log(dim(` Added \`piut\` shortcut to ${path8.basename(profile)}`));
1299
+ return;
1300
+ }
1301
+ }
1302
+ }
1303
+
1190
1304
  // src/types.ts
1191
1305
  init_esm_shims();
1192
1306
  var CliError = class extends Error {
@@ -1242,8 +1356,8 @@ async function setupCommand(options) {
1242
1356
  if (tool.skillOnly) continue;
1243
1357
  const paths = resolveConfigPaths(tool);
1244
1358
  for (const { filePath, configKey } of paths) {
1245
- const exists = fs4.existsSync(filePath);
1246
- const parentExists = fs4.existsSync(path7.dirname(filePath));
1359
+ const exists = fs6.existsSync(filePath);
1360
+ const parentExists = fs6.existsSync(path9.dirname(filePath));
1247
1361
  if (exists || parentExists) {
1248
1362
  const configured2 = exists && !!configKey && isPiutConfigured(filePath, configKey);
1249
1363
  let staleKey = false;
@@ -1323,7 +1437,7 @@ async function setupCommand(options) {
1323
1437
  if (tool.id === "claude-code" && tool.quickCommand && isCommandAvailable("claude")) {
1324
1438
  let quickSuccess = false;
1325
1439
  try {
1326
- execSync(tool.quickCommand(slug, apiKey), { stdio: "pipe" });
1440
+ execSync2(tool.quickCommand(slug, apiKey), { stdio: "pipe" });
1327
1441
  const claudeJson = expandPath("~/.claude.json");
1328
1442
  const written = tool.configKey ? getPiutConfig(claudeJson, tool.configKey) : null;
1329
1443
  if (written) {
@@ -1333,7 +1447,7 @@ async function setupCommand(options) {
1333
1447
  continue;
1334
1448
  }
1335
1449
  try {
1336
- execSync(tool.quickCommand(slug, apiKey) + " --scope user", { stdio: "pipe" });
1450
+ execSync2(tool.quickCommand(slug, apiKey) + " --scope user", { stdio: "pipe" });
1337
1451
  const retryCheck = tool.configKey ? getPiutConfig(claudeJson, tool.configKey) : null;
1338
1452
  if (retryCheck) {
1339
1453
  quickSuccess = true;
@@ -1383,7 +1497,7 @@ async function setupCommand(options) {
1383
1497
  }
1384
1498
  if (configured.length > 0) {
1385
1499
  const cwd = process.cwd();
1386
- const isProject2 = fs4.existsSync(path7.join(cwd, ".git")) || fs4.existsSync(path7.join(cwd, "package.json"));
1500
+ const isProject2 = fs6.existsSync(path9.join(cwd, ".git")) || fs6.existsSync(path9.join(cwd, "package.json"));
1387
1501
  if (isProject2) {
1388
1502
  const { serverUrl } = validationResult;
1389
1503
  writePiutConfig(cwd, { slug, apiKey, serverUrl });
@@ -1425,6 +1539,7 @@ async function setupCommand(options) {
1425
1539
  if (skipped.length > 0) {
1426
1540
  console.log(dim(` Skipped: ${skipped.join(", ")}`));
1427
1541
  }
1542
+ await offerGlobalInstall();
1428
1543
  console.log();
1429
1544
  console.log(dim(" Restart your AI tools for changes to take effect."));
1430
1545
  console.log(dim(' Verify: ask any AI "What do you know about me from my context?"'));
@@ -1433,7 +1548,7 @@ async function setupCommand(options) {
1433
1548
  }
1434
1549
  function isCommandAvailable(cmd) {
1435
1550
  try {
1436
- execSync(process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`, { stdio: "pipe" });
1551
+ execSync2(process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`, { stdio: "pipe" });
1437
1552
  return true;
1438
1553
  } catch {
1439
1554
  return false;
@@ -1442,16 +1557,16 @@ function isCommandAvailable(cmd) {
1442
1557
 
1443
1558
  // src/commands/status.ts
1444
1559
  init_esm_shims();
1445
- import fs7 from "fs";
1446
- import os6 from "os";
1447
- import path10 from "path";
1560
+ import fs8 from "fs";
1561
+ import os7 from "os";
1562
+ import path11 from "path";
1448
1563
  import chalk3 from "chalk";
1449
1564
 
1450
1565
  // src/lib/brain-scanner.ts
1451
1566
  init_esm_shims();
1452
- import fs5 from "fs";
1453
- import path8 from "path";
1454
- import os4 from "os";
1567
+ import fs7 from "fs";
1568
+ import path10 from "path";
1569
+ import os6 from "os";
1455
1570
 
1456
1571
  // src/lib/file-types.ts
1457
1572
  init_esm_shims();
@@ -1470,7 +1585,7 @@ var AI_CONFIG_FILENAMES = /* @__PURE__ */ new Set([
1470
1585
  ]);
1471
1586
 
1472
1587
  // src/lib/brain-scanner.ts
1473
- var home = os4.homedir();
1588
+ var home = os6.homedir();
1474
1589
  var SKIP_DIRS = /* @__PURE__ */ new Set([
1475
1590
  "node_modules",
1476
1591
  ".git",
@@ -1534,22 +1649,22 @@ var INCLUDE_DOT_DIRS = /* @__PURE__ */ new Set([
1534
1649
  function getDefaultScanDirs() {
1535
1650
  const dirs = [];
1536
1651
  try {
1537
- const entries = fs5.readdirSync(home, { withFileTypes: true });
1652
+ const entries = fs7.readdirSync(home, { withFileTypes: true });
1538
1653
  for (const entry of entries) {
1539
1654
  if (!entry.isDirectory()) continue;
1540
1655
  if (entry.name.startsWith(".") && !INCLUDE_DOT_DIRS.has(entry.name)) continue;
1541
1656
  if (SKIP_HOME_DIRS.has(entry.name)) continue;
1542
- dirs.push(path8.join(home, entry.name));
1657
+ dirs.push(path10.join(home, entry.name));
1543
1658
  }
1544
1659
  } catch {
1545
1660
  }
1546
- const cloudStorage = path8.join(home, "Library", "CloudStorage");
1661
+ const cloudStorage = path10.join(home, "Library", "CloudStorage");
1547
1662
  try {
1548
- if (fs5.existsSync(cloudStorage) && fs5.statSync(cloudStorage).isDirectory()) {
1549
- const entries = fs5.readdirSync(cloudStorage, { withFileTypes: true });
1663
+ if (fs7.existsSync(cloudStorage) && fs7.statSync(cloudStorage).isDirectory()) {
1664
+ const entries = fs7.readdirSync(cloudStorage, { withFileTypes: true });
1550
1665
  for (const entry of entries) {
1551
1666
  if (!entry.isDirectory()) continue;
1552
- const fullPath = path8.join(cloudStorage, entry.name);
1667
+ const fullPath = path10.join(cloudStorage, entry.name);
1553
1668
  if (!dirs.includes(fullPath)) {
1554
1669
  dirs.push(fullPath);
1555
1670
  }
@@ -1561,22 +1676,22 @@ function getDefaultScanDirs() {
1561
1676
  return dirs;
1562
1677
  }
1563
1678
  function isProject(dirPath) {
1564
- return fs5.existsSync(path8.join(dirPath, ".git")) || fs5.existsSync(path8.join(dirPath, "package.json")) || fs5.existsSync(path8.join(dirPath, "Cargo.toml")) || fs5.existsSync(path8.join(dirPath, "pyproject.toml")) || fs5.existsSync(path8.join(dirPath, "go.mod"));
1679
+ return fs7.existsSync(path10.join(dirPath, ".git")) || fs7.existsSync(path10.join(dirPath, "package.json")) || fs7.existsSync(path10.join(dirPath, "Cargo.toml")) || fs7.existsSync(path10.join(dirPath, "pyproject.toml")) || fs7.existsSync(path10.join(dirPath, "go.mod"));
1565
1680
  }
1566
1681
  function buildProjectInfo(projectPath) {
1567
- const hasPkgJson = fs5.existsSync(path8.join(projectPath, "package.json"));
1682
+ const hasPkgJson = fs7.existsSync(path10.join(projectPath, "package.json"));
1568
1683
  let description = "";
1569
1684
  if (hasPkgJson) {
1570
1685
  try {
1571
- const pkg = JSON.parse(fs5.readFileSync(path8.join(projectPath, "package.json"), "utf-8"));
1686
+ const pkg = JSON.parse(fs7.readFileSync(path10.join(projectPath, "package.json"), "utf-8"));
1572
1687
  description = pkg.description || "";
1573
1688
  } catch {
1574
1689
  }
1575
1690
  }
1576
- const readmePath = path8.join(projectPath, "README.md");
1577
- if (!description && fs5.existsSync(readmePath)) {
1691
+ const readmePath = path10.join(projectPath, "README.md");
1692
+ if (!description && fs7.existsSync(readmePath)) {
1578
1693
  try {
1579
- const content = fs5.readFileSync(readmePath, "utf-8");
1694
+ const content = fs7.readFileSync(readmePath, "utf-8");
1580
1695
  const lines = content.split("\n");
1581
1696
  let foundHeading = false;
1582
1697
  for (const line of lines) {
@@ -1593,15 +1708,15 @@ function buildProjectInfo(projectPath) {
1593
1708
  }
1594
1709
  }
1595
1710
  return {
1596
- name: path8.basename(projectPath),
1711
+ name: path10.basename(projectPath),
1597
1712
  path: projectPath,
1598
1713
  description,
1599
- hasClaudeMd: fs5.existsSync(path8.join(projectPath, "CLAUDE.md")) || fs5.existsSync(path8.join(projectPath, ".claude", "rules")),
1600
- hasCursorRules: fs5.existsSync(path8.join(projectPath, ".cursorrules")) || fs5.existsSync(path8.join(projectPath, ".cursor", "rules")),
1601
- hasWindsurfRules: fs5.existsSync(path8.join(projectPath, ".windsurfrules")) || fs5.existsSync(path8.join(projectPath, ".windsurf", "rules")),
1602
- hasCopilotInstructions: fs5.existsSync(path8.join(projectPath, ".github", "copilot-instructions.md")) || fs5.existsSync(path8.join(projectPath, ".github", "instructions")),
1603
- hasConventionsMd: fs5.existsSync(path8.join(projectPath, "CONVENTIONS.md")) || fs5.existsSync(path8.join(projectPath, ".amazonq", "rules")),
1604
- hasZedRules: fs5.existsSync(path8.join(projectPath, ".rules"))
1714
+ hasClaudeMd: fs7.existsSync(path10.join(projectPath, "CLAUDE.md")) || fs7.existsSync(path10.join(projectPath, ".claude", "rules")),
1715
+ hasCursorRules: fs7.existsSync(path10.join(projectPath, ".cursorrules")) || fs7.existsSync(path10.join(projectPath, ".cursor", "rules")),
1716
+ hasWindsurfRules: fs7.existsSync(path10.join(projectPath, ".windsurfrules")) || fs7.existsSync(path10.join(projectPath, ".windsurf", "rules")),
1717
+ hasCopilotInstructions: fs7.existsSync(path10.join(projectPath, ".github", "copilot-instructions.md")) || fs7.existsSync(path10.join(projectPath, ".github", "instructions")),
1718
+ hasConventionsMd: fs7.existsSync(path10.join(projectPath, "CONVENTIONS.md")) || fs7.existsSync(path10.join(projectPath, ".amazonq", "rules")),
1719
+ hasZedRules: fs7.existsSync(path10.join(projectPath, ".rules"))
1605
1720
  };
1606
1721
  }
1607
1722
  var MAX_PROJECT_DEPTH = 4;
@@ -1611,11 +1726,11 @@ function detectProjects(scanDirs, onProgress) {
1611
1726
  function walk(dir, depth) {
1612
1727
  if (depth > MAX_PROJECT_DEPTH) return;
1613
1728
  try {
1614
- const items = fs5.readdirSync(dir, { withFileTypes: true });
1729
+ const items = fs7.readdirSync(dir, { withFileTypes: true });
1615
1730
  for (const item of items) {
1616
1731
  if (!item.isDirectory()) continue;
1617
1732
  if (shouldSkipDir(item.name)) continue;
1618
- const fullPath = path8.join(dir, item.name);
1733
+ const fullPath = path10.join(dir, item.name);
1619
1734
  if (seen.has(fullPath)) continue;
1620
1735
  seen.add(fullPath);
1621
1736
  if (isProject(fullPath)) {
@@ -1639,20 +1754,20 @@ var MAX_BRAIN_INPUT_BYTES = 1e6;
1639
1754
  function collectGlobalConfigFiles(onProgress) {
1640
1755
  const configs = [];
1641
1756
  const globalPaths = [
1642
- path8.join(home, ".claude", "MEMORY.md"),
1643
- path8.join(home, ".claude", "CLAUDE.md"),
1644
- path8.join(home, ".openclaw", "workspace", "SOUL.md"),
1645
- path8.join(home, ".openclaw", "workspace", "MEMORY.md"),
1646
- path8.join(home, ".gemini", "MEMORY.md"),
1647
- path8.join(home, ".paperclip", "IDENTITY.md")
1757
+ path10.join(home, ".claude", "MEMORY.md"),
1758
+ path10.join(home, ".claude", "CLAUDE.md"),
1759
+ path10.join(home, ".openclaw", "workspace", "SOUL.md"),
1760
+ path10.join(home, ".openclaw", "workspace", "MEMORY.md"),
1761
+ path10.join(home, ".gemini", "MEMORY.md"),
1762
+ path10.join(home, ".paperclip", "IDENTITY.md")
1648
1763
  ];
1649
1764
  for (const gp of globalPaths) {
1650
1765
  try {
1651
- const stat = fs5.statSync(gp);
1766
+ const stat = fs7.statSync(gp);
1652
1767
  if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
1653
- const content = fs5.readFileSync(gp, "utf-8");
1768
+ const content = fs7.readFileSync(gp, "utf-8");
1654
1769
  if (content.trim()) {
1655
- const name = path8.relative(home, gp);
1770
+ const name = path10.relative(home, gp);
1656
1771
  configs.push({ name, content });
1657
1772
  onProgress?.({ phase: "configs", message: name });
1658
1773
  }
@@ -1665,11 +1780,11 @@ function collectProjectConfigFiles(projects, onProgress) {
1665
1780
  const configs = [];
1666
1781
  for (const project of projects) {
1667
1782
  for (const fileName of AI_CONFIG_FILENAMES) {
1668
- const filePath = path8.join(project.path, fileName);
1783
+ const filePath = path10.join(project.path, fileName);
1669
1784
  try {
1670
- const stat = fs5.statSync(filePath);
1785
+ const stat = fs7.statSync(filePath);
1671
1786
  if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
1672
- const content = fs5.readFileSync(filePath, "utf-8");
1787
+ const content = fs7.readFileSync(filePath, "utf-8");
1673
1788
  if (content.trim()) {
1674
1789
  const name = `${project.name}/${fileName}`;
1675
1790
  configs.push({ name, content });
@@ -1678,11 +1793,11 @@ function collectProjectConfigFiles(projects, onProgress) {
1678
1793
  } catch {
1679
1794
  }
1680
1795
  }
1681
- const pkgPath = path8.join(project.path, "package.json");
1796
+ const pkgPath = path10.join(project.path, "package.json");
1682
1797
  try {
1683
- const stat = fs5.statSync(pkgPath);
1798
+ const stat = fs7.statSync(pkgPath);
1684
1799
  if (stat.isFile() && stat.size <= MAX_CONFIG_SIZE) {
1685
- const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
1800
+ const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
1686
1801
  const summary = JSON.stringify({ name: pkg.name, description: pkg.description }, null, 2);
1687
1802
  configs.push({ name: `${project.name}/package.json`, content: summary });
1688
1803
  onProgress?.({ phase: "configs", message: `${project.name}/package.json` });
@@ -1702,35 +1817,6 @@ function formatSize(bytes) {
1702
1817
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1703
1818
  }
1704
1819
 
1705
- // src/lib/store.ts
1706
- init_esm_shims();
1707
- import fs6 from "fs";
1708
- import path9 from "path";
1709
- import os5 from "os";
1710
- var CONFIG_DIR = path9.join(os5.homedir(), ".piut");
1711
- var CONFIG_FILE2 = path9.join(CONFIG_DIR, "config.json");
1712
- function readStore() {
1713
- try {
1714
- const raw = fs6.readFileSync(CONFIG_FILE2, "utf-8");
1715
- return JSON.parse(raw);
1716
- } catch {
1717
- return {};
1718
- }
1719
- }
1720
- function updateStore(updates) {
1721
- const config = readStore();
1722
- const updated = { ...config, ...updates };
1723
- fs6.mkdirSync(CONFIG_DIR, { recursive: true });
1724
- fs6.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
1725
- return updated;
1726
- }
1727
- function clearStore() {
1728
- try {
1729
- fs6.unlinkSync(CONFIG_FILE2);
1730
- } catch {
1731
- }
1732
- }
1733
-
1734
1820
  // src/commands/status.ts
1735
1821
  var API_BASE3 = process.env.PIUT_API_BASE || "https://piut.com";
1736
1822
  var PIUT_FILES = [
@@ -1743,7 +1829,7 @@ var PIUT_FILES = [
1743
1829
  ];
1744
1830
  function hasPiutReference(filePath) {
1745
1831
  try {
1746
- const content = fs7.readFileSync(filePath, "utf-8");
1832
+ const content = fs8.readFileSync(filePath, "utf-8");
1747
1833
  return content.includes("p\u0131ut Context") || content.includes("piut Context");
1748
1834
  } catch {
1749
1835
  return false;
@@ -1795,7 +1881,7 @@ async function statusCommand(options = {}) {
1795
1881
  await verifyStatus();
1796
1882
  return;
1797
1883
  }
1798
- const thisHostname = os6.hostname();
1884
+ const thisHostname = os7.hostname();
1799
1885
  const thisMachineId = getMachineId2();
1800
1886
  console.log(` AI tools on this machine ${dim(`(${thisHostname})`)}:`);
1801
1887
  console.log();
@@ -1803,7 +1889,7 @@ async function statusCommand(options = {}) {
1803
1889
  for (const tool of TOOLS) {
1804
1890
  const paths = resolveConfigPaths(tool);
1805
1891
  for (const { filePath, configKey } of paths) {
1806
- if (!fs7.existsSync(filePath)) continue;
1892
+ if (!fs8.existsSync(filePath)) continue;
1807
1893
  foundAny = true;
1808
1894
  const configured = isPiutConfigured(filePath, configKey);
1809
1895
  if (configured) {
@@ -1826,8 +1912,8 @@ async function statusCommand(options = {}) {
1826
1912
  for (const project of projects) {
1827
1913
  const connectedFiles = [];
1828
1914
  for (const file of PIUT_FILES) {
1829
- const absPath = path10.join(project.path, file);
1830
- if (fs7.existsSync(absPath) && hasPiutReference(absPath)) {
1915
+ const absPath = path11.join(project.path, file);
1916
+ if (fs8.existsSync(absPath) && hasPiutReference(absPath)) {
1831
1917
  connectedFiles.push(file);
1832
1918
  }
1833
1919
  }
@@ -1900,7 +1986,7 @@ async function verifyStatus() {
1900
1986
  for (const tool of TOOLS) {
1901
1987
  const paths = resolveConfigPaths(tool);
1902
1988
  for (const { filePath, configKey } of paths) {
1903
- if (!fs7.existsSync(filePath)) continue;
1989
+ if (!fs8.existsSync(filePath)) continue;
1904
1990
  const piutConfig = getPiutConfig(filePath, configKey);
1905
1991
  if (!piutConfig) {
1906
1992
  toolLine(tool.name, dim("installed, not connected"), "\u25CB");
@@ -1944,7 +2030,7 @@ async function verifyStatus() {
1944
2030
 
1945
2031
  // src/commands/remove.ts
1946
2032
  init_esm_shims();
1947
- import fs8 from "fs";
2033
+ import fs9 from "fs";
1948
2034
  import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
1949
2035
  async function removeCommand() {
1950
2036
  banner();
@@ -1953,7 +2039,7 @@ async function removeCommand() {
1953
2039
  if (!tool.configKey) continue;
1954
2040
  const paths = resolveConfigPaths(tool);
1955
2041
  for (const { filePath, configKey } of paths) {
1956
- if (fs8.existsSync(filePath) && isPiutConfigured(filePath, configKey)) {
2042
+ if (fs9.existsSync(filePath) && isPiutConfigured(filePath, configKey)) {
1957
2043
  configured.push({ tool, configPath: filePath, resolvedConfigKey: configKey });
1958
2044
  break;
1959
2045
  }
@@ -2007,13 +2093,16 @@ async function removeCommand() {
2007
2093
  init_esm_shims();
2008
2094
  import { confirm as confirm3 } from "@inquirer/prompts";
2009
2095
  import chalk5 from "chalk";
2010
- import os7 from "os";
2096
+ import os8 from "os";
2011
2097
 
2012
2098
  // src/lib/auth.ts
2013
2099
  init_esm_shims();
2014
2100
  import { select, input, password as password2 } from "@inquirer/prompts";
2015
2101
  import { exec } from "child_process";
2102
+ import http from "http";
2103
+ import crypto3 from "crypto";
2016
2104
  import chalk4 from "chalk";
2105
+ var API_BASE4 = process.env.PIUT_API_BASE || "https://piut.com";
2017
2106
  function openBrowser(url) {
2018
2107
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2019
2108
  exec(`${cmd} "${url}"`);
@@ -2052,12 +2141,101 @@ async function pasteKeyFlow() {
2052
2141
  return { apiKey, validation };
2053
2142
  }
2054
2143
  async function browserFlow() {
2055
- const url = "https://piut.com/dashboard/keys";
2056
- console.log(dim(` Opening ${brand(url)}...`));
2057
- openBrowser(url);
2058
- console.log(dim(" Copy your API key from the dashboard, then paste it here."));
2059
- console.log();
2060
- return pasteKeyFlow();
2144
+ const state = crypto3.randomBytes(16).toString("hex");
2145
+ return new Promise((resolve, reject) => {
2146
+ let settled = false;
2147
+ const server = http.createServer((req, res) => {
2148
+ const url = new URL(req.url, `http://localhost`);
2149
+ if (url.pathname !== "/callback") {
2150
+ res.writeHead(404);
2151
+ res.end();
2152
+ return;
2153
+ }
2154
+ const key = url.searchParams.get("key");
2155
+ const returnedState = url.searchParams.get("state");
2156
+ if (returnedState !== state) {
2157
+ res.writeHead(400, { "Content-Type": "text/html" });
2158
+ res.end(errorPage("State mismatch. Please try again from the CLI."));
2159
+ cleanup();
2160
+ if (!settled) {
2161
+ settled = true;
2162
+ reject(new CliError("Browser auth state mismatch"));
2163
+ }
2164
+ return;
2165
+ }
2166
+ if (!key || !key.startsWith("pb_")) {
2167
+ res.writeHead(400, { "Content-Type": "text/html" });
2168
+ res.end(errorPage("No valid API key received."));
2169
+ cleanup();
2170
+ if (!settled) {
2171
+ settled = true;
2172
+ reject(new CliError("No API key received from browser"));
2173
+ }
2174
+ return;
2175
+ }
2176
+ res.writeHead(200, { "Content-Type": "text/html" });
2177
+ res.end(successPage());
2178
+ cleanup();
2179
+ validateKey(key).then((validation) => {
2180
+ if (!settled) {
2181
+ settled = true;
2182
+ resolve({ apiKey: key, validation });
2183
+ }
2184
+ }).catch((err) => {
2185
+ if (!settled) {
2186
+ settled = true;
2187
+ reject(err);
2188
+ }
2189
+ });
2190
+ });
2191
+ const timer = setTimeout(() => {
2192
+ cleanup();
2193
+ if (!settled) {
2194
+ settled = true;
2195
+ reject(new CliError("Browser login timed out after 2 minutes. Please try again."));
2196
+ }
2197
+ }, 12e4);
2198
+ function cleanup() {
2199
+ clearTimeout(timer);
2200
+ server.close();
2201
+ }
2202
+ server.listen(0, "127.0.0.1", () => {
2203
+ const { port } = server.address();
2204
+ const authUrl = `${API_BASE4}/cli/auth?port=${port}&state=${state}`;
2205
+ console.log(dim(` Opening ${brand("piut.com")} in your browser...`));
2206
+ openBrowser(authUrl);
2207
+ console.log(dim(" Waiting for browser authorization..."));
2208
+ });
2209
+ server.on("error", (err) => {
2210
+ cleanup();
2211
+ if (!settled) {
2212
+ settled = true;
2213
+ reject(new CliError(`Failed to start callback server: ${err.message}`));
2214
+ }
2215
+ });
2216
+ });
2217
+ }
2218
+ function successPage() {
2219
+ return `<!DOCTYPE html>
2220
+ <html><head><title>p\u0131ut CLI</title></head>
2221
+ <body style="background:#0a0a0a;color:white;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
2222
+ <div style="text-align:center">
2223
+ <div style="font-size:48px;color:#4ade80;margin-bottom:16px">&#10003;</div>
2224
+ <h2 style="margin:0 0 8px">CLI Authorized</h2>
2225
+ <p style="color:#a3a3a3;margin:0">You can close this tab and return to your terminal.</p>
2226
+ </div>
2227
+ </body></html>`;
2228
+ }
2229
+ function errorPage(message) {
2230
+ return `<!DOCTYPE html>
2231
+ <html><head><title>p\u0131ut CLI</title></head>
2232
+ <body style="background:#0a0a0a;color:white;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
2233
+ <div style="text-align:center">
2234
+ <div style="font-size:48px;color:#f87171;margin-bottom:16px">&#10007;</div>
2235
+ <h2 style="margin:0 0 8px">Authorization Failed</h2>
2236
+ <p style="color:#a3a3a3;margin:0">${message}</p>
2237
+ </div>
2238
+ </body></html>`;
2061
2239
  }
2062
2240
  async function promptLogin() {
2063
2241
  const method = await select({
@@ -2071,7 +2249,7 @@ async function promptLogin() {
2071
2249
  {
2072
2250
  name: "Log in with browser",
2073
2251
  value: "browser",
2074
- description: "Open piut.com to get your API key"
2252
+ description: "Sign in via piut.com (opens browser)"
2075
2253
  },
2076
2254
  {
2077
2255
  name: "Paste API key",
@@ -2119,10 +2297,95 @@ async function resolveApiKeyWithResult(keyOption) {
2119
2297
  }
2120
2298
  }
2121
2299
 
2300
+ // src/lib/sync.ts
2301
+ init_esm_shims();
2302
+ import fs10 from "fs";
2303
+ import path12 from "path";
2304
+ function syncStaleConfigs(slug, apiKey, serverUrl) {
2305
+ const updated = [];
2306
+ for (const tool of TOOLS) {
2307
+ if (tool.skillOnly || !tool.generateConfig || !tool.configKey) continue;
2308
+ const paths = resolveConfigPaths(tool);
2309
+ for (const { filePath, configKey } of paths) {
2310
+ if (!fs10.existsSync(filePath)) continue;
2311
+ const piutConfig = getPiutConfig(filePath, configKey);
2312
+ if (!piutConfig) continue;
2313
+ const existingKey = extractKeyFromConfig(piutConfig);
2314
+ const existingSlug = extractSlugFromConfig(piutConfig);
2315
+ const keyStale = !!existingKey && existingKey !== apiKey;
2316
+ const slugStale = !!existingSlug && existingSlug !== slug;
2317
+ if (keyStale || slugStale) {
2318
+ const newConfig = tool.generateConfig(slug, apiKey);
2319
+ mergeConfig(filePath, configKey, newConfig);
2320
+ updated.push(tool.name);
2321
+ }
2322
+ break;
2323
+ }
2324
+ }
2325
+ const cwd = process.cwd();
2326
+ const existing = readPiutConfig(cwd);
2327
+ if (existing && (existing.apiKey !== apiKey || existing.slug !== slug)) {
2328
+ writePiutConfig(cwd, { slug, apiKey, serverUrl });
2329
+ updated.push(".piut/config.json");
2330
+ }
2331
+ return updated;
2332
+ }
2333
+ function getConfiguredToolNames() {
2334
+ const names = [];
2335
+ for (const tool of TOOLS) {
2336
+ if (tool.skillOnly || !tool.configKey) continue;
2337
+ const paths = resolveConfigPaths(tool);
2338
+ for (const { filePath, configKey } of paths) {
2339
+ if (!fs10.existsSync(filePath)) continue;
2340
+ if (getPiutConfig(filePath, configKey)) {
2341
+ names.push(tool.name);
2342
+ break;
2343
+ }
2344
+ }
2345
+ }
2346
+ return names;
2347
+ }
2348
+ async function cycleConfigEntry(filePath, configKey, freshConfig) {
2349
+ removeFromConfig(filePath, configKey);
2350
+ await new Promise((resolve) => setTimeout(resolve, 500));
2351
+ mergeConfig(filePath, configKey, freshConfig);
2352
+ }
2353
+ async function cycleMcpConfigs(slug, apiKey) {
2354
+ for (const tool of TOOLS) {
2355
+ if (tool.skillOnly || !tool.generateConfig || !tool.configKey) continue;
2356
+ const paths = resolveConfigPaths(tool);
2357
+ for (const { filePath, configKey } of paths) {
2358
+ if (!fs10.existsSync(filePath)) continue;
2359
+ if (!getPiutConfig(filePath, configKey)) continue;
2360
+ await cycleConfigEntry(filePath, configKey, tool.generateConfig(slug, apiKey));
2361
+ break;
2362
+ }
2363
+ }
2364
+ }
2365
+ async function cycleProjectConfigs(slug, apiKey, serverUrl) {
2366
+ const projects = scanForProjects();
2367
+ const refreshed = [];
2368
+ const vscodeTool = TOOLS.find((t) => t.id === "vscode");
2369
+ for (const project of projects) {
2370
+ if (!hasPiutDir(project.path)) continue;
2371
+ const projectName = path12.basename(project.path);
2372
+ writePiutConfig(project.path, { slug, apiKey, serverUrl });
2373
+ await writePiutSkill(project.path, slug, apiKey);
2374
+ if (vscodeTool?.generateConfig && vscodeTool.configKey) {
2375
+ const vscodeMcpPath = path12.join(project.path, ".vscode", "mcp.json");
2376
+ if (fs10.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
2377
+ await cycleConfigEntry(vscodeMcpPath, "servers", vscodeTool.generateConfig(slug, apiKey));
2378
+ }
2379
+ }
2380
+ refreshed.push(projectName);
2381
+ }
2382
+ return refreshed;
2383
+ }
2384
+
2122
2385
  // src/commands/build.ts
2123
2386
  async function buildCommand(options) {
2124
2387
  banner();
2125
- const { apiKey, serverUrl } = await resolveApiKeyWithResult(options.key);
2388
+ const { apiKey, serverUrl, slug } = await resolveApiKeyWithResult(options.key);
2126
2389
  console.log(dim(" \u2501\u2501\u2501 BUILD YOUR BRAIN \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
2127
2390
  console.log();
2128
2391
  console.log(dim(" Scanning for AI config files..."));
@@ -2184,7 +2447,7 @@ async function buildCommand(options) {
2184
2447
  return;
2185
2448
  }
2186
2449
  }
2187
- const home2 = os7.homedir();
2450
+ const home2 = os8.homedir();
2188
2451
  const brainInput = {
2189
2452
  summary: {
2190
2453
  projects: projects.map((p) => ({
@@ -2195,9 +2458,9 @@ async function buildCommand(options) {
2195
2458
  configFiles: allConfigs
2196
2459
  }
2197
2460
  };
2198
- await streamBuild(apiKey, serverUrl, brainInput, options);
2461
+ await streamBuild(apiKey, serverUrl, slug, brainInput, options);
2199
2462
  }
2200
- async function streamBuild(apiKey, serverUrl, brainInput, options) {
2463
+ async function streamBuild(apiKey, serverUrl, slug, brainInput, options) {
2201
2464
  const spinner = new Spinner();
2202
2465
  spinner.start("Generating brain...");
2203
2466
  try {
@@ -2276,6 +2539,7 @@ async function streamBuild(apiKey, serverUrl, brainInput, options) {
2276
2539
  console.log(` ${brand(serverUrl)}`);
2277
2540
  console.log(dim(" (accessible only with secure authentication)"));
2278
2541
  console.log();
2542
+ await cycleMcpConfigs(slug, apiKey);
2279
2543
  } catch (err) {
2280
2544
  console.log();
2281
2545
  const msg = err.message;
@@ -2322,6 +2586,7 @@ async function deployCommand(options) {
2322
2586
  console.log(` ${brand(serverUrl)}`);
2323
2587
  console.log(dim(" (securely accessible only with authentication)"));
2324
2588
  console.log();
2589
+ await cycleMcpConfigs(slug, apiKey);
2325
2590
  console.log(dim(" Next: run ") + brand("piut connect") + dim(" to add brain references to your projects."));
2326
2591
  console.log();
2327
2592
  } catch (err) {
@@ -2342,33 +2607,33 @@ async function deployCommand(options) {
2342
2607
 
2343
2608
  // src/commands/connect.ts
2344
2609
  init_esm_shims();
2345
- import fs9 from "fs";
2346
- import path11 from "path";
2610
+ import fs11 from "fs";
2611
+ import path13 from "path";
2347
2612
  import { checkbox as checkbox3 } from "@inquirer/prompts";
2348
2613
  var RULE_FILES = [
2349
2614
  {
2350
2615
  tool: "Claude Code",
2351
2616
  filePath: "CLAUDE.md",
2352
2617
  strategy: "append",
2353
- detect: (p) => p.hasClaudeMd || fs9.existsSync(path11.join(p.path, ".claude"))
2618
+ detect: (p) => p.hasClaudeMd || fs11.existsSync(path13.join(p.path, ".claude"))
2354
2619
  },
2355
2620
  {
2356
2621
  tool: "Cursor",
2357
2622
  filePath: ".cursor/rules/piut.mdc",
2358
2623
  strategy: "create",
2359
- detect: (p) => p.hasCursorRules || fs9.existsSync(path11.join(p.path, ".cursor"))
2624
+ detect: (p) => p.hasCursorRules || fs11.existsSync(path13.join(p.path, ".cursor"))
2360
2625
  },
2361
2626
  {
2362
2627
  tool: "Windsurf",
2363
2628
  filePath: ".windsurf/rules/piut.md",
2364
2629
  strategy: "create",
2365
- detect: (p) => p.hasWindsurfRules || fs9.existsSync(path11.join(p.path, ".windsurf"))
2630
+ detect: (p) => p.hasWindsurfRules || fs11.existsSync(path13.join(p.path, ".windsurf"))
2366
2631
  },
2367
2632
  {
2368
- tool: "GitHub Copilot",
2633
+ tool: "VS Code",
2369
2634
  filePath: ".github/copilot-instructions.md",
2370
2635
  strategy: "append",
2371
- detect: (p) => p.hasCopilotInstructions || fs9.existsSync(path11.join(p.path, ".github"))
2636
+ detect: (p) => p.hasCopilotInstructions || fs11.existsSync(path13.join(p.path, ".github"))
2372
2637
  },
2373
2638
  {
2374
2639
  tool: "Amazon Q",
@@ -2380,22 +2645,22 @@ var RULE_FILES = [
2380
2645
  tool: "Zed",
2381
2646
  filePath: ".zed/rules.md",
2382
2647
  strategy: "create",
2383
- detect: (p) => p.hasZedRules || fs9.existsSync(path11.join(p.path, ".zed"))
2648
+ detect: (p) => p.hasZedRules || fs11.existsSync(path13.join(p.path, ".zed"))
2384
2649
  },
2385
2650
  {
2386
2651
  tool: "Gemini CLI",
2387
2652
  filePath: "GEMINI.md",
2388
2653
  strategy: "append",
2389
- detect: (p) => fs9.existsSync(path11.join(p.path, ".gemini"))
2654
+ detect: (p) => fs11.existsSync(path13.join(p.path, ".gemini"))
2390
2655
  },
2391
2656
  {
2392
2657
  tool: "Paperclip",
2393
2658
  filePath: "AGENTS.md",
2394
2659
  strategy: "append",
2395
- detect: (p) => fs9.existsSync(path11.join(p.path, ".paperclip"))
2660
+ detect: (p) => fs11.existsSync(path13.join(p.path, ".paperclip"))
2396
2661
  }
2397
2662
  ];
2398
- var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut-context)
2663
+ var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut)
2399
2664
 
2400
2665
  This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
2401
2666
  p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
@@ -2418,7 +2683,7 @@ Full skill reference: .piut/skill.md
2418
2683
  `;
2419
2684
  var APPEND_SECTION = `
2420
2685
 
2421
- ## p\u0131ut Context (MCP Server: piut-context)
2686
+ ## p\u0131ut Context (MCP Server: piut)
2422
2687
 
2423
2688
  p\u0131ut provides MCP tools for persistent personal context. Do NOT read local .piut/ files.
2424
2689
  Available tools: \`get_context\`, \`get_section\`, \`search_brain\`, \`append_brain\`, \`update_brain\`, \`prompt_brain\`
@@ -2427,7 +2692,7 @@ Full skill reference: .piut/skill.md
2427
2692
  `;
2428
2693
  function hasPiutReference2(filePath) {
2429
2694
  try {
2430
- const content = fs9.readFileSync(filePath, "utf-8");
2695
+ const content = fs11.readFileSync(filePath, "utf-8");
2431
2696
  return content.includes("p\u0131ut Context") || content.includes("piut Context");
2432
2697
  } catch {
2433
2698
  return false;
@@ -2467,20 +2732,20 @@ async function connectCommand(options) {
2467
2732
  for (const project of projects) {
2468
2733
  for (const rule of RULE_FILES) {
2469
2734
  if (!rule.detect(project)) continue;
2470
- const absPath = path11.join(project.path, rule.filePath);
2471
- if (fs9.existsSync(absPath) && hasPiutReference2(absPath)) continue;
2735
+ const absPath = path13.join(project.path, rule.filePath);
2736
+ if (fs11.existsSync(absPath) && hasPiutReference2(absPath)) continue;
2472
2737
  actions.push({
2473
2738
  project,
2474
2739
  tool: rule.tool,
2475
2740
  filePath: rule.filePath,
2476
2741
  absPath,
2477
- action: rule.strategy === "create" || !fs9.existsSync(absPath) ? "create" : "append"
2742
+ action: rule.strategy === "create" || !fs11.existsSync(absPath) ? "create" : "append"
2478
2743
  });
2479
2744
  }
2480
2745
  const hasAnyAction = actions.some((a) => a.project === project);
2481
2746
  if (!hasAnyAction) {
2482
- const claudeMdPath = path11.join(project.path, "CLAUDE.md");
2483
- if (!fs9.existsSync(claudeMdPath)) {
2747
+ const claudeMdPath = path13.join(project.path, "CLAUDE.md");
2748
+ if (!fs11.existsSync(claudeMdPath)) {
2484
2749
  actions.push({
2485
2750
  project,
2486
2751
  tool: "Claude Code",
@@ -2520,7 +2785,7 @@ async function connectCommand(options) {
2520
2785
  console.log();
2521
2786
  const projectChoices = [];
2522
2787
  for (const [projectPath, projectActions] of byProject) {
2523
- const projectName = path11.basename(projectPath);
2788
+ const projectName = path13.basename(projectPath);
2524
2789
  const desc = projectActions.map((a) => {
2525
2790
  const verb = a.action === "create" ? "will create" : "will append to";
2526
2791
  return `${verb} ${a.filePath}`;
@@ -2546,32 +2811,32 @@ async function connectCommand(options) {
2546
2811
  }
2547
2812
  console.log();
2548
2813
  let connected = 0;
2549
- const copilotTool = TOOLS.find((t) => t.id === "copilot");
2814
+ const vscodeTool = TOOLS.find((t) => t.id === "vscode");
2550
2815
  for (const projectPath of selectedPaths) {
2551
2816
  const projectActions = byProject.get(projectPath) || [];
2552
- const projectName = path11.basename(projectPath);
2817
+ const projectName = path13.basename(projectPath);
2553
2818
  writePiutConfig(projectPath, { slug, apiKey, serverUrl });
2554
2819
  await writePiutSkill(projectPath, slug, apiKey);
2555
2820
  ensureGitignored(projectPath);
2556
2821
  console.log(success(` \u2713 ${projectName}/.piut/`) + dim(" \u2014 credentials + skill"));
2557
- if (copilotTool) {
2558
- const hasCopilot = fs9.existsSync(path11.join(projectPath, ".github", "copilot-instructions.md")) || fs9.existsSync(path11.join(projectPath, ".github"));
2559
- if (hasCopilot) {
2560
- const vscodeMcpPath = path11.join(projectPath, ".vscode", "mcp.json");
2561
- const serverConfig = copilotTool.generateConfig(slug, apiKey);
2562
- mergeConfig(vscodeMcpPath, copilotTool.configKey, serverConfig);
2563
- console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014 Copilot MCP"));
2822
+ if (vscodeTool) {
2823
+ const hasVscode = fs11.existsSync(path13.join(projectPath, ".github", "copilot-instructions.md")) || fs11.existsSync(path13.join(projectPath, ".github"));
2824
+ if (hasVscode) {
2825
+ const vscodeMcpPath = path13.join(projectPath, ".vscode", "mcp.json");
2826
+ const serverConfig = vscodeTool.generateConfig(slug, apiKey);
2827
+ mergeConfig(vscodeMcpPath, vscodeTool.configKey, serverConfig);
2828
+ console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014 VS Code MCP"));
2564
2829
  }
2565
2830
  }
2566
2831
  for (const action of projectActions) {
2567
2832
  if (action.action === "create") {
2568
2833
  const isAppendType = RULE_FILES.find((r) => r.filePath === action.filePath)?.strategy === "append";
2569
2834
  const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
2570
- fs9.mkdirSync(path11.dirname(action.absPath), { recursive: true });
2571
- fs9.writeFileSync(action.absPath, content, "utf-8");
2835
+ fs11.mkdirSync(path13.dirname(action.absPath), { recursive: true });
2836
+ fs11.writeFileSync(action.absPath, content, "utf-8");
2572
2837
  console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 created"));
2573
2838
  } else {
2574
- fs9.appendFileSync(action.absPath, APPEND_SECTION);
2839
+ fs11.appendFileSync(action.absPath, APPEND_SECTION);
2575
2840
  console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 appended"));
2576
2841
  }
2577
2842
  connected++;
@@ -2581,7 +2846,7 @@ async function connectCommand(options) {
2581
2846
  const hostname = getHostname();
2582
2847
  for (const projectPath of selectedPaths) {
2583
2848
  const projectActions = byProject.get(projectPath) || [];
2584
- const projectName = path11.basename(projectPath);
2849
+ const projectName = path13.basename(projectPath);
2585
2850
  const toolsDetected = [...new Set(projectActions.map((a) => a.tool))];
2586
2851
  const configFilesWritten = projectActions.map((a) => a.filePath);
2587
2852
  registerProject(apiKey, {
@@ -2601,8 +2866,8 @@ async function connectCommand(options) {
2601
2866
 
2602
2867
  // src/commands/disconnect.ts
2603
2868
  init_esm_shims();
2604
- import fs10 from "fs";
2605
- import path12 from "path";
2869
+ import fs12 from "fs";
2870
+ import path14 from "path";
2606
2871
  import { checkbox as checkbox4, confirm as confirm5 } from "@inquirer/prompts";
2607
2872
  var DEDICATED_FILES = /* @__PURE__ */ new Set([
2608
2873
  ".cursor/rules/piut.mdc",
@@ -2618,7 +2883,7 @@ var APPEND_FILES = [
2618
2883
  ];
2619
2884
  function hasPiutReference3(filePath) {
2620
2885
  try {
2621
- const content = fs10.readFileSync(filePath, "utf-8");
2886
+ const content = fs12.readFileSync(filePath, "utf-8");
2622
2887
  return content.includes("p\u0131ut Context") || content.includes("piut Context");
2623
2888
  } catch {
2624
2889
  return false;
@@ -2626,7 +2891,7 @@ function hasPiutReference3(filePath) {
2626
2891
  }
2627
2892
  function removePiutSection(filePath) {
2628
2893
  try {
2629
- let content = fs10.readFileSync(filePath, "utf-8");
2894
+ let content = fs12.readFileSync(filePath, "utf-8");
2630
2895
  const patterns = [
2631
2896
  /\n*## p[ıi]ut Context[\s\S]*?(?=\n## |\n---\n|$)/g
2632
2897
  ];
@@ -2640,7 +2905,7 @@ function removePiutSection(filePath) {
2640
2905
  }
2641
2906
  if (changed) {
2642
2907
  content = content.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
2643
- fs10.writeFileSync(filePath, content, "utf-8");
2908
+ fs12.writeFileSync(filePath, content, "utf-8");
2644
2909
  }
2645
2910
  return changed;
2646
2911
  } catch {
@@ -2657,10 +2922,10 @@ async function disconnectCommand(options) {
2657
2922
  const projects = scanForProjects(scanFolders);
2658
2923
  const actions = [];
2659
2924
  for (const project of projects) {
2660
- const projectName = path12.basename(project.path);
2925
+ const projectName = path14.basename(project.path);
2661
2926
  for (const dedicatedFile of DEDICATED_FILES) {
2662
- const absPath = path12.join(project.path, dedicatedFile);
2663
- if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
2927
+ const absPath = path14.join(project.path, dedicatedFile);
2928
+ if (fs12.existsSync(absPath) && hasPiutReference3(absPath)) {
2664
2929
  actions.push({
2665
2930
  projectPath: project.path,
2666
2931
  projectName,
@@ -2671,8 +2936,8 @@ async function disconnectCommand(options) {
2671
2936
  }
2672
2937
  }
2673
2938
  for (const appendFile of APPEND_FILES) {
2674
- const absPath = path12.join(project.path, appendFile);
2675
- if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
2939
+ const absPath = path14.join(project.path, appendFile);
2940
+ if (fs12.existsSync(absPath) && hasPiutReference3(absPath)) {
2676
2941
  actions.push({
2677
2942
  projectPath: project.path,
2678
2943
  projectName,
@@ -2687,12 +2952,12 @@ async function disconnectCommand(options) {
2687
2952
  projectPath: project.path,
2688
2953
  projectName,
2689
2954
  filePath: ".piut/",
2690
- absPath: path12.join(project.path, ".piut"),
2955
+ absPath: path14.join(project.path, ".piut"),
2691
2956
  action: "remove-dir"
2692
2957
  });
2693
2958
  }
2694
- const vscodeMcpPath = path12.join(project.path, ".vscode", "mcp.json");
2695
- if (fs10.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
2959
+ const vscodeMcpPath = path14.join(project.path, ".vscode", "mcp.json");
2960
+ if (fs12.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
2696
2961
  actions.push({
2697
2962
  projectPath: project.path,
2698
2963
  projectName,
@@ -2714,7 +2979,7 @@ async function disconnectCommand(options) {
2714
2979
  }
2715
2980
  console.log();
2716
2981
  const projectChoices = Array.from(byProject.entries()).map(([projectPath, projectActions]) => {
2717
- const name = path12.basename(projectPath);
2982
+ const name = path14.basename(projectPath);
2718
2983
  const files = projectActions.map((a) => a.filePath).join(", ");
2719
2984
  return {
2720
2985
  name: `${name} ${dim(`(${files})`)}`,
@@ -2743,11 +3008,11 @@ async function disconnectCommand(options) {
2743
3008
  let disconnected = 0;
2744
3009
  for (const projectPath of selectedPaths) {
2745
3010
  const projectActions = byProject.get(projectPath) || [];
2746
- const projectName = path12.basename(projectPath);
3011
+ const projectName = path14.basename(projectPath);
2747
3012
  for (const action of projectActions) {
2748
3013
  if (action.action === "delete") {
2749
3014
  try {
2750
- fs10.unlinkSync(action.absPath);
3015
+ fs12.unlinkSync(action.absPath);
2751
3016
  console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 deleted"));
2752
3017
  disconnected++;
2753
3018
  } catch {
@@ -2761,7 +3026,7 @@ async function disconnectCommand(options) {
2761
3026
  } else if (action.action === "remove-mcp") {
2762
3027
  try {
2763
3028
  removeFromConfig(action.absPath, "servers");
2764
- console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 piut-context removed"));
3029
+ console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 piut removed"));
2765
3030
  disconnected++;
2766
3031
  } catch {
2767
3032
  console.log(warning(` \u2717 ${projectName}/${action.filePath}`) + dim(" \u2014 could not update"));
@@ -2928,7 +3193,7 @@ async function updateCommand(currentVersion) {
2928
3193
 
2929
3194
  // src/commands/doctor.ts
2930
3195
  init_esm_shims();
2931
- import fs11 from "fs";
3196
+ import fs13 from "fs";
2932
3197
  import chalk10 from "chalk";
2933
3198
  async function doctorCommand(options) {
2934
3199
  if (!options.json) banner();
@@ -2980,7 +3245,7 @@ async function doctorCommand(options) {
2980
3245
  for (const tool of TOOLS) {
2981
3246
  const paths = resolveConfigPaths(tool);
2982
3247
  for (const { filePath: configPath, configKey: resolvedKey } of paths) {
2983
- if (!fs11.existsSync(configPath)) continue;
3248
+ if (!fs13.existsSync(configPath)) continue;
2984
3249
  const piutConfig = getPiutConfig(configPath, resolvedKey);
2985
3250
  if (!piutConfig) {
2986
3251
  result.tools.push({
@@ -3080,6 +3345,12 @@ async function doctorCommand(options) {
3080
3345
  }
3081
3346
  if (result.issues === 0) {
3082
3347
  console.log(success(" All checks passed."));
3348
+ if (result.mcp.ok && result.tools.some((t) => t.configured)) {
3349
+ console.log();
3350
+ console.log(dim(" Tip: If your AI tool can't access your brain but everything"));
3351
+ console.log(dim(" checks out here, try toggling the MCP server off and on in"));
3352
+ console.log(dim(" your tool's settings. MCP connections can go stale."));
3353
+ }
3083
3354
  } else {
3084
3355
  console.log(warning(` ${result.issues} issue(s) found.`));
3085
3356
  const staleTools = result.tools.filter((t) => t.keyMatch === "stale" && !t.fixed);
@@ -3095,10 +3366,49 @@ async function doctorCommand(options) {
3095
3366
  if (result.issues > 0) throw new CliError(`${result.issues} issue(s) found`);
3096
3367
  }
3097
3368
 
3369
+ // src/commands/refresh.ts
3370
+ init_esm_shims();
3371
+ async function refreshCommand(options) {
3372
+ banner();
3373
+ const { apiKey, slug, serverUrl, status } = await resolveApiKeyWithResult(options.key);
3374
+ const configuredTools = getConfiguredToolNames();
3375
+ const isDeployed = status === "active";
3376
+ if (configuredTools.length === 0 && !isDeployed) {
3377
+ console.log();
3378
+ console.log(warning(" Nothing to refresh."));
3379
+ console.log(dim(" Run ") + brand("piut setup") + dim(" first to configure your AI tools."));
3380
+ console.log();
3381
+ return;
3382
+ }
3383
+ console.log(dim(" Refreshing..."));
3384
+ if (isDeployed) {
3385
+ try {
3386
+ await publishServer(apiKey);
3387
+ console.log(success(" \u2713 Server republished"));
3388
+ } catch {
3389
+ console.log(warning(" \u2717 Could not republish server"));
3390
+ }
3391
+ }
3392
+ if (configuredTools.length > 0) {
3393
+ await cycleMcpConfigs(slug, apiKey);
3394
+ console.log(success(" \u2713 Refreshed MCP connections"));
3395
+ console.log(dim(` Cycled: ${configuredTools.join(", ")}`));
3396
+ }
3397
+ const refreshedProjects = await cycleProjectConfigs(slug, apiKey, serverUrl);
3398
+ if (refreshedProjects.length > 0) {
3399
+ console.log(success(" \u2713 Refreshed connected projects"));
3400
+ console.log(dim(` Updated: ${refreshedProjects.join(", ")}`));
3401
+ }
3402
+ console.log();
3403
+ console.log(dim(" Tools that watch config files will reconnect automatically."));
3404
+ console.log(dim(" If a tool doesn't refresh, restart it manually."));
3405
+ console.log();
3406
+ }
3407
+
3098
3408
  // src/commands/vault.ts
3099
3409
  init_esm_shims();
3100
- import fs12 from "fs";
3101
- import path13 from "path";
3410
+ import fs14 from "fs";
3411
+ import path15 from "path";
3102
3412
  import chalk11 from "chalk";
3103
3413
  import { confirm as confirm7 } from "@inquirer/prompts";
3104
3414
  var DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
@@ -3151,26 +3461,26 @@ async function vaultListCommand(options) {
3151
3461
  }
3152
3462
  async function vaultUploadCommand(filePath, options) {
3153
3463
  const key = resolveApiKey(options);
3154
- const resolved = path13.resolve(filePath);
3155
- if (!fs12.existsSync(resolved)) {
3464
+ const resolved = path15.resolve(filePath);
3465
+ if (!fs14.existsSync(resolved)) {
3156
3466
  console.log(chalk11.red(` File not found: ${filePath}`));
3157
3467
  throw new CliError();
3158
3468
  }
3159
- const stat = fs12.statSync(resolved);
3469
+ const stat = fs14.statSync(resolved);
3160
3470
  if (!stat.isFile()) {
3161
3471
  console.log(chalk11.red(` Not a file: ${filePath}`));
3162
3472
  throw new CliError();
3163
3473
  }
3164
- const filename = path13.basename(resolved);
3474
+ const filename = path15.basename(resolved);
3165
3475
  const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
3166
3476
  const isDocument = DOCUMENT_EXTENSIONS.has(ext);
3167
3477
  let content;
3168
3478
  let encoding;
3169
3479
  if (isDocument) {
3170
- content = fs12.readFileSync(resolved).toString("base64");
3480
+ content = fs14.readFileSync(resolved).toString("base64");
3171
3481
  encoding = "base64";
3172
3482
  } else {
3173
- content = fs12.readFileSync(resolved, "utf-8");
3483
+ content = fs14.readFileSync(resolved, "utf-8");
3174
3484
  }
3175
3485
  const spinner = new Spinner();
3176
3486
  spinner.start(`Uploading ${filename}...`);
@@ -3193,8 +3503,8 @@ async function vaultReadCommand(filename, options) {
3193
3503
  try {
3194
3504
  const file = await readVaultFile(key, filename);
3195
3505
  if (options.output) {
3196
- const outPath = path13.resolve(options.output);
3197
- fs12.writeFileSync(outPath, file.content, "utf-8");
3506
+ const outPath = path15.resolve(options.output);
3507
+ fs14.writeFileSync(outPath, file.content, "utf-8");
3198
3508
  console.log(success(` Saved to ${outPath}`));
3199
3509
  console.log();
3200
3510
  } else {
@@ -3228,8 +3538,8 @@ async function vaultDeleteCommand(filename, options) {
3228
3538
  // src/commands/interactive.ts
3229
3539
  init_esm_shims();
3230
3540
  import { select as select2, confirm as confirm8, checkbox as checkbox5, Separator } from "@inquirer/prompts";
3231
- import fs14 from "fs";
3232
- import path15 from "path";
3541
+ import fs16 from "fs";
3542
+ import path17 from "path";
3233
3543
  import { exec as exec2 } from "child_process";
3234
3544
  import chalk13 from "chalk";
3235
3545
  var DOCUMENT_EXTENSIONS2 = /* @__PURE__ */ new Set([
@@ -3278,6 +3588,42 @@ async function interactiveMenu() {
3278
3588
  const auth = await authenticate();
3279
3589
  apiKey = auth.apiKey;
3280
3590
  currentValidation = auth.validation;
3591
+ const synced = syncStaleConfigs(
3592
+ currentValidation.slug,
3593
+ apiKey,
3594
+ currentValidation.serverUrl
3595
+ );
3596
+ if (synced.length > 0) {
3597
+ console.log(dim(` Updated ${synced.length} stale config(s): ${synced.join(", ")}`));
3598
+ }
3599
+ await offerGlobalInstall();
3600
+ const configuredTools = getConfiguredToolNames();
3601
+ const isDeployed = currentValidation.status === "active";
3602
+ if (configuredTools.length > 0 || isDeployed) {
3603
+ const parts = [];
3604
+ if (isDeployed) {
3605
+ try {
3606
+ await publishServer(apiKey);
3607
+ parts.push("server");
3608
+ } catch {
3609
+ }
3610
+ }
3611
+ if (configuredTools.length > 0) {
3612
+ await cycleMcpConfigs(currentValidation.slug, apiKey);
3613
+ parts.push(`${configuredTools.length} tool(s)`);
3614
+ }
3615
+ const refreshedProjects = await cycleProjectConfigs(
3616
+ currentValidation.slug,
3617
+ apiKey,
3618
+ currentValidation.serverUrl
3619
+ );
3620
+ if (refreshedProjects.length > 0) {
3621
+ parts.push(`${refreshedProjects.length} project(s)`);
3622
+ }
3623
+ if (parts.length > 0) {
3624
+ console.log(dim(` Refreshed: ${parts.join(", ")}`));
3625
+ }
3626
+ }
3281
3627
  console.log();
3282
3628
  if (currentValidation.status === "no_brain") {
3283
3629
  console.log(warning(" You haven\u2019t built a brain yet."));
@@ -3313,7 +3659,7 @@ async function interactiveMenu() {
3313
3659
  }
3314
3660
  while (true) {
3315
3661
  const hasBrain = currentValidation.status !== "no_brain";
3316
- const isDeployed = currentValidation.status === "active";
3662
+ const isDeployed2 = currentValidation.status === "active";
3317
3663
  let action;
3318
3664
  try {
3319
3665
  action = await select2({
@@ -3339,22 +3685,22 @@ async function interactiveMenu() {
3339
3685
  },
3340
3686
  new Separator(),
3341
3687
  {
3342
- name: isDeployed ? "Undeploy Brain" : "Deploy Brain",
3688
+ name: isDeployed2 ? "Undeploy Brain" : "Deploy Brain",
3343
3689
  value: "deploy",
3344
- description: isDeployed ? "Take your MCP server offline" : "Publish your MCP server (requires paid account)"
3690
+ description: isDeployed2 ? "Take your MCP server offline" : "Publish your MCP server (requires paid account)"
3345
3691
  },
3346
3692
  new Separator(),
3347
3693
  {
3348
3694
  name: "Connect Tools",
3349
3695
  value: "connect-tools",
3350
3696
  description: "Manage which AI tools use your MCP server",
3351
- disabled: !isDeployed && "(deploy brain first)"
3697
+ disabled: !isDeployed2 && "(deploy brain first)"
3352
3698
  },
3353
3699
  {
3354
3700
  name: "Connect Projects",
3355
3701
  value: "connect-projects",
3356
3702
  description: "Manage brain references in project config files",
3357
- disabled: !isDeployed && "(deploy brain first)"
3703
+ disabled: !isDeployed2 && "(deploy brain first)"
3358
3704
  },
3359
3705
  new Separator(),
3360
3706
  {
@@ -3401,7 +3747,7 @@ async function interactiveMenu() {
3401
3747
  handleEditBrain();
3402
3748
  break;
3403
3749
  case "deploy":
3404
- if (isDeployed) {
3750
+ if (isDeployed2) {
3405
3751
  await handleUndeploy(apiKey);
3406
3752
  } else {
3407
3753
  await deployCommand({ key: apiKey });
@@ -3472,8 +3818,8 @@ async function handleConnectTools(apiKey, validation) {
3472
3818
  for (const tool of TOOLS) {
3473
3819
  const paths = resolveConfigPaths(tool);
3474
3820
  for (const { filePath, configKey } of paths) {
3475
- const exists = fs14.existsSync(filePath);
3476
- const parentExists = fs14.existsSync(path15.dirname(filePath));
3821
+ const exists = fs16.existsSync(filePath);
3822
+ const parentExists = fs16.existsSync(path17.dirname(filePath));
3477
3823
  if (exists || parentExists) {
3478
3824
  const connected = exists && !!configKey && isPiutConfigured(filePath, configKey);
3479
3825
  detected.push({ tool, configPath: filePath, resolvedConfigKey: configKey, connected });
@@ -3596,29 +3942,29 @@ async function handleManageProjects(apiKey, validation) {
3596
3942
  console.log();
3597
3943
  const copilotTool = TOOLS.find((t) => t.id === "copilot");
3598
3944
  for (const { project } of toConnect) {
3599
- const projectName = path15.basename(project.path);
3945
+ const projectName = path17.basename(project.path);
3600
3946
  writePiutConfig(project.path, { slug, apiKey, serverUrl });
3601
3947
  await writePiutSkill(project.path, slug, apiKey);
3602
3948
  ensureGitignored(project.path);
3603
3949
  if (copilotTool) {
3604
- const hasCopilot = fs14.existsSync(path15.join(project.path, ".github", "copilot-instructions.md")) || fs14.existsSync(path15.join(project.path, ".github"));
3950
+ const hasCopilot = fs16.existsSync(path17.join(project.path, ".github", "copilot-instructions.md")) || fs16.existsSync(path17.join(project.path, ".github"));
3605
3951
  if (hasCopilot) {
3606
- const vscodeMcpPath = path15.join(project.path, ".vscode", "mcp.json");
3952
+ const vscodeMcpPath = path17.join(project.path, ".vscode", "mcp.json");
3607
3953
  const serverConfig = copilotTool.generateConfig(slug, apiKey);
3608
3954
  mergeConfig(vscodeMcpPath, copilotTool.configKey, serverConfig);
3609
3955
  }
3610
3956
  }
3611
3957
  for (const rule of RULE_FILES) {
3612
3958
  if (!rule.detect(project)) continue;
3613
- const absPath = path15.join(project.path, rule.filePath);
3614
- if (fs14.existsSync(absPath) && hasPiutReference2(absPath)) continue;
3615
- if (rule.strategy === "create" || !fs14.existsSync(absPath)) {
3959
+ const absPath = path17.join(project.path, rule.filePath);
3960
+ if (fs16.existsSync(absPath) && hasPiutReference2(absPath)) continue;
3961
+ if (rule.strategy === "create" || !fs16.existsSync(absPath)) {
3616
3962
  const isAppendType = rule.strategy === "append";
3617
3963
  const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
3618
- fs14.mkdirSync(path15.dirname(absPath), { recursive: true });
3619
- fs14.writeFileSync(absPath, content, "utf-8");
3964
+ fs16.mkdirSync(path17.dirname(absPath), { recursive: true });
3965
+ fs16.writeFileSync(absPath, content, "utf-8");
3620
3966
  } else {
3621
- fs14.appendFileSync(absPath, APPEND_SECTION);
3967
+ fs16.appendFileSync(absPath, APPEND_SECTION);
3622
3968
  }
3623
3969
  }
3624
3970
  toolLine(projectName, success("connected"), "\u2714");
@@ -3634,24 +3980,24 @@ async function handleManageProjects(apiKey, validation) {
3634
3980
  });
3635
3981
  }
3636
3982
  for (const { project } of toDisconnect) {
3637
- const projectName = path15.basename(project.path);
3983
+ const projectName = path17.basename(project.path);
3638
3984
  for (const dedicatedFile of DEDICATED_FILES) {
3639
- const absPath = path15.join(project.path, dedicatedFile);
3640
- if (fs14.existsSync(absPath) && hasPiutReference2(absPath)) {
3985
+ const absPath = path17.join(project.path, dedicatedFile);
3986
+ if (fs16.existsSync(absPath) && hasPiutReference2(absPath)) {
3641
3987
  try {
3642
- fs14.unlinkSync(absPath);
3988
+ fs16.unlinkSync(absPath);
3643
3989
  } catch {
3644
3990
  }
3645
3991
  }
3646
3992
  }
3647
3993
  for (const appendFile of APPEND_FILES) {
3648
- const absPath = path15.join(project.path, appendFile);
3649
- if (fs14.existsSync(absPath) && hasPiutReference2(absPath)) {
3994
+ const absPath = path17.join(project.path, appendFile);
3995
+ if (fs16.existsSync(absPath) && hasPiutReference2(absPath)) {
3650
3996
  removePiutSection(absPath);
3651
3997
  }
3652
3998
  }
3653
- const vscodeMcpPath = path15.join(project.path, ".vscode", "mcp.json");
3654
- if (fs14.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
3999
+ const vscodeMcpPath = path17.join(project.path, ".vscode", "mcp.json");
4000
+ if (fs16.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
3655
4001
  removeFromConfig(vscodeMcpPath, "servers");
3656
4002
  }
3657
4003
  removePiutDir(project.path);
@@ -3743,16 +4089,16 @@ async function handleVaultUpload(apiKey) {
3743
4089
  console.log();
3744
4090
  let uploaded = 0;
3745
4091
  for (const filePath of files) {
3746
- const filename = path15.basename(filePath);
4092
+ const filename = path17.basename(filePath);
3747
4093
  const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
3748
4094
  const isDocument = DOCUMENT_EXTENSIONS2.has(ext);
3749
4095
  let content;
3750
4096
  let encoding;
3751
4097
  if (isDocument) {
3752
- content = fs14.readFileSync(filePath).toString("base64");
4098
+ content = fs16.readFileSync(filePath).toString("base64");
3753
4099
  encoding = "base64";
3754
4100
  } else {
3755
- content = fs14.readFileSync(filePath, "utf-8");
4101
+ content = fs16.readFileSync(filePath, "utf-8");
3756
4102
  }
3757
4103
  const spinner = new Spinner();
3758
4104
  spinner.start(`Uploading ${filename}...`);
@@ -3828,7 +4174,7 @@ async function handleViewBrain(apiKey) {
3828
4174
  }
3829
4175
 
3830
4176
  // src/cli.ts
3831
- var VERSION = "3.8.0";
4177
+ var VERSION = "3.10.0";
3832
4178
  function withExit(fn) {
3833
4179
  return async (...args2) => {
3834
4180
  try {
@@ -3854,6 +4200,7 @@ program.command("remove").description("Remove all p\u0131ut configurations").act
3854
4200
  program.command("login").description("Authenticate with p\u0131ut (email, browser, or API key)").action(withExit(loginCommand));
3855
4201
  program.command("logout").description("Remove saved API key").action(logoutCommand);
3856
4202
  program.command("doctor").description("Diagnose and fix connection issues").option("-k, --key <key>", "API key to verify against").option("--fix", "Auto-fix stale configurations").option("--json", "Output results as JSON").action(withExit(doctorCommand));
4203
+ program.command("refresh").description("Force all configured tools to reconnect to your MCP server").option("-k, --key <key>", "API key").action(withExit(refreshCommand));
3857
4204
  var vault = program.command("vault").description("Manage your file vault (upload, list, read, delete)");
3858
4205
  vault.command("list").description("List all files in your vault").option("-k, --key <key>", "API key").action(withExit(vaultListCommand));
3859
4206
  vault.command("upload <file>").description("Upload a file to your vault").option("-k, --key <key>", "API key").action(withExit(vaultUploadCommand));