@piut/cli 3.7.0 → 3.9.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 +3 -3
  2. package/dist/cli.js +952 -913
  3. package/package.json +3 -6
package/dist/cli.js CHANGED
@@ -1,16 +1,338 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // node_modules/tsup/assets/esm_shims.js
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ var init_esm_shims = __esm({
16
+ "node_modules/tsup/assets/esm_shims.js"() {
17
+ "use strict";
18
+ }
19
+ });
20
+
21
+ // src/lib/tree-prompt.ts
22
+ var tree_prompt_exports = {};
23
+ __export(tree_prompt_exports, {
24
+ collapseNode: () => collapseNode,
25
+ default: () => tree_prompt_default,
26
+ expandNode: () => expandNode,
27
+ getParentIndex: () => getParentIndex,
28
+ loadChildren: () => loadChildren,
29
+ shouldShowInTree: () => shouldShowInTree
30
+ });
31
+ import fs14 from "fs";
32
+ import path14 from "path";
33
+ import os8 from "os";
34
+ import {
35
+ createPrompt,
36
+ useState,
37
+ useKeypress,
38
+ usePagination,
39
+ useRef,
40
+ isUpKey,
41
+ isDownKey,
42
+ isSpaceKey,
43
+ isEnterKey
44
+ } from "@inquirer/core";
45
+ import chalk12 from "chalk";
46
+ function shouldShowInTree(name) {
47
+ if (TREE_SKIP.has(name)) return false;
48
+ if (name.startsWith(".") && !TREE_INCLUDE_DOT_DIRS.has(name)) return false;
49
+ return true;
50
+ }
51
+ function shouldShowFile(name) {
52
+ if (name.startsWith(".")) return false;
53
+ const ext = path14.extname(name).toLowerCase();
54
+ return !HIDDEN_FILE_EXTENSIONS.has(ext);
55
+ }
56
+ function loadChildren(parentPath, parentDepth, includeFiles = false) {
57
+ try {
58
+ const entries = fs14.readdirSync(parentPath, { withFileTypes: true });
59
+ const dirs = [];
60
+ const files = [];
61
+ for (const entry of entries) {
62
+ if (entry.isDirectory()) {
63
+ if (!shouldShowInTree(entry.name)) continue;
64
+ dirs.push({
65
+ path: path14.join(parentPath, entry.name),
66
+ name: entry.name,
67
+ depth: parentDepth + 1,
68
+ expanded: false,
69
+ selected: false,
70
+ children: null
71
+ });
72
+ } else if (includeFiles && entry.isFile()) {
73
+ if (!shouldShowFile(entry.name)) continue;
74
+ files.push({
75
+ path: path14.join(parentPath, entry.name),
76
+ name: entry.name,
77
+ depth: parentDepth + 1,
78
+ expanded: false,
79
+ selected: false,
80
+ children: null,
81
+ isFile: true
82
+ });
83
+ }
84
+ }
85
+ dirs.sort((a, b) => a.name.localeCompare(b.name));
86
+ files.sort((a, b) => a.name.localeCompare(b.name));
87
+ return [...dirs, ...files];
88
+ } catch {
89
+ return [];
90
+ }
91
+ }
92
+ function expandNode(items, index, children) {
93
+ const result = [...items];
94
+ result[index] = { ...result[index], expanded: true, children, empty: children.length === 0 };
95
+ result.splice(index + 1, 0, ...children);
96
+ return result;
97
+ }
98
+ function collapseNode(items, index) {
99
+ const node = items[index];
100
+ const result = [...items];
101
+ result[index] = { ...result[index], expanded: false };
102
+ let removeCount = 0;
103
+ for (let i = index + 1; i < result.length; i++) {
104
+ if (result[i].depth > node.depth) {
105
+ removeCount++;
106
+ } else {
107
+ break;
108
+ }
109
+ }
110
+ if (removeCount > 0) {
111
+ result.splice(index + 1, removeCount);
112
+ }
113
+ return result;
114
+ }
115
+ function getParentIndex(items, index) {
116
+ const targetDepth = items[index].depth - 1;
117
+ for (let i = index - 1; i >= 0; i--) {
118
+ if (items[i].depth === targetDepth) return i;
119
+ }
120
+ return index;
121
+ }
122
+ var TREE_SKIP, TREE_INCLUDE_DOT_DIRS, HIDDEN_FILE_EXTENSIONS, treePrompt, tree_prompt_default;
123
+ var init_tree_prompt = __esm({
124
+ "src/lib/tree-prompt.ts"() {
125
+ "use strict";
126
+ init_esm_shims();
127
+ TREE_SKIP = /* @__PURE__ */ new Set([
128
+ "node_modules",
129
+ ".git",
130
+ "__pycache__",
131
+ ".venv",
132
+ "venv",
133
+ "dist",
134
+ "build",
135
+ ".next",
136
+ ".nuxt",
137
+ ".output",
138
+ ".Trash",
139
+ "Library",
140
+ ".cache",
141
+ ".npm",
142
+ ".yarn",
143
+ ".pnpm-store",
144
+ "Caches",
145
+ "Cache",
146
+ ".piut",
147
+ "Applications",
148
+ "Public",
149
+ "Movies",
150
+ "Music",
151
+ "Pictures",
152
+ "Templates"
153
+ ]);
154
+ TREE_INCLUDE_DOT_DIRS = /* @__PURE__ */ new Set([
155
+ ".cursor",
156
+ ".windsurf",
157
+ ".openclaw",
158
+ ".zed",
159
+ ".github",
160
+ ".amazonq",
161
+ ".gemini",
162
+ ".mcporter",
163
+ ".paperclip",
164
+ ".vscode"
165
+ ]);
166
+ HIDDEN_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
167
+ ".png",
168
+ ".jpg",
169
+ ".jpeg",
170
+ ".gif",
171
+ ".svg",
172
+ ".ico",
173
+ ".webp",
174
+ ".bmp",
175
+ ".tiff",
176
+ ".heic",
177
+ ".mp3",
178
+ ".mp4",
179
+ ".wav",
180
+ ".aac",
181
+ ".flac",
182
+ ".ogg",
183
+ ".avi",
184
+ ".mov",
185
+ ".mkv",
186
+ ".wmv",
187
+ ".webm",
188
+ ".zip",
189
+ ".tar",
190
+ ".gz",
191
+ ".bz2",
192
+ ".xz",
193
+ ".7z",
194
+ ".rar",
195
+ ".dmg",
196
+ ".iso",
197
+ ".exe",
198
+ ".dll",
199
+ ".so",
200
+ ".dylib",
201
+ ".o",
202
+ ".a",
203
+ ".wasm",
204
+ ".class",
205
+ ".jar",
206
+ ".pyc",
207
+ ".pyo",
208
+ ".ttf",
209
+ ".otf",
210
+ ".woff",
211
+ ".woff2",
212
+ ".eot",
213
+ ".lock",
214
+ ".map"
215
+ ]);
216
+ treePrompt = createPrompt((config, done) => {
217
+ const root = config.root ?? os8.homedir();
218
+ const pageSize = config.pageSize ?? 15;
219
+ const mode = config.mode ?? "folders";
220
+ const includeFiles = mode === "files";
221
+ const prefix = chalk12.green("?");
222
+ const childrenCache = useRef(/* @__PURE__ */ new Map()).current;
223
+ const [items, setItems] = useState(() => {
224
+ const rootChildren = loadChildren(root, -1, includeFiles);
225
+ childrenCache.set(root, rootChildren);
226
+ return rootChildren;
227
+ });
228
+ const [active, setActive] = useState(0);
229
+ const [done_, setDone] = useState(false);
230
+ useKeypress((event) => {
231
+ if (done_) return;
232
+ if (isEnterKey(event)) {
233
+ const selected = items.filter((n) => n.selected).map((n) => n.path);
234
+ setDone(true);
235
+ done(selected);
236
+ return;
237
+ }
238
+ if (isUpKey(event)) {
239
+ setActive(Math.max(0, active - 1));
240
+ return;
241
+ }
242
+ if (isDownKey(event)) {
243
+ setActive(Math.min(items.length - 1, active + 1));
244
+ return;
245
+ }
246
+ if (isSpaceKey(event)) {
247
+ const node = items[active];
248
+ if (includeFiles && !node.isFile) return;
249
+ if (!includeFiles && node.isFile) return;
250
+ const updated = [...items];
251
+ updated[active] = { ...updated[active], selected: !updated[active].selected };
252
+ setItems(updated);
253
+ return;
254
+ }
255
+ if (event.name === "right") {
256
+ const node = items[active];
257
+ if (node.isFile) return;
258
+ if (node.expanded) return;
259
+ let children;
260
+ if (childrenCache.has(node.path)) {
261
+ children = childrenCache.get(node.path);
262
+ } else {
263
+ children = loadChildren(node.path, node.depth, includeFiles);
264
+ childrenCache.set(node.path, children);
265
+ }
266
+ setItems(expandNode(items, active, children));
267
+ return;
268
+ }
269
+ if (event.name === "left") {
270
+ const node = items[active];
271
+ if (!node.isFile && node.expanded) {
272
+ setItems(collapseNode(items, active));
273
+ } else {
274
+ const parentIdx = getParentIndex(items, active);
275
+ setActive(parentIdx);
276
+ }
277
+ return;
278
+ }
279
+ });
280
+ if (done_) {
281
+ const selected = items.filter((n) => n.selected);
282
+ const label = includeFiles ? "file" : "folder";
283
+ const summary = selected.length === 0 ? chalk12.dim(`no ${label}s selected`) : selected.map((n) => n.isFile ? n.name : n.path).join(", ");
284
+ return `${prefix} ${config.message} ${chalk12.cyan(summary)}`;
285
+ }
286
+ const page = usePagination({
287
+ items,
288
+ active,
289
+ pageSize,
290
+ loop: false,
291
+ renderItem({ item, isActive }) {
292
+ const indent = " ".repeat(item.depth + 1);
293
+ if (item.isFile) {
294
+ const marker2 = item.selected ? chalk12.green("\u25CF ") : "\u25CB ";
295
+ const line2 = `${indent} ${marker2}${item.name}`;
296
+ return isActive ? chalk12.cyan(line2) : line2;
297
+ }
298
+ const icon = item.expanded ? "\u25BE" : "\u25B8";
299
+ const name = `${item.name}/`;
300
+ const suffix = item.error ? chalk12.dim(" (permission denied)") : item.empty ? chalk12.dim(" (empty)") : "";
301
+ if (includeFiles) {
302
+ const line2 = `${indent}${icon} ${name}${suffix}`;
303
+ return isActive ? chalk12.cyan(line2) : chalk12.dim(line2);
304
+ }
305
+ const marker = item.selected ? chalk12.green("\u25CF ") : "\u25CB ";
306
+ const line = `${indent}${icon} ${marker}${name}${suffix}`;
307
+ return isActive ? chalk12.cyan(line) : line;
308
+ }
309
+ });
310
+ const selectHint = includeFiles ? "space select file" : "space select";
311
+ const help = chalk12.dim(` \u2191\u2193 navigate \u2192 expand \u2190 collapse ${selectHint} enter done`);
312
+ const header = chalk12.dim(` ${root}`);
313
+ return `${prefix} ${config.message}
314
+ ${help}
315
+ ${header}
316
+ ${page}`;
317
+ });
318
+ tree_prompt_default = treePrompt;
319
+ }
320
+ });
2
321
 
3
322
  // src/cli.ts
323
+ init_esm_shims();
4
324
  import { Command } from "commander";
5
325
 
6
326
  // src/commands/setup.ts
327
+ init_esm_shims();
7
328
  import fs4 from "fs";
8
- import path6 from "path";
329
+ import path7 from "path";
9
330
  import { execSync } from "child_process";
10
331
  import { password, confirm, checkbox } from "@inquirer/prompts";
11
332
  import chalk2 from "chalk";
12
333
 
13
334
  // src/lib/api.ts
335
+ init_esm_shims();
14
336
  import os from "os";
15
337
  import crypto from "crypto";
16
338
  var API_BASE = process.env.PIUT_API_BASE || "https://piut.com";
@@ -36,7 +358,7 @@ async function loginWithEmail(email, password3) {
36
358
  }
37
359
  return res.json();
38
360
  }
39
- async function* buildBrainStreaming(key, input3) {
361
+ async function* buildBrainStreaming(key, input2) {
40
362
  const res = await fetch(`${API_BASE}/api/cli/build-brain`, {
41
363
  method: "POST",
42
364
  headers: {
@@ -44,7 +366,7 @@ async function* buildBrainStreaming(key, input3) {
44
366
  "Content-Type": "application/json",
45
367
  Accept: "text/event-stream"
46
368
  },
47
- body: JSON.stringify(input3)
369
+ body: JSON.stringify(input2)
48
370
  });
49
371
  if (!res.ok) {
50
372
  const body = await res.json().catch(() => ({ error: "Unknown error" }));
@@ -62,29 +384,33 @@ async function* buildBrainStreaming(key, input3) {
62
384
  const reader = res.body.getReader();
63
385
  const decoder = new TextDecoder();
64
386
  let buffer = "";
65
- while (true) {
66
- const { done, value } = await reader.read();
67
- if (done) break;
68
- buffer += decoder.decode(value, { stream: true });
69
- const parts = buffer.split("\n\n");
70
- buffer = parts.pop() || "";
71
- for (const part of parts) {
72
- let eventName = "";
73
- let eventData = "";
74
- for (const line of part.split("\n")) {
75
- if (line.startsWith("event: ")) {
76
- eventName = line.slice(7).trim();
77
- } else if (line.startsWith("data: ")) {
78
- 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
+ }
79
403
  }
80
- }
81
- if (eventName && eventData) {
82
- try {
83
- yield { event: eventName, data: JSON.parse(eventData) };
84
- } catch {
404
+ if (eventName && eventData) {
405
+ try {
406
+ yield { event: eventName, data: JSON.parse(eventData) };
407
+ } catch {
408
+ }
85
409
  }
86
410
  }
87
411
  }
412
+ } catch {
413
+ yield { event: "error", data: { message: "Connection lost. The build may still complete \u2014 run `piut status` to check." } };
88
414
  }
89
415
  }
90
416
  async function verifyMcpEndpoint(serverUrl, key) {
@@ -240,14 +566,16 @@ async function listVaultFiles(key) {
240
566
  }
241
567
  return res.json();
242
568
  }
243
- async function uploadVaultFile(key, filename, content) {
569
+ async function uploadVaultFile(key, filename, content, encoding) {
570
+ const payload = { filename, content };
571
+ if (encoding) payload.encoding = encoding;
244
572
  const res = await fetch(`${API_BASE}/api/cli/vault/upload`, {
245
573
  method: "POST",
246
574
  headers: {
247
575
  Authorization: `Bearer ${key}`,
248
576
  "Content-Type": "application/json"
249
577
  },
250
- body: JSON.stringify({ filename, content })
578
+ body: JSON.stringify(payload)
251
579
  });
252
580
  if (!res.ok) {
253
581
  const body = await res.json().catch(() => ({ error: "Unknown error" }));
@@ -282,8 +610,9 @@ async function deleteVaultFile(key, filename) {
282
610
  }
283
611
 
284
612
  // src/lib/tools.ts
613
+ init_esm_shims();
285
614
  import os2 from "os";
286
- import path from "path";
615
+ import path2 from "path";
287
616
  import crypto2 from "crypto";
288
617
  var MCP_URL = (slug) => `https://piut.com/api/mcp/${slug}`;
289
618
  var AUTH_HEADER = (key) => ({ Authorization: `Bearer ${key}` });
@@ -298,7 +627,7 @@ function machineHeaders(toolName) {
298
627
  };
299
628
  }
300
629
  function appData() {
301
- return process.env.APPDATA || path.join(os2.homedir(), "AppData", "Roaming");
630
+ return process.env.APPDATA || path2.join(os2.homedir(), "AppData", "Roaming");
302
631
  }
303
632
  var TOOLS = [
304
633
  {
@@ -329,7 +658,7 @@ var TOOLS = [
329
658
  configKey: "mcpServers",
330
659
  configPaths: {
331
660
  darwin: ["~/Library/Application Support/Claude/claude_desktop_config.json"],
332
- win32: [path.join(appData(), "Claude", "claude_desktop_config.json")],
661
+ win32: [path2.join(appData(), "Claude", "claude_desktop_config.json")],
333
662
  linux: ["~/.config/Claude/claude_desktop_config.json"]
334
663
  },
335
664
  generateConfig: (slug, key) => ({
@@ -375,17 +704,20 @@ var TOOLS = [
375
704
  })
376
705
  },
377
706
  {
378
- id: "copilot",
379
- name: "GitHub Copilot",
707
+ id: "vscode",
708
+ name: "VS Code",
380
709
  configKey: "servers",
381
710
  configPaths: {
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"],
382
714
  project: [".vscode/mcp.json"]
383
715
  },
384
716
  skillFilePath: ".github/copilot-instructions.md",
385
717
  generateConfig: (slug, key) => ({
386
718
  type: "http",
387
719
  url: MCP_URL(slug),
388
- headers: { ...AUTH_HEADER(key), ...machineHeaders("GitHub Copilot") }
720
+ headers: { ...AUTH_HEADER(key), ...machineHeaders("VS Code") }
389
721
  })
390
722
  },
391
723
  {
@@ -463,28 +795,31 @@ var TOOLS = [
463
795
  ];
464
796
 
465
797
  // src/lib/paths.ts
798
+ init_esm_shims();
466
799
  import os3 from "os";
467
- import path2 from "path";
800
+ import path3 from "path";
468
801
  function expandPath(p) {
469
802
  return p.replace(/^~/, os3.homedir());
470
803
  }
471
- function resolveConfigPaths(configPaths) {
804
+ function resolveConfigPaths(tool) {
472
805
  const resolved = [];
806
+ const configKey = tool.configKey || "";
473
807
  const platformKey = process.platform;
474
- const globalPaths = configPaths[platformKey] || [];
808
+ const globalPaths = tool.configPaths[platformKey] || [];
475
809
  for (const p of globalPaths) {
476
- resolved.push(expandPath(p));
810
+ resolved.push({ filePath: expandPath(p), configKey: tool.globalConfigKey || configKey });
477
811
  }
478
- const projectPaths = configPaths.project || [];
812
+ const projectPaths = tool.configPaths.project || [];
479
813
  for (const p of projectPaths) {
480
- resolved.push(path2.resolve(process.cwd(), p));
814
+ resolved.push({ filePath: path3.resolve(process.cwd(), p), configKey });
481
815
  }
482
816
  return resolved;
483
817
  }
484
818
 
485
819
  // src/lib/config.ts
820
+ init_esm_shims();
486
821
  import fs from "fs";
487
- import path3 from "path";
822
+ import path4 from "path";
488
823
  function readConfig(filePath) {
489
824
  let raw;
490
825
  try {
@@ -505,26 +840,46 @@ function readConfig(filePath) {
505
840
  }
506
841
  }
507
842
  function writeConfig(filePath, data) {
508
- fs.mkdirSync(path3.dirname(filePath), { recursive: true });
843
+ fs.mkdirSync(path4.dirname(filePath), { recursive: true });
509
844
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
510
845
  }
846
+ function resolveKeyPath(config, keyPath) {
847
+ const parts = keyPath.split(".");
848
+ let current = config;
849
+ for (const part of parts) {
850
+ if (current === void 0 || current === null || typeof current !== "object") return void 0;
851
+ current = current[part];
852
+ }
853
+ return current;
854
+ }
855
+ function setAtKeyPath(config, keyPath, value) {
856
+ const parts = keyPath.split(".");
857
+ let current = config;
858
+ for (let i = 0; i < parts.length - 1; i++) {
859
+ if (!current[parts[i]] || typeof current[parts[i]] !== "object") {
860
+ current[parts[i]] = {};
861
+ }
862
+ current = current[parts[i]];
863
+ }
864
+ current[parts[parts.length - 1]] = value;
865
+ }
511
866
  function isPiutConfigured(filePath, configKey) {
512
867
  const config = readConfig(filePath);
513
868
  if (!config) return false;
514
- const servers = config[configKey];
869
+ const servers = resolveKeyPath(config, configKey);
515
870
  return !!servers?.["piut-context"];
516
871
  }
517
872
  function mergeConfig(filePath, configKey, serverConfig) {
518
873
  const existing = readConfig(filePath) || {};
519
- const servers = existing[configKey] || {};
874
+ const servers = resolveKeyPath(existing, configKey) || {};
520
875
  servers["piut-context"] = serverConfig;
521
- existing[configKey] = servers;
876
+ setAtKeyPath(existing, configKey, servers);
522
877
  writeConfig(filePath, existing);
523
878
  }
524
879
  function getPiutConfig(filePath, configKey) {
525
880
  const config = readConfig(filePath);
526
881
  if (!config) return null;
527
- const servers = config[configKey];
882
+ const servers = resolveKeyPath(config, configKey);
528
883
  const piut = servers?.["piut-context"];
529
884
  return piut ?? null;
530
885
  }
@@ -576,10 +931,10 @@ function extractSlugFromConfig(piutConfig) {
576
931
  function removeFromConfig(filePath, configKey) {
577
932
  const config = readConfig(filePath);
578
933
  if (!config) return false;
579
- const servers = config[configKey];
934
+ const servers = resolveKeyPath(config, configKey);
580
935
  if (!servers?.["piut-context"]) return false;
581
936
  delete servers["piut-context"];
582
- if (Object.keys(servers).length === 0) {
937
+ if (Object.keys(servers).length === 0 && !configKey.includes(".")) {
583
938
  delete config[configKey];
584
939
  }
585
940
  writeConfig(filePath, config);
@@ -587,8 +942,9 @@ function removeFromConfig(filePath, configKey) {
587
942
  }
588
943
 
589
944
  // src/lib/skill.ts
945
+ init_esm_shims();
590
946
  import fs2 from "fs";
591
- import path4 from "path";
947
+ import path5 from "path";
592
948
  var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
593
949
 
594
950
  This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
@@ -631,7 +987,7 @@ p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use
631
987
  Full skill reference: .piut/skill.md`;
632
988
  var SEPARATOR = "\n\n---\n\n";
633
989
  function placeSkillFile(filePath) {
634
- const absPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
990
+ const absPath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
635
991
  try {
636
992
  const existing = fs2.readFileSync(absPath, "utf-8");
637
993
  if (existing.includes("p\u0131ut Context")) {
@@ -641,7 +997,7 @@ function placeSkillFile(filePath) {
641
997
  return { created: false, appended: true };
642
998
  } catch (err) {
643
999
  if (err.code === "ENOENT") {
644
- fs2.mkdirSync(path4.dirname(absPath), { recursive: true });
1000
+ fs2.mkdirSync(path5.dirname(absPath), { recursive: true });
645
1001
  fs2.writeFileSync(absPath, SKILL_SNIPPET + "\n", "utf-8");
646
1002
  return { created: true, appended: false };
647
1003
  }
@@ -650,8 +1006,9 @@ function placeSkillFile(filePath) {
650
1006
  }
651
1007
 
652
1008
  // src/lib/piut-dir.ts
1009
+ init_esm_shims();
653
1010
  import fs3 from "fs";
654
- import path5 from "path";
1011
+ import path6 from "path";
655
1012
  var API_BASE2 = process.env.PIUT_API_BASE || "https://piut.com";
656
1013
  var PIUT_DIR = ".piut";
657
1014
  var CONFIG_FILE = "config.json";
@@ -693,17 +1050,27 @@ Protocol: JSON-RPC 2.0 over HTTPS
693
1050
  4. Use \`append_brain\` for quick notes and facts
694
1051
  `;
695
1052
  function piutDir(projectPath) {
696
- return path5.join(projectPath, PIUT_DIR);
1053
+ return path6.join(projectPath, PIUT_DIR);
697
1054
  }
698
1055
  function writePiutConfig(projectPath, config) {
699
1056
  const dir = piutDir(projectPath);
700
1057
  fs3.mkdirSync(dir, { recursive: true });
701
1058
  fs3.writeFileSync(
702
- path5.join(dir, CONFIG_FILE),
1059
+ path6.join(dir, CONFIG_FILE),
703
1060
  JSON.stringify(config, null, 2) + "\n",
704
1061
  "utf-8"
705
1062
  );
706
1063
  }
1064
+ function readPiutConfig(projectPath) {
1065
+ try {
1066
+ const raw = fs3.readFileSync(path6.join(piutDir(projectPath), CONFIG_FILE), "utf-8");
1067
+ const parsed = JSON.parse(raw);
1068
+ if (parsed.slug && parsed.apiKey && parsed.serverUrl) return parsed;
1069
+ return null;
1070
+ } catch {
1071
+ return null;
1072
+ }
1073
+ }
707
1074
  async function writePiutSkill(projectPath, slug, apiKey) {
708
1075
  const dir = piutDir(projectPath);
709
1076
  fs3.mkdirSync(dir, { recursive: true });
@@ -716,10 +1083,10 @@ async function writePiutSkill(projectPath, slug, apiKey) {
716
1083
  content = MINIMAL_SKILL_CONTENT;
717
1084
  }
718
1085
  content = content.replaceAll("{{slug}}", slug).replaceAll("{{key}}", apiKey);
719
- fs3.writeFileSync(path5.join(dir, SKILL_FILE), content, "utf-8");
1086
+ fs3.writeFileSync(path6.join(dir, SKILL_FILE), content, "utf-8");
720
1087
  }
721
1088
  function ensureGitignored(projectPath) {
722
- const gitignorePath = path5.join(projectPath, ".gitignore");
1089
+ const gitignorePath = path6.join(projectPath, ".gitignore");
723
1090
  let content = "";
724
1091
  try {
725
1092
  content = fs3.readFileSync(gitignorePath, "utf-8");
@@ -744,10 +1111,11 @@ function removePiutDir(projectPath) {
744
1111
  return true;
745
1112
  }
746
1113
  function hasPiutDir(projectPath) {
747
- return fs3.existsSync(path5.join(piutDir(projectPath), CONFIG_FILE));
1114
+ return fs3.existsSync(path6.join(piutDir(projectPath), CONFIG_FILE));
748
1115
  }
749
1116
 
750
1117
  // src/lib/ui.ts
1118
+ init_esm_shims();
751
1119
  import chalk from "chalk";
752
1120
  var brand = chalk.hex("#8B5CF6");
753
1121
  var success = chalk.green;
@@ -833,6 +1201,7 @@ var Spinner = class {
833
1201
  };
834
1202
 
835
1203
  // src/types.ts
1204
+ init_esm_shims();
836
1205
  var CliError = class extends Error {
837
1206
  constructor(message) {
838
1207
  super(message || "");
@@ -884,15 +1253,15 @@ async function setupCommand(options) {
884
1253
  for (const tool of TOOLS) {
885
1254
  if (toolFilter && tool.id !== toolFilter) continue;
886
1255
  if (tool.skillOnly) continue;
887
- const paths = resolveConfigPaths(tool.configPaths);
888
- for (const configPath of paths) {
889
- const exists = fs4.existsSync(configPath);
890
- const parentExists = fs4.existsSync(path6.dirname(configPath));
1256
+ const paths = resolveConfigPaths(tool);
1257
+ for (const { filePath, configKey } of paths) {
1258
+ const exists = fs4.existsSync(filePath);
1259
+ const parentExists = fs4.existsSync(path7.dirname(filePath));
891
1260
  if (exists || parentExists) {
892
- const configured2 = exists && !!tool.configKey && isPiutConfigured(configPath, tool.configKey);
1261
+ const configured2 = exists && !!configKey && isPiutConfigured(filePath, configKey);
893
1262
  let staleKey = false;
894
- if (configured2 && tool.configKey) {
895
- const piutConfig = getPiutConfig(configPath, tool.configKey);
1263
+ if (configured2 && configKey) {
1264
+ const piutConfig = getPiutConfig(filePath, configKey);
896
1265
  if (piutConfig) {
897
1266
  const existingKey = extractKeyFromConfig(piutConfig);
898
1267
  if (existingKey && existingKey !== apiKey) {
@@ -906,7 +1275,8 @@ async function setupCommand(options) {
906
1275
  }
907
1276
  detected.push({
908
1277
  tool,
909
- configPath,
1278
+ configPath: filePath,
1279
+ resolvedConfigKey: configKey,
910
1280
  exists,
911
1281
  alreadyConfigured: configured2 && !staleKey,
912
1282
  staleKey
@@ -996,9 +1366,9 @@ async function setupCommand(options) {
996
1366
  }
997
1367
  if (quickSuccess) continue;
998
1368
  }
999
- if (tool.generateConfig && tool.configKey) {
1369
+ if (tool.generateConfig && det.resolvedConfigKey) {
1000
1370
  const serverConfig = tool.generateConfig(slug, apiKey);
1001
- mergeConfig(configPath, tool.configKey, serverConfig);
1371
+ mergeConfig(configPath, det.resolvedConfigKey, serverConfig);
1002
1372
  configured.push(tool.name);
1003
1373
  toolLine(tool.name, success("configured"), "\u2714");
1004
1374
  }
@@ -1026,7 +1396,7 @@ async function setupCommand(options) {
1026
1396
  }
1027
1397
  if (configured.length > 0) {
1028
1398
  const cwd = process.cwd();
1029
- const isProject2 = fs4.existsSync(path6.join(cwd, ".git")) || fs4.existsSync(path6.join(cwd, "package.json"));
1399
+ const isProject2 = fs4.existsSync(path7.join(cwd, ".git")) || fs4.existsSync(path7.join(cwd, "package.json"));
1030
1400
  if (isProject2) {
1031
1401
  const { serverUrl } = validationResult;
1032
1402
  writePiutConfig(cwd, { slug, apiKey, serverUrl });
@@ -1042,8 +1412,8 @@ async function setupCommand(options) {
1042
1412
  console.log(dim(" Verifying..."));
1043
1413
  for (const det of selected) {
1044
1414
  if (!configured.includes(det.tool.name)) continue;
1045
- if (!det.tool.configKey) continue;
1046
- const piutConfig = getPiutConfig(det.configPath, det.tool.configKey);
1415
+ if (!det.resolvedConfigKey) continue;
1416
+ const piutConfig = getPiutConfig(det.configPath, det.resolvedConfigKey);
1047
1417
  if (piutConfig) {
1048
1418
  toolLine(det.tool.name, success("config verified"), "\u2714");
1049
1419
  } else {
@@ -1084,51 +1454,20 @@ function isCommandAvailable(cmd) {
1084
1454
  }
1085
1455
 
1086
1456
  // src/commands/status.ts
1087
- import fs9 from "fs";
1088
- import os7 from "os";
1089
- import path12 from "path";
1457
+ init_esm_shims();
1458
+ import fs7 from "fs";
1459
+ import os6 from "os";
1460
+ import path10 from "path";
1090
1461
  import chalk3 from "chalk";
1091
1462
 
1092
1463
  // src/lib/brain-scanner.ts
1093
- import fs7 from "fs";
1094
- import path10 from "path";
1095
- import os5 from "os";
1464
+ init_esm_shims();
1465
+ import fs5 from "fs";
1466
+ import path8 from "path";
1467
+ import os4 from "os";
1096
1468
 
1097
1469
  // src/lib/file-types.ts
1098
- import path7 from "path";
1099
- var DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
1100
- ".pdf",
1101
- ".docx",
1102
- ".doc",
1103
- ".pptx",
1104
- ".pages",
1105
- ".key",
1106
- ".rtf",
1107
- ".odt",
1108
- ".odp",
1109
- ".eml",
1110
- ".mbox"
1111
- ]);
1112
- var PLAIN_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
1113
- ".md",
1114
- ".markdown",
1115
- ".txt",
1116
- ".csv",
1117
- ".xml",
1118
- ".html",
1119
- ".htm",
1120
- ".json",
1121
- ".yaml",
1122
- ".yml",
1123
- ".toml",
1124
- ".rst",
1125
- ".adoc",
1126
- ".tex",
1127
- ".ini",
1128
- ".cfg",
1129
- ".conf",
1130
- ".log"
1131
- ]);
1470
+ init_esm_shims();
1132
1471
  var AI_CONFIG_FILENAMES = /* @__PURE__ */ new Set([
1133
1472
  "CLAUDE.md",
1134
1473
  ".cursorrules",
@@ -1142,350 +1481,46 @@ var AI_CONFIG_FILENAMES = /* @__PURE__ */ new Set([
1142
1481
  "IDENTITY.md",
1143
1482
  "copilot-instructions.md"
1144
1483
  ]);
1145
- var PARSEABLE_EXTENSIONS = /* @__PURE__ */ new Set([
1146
- ...DOCUMENT_EXTENSIONS,
1147
- ...PLAIN_TEXT_EXTENSIONS
1148
- ]);
1149
- var EXCLUDED_EXTENSIONS = /* @__PURE__ */ new Set([
1150
- ".js",
1151
- ".ts",
1152
- ".jsx",
1153
- ".tsx",
1154
- ".mjs",
1155
- ".cjs",
1156
- ".py",
1157
- ".pyw",
1158
- ".pyi",
1159
- ".rs",
1160
- ".go",
1161
- ".java",
1162
- ".kt",
1163
- ".scala",
1164
- ".clj",
1165
- ".c",
1166
- ".cpp",
1167
- ".cc",
1168
- ".h",
1169
- ".hpp",
1170
- ".cs",
1171
- ".rb",
1172
- ".php",
1173
- ".swift",
1174
- ".m",
1175
- ".mm",
1176
- ".lua",
1177
- ".r",
1178
- ".jl",
1179
- ".zig",
1180
- ".nim",
1181
- ".v",
1182
- ".sh",
1183
- ".bash",
1184
- ".zsh",
1185
- ".fish",
1186
- ".ps1",
1187
- ".bat",
1188
- ".cmd",
1189
- ".sql",
1190
- ".graphql",
1191
- ".gql",
1192
- ".proto",
1193
- ".css",
1194
- ".scss",
1195
- ".sass",
1196
- ".less",
1197
- ".styl",
1198
- ".xls",
1199
- ".xlsx",
1200
- ".numbers",
1201
- ".sqlite",
1202
- ".db",
1203
- ".dat",
1204
- ".parquet",
1205
- ".avro",
1206
- ".png",
1207
- ".jpg",
1208
- ".jpeg",
1209
- ".gif",
1210
- ".svg",
1211
- ".ico",
1212
- ".webp",
1213
- ".bmp",
1214
- ".tiff",
1215
- ".heic",
1216
- ".mp3",
1217
- ".mp4",
1218
- ".wav",
1219
- ".aac",
1220
- ".flac",
1221
- ".ogg",
1222
- ".avi",
1223
- ".mov",
1224
- ".mkv",
1225
- ".wmv",
1226
- ".webm",
1227
- ".zip",
1228
- ".tar",
1229
- ".gz",
1230
- ".bz2",
1231
- ".xz",
1232
- ".7z",
1233
- ".rar",
1234
- ".dmg",
1235
- ".iso",
1236
- ".exe",
1237
- ".dll",
1238
- ".so",
1239
- ".dylib",
1240
- ".o",
1241
- ".a",
1242
- ".wasm",
1243
- ".class",
1244
- ".jar",
1245
- ".pyc",
1246
- ".pyo",
1247
- ".ttf",
1248
- ".otf",
1249
- ".woff",
1250
- ".woff2",
1251
- ".eot",
1252
- ".lock",
1253
- ".map",
1254
- ".min.js",
1255
- ".min.css"
1256
- ]);
1257
- function canParse(filename) {
1258
- return PARSEABLE_EXTENSIONS.has(path7.extname(filename).toLowerCase());
1259
- }
1260
- function isAiConfigFile(filename) {
1261
- return AI_CONFIG_FILENAMES.has(path7.basename(filename));
1262
- }
1263
- function getFileCategory(filename) {
1264
- const base = path7.basename(filename);
1265
- if (AI_CONFIG_FILENAMES.has(base)) return "config";
1266
- const ext = path7.extname(filename).toLowerCase();
1267
- if (DOCUMENT_EXTENSIONS.has(ext)) return "document";
1268
- if (PLAIN_TEXT_EXTENSIONS.has(ext)) return "text";
1269
- if (EXCLUDED_EXTENSIONS.has(ext)) return "excluded";
1270
- return "unknown";
1271
- }
1272
-
1273
- // src/lib/file-parsers.ts
1274
- import fs5 from "fs";
1275
- import path8 from "path";
1276
- var MAX_RAW_SIZE = 500 * 1024;
1277
- var MAX_EXTRACTED_TEXT = 100 * 1024;
1278
- function truncate(text) {
1279
- if (text.length <= MAX_EXTRACTED_TEXT) return text;
1280
- return text.slice(0, MAX_EXTRACTED_TEXT);
1281
- }
1282
- function stripHtml(html) {
1283
- return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<\/div>/gi, "\n").replace(/<\/li>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/\n{3,}/g, "\n\n").trim();
1284
- }
1285
- async function parsePdf(buffer) {
1286
- const { PDFParse } = await import("pdf-parse");
1287
- const parser = new PDFParse({ data: new Uint8Array(buffer) });
1288
- const result = await parser.getText();
1289
- return result.text;
1290
- }
1291
- async function parseDocx(buffer) {
1292
- const { extractRawText } = await import("mammoth");
1293
- const result = await extractRawText({ buffer });
1294
- return result.value;
1295
- }
1296
- async function parsePptx(buffer) {
1297
- const JSZip = (await import("jszip")).default;
1298
- const zip = await JSZip.loadAsync(buffer);
1299
- const texts = [];
1300
- const slideFiles = Object.keys(zip.files).filter((name) => /^ppt\/slides\/slide\d+\.xml$/i.test(name)).sort((a, b) => {
1301
- const numA = parseInt(a.match(/slide(\d+)/)?.[1] || "0");
1302
- const numB = parseInt(b.match(/slide(\d+)/)?.[1] || "0");
1303
- return numA - numB;
1304
- });
1305
- for (const slidePath of slideFiles) {
1306
- const xml = await zip.files[slidePath].async("string");
1307
- const matches = xml.match(/<a:t[^>]*>([^<]*)<\/a:t>/g);
1308
- if (matches) {
1309
- texts.push(matches.map((m) => m.replace(/<[^>]+>/g, "")).join(" "));
1310
- }
1311
- }
1312
- return texts.join("\n\n");
1313
- }
1314
- async function parseAppleDoc(buffer) {
1315
- const JSZip = (await import("jszip")).default;
1316
- const zip = await JSZip.loadAsync(buffer);
1317
- if (zip.files["preview.pdf"]) {
1318
- const pdfBuf = await zip.files["preview.pdf"].async("nodebuffer");
1319
- try {
1320
- return await parsePdf(pdfBuf);
1321
- } catch {
1322
- }
1323
- }
1324
- const texts = [];
1325
- for (const [name, file] of Object.entries(zip.files)) {
1326
- if (name.endsWith(".iwa") && !file.dir) {
1327
- try {
1328
- const buf = await file.async("nodebuffer");
1329
- const str = buf.toString("utf-8");
1330
- const readable = str.match(/[\x20-\x7E\xC0-\xFF]{4,}/g);
1331
- if (readable) {
1332
- texts.push(...readable.filter((s) => s.length > 10));
1333
- }
1334
- } catch {
1335
- }
1336
- }
1337
- }
1338
- return texts.join("\n\n");
1339
- }
1340
- async function parseRtf(buffer) {
1341
- const { fromString } = await import("@iarna/rtf-to-html");
1342
- return new Promise((resolve, reject) => {
1343
- fromString(buffer.toString("utf-8"), (err, html) => {
1344
- if (err) {
1345
- reject(err);
1346
- return;
1347
- }
1348
- resolve(stripHtml(html));
1349
- });
1350
- });
1351
- }
1352
- async function parseOpenDocument(buffer) {
1353
- const JSZip = (await import("jszip")).default;
1354
- const zip = await JSZip.loadAsync(buffer);
1355
- const contentXml = zip.files["content.xml"];
1356
- if (!contentXml) return "";
1357
- return stripHtml(await contentXml.async("string"));
1358
- }
1359
- function parseEml(buffer) {
1360
- const content = buffer.toString("utf-8");
1361
- const boundaryMatch = content.match(/boundary="?([^\s"]+)"?/i);
1362
- if (boundaryMatch) {
1363
- const parts = content.split(`--${boundaryMatch[1]}`);
1364
- for (const part of parts) {
1365
- if (/content-type:\s*text\/plain/i.test(part)) {
1366
- const bodyStart = part.indexOf("\n\n");
1367
- if (bodyStart !== -1) return part.slice(bodyStart + 2).trim();
1368
- }
1369
- }
1370
- }
1371
- const headerEnd = content.indexOf("\n\n");
1372
- if (headerEnd !== -1) {
1373
- const body = content.slice(headerEnd + 2);
1374
- if (!/<html/i.test(body.slice(0, 200))) return body.trim();
1375
- return stripHtml(body);
1376
- }
1377
- return content.trim();
1378
- }
1379
- function parseMbox(buffer) {
1380
- const content = buffer.toString("utf-8");
1381
- const messages = content.split(/^From /m).filter(Boolean);
1382
- const texts = [];
1383
- for (const msg of messages.slice(0, 50)) {
1384
- const headerEnd = msg.indexOf("\n\n");
1385
- if (headerEnd !== -1) {
1386
- const body = msg.slice(headerEnd + 2);
1387
- texts.push(!/<html/i.test(body.slice(0, 200)) ? body.trim() : stripHtml(body));
1388
- }
1389
- }
1390
- return texts.join("\n\n---\n\n");
1391
- }
1392
- async function extractTextFromFile(filePath) {
1393
- const ext = path8.extname(filePath).toLowerCase();
1394
- try {
1395
- const stat = fs5.statSync(filePath);
1396
- if (!stat.isFile()) return null;
1397
- if (stat.size > MAX_RAW_SIZE) return null;
1398
- } catch {
1399
- return null;
1400
- }
1401
- if (PLAIN_TEXT_EXTENSIONS.has(ext)) {
1402
- try {
1403
- return truncate(fs5.readFileSync(filePath, "utf-8"));
1404
- } catch {
1405
- return null;
1406
- }
1407
- }
1408
- if (!DOCUMENT_EXTENSIONS.has(ext)) return null;
1409
- try {
1410
- const buffer = fs5.readFileSync(filePath);
1411
- let text;
1412
- switch (ext) {
1413
- case ".pdf":
1414
- text = await parsePdf(buffer);
1415
- break;
1416
- case ".docx":
1417
- case ".doc":
1418
- text = await parseDocx(buffer);
1419
- break;
1420
- case ".pptx":
1421
- text = await parsePptx(buffer);
1422
- break;
1423
- case ".pages":
1424
- case ".key":
1425
- text = await parseAppleDoc(buffer);
1426
- break;
1427
- case ".rtf":
1428
- text = await parseRtf(buffer);
1429
- break;
1430
- case ".odt":
1431
- case ".odp":
1432
- text = await parseOpenDocument(buffer);
1433
- break;
1434
- case ".eml":
1435
- text = parseEml(buffer);
1436
- break;
1437
- case ".mbox":
1438
- text = parseMbox(buffer);
1439
- break;
1440
- default:
1441
- return null;
1442
- }
1443
- return truncate(text);
1444
- } catch {
1445
- return null;
1446
- }
1447
- }
1448
- function formatSize(bytes) {
1449
- if (bytes < 1024) return `${bytes} B`;
1450
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1451
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1452
- }
1453
1484
 
1454
- // src/lib/folder-tree.ts
1455
- import fs6 from "fs";
1456
- import path9 from "path";
1457
- import os4 from "os";
1485
+ // src/lib/brain-scanner.ts
1458
1486
  var home = os4.homedir();
1459
- function displayPath(p) {
1460
- return p.startsWith(home) ? "~" + p.slice(home.length) : p;
1461
- }
1462
- function groupFilesByFolder(files) {
1463
- const map = /* @__PURE__ */ new Map();
1464
- for (const file of files) {
1465
- const folder = file.folder;
1466
- if (!map.has(folder)) map.set(folder, []);
1467
- map.get(folder).push(file);
1468
- }
1469
- const results = [];
1470
- for (const [folderPath, folderFiles] of map) {
1471
- results.push({
1472
- path: folderPath,
1473
- displayPath: displayPath(folderPath),
1474
- files: folderFiles,
1475
- fileCount: folderFiles.length,
1476
- totalBytes: folderFiles.reduce((sum, f) => sum + f.sizeBytes, 0)
1477
- });
1478
- }
1479
- results.sort((a, b) => a.displayPath.localeCompare(b.displayPath));
1480
- return results;
1481
- }
1482
- function formatFolderChoice(folder) {
1483
- return `${folder.displayPath} \u2014 ${folder.fileCount} file${folder.fileCount === 1 ? "" : "s"} (${formatSize(folder.totalBytes)})`;
1484
- }
1485
- function formatSelectionSummary(folders) {
1486
- const totalFiles = folders.reduce((sum, f) => sum + f.fileCount, 0);
1487
- const totalBytes = folders.reduce((sum, f) => sum + f.totalBytes, 0);
1488
- return `${folders.length} folder${folders.length === 1 ? "" : "s"} selected (${totalFiles} file${totalFiles === 1 ? "" : "s"}, ${formatSize(totalBytes)})`;
1487
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
1488
+ "node_modules",
1489
+ ".git",
1490
+ "__pycache__",
1491
+ ".venv",
1492
+ "venv",
1493
+ "dist",
1494
+ "build",
1495
+ ".next",
1496
+ ".nuxt",
1497
+ ".output",
1498
+ ".Trash",
1499
+ "Library",
1500
+ ".cache",
1501
+ ".npm",
1502
+ ".yarn",
1503
+ ".pnpm-store",
1504
+ "Caches",
1505
+ "Cache",
1506
+ ".piut"
1507
+ ]);
1508
+ var SCAN_DOT_DIRS = /* @__PURE__ */ new Set([
1509
+ ".cursor",
1510
+ ".windsurf",
1511
+ ".github",
1512
+ ".zed",
1513
+ ".amazonq",
1514
+ ".vscode",
1515
+ ".gemini",
1516
+ ".openclaw",
1517
+ ".mcporter",
1518
+ ".paperclip"
1519
+ // .claude intentionally excluded — useful files collected by collectGlobalConfigFiles()
1520
+ ]);
1521
+ function shouldSkipDir(name) {
1522
+ if (name.startsWith(".") && !SCAN_DOT_DIRS.has(name)) return true;
1523
+ return SKIP_DIRS.has(name);
1489
1524
  }
1490
1525
  var SKIP_HOME_DIRS = /* @__PURE__ */ new Set([
1491
1526
  "Library",
@@ -1498,7 +1533,6 @@ var SKIP_HOME_DIRS = /* @__PURE__ */ new Set([
1498
1533
  ".Trash"
1499
1534
  ]);
1500
1535
  var INCLUDE_DOT_DIRS = /* @__PURE__ */ new Set([
1501
- ".claude",
1502
1536
  ".cursor",
1503
1537
  ".windsurf",
1504
1538
  ".openclaw",
@@ -1508,26 +1542,27 @@ var INCLUDE_DOT_DIRS = /* @__PURE__ */ new Set([
1508
1542
  ".gemini",
1509
1543
  ".mcporter",
1510
1544
  ".paperclip"
1545
+ // .claude excluded — useful files collected by collectGlobalConfigFiles()
1511
1546
  ]);
1512
1547
  function getDefaultScanDirs() {
1513
1548
  const dirs = [];
1514
1549
  try {
1515
- const entries = fs6.readdirSync(home, { withFileTypes: true });
1550
+ const entries = fs5.readdirSync(home, { withFileTypes: true });
1516
1551
  for (const entry of entries) {
1517
1552
  if (!entry.isDirectory()) continue;
1518
1553
  if (entry.name.startsWith(".") && !INCLUDE_DOT_DIRS.has(entry.name)) continue;
1519
1554
  if (SKIP_HOME_DIRS.has(entry.name)) continue;
1520
- dirs.push(path9.join(home, entry.name));
1555
+ dirs.push(path8.join(home, entry.name));
1521
1556
  }
1522
1557
  } catch {
1523
1558
  }
1524
- const cloudStorage = path9.join(home, "Library", "CloudStorage");
1559
+ const cloudStorage = path8.join(home, "Library", "CloudStorage");
1525
1560
  try {
1526
- if (fs6.existsSync(cloudStorage) && fs6.statSync(cloudStorage).isDirectory()) {
1527
- const entries = fs6.readdirSync(cloudStorage, { withFileTypes: true });
1561
+ if (fs5.existsSync(cloudStorage) && fs5.statSync(cloudStorage).isDirectory()) {
1562
+ const entries = fs5.readdirSync(cloudStorage, { withFileTypes: true });
1528
1563
  for (const entry of entries) {
1529
1564
  if (!entry.isDirectory()) continue;
1530
- const fullPath = path9.join(cloudStorage, entry.name);
1565
+ const fullPath = path8.join(cloudStorage, entry.name);
1531
1566
  if (!dirs.includes(fullPath)) {
1532
1567
  dirs.push(fullPath);
1533
1568
  }
@@ -1538,64 +1573,23 @@ function getDefaultScanDirs() {
1538
1573
  if (dirs.length === 0) dirs.push(home);
1539
1574
  return dirs;
1540
1575
  }
1541
-
1542
- // src/lib/brain-scanner.ts
1543
- var home2 = os5.homedir();
1544
- var SKIP_DIRS = /* @__PURE__ */ new Set([
1545
- "node_modules",
1546
- ".git",
1547
- "__pycache__",
1548
- ".venv",
1549
- "venv",
1550
- "dist",
1551
- "build",
1552
- ".next",
1553
- ".nuxt",
1554
- ".output",
1555
- ".Trash",
1556
- "Library",
1557
- ".cache",
1558
- ".npm",
1559
- ".yarn",
1560
- ".pnpm-store",
1561
- "Caches",
1562
- "Cache",
1563
- ".piut"
1564
- ]);
1565
- var SCAN_DOT_DIRS = /* @__PURE__ */ new Set([
1566
- ".claude",
1567
- ".cursor",
1568
- ".windsurf",
1569
- ".github",
1570
- ".zed",
1571
- ".amazonq",
1572
- ".vscode",
1573
- ".gemini",
1574
- ".openclaw",
1575
- ".mcporter",
1576
- ".paperclip"
1577
- ]);
1578
- function shouldSkipDir(name) {
1579
- if (name.startsWith(".") && !SCAN_DOT_DIRS.has(name)) return true;
1580
- return SKIP_DIRS.has(name);
1581
- }
1582
1576
  function isProject(dirPath) {
1583
- 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"));
1577
+ 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"));
1584
1578
  }
1585
1579
  function buildProjectInfo(projectPath) {
1586
- const hasPkgJson = fs7.existsSync(path10.join(projectPath, "package.json"));
1580
+ const hasPkgJson = fs5.existsSync(path8.join(projectPath, "package.json"));
1587
1581
  let description = "";
1588
1582
  if (hasPkgJson) {
1589
1583
  try {
1590
- const pkg = JSON.parse(fs7.readFileSync(path10.join(projectPath, "package.json"), "utf-8"));
1584
+ const pkg = JSON.parse(fs5.readFileSync(path8.join(projectPath, "package.json"), "utf-8"));
1591
1585
  description = pkg.description || "";
1592
1586
  } catch {
1593
1587
  }
1594
1588
  }
1595
- const readmePath = path10.join(projectPath, "README.md");
1596
- if (!description && fs7.existsSync(readmePath)) {
1589
+ const readmePath = path8.join(projectPath, "README.md");
1590
+ if (!description && fs5.existsSync(readmePath)) {
1597
1591
  try {
1598
- const content = fs7.readFileSync(readmePath, "utf-8");
1592
+ const content = fs5.readFileSync(readmePath, "utf-8");
1599
1593
  const lines = content.split("\n");
1600
1594
  let foundHeading = false;
1601
1595
  for (const line of lines) {
@@ -1612,15 +1606,15 @@ function buildProjectInfo(projectPath) {
1612
1606
  }
1613
1607
  }
1614
1608
  return {
1615
- name: path10.basename(projectPath),
1609
+ name: path8.basename(projectPath),
1616
1610
  path: projectPath,
1617
1611
  description,
1618
- hasClaudeMd: fs7.existsSync(path10.join(projectPath, "CLAUDE.md")) || fs7.existsSync(path10.join(projectPath, ".claude", "rules")),
1619
- hasCursorRules: fs7.existsSync(path10.join(projectPath, ".cursorrules")) || fs7.existsSync(path10.join(projectPath, ".cursor", "rules")),
1620
- hasWindsurfRules: fs7.existsSync(path10.join(projectPath, ".windsurfrules")) || fs7.existsSync(path10.join(projectPath, ".windsurf", "rules")),
1621
- hasCopilotInstructions: fs7.existsSync(path10.join(projectPath, ".github", "copilot-instructions.md")) || fs7.existsSync(path10.join(projectPath, ".github", "instructions")),
1622
- hasConventionsMd: fs7.existsSync(path10.join(projectPath, "CONVENTIONS.md")) || fs7.existsSync(path10.join(projectPath, ".amazonq", "rules")),
1623
- hasZedRules: fs7.existsSync(path10.join(projectPath, ".rules"))
1612
+ hasClaudeMd: fs5.existsSync(path8.join(projectPath, "CLAUDE.md")) || fs5.existsSync(path8.join(projectPath, ".claude", "rules")),
1613
+ hasCursorRules: fs5.existsSync(path8.join(projectPath, ".cursorrules")) || fs5.existsSync(path8.join(projectPath, ".cursor", "rules")),
1614
+ hasWindsurfRules: fs5.existsSync(path8.join(projectPath, ".windsurfrules")) || fs5.existsSync(path8.join(projectPath, ".windsurf", "rules")),
1615
+ hasCopilotInstructions: fs5.existsSync(path8.join(projectPath, ".github", "copilot-instructions.md")) || fs5.existsSync(path8.join(projectPath, ".github", "instructions")),
1616
+ hasConventionsMd: fs5.existsSync(path8.join(projectPath, "CONVENTIONS.md")) || fs5.existsSync(path8.join(projectPath, ".amazonq", "rules")),
1617
+ hasZedRules: fs5.existsSync(path8.join(projectPath, ".rules"))
1624
1618
  };
1625
1619
  }
1626
1620
  var MAX_PROJECT_DEPTH = 4;
@@ -1630,17 +1624,17 @@ function detectProjects(scanDirs, onProgress) {
1630
1624
  function walk(dir, depth) {
1631
1625
  if (depth > MAX_PROJECT_DEPTH) return;
1632
1626
  try {
1633
- const items = fs7.readdirSync(dir, { withFileTypes: true });
1627
+ const items = fs5.readdirSync(dir, { withFileTypes: true });
1634
1628
  for (const item of items) {
1635
1629
  if (!item.isDirectory()) continue;
1636
1630
  if (shouldSkipDir(item.name)) continue;
1637
- const fullPath = path10.join(dir, item.name);
1631
+ const fullPath = path8.join(dir, item.name);
1638
1632
  if (seen.has(fullPath)) continue;
1639
1633
  seen.add(fullPath);
1640
1634
  if (isProject(fullPath)) {
1641
1635
  const info = buildProjectInfo(fullPath);
1642
1636
  projects.push(info);
1643
- onProgress?.({ phase: "projects", message: `${info.name} (${fullPath.replace(home2, "~")})` });
1637
+ onProgress?.({ phase: "projects", message: `${info.name} (${fullPath})` });
1644
1638
  } else {
1645
1639
  walk(fullPath, depth + 1);
1646
1640
  }
@@ -1654,36 +1648,41 @@ function detectProjects(scanDirs, onProgress) {
1654
1648
  return projects;
1655
1649
  }
1656
1650
  var MAX_CONFIG_SIZE = 100 * 1024;
1657
- function collectConfigFiles(projects, onProgress) {
1651
+ var MAX_BRAIN_INPUT_BYTES = 1e6;
1652
+ function collectGlobalConfigFiles(onProgress) {
1658
1653
  const configs = [];
1659
1654
  const globalPaths = [
1660
- path10.join(home2, ".claude", "MEMORY.md"),
1661
- path10.join(home2, ".claude", "CLAUDE.md"),
1662
- path10.join(home2, ".openclaw", "workspace", "SOUL.md"),
1663
- path10.join(home2, ".openclaw", "workspace", "MEMORY.md"),
1664
- path10.join(home2, ".gemini", "MEMORY.md"),
1665
- path10.join(home2, ".paperclip", "IDENTITY.md")
1655
+ path8.join(home, ".claude", "MEMORY.md"),
1656
+ path8.join(home, ".claude", "CLAUDE.md"),
1657
+ path8.join(home, ".openclaw", "workspace", "SOUL.md"),
1658
+ path8.join(home, ".openclaw", "workspace", "MEMORY.md"),
1659
+ path8.join(home, ".gemini", "MEMORY.md"),
1660
+ path8.join(home, ".paperclip", "IDENTITY.md")
1666
1661
  ];
1667
1662
  for (const gp of globalPaths) {
1668
1663
  try {
1669
- const stat = fs7.statSync(gp);
1664
+ const stat = fs5.statSync(gp);
1670
1665
  if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
1671
- const content = fs7.readFileSync(gp, "utf-8");
1666
+ const content = fs5.readFileSync(gp, "utf-8");
1672
1667
  if (content.trim()) {
1673
- const name = `~/${path10.relative(home2, gp)}`;
1668
+ const name = path8.relative(home, gp);
1674
1669
  configs.push({ name, content });
1675
1670
  onProgress?.({ phase: "configs", message: name });
1676
1671
  }
1677
1672
  } catch {
1678
1673
  }
1679
1674
  }
1675
+ return configs;
1676
+ }
1677
+ function collectProjectConfigFiles(projects, onProgress) {
1678
+ const configs = [];
1680
1679
  for (const project of projects) {
1681
1680
  for (const fileName of AI_CONFIG_FILENAMES) {
1682
- const filePath = path10.join(project.path, fileName);
1681
+ const filePath = path8.join(project.path, fileName);
1683
1682
  try {
1684
- const stat = fs7.statSync(filePath);
1683
+ const stat = fs5.statSync(filePath);
1685
1684
  if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
1686
- const content = fs7.readFileSync(filePath, "utf-8");
1685
+ const content = fs5.readFileSync(filePath, "utf-8");
1687
1686
  if (content.trim()) {
1688
1687
  const name = `${project.name}/${fileName}`;
1689
1688
  configs.push({ name, content });
@@ -1692,11 +1691,11 @@ function collectConfigFiles(projects, onProgress) {
1692
1691
  } catch {
1693
1692
  }
1694
1693
  }
1695
- const pkgPath = path10.join(project.path, "package.json");
1694
+ const pkgPath = path8.join(project.path, "package.json");
1696
1695
  try {
1697
- const stat = fs7.statSync(pkgPath);
1696
+ const stat = fs5.statSync(pkgPath);
1698
1697
  if (stat.isFile() && stat.size <= MAX_CONFIG_SIZE) {
1699
- const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
1698
+ const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
1700
1699
  const summary = JSON.stringify({ name: pkg.name, description: pkg.description }, null, 2);
1701
1700
  configs.push({ name: `${project.name}/package.json`, content: summary });
1702
1701
  onProgress?.({ phase: "configs", message: `${project.name}/package.json` });
@@ -1706,114 +1705,26 @@ function collectConfigFiles(projects, onProgress) {
1706
1705
  }
1707
1706
  return configs;
1708
1707
  }
1709
- var MAX_SCAN_DEPTH = 6;
1710
- var MAX_FILES = 500;
1711
- async function scanFilesInDirs(dirs, onProgress) {
1712
- const files = [];
1713
- const seen = /* @__PURE__ */ new Set();
1714
- function walk(dir, depth) {
1715
- if (depth > MAX_SCAN_DEPTH) return [];
1716
- const found = [];
1717
- try {
1718
- const items = fs7.readdirSync(dir, { withFileTypes: true });
1719
- for (const item of items) {
1720
- if (item.isDirectory()) {
1721
- if (!shouldSkipDir(item.name)) {
1722
- found.push(...walk(path10.join(dir, item.name), depth + 1));
1723
- }
1724
- } else if (item.isFile()) {
1725
- if (canParse(item.name) && !isAiConfigFile(item.name)) {
1726
- const fullPath = path10.join(dir, item.name);
1727
- if (!seen.has(fullPath)) {
1728
- seen.add(fullPath);
1729
- found.push(fullPath);
1730
- }
1731
- }
1732
- }
1733
- }
1734
- } catch {
1735
- }
1736
- return found;
1737
- }
1738
- const allPaths = [];
1739
- for (const dir of dirs) {
1740
- onProgress?.({ phase: "scanning", message: displayPath(dir) });
1741
- allPaths.push(...walk(dir, 0));
1742
- if (allPaths.length > MAX_FILES) break;
1743
- }
1744
- const pathsToProcess = allPaths.slice(0, MAX_FILES);
1745
- for (const filePath of pathsToProcess) {
1746
- try {
1747
- const stat = fs7.statSync(filePath);
1748
- onProgress?.({ phase: "parsing", message: displayPath(filePath) });
1749
- const content = await extractTextFromFile(filePath);
1750
- if (content && content.trim()) {
1751
- const category = getFileCategory(filePath);
1752
- files.push({
1753
- path: filePath,
1754
- displayPath: displayPath(filePath),
1755
- content,
1756
- format: category === "document" ? path10.extname(filePath).slice(1) : "text",
1757
- sizeBytes: stat.size,
1758
- folder: path10.dirname(filePath)
1759
- });
1760
- }
1761
- } catch {
1762
- }
1763
- }
1764
- return files;
1765
- }
1766
- async function scanFolders(dirs, onProgress) {
1767
- const allFiles = await scanFilesInDirs(dirs, onProgress);
1768
- const folders = groupFilesByFolder(allFiles);
1769
- const projects = detectProjects(dirs, onProgress);
1770
- const configFiles = collectConfigFiles(projects, onProgress);
1771
- const totalFiles = allFiles.length;
1772
- const totalBytes = allFiles.reduce((sum, f) => sum + f.sizeBytes, 0);
1773
- return { folders, configFiles, projects, allFiles, totalFiles, totalBytes };
1774
- }
1775
- function buildBrainInput(scanResult, selectedFolderPaths) {
1776
- const selectedSet = new Set(selectedFolderPaths);
1777
- const selectedFiles = scanResult.allFiles.filter((f) => selectedSet.has(f.folder));
1778
- const folderTree = [];
1779
- for (const folder of scanResult.folders) {
1780
- if (selectedSet.has(folder.path)) {
1781
- folderTree.push(`${folder.displayPath}/ (${folder.fileCount} files)`);
1782
- }
1783
- }
1784
- const personalDocuments = selectedFiles.map((f) => ({
1785
- name: f.displayPath,
1786
- content: f.content,
1787
- format: f.format
1788
- }));
1789
- return {
1790
- summary: {
1791
- folders: folderTree,
1792
- projects: scanResult.projects.map((p) => ({
1793
- name: p.name,
1794
- path: p.path.replace(home2, "~"),
1795
- description: p.description
1796
- })),
1797
- configFiles: scanResult.configFiles,
1798
- recentDocuments: [],
1799
- personalDocuments
1800
- }
1801
- };
1802
- }
1803
1708
  function scanForProjects(folders) {
1804
1709
  const scanDirs = folders || getDefaultScanDirs();
1805
1710
  return detectProjects(scanDirs);
1806
1711
  }
1712
+ function formatSize(bytes) {
1713
+ if (bytes < 1024) return `${bytes} B`;
1714
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1715
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1716
+ }
1807
1717
 
1808
1718
  // src/lib/store.ts
1809
- import fs8 from "fs";
1810
- import path11 from "path";
1811
- import os6 from "os";
1812
- var CONFIG_DIR = path11.join(os6.homedir(), ".piut");
1813
- var CONFIG_FILE2 = path11.join(CONFIG_DIR, "config.json");
1719
+ init_esm_shims();
1720
+ import fs6 from "fs";
1721
+ import path9 from "path";
1722
+ import os5 from "os";
1723
+ var CONFIG_DIR = path9.join(os5.homedir(), ".piut");
1724
+ var CONFIG_FILE2 = path9.join(CONFIG_DIR, "config.json");
1814
1725
  function readStore() {
1815
1726
  try {
1816
- const raw = fs8.readFileSync(CONFIG_FILE2, "utf-8");
1727
+ const raw = fs6.readFileSync(CONFIG_FILE2, "utf-8");
1817
1728
  return JSON.parse(raw);
1818
1729
  } catch {
1819
1730
  return {};
@@ -1822,13 +1733,13 @@ function readStore() {
1822
1733
  function updateStore(updates) {
1823
1734
  const config = readStore();
1824
1735
  const updated = { ...config, ...updates };
1825
- fs8.mkdirSync(CONFIG_DIR, { recursive: true });
1826
- fs8.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
1736
+ fs6.mkdirSync(CONFIG_DIR, { recursive: true });
1737
+ fs6.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
1827
1738
  return updated;
1828
1739
  }
1829
1740
  function clearStore() {
1830
1741
  try {
1831
- fs8.unlinkSync(CONFIG_FILE2);
1742
+ fs6.unlinkSync(CONFIG_FILE2);
1832
1743
  } catch {
1833
1744
  }
1834
1745
  }
@@ -1845,7 +1756,7 @@ var PIUT_FILES = [
1845
1756
  ];
1846
1757
  function hasPiutReference(filePath) {
1847
1758
  try {
1848
- const content = fs9.readFileSync(filePath, "utf-8");
1759
+ const content = fs7.readFileSync(filePath, "utf-8");
1849
1760
  return content.includes("p\u0131ut Context") || content.includes("piut Context");
1850
1761
  } catch {
1851
1762
  return false;
@@ -1897,17 +1808,17 @@ async function statusCommand(options = {}) {
1897
1808
  await verifyStatus();
1898
1809
  return;
1899
1810
  }
1900
- const thisHostname = os7.hostname();
1811
+ const thisHostname = os6.hostname();
1901
1812
  const thisMachineId = getMachineId2();
1902
1813
  console.log(` AI tools on this machine ${dim(`(${thisHostname})`)}:`);
1903
1814
  console.log();
1904
1815
  let foundAny = false;
1905
1816
  for (const tool of TOOLS) {
1906
- const paths = resolveConfigPaths(tool.configPaths);
1907
- for (const configPath of paths) {
1908
- if (!fs9.existsSync(configPath)) continue;
1817
+ const paths = resolveConfigPaths(tool);
1818
+ for (const { filePath, configKey } of paths) {
1819
+ if (!fs7.existsSync(filePath)) continue;
1909
1820
  foundAny = true;
1910
- const configured = isPiutConfigured(configPath, tool.configKey);
1821
+ const configured = isPiutConfigured(filePath, configKey);
1911
1822
  if (configured) {
1912
1823
  toolLine(tool.name, success("connected"), "\u2714");
1913
1824
  } else {
@@ -1928,8 +1839,8 @@ async function statusCommand(options = {}) {
1928
1839
  for (const project of projects) {
1929
1840
  const connectedFiles = [];
1930
1841
  for (const file of PIUT_FILES) {
1931
- const absPath = path12.join(project.path, file);
1932
- if (fs9.existsSync(absPath) && hasPiutReference(absPath)) {
1842
+ const absPath = path10.join(project.path, file);
1843
+ if (fs7.existsSync(absPath) && hasPiutReference(absPath)) {
1933
1844
  connectedFiles.push(file);
1934
1845
  }
1935
1846
  }
@@ -2000,19 +1911,19 @@ async function verifyStatus() {
2000
1911
  console.log();
2001
1912
  console.log(" Tool Configurations");
2002
1913
  for (const tool of TOOLS) {
2003
- const paths = resolveConfigPaths(tool.configPaths);
2004
- for (const configPath of paths) {
2005
- if (!fs9.existsSync(configPath)) continue;
2006
- const piutConfig = getPiutConfig(configPath, tool.configKey);
1914
+ const paths = resolveConfigPaths(tool);
1915
+ for (const { filePath, configKey } of paths) {
1916
+ if (!fs7.existsSync(filePath)) continue;
1917
+ const piutConfig = getPiutConfig(filePath, configKey);
2007
1918
  if (!piutConfig) {
2008
1919
  toolLine(tool.name, dim("installed, not connected"), "\u25CB");
2009
1920
  break;
2010
1921
  }
2011
- const configKey = extractKeyFromConfig(piutConfig);
2012
- if (configKey && configKey === store.apiKey) {
1922
+ const extractedKey = extractKeyFromConfig(piutConfig);
1923
+ if (extractedKey && extractedKey === store.apiKey) {
2013
1924
  toolLine(tool.name, success("key matches"), "\u2714");
2014
- } else if (configKey) {
2015
- const masked = configKey.slice(0, 6) + "...";
1925
+ } else if (extractedKey) {
1926
+ const masked = extractedKey.slice(0, 6) + "...";
2016
1927
  toolLine(tool.name, chalk3.red(`key STALE (${masked})`), "\u2717");
2017
1928
  issues++;
2018
1929
  } else {
@@ -2045,17 +1956,18 @@ async function verifyStatus() {
2045
1956
  }
2046
1957
 
2047
1958
  // src/commands/remove.ts
2048
- import fs10 from "fs";
1959
+ init_esm_shims();
1960
+ import fs8 from "fs";
2049
1961
  import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
2050
1962
  async function removeCommand() {
2051
1963
  banner();
2052
1964
  const configured = [];
2053
1965
  for (const tool of TOOLS) {
2054
1966
  if (!tool.configKey) continue;
2055
- const paths = resolveConfigPaths(tool.configPaths);
2056
- for (const configPath of paths) {
2057
- if (fs10.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
2058
- configured.push({ tool, configPath });
1967
+ const paths = resolveConfigPaths(tool);
1968
+ for (const { filePath, configKey } of paths) {
1969
+ if (fs8.existsSync(filePath) && isPiutConfigured(filePath, configKey)) {
1970
+ configured.push({ tool, configPath: filePath, resolvedConfigKey: configKey });
2059
1971
  break;
2060
1972
  }
2061
1973
  }
@@ -2084,9 +1996,9 @@ async function removeCommand() {
2084
1996
  if (!proceed) return;
2085
1997
  console.log();
2086
1998
  const removedNames = [];
2087
- for (const { tool, configPath } of selected) {
2088
- if (!tool.configKey) continue;
2089
- const removed = removeFromConfig(configPath, tool.configKey);
1999
+ for (const { tool, configPath, resolvedConfigKey } of selected) {
2000
+ if (!resolvedConfigKey) continue;
2001
+ const removed = removeFromConfig(configPath, resolvedConfigKey);
2090
2002
  if (removed) {
2091
2003
  removedNames.push(tool.name);
2092
2004
  toolLine(tool.name, success("removed"), "\u2714");
@@ -2105,14 +2017,19 @@ async function removeCommand() {
2105
2017
  }
2106
2018
 
2107
2019
  // src/commands/build.ts
2108
- import { select as select2, checkbox as checkbox3, input as input2, confirm as confirm3 } from "@inquirer/prompts";
2020
+ init_esm_shims();
2021
+ import { confirm as confirm3 } from "@inquirer/prompts";
2109
2022
  import chalk5 from "chalk";
2110
- import os8 from "os";
2023
+ import os7 from "os";
2111
2024
 
2112
2025
  // src/lib/auth.ts
2026
+ init_esm_shims();
2113
2027
  import { select, input, password as password2 } from "@inquirer/prompts";
2114
2028
  import { exec } from "child_process";
2029
+ import http from "http";
2030
+ import crypto3 from "crypto";
2115
2031
  import chalk4 from "chalk";
2032
+ var API_BASE4 = process.env.PIUT_API_BASE || "https://piut.com";
2116
2033
  function openBrowser(url) {
2117
2034
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
2118
2035
  exec(`${cmd} "${url}"`);
@@ -2151,12 +2068,101 @@ async function pasteKeyFlow() {
2151
2068
  return { apiKey, validation };
2152
2069
  }
2153
2070
  async function browserFlow() {
2154
- const url = "https://piut.com/dashboard/keys";
2155
- console.log(dim(` Opening ${brand(url)}...`));
2156
- openBrowser(url);
2157
- console.log(dim(" Copy your API key from the dashboard, then paste it here."));
2158
- console.log();
2159
- return pasteKeyFlow();
2071
+ const state = crypto3.randomBytes(16).toString("hex");
2072
+ return new Promise((resolve, reject) => {
2073
+ let settled = false;
2074
+ const server = http.createServer((req, res) => {
2075
+ const url = new URL(req.url, `http://localhost`);
2076
+ if (url.pathname !== "/callback") {
2077
+ res.writeHead(404);
2078
+ res.end();
2079
+ return;
2080
+ }
2081
+ const key = url.searchParams.get("key");
2082
+ const returnedState = url.searchParams.get("state");
2083
+ if (returnedState !== state) {
2084
+ res.writeHead(400, { "Content-Type": "text/html" });
2085
+ res.end(errorPage("State mismatch. Please try again from the CLI."));
2086
+ cleanup();
2087
+ if (!settled) {
2088
+ settled = true;
2089
+ reject(new CliError("Browser auth state mismatch"));
2090
+ }
2091
+ return;
2092
+ }
2093
+ if (!key || !key.startsWith("pb_")) {
2094
+ res.writeHead(400, { "Content-Type": "text/html" });
2095
+ res.end(errorPage("No valid API key received."));
2096
+ cleanup();
2097
+ if (!settled) {
2098
+ settled = true;
2099
+ reject(new CliError("No API key received from browser"));
2100
+ }
2101
+ return;
2102
+ }
2103
+ res.writeHead(200, { "Content-Type": "text/html" });
2104
+ res.end(successPage());
2105
+ cleanup();
2106
+ validateKey(key).then((validation) => {
2107
+ if (!settled) {
2108
+ settled = true;
2109
+ resolve({ apiKey: key, validation });
2110
+ }
2111
+ }).catch((err) => {
2112
+ if (!settled) {
2113
+ settled = true;
2114
+ reject(err);
2115
+ }
2116
+ });
2117
+ });
2118
+ const timer = setTimeout(() => {
2119
+ cleanup();
2120
+ if (!settled) {
2121
+ settled = true;
2122
+ reject(new CliError("Browser login timed out after 2 minutes. Please try again."));
2123
+ }
2124
+ }, 12e4);
2125
+ function cleanup() {
2126
+ clearTimeout(timer);
2127
+ server.close();
2128
+ }
2129
+ server.listen(0, "127.0.0.1", () => {
2130
+ const { port } = server.address();
2131
+ const authUrl = `${API_BASE4}/cli/auth?port=${port}&state=${state}`;
2132
+ console.log(dim(` Opening ${brand("piut.com")} in your browser...`));
2133
+ openBrowser(authUrl);
2134
+ console.log(dim(" Waiting for browser authorization..."));
2135
+ });
2136
+ server.on("error", (err) => {
2137
+ cleanup();
2138
+ if (!settled) {
2139
+ settled = true;
2140
+ reject(new CliError(`Failed to start callback server: ${err.message}`));
2141
+ }
2142
+ });
2143
+ });
2144
+ }
2145
+ function successPage() {
2146
+ return `<!DOCTYPE html>
2147
+ <html><head><title>p\u0131ut CLI</title></head>
2148
+ <body style="background:#0a0a0a;color:white;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
2149
+ <div style="text-align:center">
2150
+ <div style="font-size:48px;color:#4ade80;margin-bottom:16px">&#10003;</div>
2151
+ <h2 style="margin:0 0 8px">CLI Authorized</h2>
2152
+ <p style="color:#a3a3a3;margin:0">You can close this tab and return to your terminal.</p>
2153
+ </div>
2154
+ </body></html>`;
2155
+ }
2156
+ function errorPage(message) {
2157
+ return `<!DOCTYPE html>
2158
+ <html><head><title>p\u0131ut CLI</title></head>
2159
+ <body style="background:#0a0a0a;color:white;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
2160
+ <div style="text-align:center">
2161
+ <div style="font-size:48px;color:#f87171;margin-bottom:16px">&#10007;</div>
2162
+ <h2 style="margin:0 0 8px">Authorization Failed</h2>
2163
+ <p style="color:#a3a3a3;margin:0">${message}</p>
2164
+ </div>
2165
+ </body></html>`;
2160
2166
  }
2161
2167
  async function promptLogin() {
2162
2168
  const method = await select({
@@ -2170,7 +2176,7 @@ async function promptLogin() {
2170
2176
  {
2171
2177
  name: "Log in with browser",
2172
2178
  value: "browser",
2173
- description: "Open piut.com to get your API key"
2179
+ description: "Sign in via piut.com (opens browser)"
2174
2180
  },
2175
2181
  {
2176
2182
  name: "Paste API key",
@@ -2222,66 +2228,53 @@ async function resolveApiKeyWithResult(keyOption) {
2222
2228
  async function buildCommand(options) {
2223
2229
  banner();
2224
2230
  const { apiKey, serverUrl } = await resolveApiKeyWithResult(options.key);
2225
- let scanDirs;
2226
- if (options.folders) {
2227
- scanDirs = options.folders.split(",").map((f) => expandPath(f.trim()));
2228
- } else {
2229
- 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"));
2230
- console.log();
2231
- console.log(dim(" This is a local scan only \u2014 no files leave your machine"));
2232
- console.log(dim(" until you review and explicitly approve."));
2233
- console.log();
2234
- scanDirs = await selectFolders();
2235
- }
2236
- if (scanDirs.length === 0) {
2237
- console.log(chalk5.yellow(" No folders selected."));
2238
- return;
2239
- }
2231
+ 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"));
2240
2232
  console.log();
2241
- console.log(dim(" Scanning locally \u2014 no data is shared..."));
2233
+ console.log(dim(" Scanning for AI config files..."));
2242
2234
  console.log();
2243
- let fileCount = 0;
2235
+ const cwd = process.cwd();
2236
+ const homeDirs = getDefaultScanDirs();
2237
+ const allScanDirs = [cwd, ...homeDirs.filter((d) => !d.startsWith(cwd) && !cwd.startsWith(d))];
2244
2238
  const onProgress = (progress) => {
2245
- if (progress.phase === "scanning") {
2246
- console.log(dim(` ${progress.message}`));
2247
- } else if (progress.phase === "parsing") {
2248
- fileCount++;
2249
- console.log(dim(` [${fileCount}] ${progress.message}`));
2250
- } else if (progress.phase === "projects") {
2239
+ if (progress.phase === "projects") {
2251
2240
  console.log(dim(` [project] ${progress.message}`));
2252
2241
  } else if (progress.phase === "configs") {
2253
2242
  console.log(dim(` [config] ${progress.message}`));
2254
2243
  }
2255
2244
  };
2256
- const scanResult = await scanFolders(scanDirs, onProgress);
2245
+ const projects = detectProjects(allScanDirs, onProgress);
2246
+ const globalConfigs = collectGlobalConfigFiles(onProgress);
2247
+ const projectConfigs = collectProjectConfigFiles(projects, onProgress);
2248
+ const allConfigs = [...globalConfigs, ...projectConfigs];
2257
2249
  console.log();
2258
- console.log(success(` \u2713 Scan complete: ${scanResult.totalFiles} files found across ${scanResult.folders.length} folders (local only)`));
2259
- console.log();
2260
- if (scanResult.totalFiles === 0 && scanResult.configFiles.length === 0) {
2261
- console.log(chalk5.yellow(" No parseable files found in the selected folders."));
2262
- console.log(dim(" Try scanning a different directory, or use --folders to specify paths."));
2250
+ if (allConfigs.length > 0) {
2251
+ console.log(success(` \u2713 Found ${allConfigs.length} config file${allConfigs.length === 1 ? "" : "s"} in ${projects.length} project${projects.length === 1 ? "" : "s"}`));
2252
+ } else {
2253
+ console.log(dim(" No AI config files found."));
2254
+ }
2255
+ const totalBytes = allConfigs.reduce((sum, c) => sum + Buffer.byteLength(c.content, "utf-8"), 0);
2256
+ const totalFiles = allConfigs.length;
2257
+ if (totalFiles === 0) {
2258
+ console.log();
2259
+ console.log(chalk5.yellow(" No config files found to build your brain."));
2260
+ console.log(dim(" Add AI config files (CLAUDE.md, .cursorrules, etc.) to your projects,"));
2261
+ console.log(dim(" or upload documents via: piut vault upload <file>"));
2263
2262
  console.log();
2264
2263
  return;
2265
2264
  }
2266
- let selectedFolderPaths;
2267
- if (options.yes || scanResult.folders.length === 0) {
2268
- selectedFolderPaths = scanResult.folders.map((f) => f.path);
2269
- } else {
2270
- selectedFolderPaths = await reviewFolders(scanResult);
2271
- }
2272
- if (selectedFolderPaths.length === 0 && scanResult.configFiles.length === 0) {
2273
- console.log(chalk5.yellow(" No folders selected."));
2265
+ if (totalBytes > MAX_BRAIN_INPUT_BYTES) {
2266
+ console.log();
2267
+ console.log(chalk5.yellow(` Total data: ${formatSize(totalBytes)} exceeds the 1MB limit.`));
2268
+ console.log();
2274
2269
  return;
2275
2270
  }
2276
- const selectedFolders = scanResult.folders.filter((f) => selectedFolderPaths.includes(f.path));
2277
- const totalSelectedFiles = selectedFolders.reduce((sum, f) => sum + f.fileCount, 0) + scanResult.configFiles.length;
2278
2271
  if (!options.yes) {
2279
2272
  console.log();
2280
2273
  console.log(dim(" \u2501\u2501\u2501 READY TO BUILD \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\u2501"));
2281
2274
  console.log();
2282
- console.log(dim(` ${totalSelectedFiles} files will be sent to p\u0131ut and processed by Claude`));
2283
- console.log(dim(" Sonnet to design your brain. File contents are used for"));
2284
- console.log(dim(" brain generation only and are not retained."));
2275
+ console.log(dim(` ${totalFiles} file${totalFiles === 1 ? "" : "s"} (${formatSize(totalBytes)}) will be processed by Claude Sonnet to design your brain.`));
2276
+ console.log(dim(" File contents are used for brain generation only and"));
2277
+ console.log(dim(` are not retained by p\u0131ut.`));
2285
2278
  console.log();
2286
2279
  console.log(dim(` Privacy policy: ${brand("https://piut.com/privacy")}`));
2287
2280
  console.log();
@@ -2296,7 +2289,20 @@ async function buildCommand(options) {
2296
2289
  return;
2297
2290
  }
2298
2291
  }
2299
- const brainInput = buildBrainInput(scanResult, selectedFolderPaths);
2292
+ const home2 = os7.homedir();
2293
+ const brainInput = {
2294
+ summary: {
2295
+ projects: projects.map((p) => ({
2296
+ name: p.name,
2297
+ path: p.path.replace(home2, "~"),
2298
+ description: p.description
2299
+ })),
2300
+ configFiles: allConfigs
2301
+ }
2302
+ };
2303
+ await streamBuild(apiKey, serverUrl, brainInput, options);
2304
+ }
2305
+ async function streamBuild(apiKey, serverUrl, brainInput, options) {
2300
2306
  const spinner = new Spinner();
2301
2307
  spinner.start("Generating brain...");
2302
2308
  try {
@@ -2396,85 +2402,13 @@ async function buildCommand(options) {
2396
2402
  spinner.stop();
2397
2403
  if (err instanceof CliError) throw err;
2398
2404
  const msg = err.message || "Unknown error";
2399
- 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;
2400
- console.log(chalk5.red(` \u2717 ${hint}`));
2401
- throw new CliError(hint);
2402
- }
2403
- }
2404
- async function selectFolders() {
2405
- const defaults = getDefaultScanDirs();
2406
- const homeDir = os8.homedir();
2407
- const ALL_VALUE = "__all__";
2408
- const CUSTOM_VALUE = "__custom__";
2409
- const choices = [
2410
- { name: `All home folders (~) ${chalk5.dim("(Recommended)")}`, value: ALL_VALUE },
2411
- { name: chalk5.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), value: "__sep__", disabled: true },
2412
- ...defaults.map((d) => ({ name: displayPath(d), value: d })),
2413
- { name: chalk5.dim("Browse to a folder..."), value: CUSTOM_VALUE }
2414
- ];
2415
- const selected = await checkbox3({
2416
- message: "Select folders to scan:",
2417
- choices,
2418
- required: true
2419
- });
2420
- let scanDirs;
2421
- if (selected.includes(ALL_VALUE)) {
2422
- scanDirs = defaults;
2423
- } else {
2424
- scanDirs = selected.filter((v) => v !== CUSTOM_VALUE);
2425
- if (selected.includes(CUSTOM_VALUE)) {
2426
- const custom = await input2({
2427
- message: "Enter folder path(s), comma-separated:"
2428
- });
2429
- const customPaths = custom.split(",").map((f) => expandPath(f.trim())).filter(Boolean);
2430
- scanDirs = [...scanDirs, ...customPaths];
2431
- }
2405
+ console.log(chalk5.red(` \u2717 ${msg}`));
2406
+ throw new CliError(msg);
2432
2407
  }
2433
- if (scanDirs.length > 0 && !selected.includes(ALL_VALUE)) {
2434
- console.log();
2435
- console.log(dim(" Selected:"));
2436
- for (const d of scanDirs) {
2437
- console.log(dim(` ${displayPath(d)}`));
2438
- }
2439
- const addMore = await select2({
2440
- message: "Add more folders or continue?",
2441
- choices: [
2442
- { name: "Continue with scan", value: "continue" },
2443
- { name: "Add another folder...", value: "add" }
2444
- ]
2445
- });
2446
- if (addMore === "add") {
2447
- const extra = await input2({
2448
- message: "Enter folder path(s), comma-separated:"
2449
- });
2450
- const extraPaths = extra.split(",").map((f) => expandPath(f.trim())).filter(Boolean);
2451
- scanDirs = [...scanDirs, ...extraPaths];
2452
- }
2453
- }
2454
- return scanDirs;
2455
- }
2456
- async function reviewFolders(scanResult) {
2457
- if (scanResult.folders.length === 0) return [];
2458
- console.log(dim(" \u2501\u2501\u2501 REVIEW SCANNED FOLDERS \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"));
2459
- console.log();
2460
- console.log(dim(" All folders selected by default. Deselect any you want to exclude."));
2461
- console.log();
2462
- const choices = scanResult.folders.map((folder) => ({
2463
- name: formatFolderChoice(folder),
2464
- value: folder.path,
2465
- checked: true
2466
- }));
2467
- const selected = await checkbox3({
2468
- message: "Select folders to include in your brain:",
2469
- choices
2470
- });
2471
- const selectedFolders = scanResult.folders.filter((f) => selected.includes(f.path));
2472
- console.log();
2473
- console.log(dim(` ${formatSelectionSummary(selectedFolders)}`));
2474
- return selected;
2475
2408
  }
2476
2409
 
2477
2410
  // src/commands/deploy.ts
2411
+ init_esm_shims();
2478
2412
  import chalk6 from "chalk";
2479
2413
  async function deployCommand(options) {
2480
2414
  banner();
@@ -2512,33 +2446,34 @@ async function deployCommand(options) {
2512
2446
  }
2513
2447
 
2514
2448
  // src/commands/connect.ts
2515
- import fs11 from "fs";
2516
- import path13 from "path";
2517
- import { checkbox as checkbox4 } from "@inquirer/prompts";
2449
+ init_esm_shims();
2450
+ import fs9 from "fs";
2451
+ import path11 from "path";
2452
+ import { checkbox as checkbox3 } from "@inquirer/prompts";
2518
2453
  var RULE_FILES = [
2519
2454
  {
2520
2455
  tool: "Claude Code",
2521
2456
  filePath: "CLAUDE.md",
2522
2457
  strategy: "append",
2523
- detect: (p) => p.hasClaudeMd || fs11.existsSync(path13.join(p.path, ".claude"))
2458
+ detect: (p) => p.hasClaudeMd || fs9.existsSync(path11.join(p.path, ".claude"))
2524
2459
  },
2525
2460
  {
2526
2461
  tool: "Cursor",
2527
2462
  filePath: ".cursor/rules/piut.mdc",
2528
2463
  strategy: "create",
2529
- detect: (p) => p.hasCursorRules || fs11.existsSync(path13.join(p.path, ".cursor"))
2464
+ detect: (p) => p.hasCursorRules || fs9.existsSync(path11.join(p.path, ".cursor"))
2530
2465
  },
2531
2466
  {
2532
2467
  tool: "Windsurf",
2533
2468
  filePath: ".windsurf/rules/piut.md",
2534
2469
  strategy: "create",
2535
- detect: (p) => p.hasWindsurfRules || fs11.existsSync(path13.join(p.path, ".windsurf"))
2470
+ detect: (p) => p.hasWindsurfRules || fs9.existsSync(path11.join(p.path, ".windsurf"))
2536
2471
  },
2537
2472
  {
2538
- tool: "GitHub Copilot",
2473
+ tool: "VS Code",
2539
2474
  filePath: ".github/copilot-instructions.md",
2540
2475
  strategy: "append",
2541
- detect: (p) => p.hasCopilotInstructions || fs11.existsSync(path13.join(p.path, ".github"))
2476
+ detect: (p) => p.hasCopilotInstructions || fs9.existsSync(path11.join(p.path, ".github"))
2542
2477
  },
2543
2478
  {
2544
2479
  tool: "Amazon Q",
@@ -2550,19 +2485,19 @@ var RULE_FILES = [
2550
2485
  tool: "Zed",
2551
2486
  filePath: ".zed/rules.md",
2552
2487
  strategy: "create",
2553
- detect: (p) => p.hasZedRules || fs11.existsSync(path13.join(p.path, ".zed"))
2488
+ detect: (p) => p.hasZedRules || fs9.existsSync(path11.join(p.path, ".zed"))
2554
2489
  },
2555
2490
  {
2556
2491
  tool: "Gemini CLI",
2557
2492
  filePath: "GEMINI.md",
2558
2493
  strategy: "append",
2559
- detect: (p) => fs11.existsSync(path13.join(p.path, ".gemini"))
2494
+ detect: (p) => fs9.existsSync(path11.join(p.path, ".gemini"))
2560
2495
  },
2561
2496
  {
2562
2497
  tool: "Paperclip",
2563
2498
  filePath: "AGENTS.md",
2564
2499
  strategy: "append",
2565
- detect: (p) => fs11.existsSync(path13.join(p.path, ".paperclip"))
2500
+ detect: (p) => fs9.existsSync(path11.join(p.path, ".paperclip"))
2566
2501
  }
2567
2502
  ];
2568
2503
  var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut-context)
@@ -2597,7 +2532,7 @@ Full skill reference: .piut/skill.md
2597
2532
  `;
2598
2533
  function hasPiutReference2(filePath) {
2599
2534
  try {
2600
- const content = fs11.readFileSync(filePath, "utf-8");
2535
+ const content = fs9.readFileSync(filePath, "utf-8");
2601
2536
  return content.includes("p\u0131ut Context") || content.includes("piut Context");
2602
2537
  } catch {
2603
2538
  return false;
@@ -2620,13 +2555,13 @@ async function connectCommand(options) {
2620
2555
  console.log();
2621
2556
  return;
2622
2557
  }
2623
- let scanFolders2;
2558
+ let scanFolders;
2624
2559
  if (options.folders) {
2625
- scanFolders2 = options.folders.split(",").map((f) => expandPath(f.trim()));
2560
+ scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
2626
2561
  }
2627
2562
  console.log();
2628
2563
  console.log(dim(" Scanning for projects..."));
2629
- const projects = scanForProjects(scanFolders2);
2564
+ const projects = scanForProjects(scanFolders);
2630
2565
  if (projects.length === 0) {
2631
2566
  console.log(warning(" No projects found."));
2632
2567
  console.log(dim(" Try running from a directory with your projects, or use --folders."));
@@ -2637,20 +2572,20 @@ async function connectCommand(options) {
2637
2572
  for (const project of projects) {
2638
2573
  for (const rule of RULE_FILES) {
2639
2574
  if (!rule.detect(project)) continue;
2640
- const absPath = path13.join(project.path, rule.filePath);
2641
- if (fs11.existsSync(absPath) && hasPiutReference2(absPath)) continue;
2575
+ const absPath = path11.join(project.path, rule.filePath);
2576
+ if (fs9.existsSync(absPath) && hasPiutReference2(absPath)) continue;
2642
2577
  actions.push({
2643
2578
  project,
2644
2579
  tool: rule.tool,
2645
2580
  filePath: rule.filePath,
2646
2581
  absPath,
2647
- action: rule.strategy === "create" || !fs11.existsSync(absPath) ? "create" : "append"
2582
+ action: rule.strategy === "create" || !fs9.existsSync(absPath) ? "create" : "append"
2648
2583
  });
2649
2584
  }
2650
2585
  const hasAnyAction = actions.some((a) => a.project === project);
2651
2586
  if (!hasAnyAction) {
2652
- const claudeMdPath = path13.join(project.path, "CLAUDE.md");
2653
- if (!fs11.existsSync(claudeMdPath)) {
2587
+ const claudeMdPath = path11.join(project.path, "CLAUDE.md");
2588
+ if (!fs9.existsSync(claudeMdPath)) {
2654
2589
  actions.push({
2655
2590
  project,
2656
2591
  tool: "Claude Code",
@@ -2690,7 +2625,7 @@ async function connectCommand(options) {
2690
2625
  console.log();
2691
2626
  const projectChoices = [];
2692
2627
  for (const [projectPath, projectActions] of byProject) {
2693
- const projectName = path13.basename(projectPath);
2628
+ const projectName = path11.basename(projectPath);
2694
2629
  const desc = projectActions.map((a) => {
2695
2630
  const verb = a.action === "create" ? "will create" : "will append to";
2696
2631
  return `${verb} ${a.filePath}`;
@@ -2705,7 +2640,7 @@ async function connectCommand(options) {
2705
2640
  if (options.yes) {
2706
2641
  selectedPaths = Array.from(byProject.keys());
2707
2642
  } else {
2708
- selectedPaths = await checkbox4({
2643
+ selectedPaths = await checkbox3({
2709
2644
  message: "Select projects to connect:",
2710
2645
  choices: projectChoices
2711
2646
  });
@@ -2716,32 +2651,32 @@ async function connectCommand(options) {
2716
2651
  }
2717
2652
  console.log();
2718
2653
  let connected = 0;
2719
- const copilotTool = TOOLS.find((t) => t.id === "copilot");
2654
+ const vscodeTool = TOOLS.find((t) => t.id === "vscode");
2720
2655
  for (const projectPath of selectedPaths) {
2721
2656
  const projectActions = byProject.get(projectPath) || [];
2722
- const projectName = path13.basename(projectPath);
2657
+ const projectName = path11.basename(projectPath);
2723
2658
  writePiutConfig(projectPath, { slug, apiKey, serverUrl });
2724
2659
  await writePiutSkill(projectPath, slug, apiKey);
2725
2660
  ensureGitignored(projectPath);
2726
2661
  console.log(success(` \u2713 ${projectName}/.piut/`) + dim(" \u2014 credentials + skill"));
2727
- if (copilotTool) {
2728
- const hasCopilot = fs11.existsSync(path13.join(projectPath, ".github", "copilot-instructions.md")) || fs11.existsSync(path13.join(projectPath, ".github"));
2729
- if (hasCopilot) {
2730
- const vscodeMcpPath = path13.join(projectPath, ".vscode", "mcp.json");
2731
- const serverConfig = copilotTool.generateConfig(slug, apiKey);
2732
- mergeConfig(vscodeMcpPath, copilotTool.configKey, serverConfig);
2733
- console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014 Copilot MCP"));
2662
+ if (vscodeTool) {
2663
+ const hasVscode = fs9.existsSync(path11.join(projectPath, ".github", "copilot-instructions.md")) || fs9.existsSync(path11.join(projectPath, ".github"));
2664
+ if (hasVscode) {
2665
+ const vscodeMcpPath = path11.join(projectPath, ".vscode", "mcp.json");
2666
+ const serverConfig = vscodeTool.generateConfig(slug, apiKey);
2667
+ mergeConfig(vscodeMcpPath, vscodeTool.configKey, serverConfig);
2668
+ console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014 VS Code MCP"));
2734
2669
  }
2735
2670
  }
2736
2671
  for (const action of projectActions) {
2737
2672
  if (action.action === "create") {
2738
2673
  const isAppendType = RULE_FILES.find((r) => r.filePath === action.filePath)?.strategy === "append";
2739
2674
  const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
2740
- fs11.mkdirSync(path13.dirname(action.absPath), { recursive: true });
2741
- fs11.writeFileSync(action.absPath, content, "utf-8");
2675
+ fs9.mkdirSync(path11.dirname(action.absPath), { recursive: true });
2676
+ fs9.writeFileSync(action.absPath, content, "utf-8");
2742
2677
  console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 created"));
2743
2678
  } else {
2744
- fs11.appendFileSync(action.absPath, APPEND_SECTION);
2679
+ fs9.appendFileSync(action.absPath, APPEND_SECTION);
2745
2680
  console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 appended"));
2746
2681
  }
2747
2682
  connected++;
@@ -2751,7 +2686,7 @@ async function connectCommand(options) {
2751
2686
  const hostname = getHostname();
2752
2687
  for (const projectPath of selectedPaths) {
2753
2688
  const projectActions = byProject.get(projectPath) || [];
2754
- const projectName = path13.basename(projectPath);
2689
+ const projectName = path11.basename(projectPath);
2755
2690
  const toolsDetected = [...new Set(projectActions.map((a) => a.tool))];
2756
2691
  const configFilesWritten = projectActions.map((a) => a.filePath);
2757
2692
  registerProject(apiKey, {
@@ -2770,9 +2705,10 @@ async function connectCommand(options) {
2770
2705
  }
2771
2706
 
2772
2707
  // src/commands/disconnect.ts
2773
- import fs12 from "fs";
2774
- import path14 from "path";
2775
- import { checkbox as checkbox5, confirm as confirm5 } from "@inquirer/prompts";
2708
+ init_esm_shims();
2709
+ import fs10 from "fs";
2710
+ import path12 from "path";
2711
+ import { checkbox as checkbox4, confirm as confirm5 } from "@inquirer/prompts";
2776
2712
  var DEDICATED_FILES = /* @__PURE__ */ new Set([
2777
2713
  ".cursor/rules/piut.mdc",
2778
2714
  ".windsurf/rules/piut.md",
@@ -2787,7 +2723,7 @@ var APPEND_FILES = [
2787
2723
  ];
2788
2724
  function hasPiutReference3(filePath) {
2789
2725
  try {
2790
- const content = fs12.readFileSync(filePath, "utf-8");
2726
+ const content = fs10.readFileSync(filePath, "utf-8");
2791
2727
  return content.includes("p\u0131ut Context") || content.includes("piut Context");
2792
2728
  } catch {
2793
2729
  return false;
@@ -2795,7 +2731,7 @@ function hasPiutReference3(filePath) {
2795
2731
  }
2796
2732
  function removePiutSection(filePath) {
2797
2733
  try {
2798
- let content = fs12.readFileSync(filePath, "utf-8");
2734
+ let content = fs10.readFileSync(filePath, "utf-8");
2799
2735
  const patterns = [
2800
2736
  /\n*## p[ıi]ut Context[\s\S]*?(?=\n## |\n---\n|$)/g
2801
2737
  ];
@@ -2809,7 +2745,7 @@ function removePiutSection(filePath) {
2809
2745
  }
2810
2746
  if (changed) {
2811
2747
  content = content.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
2812
- fs12.writeFileSync(filePath, content, "utf-8");
2748
+ fs10.writeFileSync(filePath, content, "utf-8");
2813
2749
  }
2814
2750
  return changed;
2815
2751
  } catch {
@@ -2818,18 +2754,18 @@ function removePiutSection(filePath) {
2818
2754
  }
2819
2755
  async function disconnectCommand(options) {
2820
2756
  banner();
2821
- let scanFolders2;
2757
+ let scanFolders;
2822
2758
  if (options.folders) {
2823
- scanFolders2 = options.folders.split(",").map((f) => expandPath(f.trim()));
2759
+ scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
2824
2760
  }
2825
2761
  console.log(dim(" Scanning for connected projects..."));
2826
- const projects = scanForProjects(scanFolders2);
2762
+ const projects = scanForProjects(scanFolders);
2827
2763
  const actions = [];
2828
2764
  for (const project of projects) {
2829
- const projectName = path14.basename(project.path);
2765
+ const projectName = path12.basename(project.path);
2830
2766
  for (const dedicatedFile of DEDICATED_FILES) {
2831
- const absPath = path14.join(project.path, dedicatedFile);
2832
- if (fs12.existsSync(absPath) && hasPiutReference3(absPath)) {
2767
+ const absPath = path12.join(project.path, dedicatedFile);
2768
+ if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
2833
2769
  actions.push({
2834
2770
  projectPath: project.path,
2835
2771
  projectName,
@@ -2840,8 +2776,8 @@ async function disconnectCommand(options) {
2840
2776
  }
2841
2777
  }
2842
2778
  for (const appendFile of APPEND_FILES) {
2843
- const absPath = path14.join(project.path, appendFile);
2844
- if (fs12.existsSync(absPath) && hasPiutReference3(absPath)) {
2779
+ const absPath = path12.join(project.path, appendFile);
2780
+ if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
2845
2781
  actions.push({
2846
2782
  projectPath: project.path,
2847
2783
  projectName,
@@ -2856,12 +2792,12 @@ async function disconnectCommand(options) {
2856
2792
  projectPath: project.path,
2857
2793
  projectName,
2858
2794
  filePath: ".piut/",
2859
- absPath: path14.join(project.path, ".piut"),
2795
+ absPath: path12.join(project.path, ".piut"),
2860
2796
  action: "remove-dir"
2861
2797
  });
2862
2798
  }
2863
- const vscodeMcpPath = path14.join(project.path, ".vscode", "mcp.json");
2864
- if (fs12.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
2799
+ const vscodeMcpPath = path12.join(project.path, ".vscode", "mcp.json");
2800
+ if (fs10.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
2865
2801
  actions.push({
2866
2802
  projectPath: project.path,
2867
2803
  projectName,
@@ -2883,7 +2819,7 @@ async function disconnectCommand(options) {
2883
2819
  }
2884
2820
  console.log();
2885
2821
  const projectChoices = Array.from(byProject.entries()).map(([projectPath, projectActions]) => {
2886
- const name = path14.basename(projectPath);
2822
+ const name = path12.basename(projectPath);
2887
2823
  const files = projectActions.map((a) => a.filePath).join(", ");
2888
2824
  return {
2889
2825
  name: `${name} ${dim(`(${files})`)}`,
@@ -2894,7 +2830,7 @@ async function disconnectCommand(options) {
2894
2830
  if (options.yes) {
2895
2831
  selectedPaths = Array.from(byProject.keys());
2896
2832
  } else {
2897
- selectedPaths = await checkbox5({
2833
+ selectedPaths = await checkbox4({
2898
2834
  message: "Select projects to disconnect:",
2899
2835
  choices: projectChoices
2900
2836
  });
@@ -2912,11 +2848,11 @@ async function disconnectCommand(options) {
2912
2848
  let disconnected = 0;
2913
2849
  for (const projectPath of selectedPaths) {
2914
2850
  const projectActions = byProject.get(projectPath) || [];
2915
- const projectName = path14.basename(projectPath);
2851
+ const projectName = path12.basename(projectPath);
2916
2852
  for (const action of projectActions) {
2917
2853
  if (action.action === "delete") {
2918
2854
  try {
2919
- fs12.unlinkSync(action.absPath);
2855
+ fs10.unlinkSync(action.absPath);
2920
2856
  console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 deleted"));
2921
2857
  disconnected++;
2922
2858
  } catch {
@@ -2960,6 +2896,7 @@ async function disconnectCommand(options) {
2960
2896
  }
2961
2897
 
2962
2898
  // src/commands/login.ts
2899
+ init_esm_shims();
2963
2900
  import chalk7 from "chalk";
2964
2901
  async function loginCommand() {
2965
2902
  try {
@@ -2975,6 +2912,7 @@ async function loginCommand() {
2975
2912
  }
2976
2913
 
2977
2914
  // src/commands/logout.ts
2915
+ init_esm_shims();
2978
2916
  async function logoutCommand() {
2979
2917
  banner();
2980
2918
  const config = readStore();
@@ -2991,9 +2929,11 @@ async function logoutCommand() {
2991
2929
  }
2992
2930
 
2993
2931
  // src/commands/update.ts
2932
+ init_esm_shims();
2994
2933
  import chalk9 from "chalk";
2995
2934
 
2996
2935
  // src/lib/update-check.ts
2936
+ init_esm_shims();
2997
2937
  import { execFile } from "child_process";
2998
2938
  import chalk8 from "chalk";
2999
2939
  import { confirm as confirm6 } from "@inquirer/prompts";
@@ -3092,7 +3032,8 @@ async function updateCommand(currentVersion) {
3092
3032
  }
3093
3033
 
3094
3034
  // src/commands/doctor.ts
3095
- import fs13 from "fs";
3035
+ init_esm_shims();
3036
+ import fs11 from "fs";
3096
3037
  import chalk10 from "chalk";
3097
3038
  async function doctorCommand(options) {
3098
3039
  if (!options.json) banner();
@@ -3142,10 +3083,10 @@ async function doctorCommand(options) {
3142
3083
  }
3143
3084
  let toolsFixed = 0;
3144
3085
  for (const tool of TOOLS) {
3145
- const paths = resolveConfigPaths(tool.configPaths);
3146
- for (const configPath of paths) {
3147
- if (!fs13.existsSync(configPath)) continue;
3148
- const piutConfig = getPiutConfig(configPath, tool.configKey);
3086
+ const paths = resolveConfigPaths(tool);
3087
+ for (const { filePath: configPath, configKey: resolvedKey } of paths) {
3088
+ if (!fs11.existsSync(configPath)) continue;
3089
+ const piutConfig = getPiutConfig(configPath, resolvedKey);
3149
3090
  if (!piutConfig) {
3150
3091
  result.tools.push({
3151
3092
  name: tool.name,
@@ -3160,13 +3101,13 @@ async function doctorCommand(options) {
3160
3101
  }
3161
3102
  break;
3162
3103
  }
3163
- const configKey = extractKeyFromConfig(piutConfig);
3164
- const configPrefix = configKey ? configKey.slice(0, 7) + "..." : "(none)";
3104
+ const extractedKey = extractKeyFromConfig(piutConfig);
3105
+ const configPrefix = extractedKey ? extractedKey.slice(0, 7) + "..." : "(none)";
3165
3106
  let keyMatch = "missing";
3166
- if (!configKey) {
3107
+ if (!extractedKey) {
3167
3108
  keyMatch = "missing";
3168
3109
  result.issues++;
3169
- } else if (apiKey && configKey === apiKey) {
3110
+ } else if (apiKey && extractedKey === apiKey) {
3170
3111
  keyMatch = "match";
3171
3112
  } else {
3172
3113
  keyMatch = "stale";
@@ -3184,7 +3125,7 @@ async function doctorCommand(options) {
3184
3125
  };
3185
3126
  if (keyMatch === "stale" && options.fix && apiKey && result.key.valid && result.key.slug) {
3186
3127
  const serverConfig = tool.generateConfig(result.key.slug, apiKey);
3187
- mergeConfig(configPath, tool.configKey, serverConfig);
3128
+ mergeConfig(configPath, resolvedKey, serverConfig);
3188
3129
  toolResult.fixed = true;
3189
3130
  toolResult.keyMatch = "match";
3190
3131
  result.issues--;
@@ -3260,10 +3201,24 @@ async function doctorCommand(options) {
3260
3201
  }
3261
3202
 
3262
3203
  // src/commands/vault.ts
3263
- import fs14 from "fs";
3264
- import path15 from "path";
3204
+ init_esm_shims();
3205
+ import fs12 from "fs";
3206
+ import path13 from "path";
3265
3207
  import chalk11 from "chalk";
3266
3208
  import { confirm as confirm7 } from "@inquirer/prompts";
3209
+ var DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
3210
+ "pdf",
3211
+ "docx",
3212
+ "doc",
3213
+ "pptx",
3214
+ "pages",
3215
+ "key",
3216
+ "rtf",
3217
+ "odt",
3218
+ "odp",
3219
+ "eml",
3220
+ "mbox"
3221
+ ]);
3267
3222
  function formatSize2(bytes) {
3268
3223
  if (bytes < 1024) return `${bytes} B`;
3269
3224
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -3301,22 +3256,31 @@ async function vaultListCommand(options) {
3301
3256
  }
3302
3257
  async function vaultUploadCommand(filePath, options) {
3303
3258
  const key = resolveApiKey(options);
3304
- const resolved = path15.resolve(filePath);
3305
- if (!fs14.existsSync(resolved)) {
3259
+ const resolved = path13.resolve(filePath);
3260
+ if (!fs12.existsSync(resolved)) {
3306
3261
  console.log(chalk11.red(` File not found: ${filePath}`));
3307
3262
  throw new CliError();
3308
3263
  }
3309
- const stat = fs14.statSync(resolved);
3264
+ const stat = fs12.statSync(resolved);
3310
3265
  if (!stat.isFile()) {
3311
3266
  console.log(chalk11.red(` Not a file: ${filePath}`));
3312
3267
  throw new CliError();
3313
3268
  }
3314
- const filename = path15.basename(resolved);
3315
- const content = fs14.readFileSync(resolved, "utf-8");
3269
+ const filename = path13.basename(resolved);
3270
+ const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
3271
+ const isDocument = DOCUMENT_EXTENSIONS.has(ext);
3272
+ let content;
3273
+ let encoding;
3274
+ if (isDocument) {
3275
+ content = fs12.readFileSync(resolved).toString("base64");
3276
+ encoding = "base64";
3277
+ } else {
3278
+ content = fs12.readFileSync(resolved, "utf-8");
3279
+ }
3316
3280
  const spinner = new Spinner();
3317
3281
  spinner.start(`Uploading ${filename}...`);
3318
3282
  try {
3319
- const result = await uploadVaultFile(key, filename, content);
3283
+ const result = await uploadVaultFile(key, filename, content, encoding);
3320
3284
  spinner.stop();
3321
3285
  console.log(success(` Uploaded ${result.filename}`) + dim(` (${formatSize2(result.sizeBytes)})`));
3322
3286
  if (result.summary) {
@@ -3334,8 +3298,8 @@ async function vaultReadCommand(filename, options) {
3334
3298
  try {
3335
3299
  const file = await readVaultFile(key, filename);
3336
3300
  if (options.output) {
3337
- const outPath = path15.resolve(options.output);
3338
- fs14.writeFileSync(outPath, file.content, "utf-8");
3301
+ const outPath = path13.resolve(options.output);
3302
+ fs12.writeFileSync(outPath, file.content, "utf-8");
3339
3303
  console.log(success(` Saved to ${outPath}`));
3340
3304
  console.log();
3341
3305
  } else {
@@ -3367,11 +3331,60 @@ async function vaultDeleteCommand(filename, options) {
3367
3331
  }
3368
3332
 
3369
3333
  // src/commands/interactive.ts
3370
- import { select as select3, confirm as confirm8, checkbox as checkbox6, Separator } from "@inquirer/prompts";
3334
+ init_esm_shims();
3335
+ import { select as select2, confirm as confirm8, checkbox as checkbox5, Separator } from "@inquirer/prompts";
3371
3336
  import fs15 from "fs";
3372
- import path16 from "path";
3337
+ import path15 from "path";
3373
3338
  import { exec as exec2 } from "child_process";
3374
- import chalk12 from "chalk";
3339
+ import chalk13 from "chalk";
3340
+
3341
+ // src/lib/sync.ts
3342
+ init_esm_shims();
3343
+ import fs13 from "fs";
3344
+ function syncStaleConfigs(slug, apiKey, serverUrl) {
3345
+ const updated = [];
3346
+ for (const tool of TOOLS) {
3347
+ if (tool.skillOnly || !tool.generateConfig || !tool.configKey) continue;
3348
+ const paths = resolveConfigPaths(tool);
3349
+ for (const { filePath, configKey } of paths) {
3350
+ if (!fs13.existsSync(filePath)) continue;
3351
+ const piutConfig = getPiutConfig(filePath, configKey);
3352
+ if (!piutConfig) continue;
3353
+ const existingKey = extractKeyFromConfig(piutConfig);
3354
+ const existingSlug = extractSlugFromConfig(piutConfig);
3355
+ const keyStale = !!existingKey && existingKey !== apiKey;
3356
+ const slugStale = !!existingSlug && existingSlug !== slug;
3357
+ if (keyStale || slugStale) {
3358
+ const newConfig = tool.generateConfig(slug, apiKey);
3359
+ mergeConfig(filePath, configKey, newConfig);
3360
+ updated.push(tool.name);
3361
+ }
3362
+ break;
3363
+ }
3364
+ }
3365
+ const cwd = process.cwd();
3366
+ const existing = readPiutConfig(cwd);
3367
+ if (existing && (existing.apiKey !== apiKey || existing.slug !== slug)) {
3368
+ writePiutConfig(cwd, { slug, apiKey, serverUrl });
3369
+ updated.push(".piut/config.json");
3370
+ }
3371
+ return updated;
3372
+ }
3373
+
3374
+ // src/commands/interactive.ts
3375
+ var DOCUMENT_EXTENSIONS2 = /* @__PURE__ */ new Set([
3376
+ "pdf",
3377
+ "docx",
3378
+ "doc",
3379
+ "pptx",
3380
+ "pages",
3381
+ "key",
3382
+ "rtf",
3383
+ "odt",
3384
+ "odp",
3385
+ "eml",
3386
+ "mbox"
3387
+ ]);
3375
3388
  async function authenticate() {
3376
3389
  const config = readStore();
3377
3390
  const apiKey = config.apiKey;
@@ -3405,6 +3418,14 @@ async function interactiveMenu() {
3405
3418
  const auth = await authenticate();
3406
3419
  apiKey = auth.apiKey;
3407
3420
  currentValidation = auth.validation;
3421
+ const synced = syncStaleConfigs(
3422
+ currentValidation.slug,
3423
+ apiKey,
3424
+ currentValidation.serverUrl
3425
+ );
3426
+ if (synced.length > 0) {
3427
+ console.log(dim(` Updated ${synced.length} stale config(s): ${synced.join(", ")}`));
3428
+ }
3408
3429
  console.log();
3409
3430
  if (currentValidation.status === "no_brain") {
3410
3431
  console.log(warning(" You haven\u2019t built a brain yet."));
@@ -3443,7 +3464,7 @@ async function interactiveMenu() {
3443
3464
  const isDeployed = currentValidation.status === "active";
3444
3465
  let action;
3445
3466
  try {
3446
- action = await select3({
3467
+ action = await select2({
3447
3468
  message: "What would you like to do?",
3448
3469
  loop: false,
3449
3470
  choices: [
@@ -3567,7 +3588,7 @@ async function interactiveMenu() {
3567
3588
  } else if (err instanceof CliError) {
3568
3589
  console.log();
3569
3590
  } else {
3570
- console.log(chalk12.red(` Error: ${err.message}`));
3591
+ console.log(chalk13.red(` Error: ${err.message}`));
3571
3592
  console.log();
3572
3593
  }
3573
3594
  }
@@ -3590,20 +3611,20 @@ async function handleUndeploy(apiKey) {
3590
3611
  console.log(dim(" Run ") + brand("piut deploy") + dim(" to re-deploy anytime."));
3591
3612
  console.log();
3592
3613
  } catch (err) {
3593
- console.log(chalk12.red(` \u2717 ${err.message}`));
3614
+ console.log(chalk13.red(` \u2717 ${err.message}`));
3594
3615
  }
3595
3616
  }
3596
3617
  async function handleConnectTools(apiKey, validation) {
3597
3618
  const { slug } = validation;
3598
3619
  const detected = [];
3599
3620
  for (const tool of TOOLS) {
3600
- const paths = resolveConfigPaths(tool.configPaths);
3601
- for (const configPath of paths) {
3602
- const exists = fs15.existsSync(configPath);
3603
- const parentExists = fs15.existsSync(path16.dirname(configPath));
3621
+ const paths = resolveConfigPaths(tool);
3622
+ for (const { filePath, configKey } of paths) {
3623
+ const exists = fs15.existsSync(filePath);
3624
+ const parentExists = fs15.existsSync(path15.dirname(filePath));
3604
3625
  if (exists || parentExists) {
3605
- const connected = exists && !!tool.configKey && isPiutConfigured(configPath, tool.configKey);
3606
- detected.push({ tool, configPath, connected });
3626
+ const connected = exists && !!configKey && isPiutConfigured(filePath, configKey);
3627
+ detected.push({ tool, configPath: filePath, resolvedConfigKey: configKey, connected });
3607
3628
  break;
3608
3629
  }
3609
3630
  }
@@ -3639,7 +3660,7 @@ async function handleConnectTools(apiKey, validation) {
3639
3660
  value: d,
3640
3661
  checked: d.connected
3641
3662
  }));
3642
- const selected = await checkbox6({
3663
+ const selected = await checkbox5({
3643
3664
  message: "Select tools to keep connected (toggle with space):",
3644
3665
  choices
3645
3666
  });
@@ -3651,17 +3672,17 @@ async function handleConnectTools(apiKey, validation) {
3651
3672
  return;
3652
3673
  }
3653
3674
  console.log();
3654
- for (const { tool, configPath } of toConnect) {
3655
- if (tool.generateConfig && tool.configKey) {
3675
+ for (const { tool, configPath, resolvedConfigKey } of toConnect) {
3676
+ if (tool.generateConfig && resolvedConfigKey) {
3656
3677
  const serverConfig = tool.generateConfig(slug, apiKey);
3657
- mergeConfig(configPath, tool.configKey, serverConfig);
3678
+ mergeConfig(configPath, resolvedConfigKey, serverConfig);
3658
3679
  toolLine(tool.name, success("connected"), "\u2714");
3659
3680
  }
3660
3681
  }
3661
3682
  const removedNames = [];
3662
- for (const { tool, configPath } of toDisconnect) {
3663
- if (!tool.configKey) continue;
3664
- const removed = removeFromConfig(configPath, tool.configKey);
3683
+ for (const { tool, configPath, resolvedConfigKey } of toDisconnect) {
3684
+ if (!resolvedConfigKey) continue;
3685
+ const removed = removeFromConfig(configPath, resolvedConfigKey);
3665
3686
  if (removed) {
3666
3687
  removedNames.push(tool.name);
3667
3688
  toolLine(tool.name, warning("disconnected"), "\u2714");
@@ -3709,7 +3730,7 @@ async function handleManageProjects(apiKey, validation) {
3709
3730
  value: i,
3710
3731
  checked: i.connected
3711
3732
  }));
3712
- const selected = await checkbox6({
3733
+ const selected = await checkbox5({
3713
3734
  message: "Select projects to keep connected (toggle with space):",
3714
3735
  choices
3715
3736
  });
@@ -3723,26 +3744,26 @@ async function handleManageProjects(apiKey, validation) {
3723
3744
  console.log();
3724
3745
  const copilotTool = TOOLS.find((t) => t.id === "copilot");
3725
3746
  for (const { project } of toConnect) {
3726
- const projectName = path16.basename(project.path);
3747
+ const projectName = path15.basename(project.path);
3727
3748
  writePiutConfig(project.path, { slug, apiKey, serverUrl });
3728
3749
  await writePiutSkill(project.path, slug, apiKey);
3729
3750
  ensureGitignored(project.path);
3730
3751
  if (copilotTool) {
3731
- const hasCopilot = fs15.existsSync(path16.join(project.path, ".github", "copilot-instructions.md")) || fs15.existsSync(path16.join(project.path, ".github"));
3752
+ const hasCopilot = fs15.existsSync(path15.join(project.path, ".github", "copilot-instructions.md")) || fs15.existsSync(path15.join(project.path, ".github"));
3732
3753
  if (hasCopilot) {
3733
- const vscodeMcpPath = path16.join(project.path, ".vscode", "mcp.json");
3754
+ const vscodeMcpPath = path15.join(project.path, ".vscode", "mcp.json");
3734
3755
  const serverConfig = copilotTool.generateConfig(slug, apiKey);
3735
3756
  mergeConfig(vscodeMcpPath, copilotTool.configKey, serverConfig);
3736
3757
  }
3737
3758
  }
3738
3759
  for (const rule of RULE_FILES) {
3739
3760
  if (!rule.detect(project)) continue;
3740
- const absPath = path16.join(project.path, rule.filePath);
3761
+ const absPath = path15.join(project.path, rule.filePath);
3741
3762
  if (fs15.existsSync(absPath) && hasPiutReference2(absPath)) continue;
3742
3763
  if (rule.strategy === "create" || !fs15.existsSync(absPath)) {
3743
3764
  const isAppendType = rule.strategy === "append";
3744
3765
  const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
3745
- fs15.mkdirSync(path16.dirname(absPath), { recursive: true });
3766
+ fs15.mkdirSync(path15.dirname(absPath), { recursive: true });
3746
3767
  fs15.writeFileSync(absPath, content, "utf-8");
3747
3768
  } else {
3748
3769
  fs15.appendFileSync(absPath, APPEND_SECTION);
@@ -3761,9 +3782,9 @@ async function handleManageProjects(apiKey, validation) {
3761
3782
  });
3762
3783
  }
3763
3784
  for (const { project } of toDisconnect) {
3764
- const projectName = path16.basename(project.path);
3785
+ const projectName = path15.basename(project.path);
3765
3786
  for (const dedicatedFile of DEDICATED_FILES) {
3766
- const absPath = path16.join(project.path, dedicatedFile);
3787
+ const absPath = path15.join(project.path, dedicatedFile);
3767
3788
  if (fs15.existsSync(absPath) && hasPiutReference2(absPath)) {
3768
3789
  try {
3769
3790
  fs15.unlinkSync(absPath);
@@ -3772,12 +3793,12 @@ async function handleManageProjects(apiKey, validation) {
3772
3793
  }
3773
3794
  }
3774
3795
  for (const appendFile of APPEND_FILES) {
3775
- const absPath = path16.join(project.path, appendFile);
3796
+ const absPath = path15.join(project.path, appendFile);
3776
3797
  if (fs15.existsSync(absPath) && hasPiutReference2(absPath)) {
3777
3798
  removePiutSection(absPath);
3778
3799
  }
3779
3800
  }
3780
- const vscodeMcpPath = path16.join(project.path, ".vscode", "mcp.json");
3801
+ const vscodeMcpPath = path15.join(project.path, ".vscode", "mcp.json");
3781
3802
  if (fs15.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
3782
3803
  removeFromConfig(vscodeMcpPath, "servers");
3783
3804
  }
@@ -3823,7 +3844,7 @@ async function handleVaultView(apiKey) {
3823
3844
  console.log();
3824
3845
  console.log(dim(` ${data.usage.fileCount} file(s), ${formatSize3(data.usage.totalBytes)} / ${formatSize3(data.usage.maxBytes)} used`));
3825
3846
  console.log();
3826
- const action = await select3({
3847
+ const action = await select2({
3827
3848
  message: "Actions:",
3828
3849
  choices: [
3829
3850
  { name: "Delete a file", value: "delete" },
@@ -3836,7 +3857,7 @@ async function handleVaultView(apiKey) {
3836
3857
  name: `${f.filename} ${dim(`(${formatSize3(f.sizeBytes)})`)}`,
3837
3858
  value: f.filename
3838
3859
  }));
3839
- const filename = await select3({
3860
+ const filename = await select2({
3840
3861
  message: "Which file to delete?",
3841
3862
  choices: fileChoices
3842
3863
  });
@@ -3850,37 +3871,55 @@ async function handleVaultView(apiKey) {
3850
3871
  console.log(success(` Deleted ${filename}`));
3851
3872
  console.log();
3852
3873
  } catch (err) {
3853
- console.log(chalk12.red(` ${err.message}`));
3874
+ console.log(chalk13.red(` ${err.message}`));
3854
3875
  console.log();
3855
3876
  }
3856
3877
  }
3857
3878
  }
3858
3879
  }
3859
3880
  async function handleVaultUpload(apiKey) {
3860
- const { input: input3 } = await import("@inquirer/prompts");
3861
- const filePath = await input3({ message: "File path:" });
3862
- if (!filePath.trim()) return;
3863
- const resolved = path16.resolve(filePath);
3864
- if (!fs15.existsSync(resolved) || !fs15.statSync(resolved).isFile()) {
3865
- console.log(chalk12.red(` File not found: ${filePath}`));
3881
+ const treePrompt2 = (await Promise.resolve().then(() => (init_tree_prompt(), tree_prompt_exports))).default;
3882
+ const files = await treePrompt2({
3883
+ message: "Select files to upload:",
3884
+ mode: "files"
3885
+ });
3886
+ if (files.length === 0) {
3887
+ console.log(dim(" No files selected."));
3866
3888
  console.log();
3867
3889
  return;
3868
3890
  }
3869
- const filename = path16.basename(resolved);
3870
- const content = fs15.readFileSync(resolved, "utf-8");
3871
- const spinner = new Spinner();
3872
- spinner.start(`Uploading ${filename}...`);
3873
- try {
3874
- const result = await uploadVaultFile(apiKey, filename, content);
3875
- spinner.stop();
3876
- console.log(success(` Uploaded ${result.filename}`) + dim(` (${formatSize3(result.sizeBytes)})`));
3877
- if (result.summary) console.log(dim(` ${result.summary}`));
3878
- console.log();
3879
- } catch (err) {
3880
- spinner.stop();
3881
- console.log(chalk12.red(` ${err.message}`));
3891
+ console.log();
3892
+ let uploaded = 0;
3893
+ for (const filePath of files) {
3894
+ const filename = path15.basename(filePath);
3895
+ const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
3896
+ const isDocument = DOCUMENT_EXTENSIONS2.has(ext);
3897
+ let content;
3898
+ let encoding;
3899
+ if (isDocument) {
3900
+ content = fs15.readFileSync(filePath).toString("base64");
3901
+ encoding = "base64";
3902
+ } else {
3903
+ content = fs15.readFileSync(filePath, "utf-8");
3904
+ }
3905
+ const spinner = new Spinner();
3906
+ spinner.start(`Uploading ${filename}...`);
3907
+ try {
3908
+ const result = await uploadVaultFile(apiKey, filename, content, encoding);
3909
+ spinner.stop();
3910
+ console.log(success(` Uploaded ${result.filename}`) + dim(` (${formatSize3(result.sizeBytes)})`));
3911
+ if (result.summary) console.log(dim(` ${result.summary}`));
3912
+ uploaded++;
3913
+ } catch (err) {
3914
+ spinner.stop();
3915
+ console.log(chalk13.red(` ${err.message}`));
3916
+ }
3917
+ }
3918
+ if (uploaded > 0) {
3882
3919
  console.log();
3920
+ console.log(success(` ${uploaded} file${uploaded === 1 ? "" : "s"} uploaded to vault.`));
3883
3921
  }
3922
+ console.log();
3884
3923
  }
3885
3924
  async function handleViewBrain(apiKey) {
3886
3925
  console.log(dim(" Loading brain..."));
@@ -3924,11 +3963,11 @@ async function handleViewBrain(apiKey) {
3924
3963
  console.log();
3925
3964
  const msg = err.message;
3926
3965
  if (msg === "REQUIRES_SUBSCRIPTION") {
3927
- console.log(chalk12.yellow(" Deploy requires an active subscription ($10/mo)."));
3966
+ console.log(chalk13.yellow(" Deploy requires an active subscription ($10/mo)."));
3928
3967
  console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
3929
3968
  console.log(dim(" 14-day free trial included."));
3930
3969
  } else {
3931
- console.log(chalk12.red(` \u2717 ${msg}`));
3970
+ console.log(chalk13.red(` \u2717 ${msg}`));
3932
3971
  }
3933
3972
  console.log();
3934
3973
  }
@@ -3937,7 +3976,7 @@ async function handleViewBrain(apiKey) {
3937
3976
  }
3938
3977
 
3939
3978
  // src/cli.ts
3940
- var VERSION = "3.7.0";
3979
+ var VERSION = "3.9.0";
3941
3980
  function withExit(fn) {
3942
3981
  return async (...args2) => {
3943
3982
  try {
@@ -3953,7 +3992,7 @@ program.name("piut").description("Build your AI brain instantly. Deploy it as an
3953
3992
  if (actionCommand.name() === "update") return;
3954
3993
  return checkForUpdate(VERSION);
3955
3994
  }).action(interactiveMenu);
3956
- 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));
3995
+ program.command("build").description("Build or rebuild your brain from your AI config files").option("-k, --key <key>", "API key").option("-y, --yes", "Auto-publish after build").option("--no-publish", "Skip publish prompt after build").action(withExit(buildCommand));
3957
3996
  program.command("deploy").description("Publish your MCP server (requires paid account)").option("-k, --key <key>", "API key").action(withExit(deployCommand));
3958
3997
  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));
3959
3998
  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));