@piut/cli 3.6.0 → 3.8.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.
- package/dist/cli.js +1200 -972
- 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 fs13 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 = fs13.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
|
|
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,
|
|
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(
|
|
369
|
+
body: JSON.stringify(input2)
|
|
48
370
|
});
|
|
49
371
|
if (!res.ok) {
|
|
50
372
|
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
@@ -122,7 +444,10 @@ async function pingMcp(serverUrl, key, toolName) {
|
|
|
122
444
|
headers: {
|
|
123
445
|
Authorization: `Bearer ${key}`,
|
|
124
446
|
"Content-Type": "application/json",
|
|
125
|
-
"User-Agent": `piut-cli (configured: ${toolName})
|
|
447
|
+
"User-Agent": `piut-cli (configured: ${toolName})`,
|
|
448
|
+
"X-Piut-Hostname": os.hostname(),
|
|
449
|
+
"X-Piut-Machine-Id": getMachineId(),
|
|
450
|
+
"X-Piut-Tool": toolName
|
|
126
451
|
},
|
|
127
452
|
body: JSON.stringify({
|
|
128
453
|
jsonrpc: "2.0",
|
|
@@ -167,6 +492,9 @@ function getMachineId() {
|
|
|
167
492
|
const hostname = os.hostname();
|
|
168
493
|
return crypto.createHash("sha256").update(hostname).digest("hex").slice(0, 16);
|
|
169
494
|
}
|
|
495
|
+
function getHostname() {
|
|
496
|
+
return os.hostname();
|
|
497
|
+
}
|
|
170
498
|
async function registerProject(key, project) {
|
|
171
499
|
const res = await fetch(`${API_BASE}/api/cli/projects`, {
|
|
172
500
|
method: "POST",
|
|
@@ -209,57 +537,93 @@ async function deleteConnections(key, toolNames) {
|
|
|
209
537
|
} catch {
|
|
210
538
|
}
|
|
211
539
|
}
|
|
212
|
-
async function
|
|
213
|
-
const res = await fetch(
|
|
540
|
+
async function unpublishServer(key) {
|
|
541
|
+
const res = await fetch(`${API_BASE}/api/mcp/publish`, {
|
|
214
542
|
method: "POST",
|
|
215
543
|
headers: {
|
|
216
544
|
Authorization: `Bearer ${key}`,
|
|
217
545
|
"Content-Type": "application/json"
|
|
218
546
|
},
|
|
219
|
-
body: JSON.stringify({
|
|
220
|
-
jsonrpc: "2.0",
|
|
221
|
-
id: 1,
|
|
222
|
-
method: "tools/call",
|
|
223
|
-
params: {
|
|
224
|
-
name: "update_brain",
|
|
225
|
-
arguments: { content }
|
|
226
|
-
}
|
|
227
|
-
})
|
|
547
|
+
body: JSON.stringify({ published: false })
|
|
228
548
|
});
|
|
229
549
|
if (!res.ok) {
|
|
230
|
-
|
|
550
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
551
|
+
throw new Error(body.error || `Unpublish failed (HTTP ${res.status})`);
|
|
231
552
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
553
|
+
return res.json();
|
|
554
|
+
}
|
|
555
|
+
async function listVaultFiles(key) {
|
|
556
|
+
const res = await fetch(`${API_BASE}/api/cli/vault`, {
|
|
557
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
558
|
+
});
|
|
559
|
+
if (!res.ok) {
|
|
560
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
561
|
+
throw new Error(body.error || `Failed to list vault files (HTTP ${res.status})`);
|
|
235
562
|
}
|
|
236
|
-
|
|
237
|
-
const text = Array.isArray(resultContent) && resultContent[0]?.text ? resultContent[0].text : "Brain updated";
|
|
238
|
-
return { summary: text };
|
|
563
|
+
return res.json();
|
|
239
564
|
}
|
|
240
|
-
async function
|
|
241
|
-
const
|
|
565
|
+
async function uploadVaultFile(key, filename, content, encoding) {
|
|
566
|
+
const payload = { filename, content };
|
|
567
|
+
if (encoding) payload.encoding = encoding;
|
|
568
|
+
const res = await fetch(`${API_BASE}/api/cli/vault/upload`, {
|
|
242
569
|
method: "POST",
|
|
243
570
|
headers: {
|
|
244
571
|
Authorization: `Bearer ${key}`,
|
|
245
572
|
"Content-Type": "application/json"
|
|
246
573
|
},
|
|
247
|
-
body: JSON.stringify(
|
|
574
|
+
body: JSON.stringify(payload)
|
|
248
575
|
});
|
|
249
576
|
if (!res.ok) {
|
|
250
577
|
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
251
|
-
throw new Error(body.error || `
|
|
578
|
+
throw new Error(body.error || `Upload failed (HTTP ${res.status})`);
|
|
579
|
+
}
|
|
580
|
+
return res.json();
|
|
581
|
+
}
|
|
582
|
+
async function readVaultFile(key, filename) {
|
|
583
|
+
const encoded = encodeURIComponent(filename);
|
|
584
|
+
const res = await fetch(`${API_BASE}/api/cli/vault/${encoded}`, {
|
|
585
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
586
|
+
});
|
|
587
|
+
if (!res.ok) {
|
|
588
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
589
|
+
throw new Error(body.error || `Failed to read vault file (HTTP ${res.status})`);
|
|
252
590
|
}
|
|
253
591
|
return res.json();
|
|
254
592
|
}
|
|
593
|
+
async function deleteVaultFile(key, filename) {
|
|
594
|
+
const res = await fetch(`${API_BASE}/api/cli/vault`, {
|
|
595
|
+
method: "DELETE",
|
|
596
|
+
headers: {
|
|
597
|
+
Authorization: `Bearer ${key}`,
|
|
598
|
+
"Content-Type": "application/json"
|
|
599
|
+
},
|
|
600
|
+
body: JSON.stringify({ filename })
|
|
601
|
+
});
|
|
602
|
+
if (!res.ok) {
|
|
603
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
604
|
+
throw new Error(body.error || `Delete failed (HTTP ${res.status})`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
255
607
|
|
|
256
608
|
// src/lib/tools.ts
|
|
609
|
+
init_esm_shims();
|
|
257
610
|
import os2 from "os";
|
|
258
|
-
import
|
|
611
|
+
import path2 from "path";
|
|
612
|
+
import crypto2 from "crypto";
|
|
259
613
|
var MCP_URL = (slug) => `https://piut.com/api/mcp/${slug}`;
|
|
260
614
|
var AUTH_HEADER = (key) => ({ Authorization: `Bearer ${key}` });
|
|
615
|
+
function getMachineId2() {
|
|
616
|
+
return crypto2.createHash("sha256").update(os2.hostname()).digest("hex").slice(0, 16);
|
|
617
|
+
}
|
|
618
|
+
function machineHeaders(toolName) {
|
|
619
|
+
return {
|
|
620
|
+
"X-Piut-Hostname": os2.hostname(),
|
|
621
|
+
"X-Piut-Machine-Id": getMachineId2(),
|
|
622
|
+
"X-Piut-Tool": toolName
|
|
623
|
+
};
|
|
624
|
+
}
|
|
261
625
|
function appData() {
|
|
262
|
-
return process.env.APPDATA ||
|
|
626
|
+
return process.env.APPDATA || path2.join(os2.homedir(), "AppData", "Roaming");
|
|
263
627
|
}
|
|
264
628
|
var TOOLS = [
|
|
265
629
|
{
|
|
@@ -269,18 +633,19 @@ var TOOLS = [
|
|
|
269
633
|
configPaths: {
|
|
270
634
|
darwin: ["~/.claude.json"],
|
|
271
635
|
win32: ["~/.claude.json"],
|
|
272
|
-
linux: ["~/.claude.json"]
|
|
636
|
+
linux: ["~/.claude.json"],
|
|
637
|
+
project: [".mcp.json"]
|
|
273
638
|
},
|
|
274
639
|
skillFilePath: "CLAUDE.md",
|
|
275
640
|
quickCommand: (slug, key) => `claude mcp add-json piut-context '${JSON.stringify({
|
|
276
641
|
type: "http",
|
|
277
642
|
url: MCP_URL(slug),
|
|
278
|
-
headers: AUTH_HEADER(key)
|
|
643
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("Claude Code") }
|
|
279
644
|
})}'`,
|
|
280
645
|
generateConfig: (slug, key) => ({
|
|
281
646
|
type: "http",
|
|
282
647
|
url: MCP_URL(slug),
|
|
283
|
-
headers: AUTH_HEADER(key)
|
|
648
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("Claude Code") }
|
|
284
649
|
})
|
|
285
650
|
},
|
|
286
651
|
{
|
|
@@ -289,7 +654,7 @@ var TOOLS = [
|
|
|
289
654
|
configKey: "mcpServers",
|
|
290
655
|
configPaths: {
|
|
291
656
|
darwin: ["~/Library/Application Support/Claude/claude_desktop_config.json"],
|
|
292
|
-
win32: [
|
|
657
|
+
win32: [path2.join(appData(), "Claude", "claude_desktop_config.json")],
|
|
293
658
|
linux: ["~/.config/Claude/claude_desktop_config.json"]
|
|
294
659
|
},
|
|
295
660
|
generateConfig: (slug, key) => ({
|
|
@@ -316,7 +681,7 @@ var TOOLS = [
|
|
|
316
681
|
skillFilePath: ".cursor/rules/piut.mdc",
|
|
317
682
|
generateConfig: (slug, key) => ({
|
|
318
683
|
url: MCP_URL(slug),
|
|
319
|
-
headers: AUTH_HEADER(key)
|
|
684
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("Cursor") }
|
|
320
685
|
})
|
|
321
686
|
},
|
|
322
687
|
{
|
|
@@ -331,21 +696,25 @@ var TOOLS = [
|
|
|
331
696
|
skillFilePath: ".windsurf/rules/piut.md",
|
|
332
697
|
generateConfig: (slug, key) => ({
|
|
333
698
|
serverUrl: MCP_URL(slug),
|
|
334
|
-
headers: AUTH_HEADER(key)
|
|
699
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("Windsurf") }
|
|
335
700
|
})
|
|
336
701
|
},
|
|
337
702
|
{
|
|
338
703
|
id: "copilot",
|
|
339
704
|
name: "GitHub Copilot",
|
|
340
705
|
configKey: "servers",
|
|
706
|
+
globalConfigKey: "mcp.servers",
|
|
341
707
|
configPaths: {
|
|
708
|
+
darwin: ["~/Library/Application Support/Code/User/settings.json"],
|
|
709
|
+
win32: [path2.join(appData(), "Code", "User", "settings.json")],
|
|
710
|
+
linux: ["~/.config/Code/User/settings.json"],
|
|
342
711
|
project: [".vscode/mcp.json"]
|
|
343
712
|
},
|
|
344
713
|
skillFilePath: ".github/copilot-instructions.md",
|
|
345
714
|
generateConfig: (slug, key) => ({
|
|
346
715
|
type: "http",
|
|
347
716
|
url: MCP_URL(slug),
|
|
348
|
-
headers: AUTH_HEADER(key)
|
|
717
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("GitHub Copilot") }
|
|
349
718
|
})
|
|
350
719
|
},
|
|
351
720
|
{
|
|
@@ -361,7 +730,7 @@ var TOOLS = [
|
|
|
361
730
|
generateConfig: (slug, key) => ({
|
|
362
731
|
type: "http",
|
|
363
732
|
url: MCP_URL(slug),
|
|
364
|
-
headers: AUTH_HEADER(key)
|
|
733
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("Amazon Q") }
|
|
365
734
|
})
|
|
366
735
|
},
|
|
367
736
|
{
|
|
@@ -376,35 +745,78 @@ var TOOLS = [
|
|
|
376
745
|
generateConfig: (slug, key) => ({
|
|
377
746
|
settings: {
|
|
378
747
|
url: MCP_URL(slug),
|
|
379
|
-
headers: AUTH_HEADER(key)
|
|
748
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("Zed") }
|
|
380
749
|
}
|
|
381
750
|
})
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
id: "gemini-cli",
|
|
754
|
+
name: "Gemini CLI",
|
|
755
|
+
configKey: "mcpServers",
|
|
756
|
+
configPaths: {
|
|
757
|
+
darwin: ["~/.gemini/settings.json"],
|
|
758
|
+
win32: ["~/.gemini/settings.json"],
|
|
759
|
+
linux: ["~/.gemini/settings.json"],
|
|
760
|
+
project: [".gemini/settings.json"]
|
|
761
|
+
},
|
|
762
|
+
generateConfig: (slug, key) => ({
|
|
763
|
+
httpUrl: MCP_URL(slug),
|
|
764
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("Gemini CLI") }
|
|
765
|
+
})
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
id: "openclaw",
|
|
769
|
+
name: "OpenClaw",
|
|
770
|
+
configKey: "mcpServers",
|
|
771
|
+
configPaths: {
|
|
772
|
+
darwin: ["~/.mcporter/mcporter.json", "~/.openclaw/workspace/config/mcporter.json"],
|
|
773
|
+
win32: ["~/.mcporter/mcporter.json", "~/.openclaw/workspace/config/mcporter.json"],
|
|
774
|
+
linux: ["~/.mcporter/mcporter.json", "~/.openclaw/workspace/config/mcporter.json"]
|
|
775
|
+
},
|
|
776
|
+
quickCommand: (slug, key) => `npx mcporter config add piut-context ${MCP_URL(slug)} --header "Authorization=Bearer ${key}"`,
|
|
777
|
+
generateConfig: (slug, key) => ({
|
|
778
|
+
url: MCP_URL(slug),
|
|
779
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("OpenClaw") }
|
|
780
|
+
})
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
id: "paperclip",
|
|
784
|
+
name: "Paperclip",
|
|
785
|
+
skillOnly: true,
|
|
786
|
+
configPaths: {
|
|
787
|
+
darwin: ["~/.paperclip/config.json"],
|
|
788
|
+
win32: ["~/.paperclip/config.json"],
|
|
789
|
+
linux: ["~/.paperclip/config.json"]
|
|
790
|
+
}
|
|
382
791
|
}
|
|
383
792
|
];
|
|
384
793
|
|
|
385
794
|
// src/lib/paths.ts
|
|
795
|
+
init_esm_shims();
|
|
386
796
|
import os3 from "os";
|
|
387
|
-
import
|
|
797
|
+
import path3 from "path";
|
|
388
798
|
function expandPath(p) {
|
|
389
799
|
return p.replace(/^~/, os3.homedir());
|
|
390
800
|
}
|
|
391
|
-
function resolveConfigPaths(
|
|
801
|
+
function resolveConfigPaths(tool) {
|
|
392
802
|
const resolved = [];
|
|
803
|
+
const configKey = tool.configKey || "";
|
|
393
804
|
const platformKey = process.platform;
|
|
394
|
-
const globalPaths = configPaths[platformKey] || [];
|
|
805
|
+
const globalPaths = tool.configPaths[platformKey] || [];
|
|
395
806
|
for (const p of globalPaths) {
|
|
396
|
-
resolved.push(expandPath(p));
|
|
807
|
+
resolved.push({ filePath: expandPath(p), configKey: tool.globalConfigKey || configKey });
|
|
397
808
|
}
|
|
398
|
-
const projectPaths = configPaths.project || [];
|
|
809
|
+
const projectPaths = tool.configPaths.project || [];
|
|
399
810
|
for (const p of projectPaths) {
|
|
400
|
-
resolved.push(
|
|
811
|
+
resolved.push({ filePath: path3.resolve(process.cwd(), p), configKey });
|
|
401
812
|
}
|
|
402
813
|
return resolved;
|
|
403
814
|
}
|
|
404
815
|
|
|
405
816
|
// src/lib/config.ts
|
|
817
|
+
init_esm_shims();
|
|
406
818
|
import fs from "fs";
|
|
407
|
-
import
|
|
819
|
+
import path4 from "path";
|
|
408
820
|
function readConfig(filePath) {
|
|
409
821
|
let raw;
|
|
410
822
|
try {
|
|
@@ -425,26 +837,46 @@ function readConfig(filePath) {
|
|
|
425
837
|
}
|
|
426
838
|
}
|
|
427
839
|
function writeConfig(filePath, data) {
|
|
428
|
-
fs.mkdirSync(
|
|
840
|
+
fs.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
429
841
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
430
842
|
}
|
|
843
|
+
function resolveKeyPath(config, keyPath) {
|
|
844
|
+
const parts = keyPath.split(".");
|
|
845
|
+
let current = config;
|
|
846
|
+
for (const part of parts) {
|
|
847
|
+
if (current === void 0 || current === null || typeof current !== "object") return void 0;
|
|
848
|
+
current = current[part];
|
|
849
|
+
}
|
|
850
|
+
return current;
|
|
851
|
+
}
|
|
852
|
+
function setAtKeyPath(config, keyPath, value) {
|
|
853
|
+
const parts = keyPath.split(".");
|
|
854
|
+
let current = config;
|
|
855
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
856
|
+
if (!current[parts[i]] || typeof current[parts[i]] !== "object") {
|
|
857
|
+
current[parts[i]] = {};
|
|
858
|
+
}
|
|
859
|
+
current = current[parts[i]];
|
|
860
|
+
}
|
|
861
|
+
current[parts[parts.length - 1]] = value;
|
|
862
|
+
}
|
|
431
863
|
function isPiutConfigured(filePath, configKey) {
|
|
432
864
|
const config = readConfig(filePath);
|
|
433
865
|
if (!config) return false;
|
|
434
|
-
const servers = config
|
|
866
|
+
const servers = resolveKeyPath(config, configKey);
|
|
435
867
|
return !!servers?.["piut-context"];
|
|
436
868
|
}
|
|
437
869
|
function mergeConfig(filePath, configKey, serverConfig) {
|
|
438
870
|
const existing = readConfig(filePath) || {};
|
|
439
|
-
const servers = existing
|
|
871
|
+
const servers = resolveKeyPath(existing, configKey) || {};
|
|
440
872
|
servers["piut-context"] = serverConfig;
|
|
441
|
-
existing
|
|
873
|
+
setAtKeyPath(existing, configKey, servers);
|
|
442
874
|
writeConfig(filePath, existing);
|
|
443
875
|
}
|
|
444
876
|
function getPiutConfig(filePath, configKey) {
|
|
445
877
|
const config = readConfig(filePath);
|
|
446
878
|
if (!config) return null;
|
|
447
|
-
const servers = config
|
|
879
|
+
const servers = resolveKeyPath(config, configKey);
|
|
448
880
|
const piut = servers?.["piut-context"];
|
|
449
881
|
return piut ?? null;
|
|
450
882
|
}
|
|
@@ -471,13 +903,35 @@ function extractKeyFromConfig(piutConfig) {
|
|
|
471
903
|
}
|
|
472
904
|
return null;
|
|
473
905
|
}
|
|
906
|
+
function extractSlugFromConfig(piutConfig) {
|
|
907
|
+
const slugFromUrl = (u) => {
|
|
908
|
+
if (typeof u !== "string") return null;
|
|
909
|
+
const m = u.match(/\/api\/mcp\/([^/?#]+)/);
|
|
910
|
+
return m ? m[1] : null;
|
|
911
|
+
};
|
|
912
|
+
const fromUrl = slugFromUrl(piutConfig.url) || slugFromUrl(piutConfig.serverUrl) || slugFromUrl(piutConfig.httpUrl);
|
|
913
|
+
if (fromUrl) return fromUrl;
|
|
914
|
+
const settings = piutConfig.settings;
|
|
915
|
+
if (settings) {
|
|
916
|
+
const fromSettings = slugFromUrl(settings.url);
|
|
917
|
+
if (fromSettings) return fromSettings;
|
|
918
|
+
}
|
|
919
|
+
const args2 = piutConfig.args;
|
|
920
|
+
if (Array.isArray(args2)) {
|
|
921
|
+
for (const arg of args2) {
|
|
922
|
+
const fromArg = slugFromUrl(arg);
|
|
923
|
+
if (fromArg) return fromArg;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return null;
|
|
927
|
+
}
|
|
474
928
|
function removeFromConfig(filePath, configKey) {
|
|
475
929
|
const config = readConfig(filePath);
|
|
476
930
|
if (!config) return false;
|
|
477
|
-
const servers = config
|
|
931
|
+
const servers = resolveKeyPath(config, configKey);
|
|
478
932
|
if (!servers?.["piut-context"]) return false;
|
|
479
933
|
delete servers["piut-context"];
|
|
480
|
-
if (Object.keys(servers).length === 0) {
|
|
934
|
+
if (Object.keys(servers).length === 0 && !configKey.includes(".")) {
|
|
481
935
|
delete config[configKey];
|
|
482
936
|
}
|
|
483
937
|
writeConfig(filePath, config);
|
|
@@ -485,8 +939,9 @@ function removeFromConfig(filePath, configKey) {
|
|
|
485
939
|
}
|
|
486
940
|
|
|
487
941
|
// src/lib/skill.ts
|
|
942
|
+
init_esm_shims();
|
|
488
943
|
import fs2 from "fs";
|
|
489
|
-
import
|
|
944
|
+
import path5 from "path";
|
|
490
945
|
var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
|
|
491
946
|
|
|
492
947
|
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
@@ -529,7 +984,7 @@ p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use
|
|
|
529
984
|
Full skill reference: .piut/skill.md`;
|
|
530
985
|
var SEPARATOR = "\n\n---\n\n";
|
|
531
986
|
function placeSkillFile(filePath) {
|
|
532
|
-
const absPath =
|
|
987
|
+
const absPath = path5.isAbsolute(filePath) ? filePath : path5.resolve(process.cwd(), filePath);
|
|
533
988
|
try {
|
|
534
989
|
const existing = fs2.readFileSync(absPath, "utf-8");
|
|
535
990
|
if (existing.includes("p\u0131ut Context")) {
|
|
@@ -539,7 +994,7 @@ function placeSkillFile(filePath) {
|
|
|
539
994
|
return { created: false, appended: true };
|
|
540
995
|
} catch (err) {
|
|
541
996
|
if (err.code === "ENOENT") {
|
|
542
|
-
fs2.mkdirSync(
|
|
997
|
+
fs2.mkdirSync(path5.dirname(absPath), { recursive: true });
|
|
543
998
|
fs2.writeFileSync(absPath, SKILL_SNIPPET + "\n", "utf-8");
|
|
544
999
|
return { created: true, appended: false };
|
|
545
1000
|
}
|
|
@@ -548,8 +1003,9 @@ function placeSkillFile(filePath) {
|
|
|
548
1003
|
}
|
|
549
1004
|
|
|
550
1005
|
// src/lib/piut-dir.ts
|
|
1006
|
+
init_esm_shims();
|
|
551
1007
|
import fs3 from "fs";
|
|
552
|
-
import
|
|
1008
|
+
import path6 from "path";
|
|
553
1009
|
var API_BASE2 = process.env.PIUT_API_BASE || "https://piut.com";
|
|
554
1010
|
var PIUT_DIR = ".piut";
|
|
555
1011
|
var CONFIG_FILE = "config.json";
|
|
@@ -591,13 +1047,13 @@ Protocol: JSON-RPC 2.0 over HTTPS
|
|
|
591
1047
|
4. Use \`append_brain\` for quick notes and facts
|
|
592
1048
|
`;
|
|
593
1049
|
function piutDir(projectPath) {
|
|
594
|
-
return
|
|
1050
|
+
return path6.join(projectPath, PIUT_DIR);
|
|
595
1051
|
}
|
|
596
1052
|
function writePiutConfig(projectPath, config) {
|
|
597
1053
|
const dir = piutDir(projectPath);
|
|
598
1054
|
fs3.mkdirSync(dir, { recursive: true });
|
|
599
1055
|
fs3.writeFileSync(
|
|
600
|
-
|
|
1056
|
+
path6.join(dir, CONFIG_FILE),
|
|
601
1057
|
JSON.stringify(config, null, 2) + "\n",
|
|
602
1058
|
"utf-8"
|
|
603
1059
|
);
|
|
@@ -614,10 +1070,10 @@ async function writePiutSkill(projectPath, slug, apiKey) {
|
|
|
614
1070
|
content = MINIMAL_SKILL_CONTENT;
|
|
615
1071
|
}
|
|
616
1072
|
content = content.replaceAll("{{slug}}", slug).replaceAll("{{key}}", apiKey);
|
|
617
|
-
fs3.writeFileSync(
|
|
1073
|
+
fs3.writeFileSync(path6.join(dir, SKILL_FILE), content, "utf-8");
|
|
618
1074
|
}
|
|
619
1075
|
function ensureGitignored(projectPath) {
|
|
620
|
-
const gitignorePath =
|
|
1076
|
+
const gitignorePath = path6.join(projectPath, ".gitignore");
|
|
621
1077
|
let content = "";
|
|
622
1078
|
try {
|
|
623
1079
|
content = fs3.readFileSync(gitignorePath, "utf-8");
|
|
@@ -642,10 +1098,11 @@ function removePiutDir(projectPath) {
|
|
|
642
1098
|
return true;
|
|
643
1099
|
}
|
|
644
1100
|
function hasPiutDir(projectPath) {
|
|
645
|
-
return fs3.existsSync(
|
|
1101
|
+
return fs3.existsSync(path6.join(piutDir(projectPath), CONFIG_FILE));
|
|
646
1102
|
}
|
|
647
1103
|
|
|
648
1104
|
// src/lib/ui.ts
|
|
1105
|
+
init_esm_shims();
|
|
649
1106
|
import chalk from "chalk";
|
|
650
1107
|
var brand = chalk.hex("#8B5CF6");
|
|
651
1108
|
var success = chalk.green;
|
|
@@ -731,6 +1188,7 @@ var Spinner = class {
|
|
|
731
1188
|
};
|
|
732
1189
|
|
|
733
1190
|
// src/types.ts
|
|
1191
|
+
init_esm_shims();
|
|
734
1192
|
var CliError = class extends Error {
|
|
735
1193
|
constructor(message) {
|
|
736
1194
|
super(message || "");
|
|
@@ -781,25 +1239,31 @@ async function setupCommand(options) {
|
|
|
781
1239
|
const toolFilter = options.tool;
|
|
782
1240
|
for (const tool of TOOLS) {
|
|
783
1241
|
if (toolFilter && tool.id !== toolFilter) continue;
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
const
|
|
1242
|
+
if (tool.skillOnly) continue;
|
|
1243
|
+
const paths = resolveConfigPaths(tool);
|
|
1244
|
+
for (const { filePath, configKey } of paths) {
|
|
1245
|
+
const exists = fs4.existsSync(filePath);
|
|
1246
|
+
const parentExists = fs4.existsSync(path7.dirname(filePath));
|
|
788
1247
|
if (exists || parentExists) {
|
|
789
|
-
const configured2 = exists && isPiutConfigured(
|
|
1248
|
+
const configured2 = exists && !!configKey && isPiutConfigured(filePath, configKey);
|
|
790
1249
|
let staleKey = false;
|
|
791
|
-
if (configured2) {
|
|
792
|
-
const piutConfig = getPiutConfig(
|
|
1250
|
+
if (configured2 && configKey) {
|
|
1251
|
+
const piutConfig = getPiutConfig(filePath, configKey);
|
|
793
1252
|
if (piutConfig) {
|
|
794
1253
|
const existingKey = extractKeyFromConfig(piutConfig);
|
|
795
1254
|
if (existingKey && existingKey !== apiKey) {
|
|
796
1255
|
staleKey = true;
|
|
797
1256
|
}
|
|
1257
|
+
const existingSlug = extractSlugFromConfig(piutConfig);
|
|
1258
|
+
if (existingSlug && existingSlug !== slug) {
|
|
1259
|
+
staleKey = true;
|
|
1260
|
+
}
|
|
798
1261
|
}
|
|
799
1262
|
}
|
|
800
1263
|
detected.push({
|
|
801
1264
|
tool,
|
|
802
|
-
configPath,
|
|
1265
|
+
configPath: filePath,
|
|
1266
|
+
resolvedConfigKey: configKey,
|
|
803
1267
|
exists,
|
|
804
1268
|
alreadyConfigured: configured2 && !staleKey,
|
|
805
1269
|
staleKey
|
|
@@ -810,7 +1274,6 @@ async function setupCommand(options) {
|
|
|
810
1274
|
}
|
|
811
1275
|
if (detected.length === 0) {
|
|
812
1276
|
console.log(warning(" No supported AI tools detected."));
|
|
813
|
-
console.log(dim(" Supported: Claude Code, Claude Desktop, Cursor, Windsurf, GitHub Copilot, Amazon Q, Zed"));
|
|
814
1277
|
console.log(dim(" See https://piut.com/docs for manual setup."));
|
|
815
1278
|
console.log();
|
|
816
1279
|
return;
|
|
@@ -862,7 +1325,7 @@ async function setupCommand(options) {
|
|
|
862
1325
|
try {
|
|
863
1326
|
execSync(tool.quickCommand(slug, apiKey), { stdio: "pipe" });
|
|
864
1327
|
const claudeJson = expandPath("~/.claude.json");
|
|
865
|
-
const written = getPiutConfig(claudeJson, tool.configKey);
|
|
1328
|
+
const written = tool.configKey ? getPiutConfig(claudeJson, tool.configKey) : null;
|
|
866
1329
|
if (written) {
|
|
867
1330
|
quickSuccess = true;
|
|
868
1331
|
configured.push(tool.name);
|
|
@@ -871,7 +1334,7 @@ async function setupCommand(options) {
|
|
|
871
1334
|
}
|
|
872
1335
|
try {
|
|
873
1336
|
execSync(tool.quickCommand(slug, apiKey) + " --scope user", { stdio: "pipe" });
|
|
874
|
-
const retryCheck = getPiutConfig(claudeJson, tool.configKey);
|
|
1337
|
+
const retryCheck = tool.configKey ? getPiutConfig(claudeJson, tool.configKey) : null;
|
|
875
1338
|
if (retryCheck) {
|
|
876
1339
|
quickSuccess = true;
|
|
877
1340
|
configured.push(tool.name);
|
|
@@ -890,10 +1353,12 @@ async function setupCommand(options) {
|
|
|
890
1353
|
}
|
|
891
1354
|
if (quickSuccess) continue;
|
|
892
1355
|
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1356
|
+
if (tool.generateConfig && det.resolvedConfigKey) {
|
|
1357
|
+
const serverConfig = tool.generateConfig(slug, apiKey);
|
|
1358
|
+
mergeConfig(configPath, det.resolvedConfigKey, serverConfig);
|
|
1359
|
+
configured.push(tool.name);
|
|
1360
|
+
toolLine(tool.name, success("configured"), "\u2714");
|
|
1361
|
+
}
|
|
897
1362
|
}
|
|
898
1363
|
if (!options.skipSkill && configured.length > 0) {
|
|
899
1364
|
const addSkill = options.yes ? true : await confirm({
|
|
@@ -918,7 +1383,7 @@ async function setupCommand(options) {
|
|
|
918
1383
|
}
|
|
919
1384
|
if (configured.length > 0) {
|
|
920
1385
|
const cwd = process.cwd();
|
|
921
|
-
const isProject2 = fs4.existsSync(
|
|
1386
|
+
const isProject2 = fs4.existsSync(path7.join(cwd, ".git")) || fs4.existsSync(path7.join(cwd, "package.json"));
|
|
922
1387
|
if (isProject2) {
|
|
923
1388
|
const { serverUrl } = validationResult;
|
|
924
1389
|
writePiutConfig(cwd, { slug, apiKey, serverUrl });
|
|
@@ -934,7 +1399,8 @@ async function setupCommand(options) {
|
|
|
934
1399
|
console.log(dim(" Verifying..."));
|
|
935
1400
|
for (const det of selected) {
|
|
936
1401
|
if (!configured.includes(det.tool.name)) continue;
|
|
937
|
-
|
|
1402
|
+
if (!det.resolvedConfigKey) continue;
|
|
1403
|
+
const piutConfig = getPiutConfig(det.configPath, det.resolvedConfigKey);
|
|
938
1404
|
if (piutConfig) {
|
|
939
1405
|
toolLine(det.tool.name, success("config verified"), "\u2714");
|
|
940
1406
|
} else {
|
|
@@ -975,50 +1441,20 @@ function isCommandAvailable(cmd) {
|
|
|
975
1441
|
}
|
|
976
1442
|
|
|
977
1443
|
// src/commands/status.ts
|
|
978
|
-
|
|
979
|
-
import
|
|
1444
|
+
init_esm_shims();
|
|
1445
|
+
import fs7 from "fs";
|
|
1446
|
+
import os6 from "os";
|
|
1447
|
+
import path10 from "path";
|
|
980
1448
|
import chalk3 from "chalk";
|
|
981
1449
|
|
|
982
1450
|
// src/lib/brain-scanner.ts
|
|
983
|
-
|
|
984
|
-
import
|
|
985
|
-
import
|
|
1451
|
+
init_esm_shims();
|
|
1452
|
+
import fs5 from "fs";
|
|
1453
|
+
import path8 from "path";
|
|
1454
|
+
import os4 from "os";
|
|
986
1455
|
|
|
987
1456
|
// src/lib/file-types.ts
|
|
988
|
-
|
|
989
|
-
var DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
990
|
-
".pdf",
|
|
991
|
-
".docx",
|
|
992
|
-
".doc",
|
|
993
|
-
".pptx",
|
|
994
|
-
".pages",
|
|
995
|
-
".key",
|
|
996
|
-
".rtf",
|
|
997
|
-
".odt",
|
|
998
|
-
".odp",
|
|
999
|
-
".eml",
|
|
1000
|
-
".mbox"
|
|
1001
|
-
]);
|
|
1002
|
-
var PLAIN_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1003
|
-
".md",
|
|
1004
|
-
".markdown",
|
|
1005
|
-
".txt",
|
|
1006
|
-
".csv",
|
|
1007
|
-
".xml",
|
|
1008
|
-
".html",
|
|
1009
|
-
".htm",
|
|
1010
|
-
".json",
|
|
1011
|
-
".yaml",
|
|
1012
|
-
".yml",
|
|
1013
|
-
".toml",
|
|
1014
|
-
".rst",
|
|
1015
|
-
".adoc",
|
|
1016
|
-
".tex",
|
|
1017
|
-
".ini",
|
|
1018
|
-
".cfg",
|
|
1019
|
-
".conf",
|
|
1020
|
-
".log"
|
|
1021
|
-
]);
|
|
1457
|
+
init_esm_shims();
|
|
1022
1458
|
var AI_CONFIG_FILENAMES = /* @__PURE__ */ new Set([
|
|
1023
1459
|
"CLAUDE.md",
|
|
1024
1460
|
".cursorrules",
|
|
@@ -1032,350 +1468,46 @@ var AI_CONFIG_FILENAMES = /* @__PURE__ */ new Set([
|
|
|
1032
1468
|
"IDENTITY.md",
|
|
1033
1469
|
"copilot-instructions.md"
|
|
1034
1470
|
]);
|
|
1035
|
-
var PARSEABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1036
|
-
...DOCUMENT_EXTENSIONS,
|
|
1037
|
-
...PLAIN_TEXT_EXTENSIONS
|
|
1038
|
-
]);
|
|
1039
|
-
var EXCLUDED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1040
|
-
".js",
|
|
1041
|
-
".ts",
|
|
1042
|
-
".jsx",
|
|
1043
|
-
".tsx",
|
|
1044
|
-
".mjs",
|
|
1045
|
-
".cjs",
|
|
1046
|
-
".py",
|
|
1047
|
-
".pyw",
|
|
1048
|
-
".pyi",
|
|
1049
|
-
".rs",
|
|
1050
|
-
".go",
|
|
1051
|
-
".java",
|
|
1052
|
-
".kt",
|
|
1053
|
-
".scala",
|
|
1054
|
-
".clj",
|
|
1055
|
-
".c",
|
|
1056
|
-
".cpp",
|
|
1057
|
-
".cc",
|
|
1058
|
-
".h",
|
|
1059
|
-
".hpp",
|
|
1060
|
-
".cs",
|
|
1061
|
-
".rb",
|
|
1062
|
-
".php",
|
|
1063
|
-
".swift",
|
|
1064
|
-
".m",
|
|
1065
|
-
".mm",
|
|
1066
|
-
".lua",
|
|
1067
|
-
".r",
|
|
1068
|
-
".jl",
|
|
1069
|
-
".zig",
|
|
1070
|
-
".nim",
|
|
1071
|
-
".v",
|
|
1072
|
-
".sh",
|
|
1073
|
-
".bash",
|
|
1074
|
-
".zsh",
|
|
1075
|
-
".fish",
|
|
1076
|
-
".ps1",
|
|
1077
|
-
".bat",
|
|
1078
|
-
".cmd",
|
|
1079
|
-
".sql",
|
|
1080
|
-
".graphql",
|
|
1081
|
-
".gql",
|
|
1082
|
-
".proto",
|
|
1083
|
-
".css",
|
|
1084
|
-
".scss",
|
|
1085
|
-
".sass",
|
|
1086
|
-
".less",
|
|
1087
|
-
".styl",
|
|
1088
|
-
".xls",
|
|
1089
|
-
".xlsx",
|
|
1090
|
-
".numbers",
|
|
1091
|
-
".sqlite",
|
|
1092
|
-
".db",
|
|
1093
|
-
".dat",
|
|
1094
|
-
".parquet",
|
|
1095
|
-
".avro",
|
|
1096
|
-
".png",
|
|
1097
|
-
".jpg",
|
|
1098
|
-
".jpeg",
|
|
1099
|
-
".gif",
|
|
1100
|
-
".svg",
|
|
1101
|
-
".ico",
|
|
1102
|
-
".webp",
|
|
1103
|
-
".bmp",
|
|
1104
|
-
".tiff",
|
|
1105
|
-
".heic",
|
|
1106
|
-
".mp3",
|
|
1107
|
-
".mp4",
|
|
1108
|
-
".wav",
|
|
1109
|
-
".aac",
|
|
1110
|
-
".flac",
|
|
1111
|
-
".ogg",
|
|
1112
|
-
".avi",
|
|
1113
|
-
".mov",
|
|
1114
|
-
".mkv",
|
|
1115
|
-
".wmv",
|
|
1116
|
-
".webm",
|
|
1117
|
-
".zip",
|
|
1118
|
-
".tar",
|
|
1119
|
-
".gz",
|
|
1120
|
-
".bz2",
|
|
1121
|
-
".xz",
|
|
1122
|
-
".7z",
|
|
1123
|
-
".rar",
|
|
1124
|
-
".dmg",
|
|
1125
|
-
".iso",
|
|
1126
|
-
".exe",
|
|
1127
|
-
".dll",
|
|
1128
|
-
".so",
|
|
1129
|
-
".dylib",
|
|
1130
|
-
".o",
|
|
1131
|
-
".a",
|
|
1132
|
-
".wasm",
|
|
1133
|
-
".class",
|
|
1134
|
-
".jar",
|
|
1135
|
-
".pyc",
|
|
1136
|
-
".pyo",
|
|
1137
|
-
".ttf",
|
|
1138
|
-
".otf",
|
|
1139
|
-
".woff",
|
|
1140
|
-
".woff2",
|
|
1141
|
-
".eot",
|
|
1142
|
-
".lock",
|
|
1143
|
-
".map",
|
|
1144
|
-
".min.js",
|
|
1145
|
-
".min.css"
|
|
1146
|
-
]);
|
|
1147
|
-
function canParse(filename) {
|
|
1148
|
-
return PARSEABLE_EXTENSIONS.has(path7.extname(filename).toLowerCase());
|
|
1149
|
-
}
|
|
1150
|
-
function isAiConfigFile(filename) {
|
|
1151
|
-
return AI_CONFIG_FILENAMES.has(path7.basename(filename));
|
|
1152
|
-
}
|
|
1153
|
-
function getFileCategory(filename) {
|
|
1154
|
-
const base = path7.basename(filename);
|
|
1155
|
-
if (AI_CONFIG_FILENAMES.has(base)) return "config";
|
|
1156
|
-
const ext = path7.extname(filename).toLowerCase();
|
|
1157
|
-
if (DOCUMENT_EXTENSIONS.has(ext)) return "document";
|
|
1158
|
-
if (PLAIN_TEXT_EXTENSIONS.has(ext)) return "text";
|
|
1159
|
-
if (EXCLUDED_EXTENSIONS.has(ext)) return "excluded";
|
|
1160
|
-
return "unknown";
|
|
1161
|
-
}
|
|
1162
1471
|
|
|
1163
|
-
// src/lib/
|
|
1164
|
-
import fs5 from "fs";
|
|
1165
|
-
import path8 from "path";
|
|
1166
|
-
var MAX_RAW_SIZE = 500 * 1024;
|
|
1167
|
-
var MAX_EXTRACTED_TEXT = 100 * 1024;
|
|
1168
|
-
function truncate(text) {
|
|
1169
|
-
if (text.length <= MAX_EXTRACTED_TEXT) return text;
|
|
1170
|
-
return text.slice(0, MAX_EXTRACTED_TEXT);
|
|
1171
|
-
}
|
|
1172
|
-
function stripHtml(html) {
|
|
1173
|
-
return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<\/div>/gi, "\n").replace(/<\/li>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/\n{3,}/g, "\n\n").trim();
|
|
1174
|
-
}
|
|
1175
|
-
async function parsePdf(buffer) {
|
|
1176
|
-
const { PDFParse } = await import("pdf-parse");
|
|
1177
|
-
const parser = new PDFParse({ data: new Uint8Array(buffer) });
|
|
1178
|
-
const result = await parser.getText();
|
|
1179
|
-
return result.text;
|
|
1180
|
-
}
|
|
1181
|
-
async function parseDocx(buffer) {
|
|
1182
|
-
const { extractRawText } = await import("mammoth");
|
|
1183
|
-
const result = await extractRawText({ buffer });
|
|
1184
|
-
return result.value;
|
|
1185
|
-
}
|
|
1186
|
-
async function parsePptx(buffer) {
|
|
1187
|
-
const JSZip = (await import("jszip")).default;
|
|
1188
|
-
const zip = await JSZip.loadAsync(buffer);
|
|
1189
|
-
const texts = [];
|
|
1190
|
-
const slideFiles = Object.keys(zip.files).filter((name) => /^ppt\/slides\/slide\d+\.xml$/i.test(name)).sort((a, b) => {
|
|
1191
|
-
const numA = parseInt(a.match(/slide(\d+)/)?.[1] || "0");
|
|
1192
|
-
const numB = parseInt(b.match(/slide(\d+)/)?.[1] || "0");
|
|
1193
|
-
return numA - numB;
|
|
1194
|
-
});
|
|
1195
|
-
for (const slidePath of slideFiles) {
|
|
1196
|
-
const xml = await zip.files[slidePath].async("string");
|
|
1197
|
-
const matches = xml.match(/<a:t[^>]*>([^<]*)<\/a:t>/g);
|
|
1198
|
-
if (matches) {
|
|
1199
|
-
texts.push(matches.map((m) => m.replace(/<[^>]+>/g, "")).join(" "));
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
return texts.join("\n\n");
|
|
1203
|
-
}
|
|
1204
|
-
async function parseAppleDoc(buffer) {
|
|
1205
|
-
const JSZip = (await import("jszip")).default;
|
|
1206
|
-
const zip = await JSZip.loadAsync(buffer);
|
|
1207
|
-
if (zip.files["preview.pdf"]) {
|
|
1208
|
-
const pdfBuf = await zip.files["preview.pdf"].async("nodebuffer");
|
|
1209
|
-
try {
|
|
1210
|
-
return await parsePdf(pdfBuf);
|
|
1211
|
-
} catch {
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
const texts = [];
|
|
1215
|
-
for (const [name, file] of Object.entries(zip.files)) {
|
|
1216
|
-
if (name.endsWith(".iwa") && !file.dir) {
|
|
1217
|
-
try {
|
|
1218
|
-
const buf = await file.async("nodebuffer");
|
|
1219
|
-
const str = buf.toString("utf-8");
|
|
1220
|
-
const readable = str.match(/[\x20-\x7E\xC0-\xFF]{4,}/g);
|
|
1221
|
-
if (readable) {
|
|
1222
|
-
texts.push(...readable.filter((s) => s.length > 10));
|
|
1223
|
-
}
|
|
1224
|
-
} catch {
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
return texts.join("\n\n");
|
|
1229
|
-
}
|
|
1230
|
-
async function parseRtf(buffer) {
|
|
1231
|
-
const { fromString } = await import("@iarna/rtf-to-html");
|
|
1232
|
-
return new Promise((resolve, reject) => {
|
|
1233
|
-
fromString(buffer.toString("utf-8"), (err, html) => {
|
|
1234
|
-
if (err) {
|
|
1235
|
-
reject(err);
|
|
1236
|
-
return;
|
|
1237
|
-
}
|
|
1238
|
-
resolve(stripHtml(html));
|
|
1239
|
-
});
|
|
1240
|
-
});
|
|
1241
|
-
}
|
|
1242
|
-
async function parseOpenDocument(buffer) {
|
|
1243
|
-
const JSZip = (await import("jszip")).default;
|
|
1244
|
-
const zip = await JSZip.loadAsync(buffer);
|
|
1245
|
-
const contentXml = zip.files["content.xml"];
|
|
1246
|
-
if (!contentXml) return "";
|
|
1247
|
-
return stripHtml(await contentXml.async("string"));
|
|
1248
|
-
}
|
|
1249
|
-
function parseEml(buffer) {
|
|
1250
|
-
const content = buffer.toString("utf-8");
|
|
1251
|
-
const boundaryMatch = content.match(/boundary="?([^\s"]+)"?/i);
|
|
1252
|
-
if (boundaryMatch) {
|
|
1253
|
-
const parts = content.split(`--${boundaryMatch[1]}`);
|
|
1254
|
-
for (const part of parts) {
|
|
1255
|
-
if (/content-type:\s*text\/plain/i.test(part)) {
|
|
1256
|
-
const bodyStart = part.indexOf("\n\n");
|
|
1257
|
-
if (bodyStart !== -1) return part.slice(bodyStart + 2).trim();
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
const headerEnd = content.indexOf("\n\n");
|
|
1262
|
-
if (headerEnd !== -1) {
|
|
1263
|
-
const body = content.slice(headerEnd + 2);
|
|
1264
|
-
if (!/<html/i.test(body.slice(0, 200))) return body.trim();
|
|
1265
|
-
return stripHtml(body);
|
|
1266
|
-
}
|
|
1267
|
-
return content.trim();
|
|
1268
|
-
}
|
|
1269
|
-
function parseMbox(buffer) {
|
|
1270
|
-
const content = buffer.toString("utf-8");
|
|
1271
|
-
const messages = content.split(/^From /m).filter(Boolean);
|
|
1272
|
-
const texts = [];
|
|
1273
|
-
for (const msg of messages.slice(0, 50)) {
|
|
1274
|
-
const headerEnd = msg.indexOf("\n\n");
|
|
1275
|
-
if (headerEnd !== -1) {
|
|
1276
|
-
const body = msg.slice(headerEnd + 2);
|
|
1277
|
-
texts.push(!/<html/i.test(body.slice(0, 200)) ? body.trim() : stripHtml(body));
|
|
1278
|
-
}
|
|
1279
|
-
}
|
|
1280
|
-
return texts.join("\n\n---\n\n");
|
|
1281
|
-
}
|
|
1282
|
-
async function extractTextFromFile(filePath) {
|
|
1283
|
-
const ext = path8.extname(filePath).toLowerCase();
|
|
1284
|
-
try {
|
|
1285
|
-
const stat = fs5.statSync(filePath);
|
|
1286
|
-
if (!stat.isFile()) return null;
|
|
1287
|
-
if (stat.size > MAX_RAW_SIZE) return null;
|
|
1288
|
-
} catch {
|
|
1289
|
-
return null;
|
|
1290
|
-
}
|
|
1291
|
-
if (PLAIN_TEXT_EXTENSIONS.has(ext)) {
|
|
1292
|
-
try {
|
|
1293
|
-
return truncate(fs5.readFileSync(filePath, "utf-8"));
|
|
1294
|
-
} catch {
|
|
1295
|
-
return null;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
if (!DOCUMENT_EXTENSIONS.has(ext)) return null;
|
|
1299
|
-
try {
|
|
1300
|
-
const buffer = fs5.readFileSync(filePath);
|
|
1301
|
-
let text;
|
|
1302
|
-
switch (ext) {
|
|
1303
|
-
case ".pdf":
|
|
1304
|
-
text = await parsePdf(buffer);
|
|
1305
|
-
break;
|
|
1306
|
-
case ".docx":
|
|
1307
|
-
case ".doc":
|
|
1308
|
-
text = await parseDocx(buffer);
|
|
1309
|
-
break;
|
|
1310
|
-
case ".pptx":
|
|
1311
|
-
text = await parsePptx(buffer);
|
|
1312
|
-
break;
|
|
1313
|
-
case ".pages":
|
|
1314
|
-
case ".key":
|
|
1315
|
-
text = await parseAppleDoc(buffer);
|
|
1316
|
-
break;
|
|
1317
|
-
case ".rtf":
|
|
1318
|
-
text = await parseRtf(buffer);
|
|
1319
|
-
break;
|
|
1320
|
-
case ".odt":
|
|
1321
|
-
case ".odp":
|
|
1322
|
-
text = await parseOpenDocument(buffer);
|
|
1323
|
-
break;
|
|
1324
|
-
case ".eml":
|
|
1325
|
-
text = parseEml(buffer);
|
|
1326
|
-
break;
|
|
1327
|
-
case ".mbox":
|
|
1328
|
-
text = parseMbox(buffer);
|
|
1329
|
-
break;
|
|
1330
|
-
default:
|
|
1331
|
-
return null;
|
|
1332
|
-
}
|
|
1333
|
-
return truncate(text);
|
|
1334
|
-
} catch {
|
|
1335
|
-
return null;
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
function formatSize(bytes) {
|
|
1339
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
1340
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1341
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
// src/lib/folder-tree.ts
|
|
1345
|
-
import fs6 from "fs";
|
|
1346
|
-
import path9 from "path";
|
|
1347
|
-
import os4 from "os";
|
|
1472
|
+
// src/lib/brain-scanner.ts
|
|
1348
1473
|
var home = os4.homedir();
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1474
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1475
|
+
"node_modules",
|
|
1476
|
+
".git",
|
|
1477
|
+
"__pycache__",
|
|
1478
|
+
".venv",
|
|
1479
|
+
"venv",
|
|
1480
|
+
"dist",
|
|
1481
|
+
"build",
|
|
1482
|
+
".next",
|
|
1483
|
+
".nuxt",
|
|
1484
|
+
".output",
|
|
1485
|
+
".Trash",
|
|
1486
|
+
"Library",
|
|
1487
|
+
".cache",
|
|
1488
|
+
".npm",
|
|
1489
|
+
".yarn",
|
|
1490
|
+
".pnpm-store",
|
|
1491
|
+
"Caches",
|
|
1492
|
+
"Cache",
|
|
1493
|
+
".piut"
|
|
1494
|
+
]);
|
|
1495
|
+
var SCAN_DOT_DIRS = /* @__PURE__ */ new Set([
|
|
1496
|
+
".cursor",
|
|
1497
|
+
".windsurf",
|
|
1498
|
+
".github",
|
|
1499
|
+
".zed",
|
|
1500
|
+
".amazonq",
|
|
1501
|
+
".vscode",
|
|
1502
|
+
".gemini",
|
|
1503
|
+
".openclaw",
|
|
1504
|
+
".mcporter",
|
|
1505
|
+
".paperclip"
|
|
1506
|
+
// .claude intentionally excluded — useful files collected by collectGlobalConfigFiles()
|
|
1507
|
+
]);
|
|
1508
|
+
function shouldSkipDir(name) {
|
|
1509
|
+
if (name.startsWith(".") && !SCAN_DOT_DIRS.has(name)) return true;
|
|
1510
|
+
return SKIP_DIRS.has(name);
|
|
1379
1511
|
}
|
|
1380
1512
|
var SKIP_HOME_DIRS = /* @__PURE__ */ new Set([
|
|
1381
1513
|
"Library",
|
|
@@ -1388,33 +1520,36 @@ var SKIP_HOME_DIRS = /* @__PURE__ */ new Set([
|
|
|
1388
1520
|
".Trash"
|
|
1389
1521
|
]);
|
|
1390
1522
|
var INCLUDE_DOT_DIRS = /* @__PURE__ */ new Set([
|
|
1391
|
-
".claude",
|
|
1392
1523
|
".cursor",
|
|
1393
1524
|
".windsurf",
|
|
1394
1525
|
".openclaw",
|
|
1395
1526
|
".zed",
|
|
1396
1527
|
".github",
|
|
1397
|
-
".amazonq"
|
|
1528
|
+
".amazonq",
|
|
1529
|
+
".gemini",
|
|
1530
|
+
".mcporter",
|
|
1531
|
+
".paperclip"
|
|
1532
|
+
// .claude excluded — useful files collected by collectGlobalConfigFiles()
|
|
1398
1533
|
]);
|
|
1399
1534
|
function getDefaultScanDirs() {
|
|
1400
1535
|
const dirs = [];
|
|
1401
1536
|
try {
|
|
1402
|
-
const entries =
|
|
1537
|
+
const entries = fs5.readdirSync(home, { withFileTypes: true });
|
|
1403
1538
|
for (const entry of entries) {
|
|
1404
1539
|
if (!entry.isDirectory()) continue;
|
|
1405
1540
|
if (entry.name.startsWith(".") && !INCLUDE_DOT_DIRS.has(entry.name)) continue;
|
|
1406
1541
|
if (SKIP_HOME_DIRS.has(entry.name)) continue;
|
|
1407
|
-
dirs.push(
|
|
1542
|
+
dirs.push(path8.join(home, entry.name));
|
|
1408
1543
|
}
|
|
1409
1544
|
} catch {
|
|
1410
1545
|
}
|
|
1411
|
-
const cloudStorage =
|
|
1546
|
+
const cloudStorage = path8.join(home, "Library", "CloudStorage");
|
|
1412
1547
|
try {
|
|
1413
|
-
if (
|
|
1414
|
-
const entries =
|
|
1548
|
+
if (fs5.existsSync(cloudStorage) && fs5.statSync(cloudStorage).isDirectory()) {
|
|
1549
|
+
const entries = fs5.readdirSync(cloudStorage, { withFileTypes: true });
|
|
1415
1550
|
for (const entry of entries) {
|
|
1416
1551
|
if (!entry.isDirectory()) continue;
|
|
1417
|
-
const fullPath =
|
|
1552
|
+
const fullPath = path8.join(cloudStorage, entry.name);
|
|
1418
1553
|
if (!dirs.includes(fullPath)) {
|
|
1419
1554
|
dirs.push(fullPath);
|
|
1420
1555
|
}
|
|
@@ -1425,60 +1560,23 @@ function getDefaultScanDirs() {
|
|
|
1425
1560
|
if (dirs.length === 0) dirs.push(home);
|
|
1426
1561
|
return dirs;
|
|
1427
1562
|
}
|
|
1428
|
-
|
|
1429
|
-
// src/lib/brain-scanner.ts
|
|
1430
|
-
var home2 = os5.homedir();
|
|
1431
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1432
|
-
"node_modules",
|
|
1433
|
-
".git",
|
|
1434
|
-
"__pycache__",
|
|
1435
|
-
".venv",
|
|
1436
|
-
"venv",
|
|
1437
|
-
"dist",
|
|
1438
|
-
"build",
|
|
1439
|
-
".next",
|
|
1440
|
-
".nuxt",
|
|
1441
|
-
".output",
|
|
1442
|
-
".Trash",
|
|
1443
|
-
"Library",
|
|
1444
|
-
".cache",
|
|
1445
|
-
".npm",
|
|
1446
|
-
".yarn",
|
|
1447
|
-
".pnpm-store",
|
|
1448
|
-
"Caches",
|
|
1449
|
-
"Cache",
|
|
1450
|
-
".piut"
|
|
1451
|
-
]);
|
|
1452
|
-
var SCAN_DOT_DIRS = /* @__PURE__ */ new Set([
|
|
1453
|
-
".claude",
|
|
1454
|
-
".cursor",
|
|
1455
|
-
".windsurf",
|
|
1456
|
-
".github",
|
|
1457
|
-
".zed",
|
|
1458
|
-
".amazonq",
|
|
1459
|
-
".vscode"
|
|
1460
|
-
]);
|
|
1461
|
-
function shouldSkipDir(name) {
|
|
1462
|
-
if (name.startsWith(".") && !SCAN_DOT_DIRS.has(name)) return true;
|
|
1463
|
-
return SKIP_DIRS.has(name);
|
|
1464
|
-
}
|
|
1465
1563
|
function isProject(dirPath) {
|
|
1466
|
-
return
|
|
1564
|
+
return fs5.existsSync(path8.join(dirPath, ".git")) || fs5.existsSync(path8.join(dirPath, "package.json")) || fs5.existsSync(path8.join(dirPath, "Cargo.toml")) || fs5.existsSync(path8.join(dirPath, "pyproject.toml")) || fs5.existsSync(path8.join(dirPath, "go.mod"));
|
|
1467
1565
|
}
|
|
1468
1566
|
function buildProjectInfo(projectPath) {
|
|
1469
|
-
const hasPkgJson =
|
|
1567
|
+
const hasPkgJson = fs5.existsSync(path8.join(projectPath, "package.json"));
|
|
1470
1568
|
let description = "";
|
|
1471
1569
|
if (hasPkgJson) {
|
|
1472
1570
|
try {
|
|
1473
|
-
const pkg = JSON.parse(
|
|
1571
|
+
const pkg = JSON.parse(fs5.readFileSync(path8.join(projectPath, "package.json"), "utf-8"));
|
|
1474
1572
|
description = pkg.description || "";
|
|
1475
1573
|
} catch {
|
|
1476
1574
|
}
|
|
1477
1575
|
}
|
|
1478
|
-
const readmePath =
|
|
1479
|
-
if (!description &&
|
|
1576
|
+
const readmePath = path8.join(projectPath, "README.md");
|
|
1577
|
+
if (!description && fs5.existsSync(readmePath)) {
|
|
1480
1578
|
try {
|
|
1481
|
-
const content =
|
|
1579
|
+
const content = fs5.readFileSync(readmePath, "utf-8");
|
|
1482
1580
|
const lines = content.split("\n");
|
|
1483
1581
|
let foundHeading = false;
|
|
1484
1582
|
for (const line of lines) {
|
|
@@ -1495,15 +1593,15 @@ function buildProjectInfo(projectPath) {
|
|
|
1495
1593
|
}
|
|
1496
1594
|
}
|
|
1497
1595
|
return {
|
|
1498
|
-
name:
|
|
1596
|
+
name: path8.basename(projectPath),
|
|
1499
1597
|
path: projectPath,
|
|
1500
1598
|
description,
|
|
1501
|
-
hasClaudeMd:
|
|
1502
|
-
hasCursorRules:
|
|
1503
|
-
hasWindsurfRules:
|
|
1504
|
-
hasCopilotInstructions:
|
|
1505
|
-
hasConventionsMd:
|
|
1506
|
-
hasZedRules:
|
|
1599
|
+
hasClaudeMd: fs5.existsSync(path8.join(projectPath, "CLAUDE.md")) || fs5.existsSync(path8.join(projectPath, ".claude", "rules")),
|
|
1600
|
+
hasCursorRules: fs5.existsSync(path8.join(projectPath, ".cursorrules")) || fs5.existsSync(path8.join(projectPath, ".cursor", "rules")),
|
|
1601
|
+
hasWindsurfRules: fs5.existsSync(path8.join(projectPath, ".windsurfrules")) || fs5.existsSync(path8.join(projectPath, ".windsurf", "rules")),
|
|
1602
|
+
hasCopilotInstructions: fs5.existsSync(path8.join(projectPath, ".github", "copilot-instructions.md")) || fs5.existsSync(path8.join(projectPath, ".github", "instructions")),
|
|
1603
|
+
hasConventionsMd: fs5.existsSync(path8.join(projectPath, "CONVENTIONS.md")) || fs5.existsSync(path8.join(projectPath, ".amazonq", "rules")),
|
|
1604
|
+
hasZedRules: fs5.existsSync(path8.join(projectPath, ".rules"))
|
|
1507
1605
|
};
|
|
1508
1606
|
}
|
|
1509
1607
|
var MAX_PROJECT_DEPTH = 4;
|
|
@@ -1513,17 +1611,17 @@ function detectProjects(scanDirs, onProgress) {
|
|
|
1513
1611
|
function walk(dir, depth) {
|
|
1514
1612
|
if (depth > MAX_PROJECT_DEPTH) return;
|
|
1515
1613
|
try {
|
|
1516
|
-
const items =
|
|
1614
|
+
const items = fs5.readdirSync(dir, { withFileTypes: true });
|
|
1517
1615
|
for (const item of items) {
|
|
1518
1616
|
if (!item.isDirectory()) continue;
|
|
1519
1617
|
if (shouldSkipDir(item.name)) continue;
|
|
1520
|
-
const fullPath =
|
|
1618
|
+
const fullPath = path8.join(dir, item.name);
|
|
1521
1619
|
if (seen.has(fullPath)) continue;
|
|
1522
1620
|
seen.add(fullPath);
|
|
1523
1621
|
if (isProject(fullPath)) {
|
|
1524
1622
|
const info = buildProjectInfo(fullPath);
|
|
1525
1623
|
projects.push(info);
|
|
1526
|
-
onProgress?.({ phase: "projects", message: `${info.name} (${fullPath
|
|
1624
|
+
onProgress?.({ phase: "projects", message: `${info.name} (${fullPath})` });
|
|
1527
1625
|
} else {
|
|
1528
1626
|
walk(fullPath, depth + 1);
|
|
1529
1627
|
}
|
|
@@ -1537,34 +1635,41 @@ function detectProjects(scanDirs, onProgress) {
|
|
|
1537
1635
|
return projects;
|
|
1538
1636
|
}
|
|
1539
1637
|
var MAX_CONFIG_SIZE = 100 * 1024;
|
|
1540
|
-
|
|
1638
|
+
var MAX_BRAIN_INPUT_BYTES = 1e6;
|
|
1639
|
+
function collectGlobalConfigFiles(onProgress) {
|
|
1541
1640
|
const configs = [];
|
|
1542
1641
|
const globalPaths = [
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1642
|
+
path8.join(home, ".claude", "MEMORY.md"),
|
|
1643
|
+
path8.join(home, ".claude", "CLAUDE.md"),
|
|
1644
|
+
path8.join(home, ".openclaw", "workspace", "SOUL.md"),
|
|
1645
|
+
path8.join(home, ".openclaw", "workspace", "MEMORY.md"),
|
|
1646
|
+
path8.join(home, ".gemini", "MEMORY.md"),
|
|
1647
|
+
path8.join(home, ".paperclip", "IDENTITY.md")
|
|
1547
1648
|
];
|
|
1548
1649
|
for (const gp of globalPaths) {
|
|
1549
1650
|
try {
|
|
1550
|
-
const stat =
|
|
1651
|
+
const stat = fs5.statSync(gp);
|
|
1551
1652
|
if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
|
|
1552
|
-
const content =
|
|
1653
|
+
const content = fs5.readFileSync(gp, "utf-8");
|
|
1553
1654
|
if (content.trim()) {
|
|
1554
|
-
const name =
|
|
1655
|
+
const name = path8.relative(home, gp);
|
|
1555
1656
|
configs.push({ name, content });
|
|
1556
1657
|
onProgress?.({ phase: "configs", message: name });
|
|
1557
1658
|
}
|
|
1558
1659
|
} catch {
|
|
1559
1660
|
}
|
|
1560
1661
|
}
|
|
1662
|
+
return configs;
|
|
1663
|
+
}
|
|
1664
|
+
function collectProjectConfigFiles(projects, onProgress) {
|
|
1665
|
+
const configs = [];
|
|
1561
1666
|
for (const project of projects) {
|
|
1562
1667
|
for (const fileName of AI_CONFIG_FILENAMES) {
|
|
1563
|
-
const filePath =
|
|
1668
|
+
const filePath = path8.join(project.path, fileName);
|
|
1564
1669
|
try {
|
|
1565
|
-
const stat =
|
|
1670
|
+
const stat = fs5.statSync(filePath);
|
|
1566
1671
|
if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
|
|
1567
|
-
const content =
|
|
1672
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
1568
1673
|
if (content.trim()) {
|
|
1569
1674
|
const name = `${project.name}/${fileName}`;
|
|
1570
1675
|
configs.push({ name, content });
|
|
@@ -1573,128 +1678,40 @@ function collectConfigFiles(projects, onProgress) {
|
|
|
1573
1678
|
} catch {
|
|
1574
1679
|
}
|
|
1575
1680
|
}
|
|
1576
|
-
const pkgPath =
|
|
1681
|
+
const pkgPath = path8.join(project.path, "package.json");
|
|
1577
1682
|
try {
|
|
1578
|
-
const stat =
|
|
1683
|
+
const stat = fs5.statSync(pkgPath);
|
|
1579
1684
|
if (stat.isFile() && stat.size <= MAX_CONFIG_SIZE) {
|
|
1580
|
-
const pkg = JSON.parse(
|
|
1685
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
1581
1686
|
const summary = JSON.stringify({ name: pkg.name, description: pkg.description }, null, 2);
|
|
1582
|
-
configs.push({ name: `${project.name}/package.json`, content: summary });
|
|
1583
|
-
onProgress?.({ phase: "configs", message: `${project.name}/package.json` });
|
|
1584
|
-
}
|
|
1585
|
-
} catch {
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
return configs;
|
|
1589
|
-
}
|
|
1590
|
-
var MAX_SCAN_DEPTH = 6;
|
|
1591
|
-
var MAX_FILES = 500;
|
|
1592
|
-
async function scanFilesInDirs(dirs, onProgress) {
|
|
1593
|
-
const files = [];
|
|
1594
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1595
|
-
function walk(dir, depth) {
|
|
1596
|
-
if (depth > MAX_SCAN_DEPTH) return [];
|
|
1597
|
-
const found = [];
|
|
1598
|
-
try {
|
|
1599
|
-
const items = fs7.readdirSync(dir, { withFileTypes: true });
|
|
1600
|
-
for (const item of items) {
|
|
1601
|
-
if (item.isDirectory()) {
|
|
1602
|
-
if (!shouldSkipDir(item.name)) {
|
|
1603
|
-
found.push(...walk(path10.join(dir, item.name), depth + 1));
|
|
1604
|
-
}
|
|
1605
|
-
} else if (item.isFile()) {
|
|
1606
|
-
if (canParse(item.name) && !isAiConfigFile(item.name)) {
|
|
1607
|
-
const fullPath = path10.join(dir, item.name);
|
|
1608
|
-
if (!seen.has(fullPath)) {
|
|
1609
|
-
seen.add(fullPath);
|
|
1610
|
-
found.push(fullPath);
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
} catch {
|
|
1616
|
-
}
|
|
1617
|
-
return found;
|
|
1618
|
-
}
|
|
1619
|
-
const allPaths = [];
|
|
1620
|
-
for (const dir of dirs) {
|
|
1621
|
-
onProgress?.({ phase: "scanning", message: displayPath(dir) });
|
|
1622
|
-
allPaths.push(...walk(dir, 0));
|
|
1623
|
-
if (allPaths.length > MAX_FILES) break;
|
|
1624
|
-
}
|
|
1625
|
-
const pathsToProcess = allPaths.slice(0, MAX_FILES);
|
|
1626
|
-
for (const filePath of pathsToProcess) {
|
|
1627
|
-
try {
|
|
1628
|
-
const stat = fs7.statSync(filePath);
|
|
1629
|
-
onProgress?.({ phase: "parsing", message: displayPath(filePath) });
|
|
1630
|
-
const content = await extractTextFromFile(filePath);
|
|
1631
|
-
if (content && content.trim()) {
|
|
1632
|
-
const category = getFileCategory(filePath);
|
|
1633
|
-
files.push({
|
|
1634
|
-
path: filePath,
|
|
1635
|
-
displayPath: displayPath(filePath),
|
|
1636
|
-
content,
|
|
1637
|
-
format: category === "document" ? path10.extname(filePath).slice(1) : "text",
|
|
1638
|
-
sizeBytes: stat.size,
|
|
1639
|
-
folder: path10.dirname(filePath)
|
|
1640
|
-
});
|
|
1687
|
+
configs.push({ name: `${project.name}/package.json`, content: summary });
|
|
1688
|
+
onProgress?.({ phase: "configs", message: `${project.name}/package.json` });
|
|
1641
1689
|
}
|
|
1642
1690
|
} catch {
|
|
1643
1691
|
}
|
|
1644
1692
|
}
|
|
1645
|
-
return
|
|
1646
|
-
}
|
|
1647
|
-
async function scanFolders(dirs, onProgress) {
|
|
1648
|
-
const allFiles = await scanFilesInDirs(dirs, onProgress);
|
|
1649
|
-
const folders = groupFilesByFolder(allFiles);
|
|
1650
|
-
const projects = detectProjects(dirs, onProgress);
|
|
1651
|
-
const configFiles = collectConfigFiles(projects, onProgress);
|
|
1652
|
-
const totalFiles = allFiles.length;
|
|
1653
|
-
const totalBytes = allFiles.reduce((sum, f) => sum + f.sizeBytes, 0);
|
|
1654
|
-
return { folders, configFiles, projects, allFiles, totalFiles, totalBytes };
|
|
1655
|
-
}
|
|
1656
|
-
function buildBrainInput(scanResult, selectedFolderPaths) {
|
|
1657
|
-
const selectedSet = new Set(selectedFolderPaths);
|
|
1658
|
-
const selectedFiles = scanResult.allFiles.filter((f) => selectedSet.has(f.folder));
|
|
1659
|
-
const folderTree = [];
|
|
1660
|
-
for (const folder of scanResult.folders) {
|
|
1661
|
-
if (selectedSet.has(folder.path)) {
|
|
1662
|
-
folderTree.push(`${folder.displayPath}/ (${folder.fileCount} files)`);
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
const personalDocuments = selectedFiles.map((f) => ({
|
|
1666
|
-
name: f.displayPath,
|
|
1667
|
-
content: f.content,
|
|
1668
|
-
format: f.format
|
|
1669
|
-
}));
|
|
1670
|
-
return {
|
|
1671
|
-
summary: {
|
|
1672
|
-
folders: folderTree,
|
|
1673
|
-
projects: scanResult.projects.map((p) => ({
|
|
1674
|
-
name: p.name,
|
|
1675
|
-
path: p.path.replace(home2, "~"),
|
|
1676
|
-
description: p.description
|
|
1677
|
-
})),
|
|
1678
|
-
configFiles: scanResult.configFiles,
|
|
1679
|
-
recentDocuments: [],
|
|
1680
|
-
personalDocuments
|
|
1681
|
-
}
|
|
1682
|
-
};
|
|
1693
|
+
return configs;
|
|
1683
1694
|
}
|
|
1684
1695
|
function scanForProjects(folders) {
|
|
1685
1696
|
const scanDirs = folders || getDefaultScanDirs();
|
|
1686
1697
|
return detectProjects(scanDirs);
|
|
1687
1698
|
}
|
|
1699
|
+
function formatSize(bytes) {
|
|
1700
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
1701
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
1702
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1703
|
+
}
|
|
1688
1704
|
|
|
1689
1705
|
// src/lib/store.ts
|
|
1690
|
-
|
|
1691
|
-
import
|
|
1692
|
-
import
|
|
1693
|
-
|
|
1694
|
-
var
|
|
1706
|
+
init_esm_shims();
|
|
1707
|
+
import fs6 from "fs";
|
|
1708
|
+
import path9 from "path";
|
|
1709
|
+
import os5 from "os";
|
|
1710
|
+
var CONFIG_DIR = path9.join(os5.homedir(), ".piut");
|
|
1711
|
+
var CONFIG_FILE2 = path9.join(CONFIG_DIR, "config.json");
|
|
1695
1712
|
function readStore() {
|
|
1696
1713
|
try {
|
|
1697
|
-
const raw =
|
|
1714
|
+
const raw = fs6.readFileSync(CONFIG_FILE2, "utf-8");
|
|
1698
1715
|
return JSON.parse(raw);
|
|
1699
1716
|
} catch {
|
|
1700
1717
|
return {};
|
|
@@ -1703,18 +1720,19 @@ function readStore() {
|
|
|
1703
1720
|
function updateStore(updates) {
|
|
1704
1721
|
const config = readStore();
|
|
1705
1722
|
const updated = { ...config, ...updates };
|
|
1706
|
-
|
|
1707
|
-
|
|
1723
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1724
|
+
fs6.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
1708
1725
|
return updated;
|
|
1709
1726
|
}
|
|
1710
1727
|
function clearStore() {
|
|
1711
1728
|
try {
|
|
1712
|
-
|
|
1729
|
+
fs6.unlinkSync(CONFIG_FILE2);
|
|
1713
1730
|
} catch {
|
|
1714
1731
|
}
|
|
1715
1732
|
}
|
|
1716
1733
|
|
|
1717
1734
|
// src/commands/status.ts
|
|
1735
|
+
var API_BASE3 = process.env.PIUT_API_BASE || "https://piut.com";
|
|
1718
1736
|
var PIUT_FILES = [
|
|
1719
1737
|
"CLAUDE.md",
|
|
1720
1738
|
".cursor/rules/piut.mdc",
|
|
@@ -1725,27 +1743,69 @@ var PIUT_FILES = [
|
|
|
1725
1743
|
];
|
|
1726
1744
|
function hasPiutReference(filePath) {
|
|
1727
1745
|
try {
|
|
1728
|
-
const content =
|
|
1746
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1729
1747
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
1730
1748
|
} catch {
|
|
1731
1749
|
return false;
|
|
1732
1750
|
}
|
|
1733
1751
|
}
|
|
1752
|
+
async function fetchRemoteConnections(key) {
|
|
1753
|
+
try {
|
|
1754
|
+
const res = await fetch(`${API_BASE3}/api/mcp/connections`, {
|
|
1755
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
1756
|
+
});
|
|
1757
|
+
if (!res.ok) return [];
|
|
1758
|
+
const data = await res.json();
|
|
1759
|
+
return data.connections || [];
|
|
1760
|
+
} catch {
|
|
1761
|
+
return [];
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
async function fetchRemoteProjects(key) {
|
|
1765
|
+
try {
|
|
1766
|
+
const res = await fetch(`${API_BASE3}/api/cli/projects`, {
|
|
1767
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
1768
|
+
});
|
|
1769
|
+
if (!res.ok) return [];
|
|
1770
|
+
const data = await res.json();
|
|
1771
|
+
return data.projects || [];
|
|
1772
|
+
} catch {
|
|
1773
|
+
return [];
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
function machineLabel(hostname, machineId) {
|
|
1777
|
+
if (hostname) return hostname;
|
|
1778
|
+
if (machineId && machineId !== "unknown") return machineId.slice(0, 8);
|
|
1779
|
+
return "unknown";
|
|
1780
|
+
}
|
|
1781
|
+
function timeAgo(dateStr) {
|
|
1782
|
+
const diff = Date.now() - new Date(dateStr).getTime();
|
|
1783
|
+
const minutes = Math.floor(diff / 6e4);
|
|
1784
|
+
if (minutes < 1) return "just now";
|
|
1785
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
1786
|
+
const hours = Math.floor(minutes / 60);
|
|
1787
|
+
if (hours < 24) return `${hours}h ago`;
|
|
1788
|
+
const days = Math.floor(hours / 24);
|
|
1789
|
+
if (days < 7) return `${days}d ago`;
|
|
1790
|
+
return new Date(dateStr).toLocaleDateString();
|
|
1791
|
+
}
|
|
1734
1792
|
async function statusCommand(options = {}) {
|
|
1735
1793
|
banner();
|
|
1736
1794
|
if (options.verify) {
|
|
1737
1795
|
await verifyStatus();
|
|
1738
1796
|
return;
|
|
1739
1797
|
}
|
|
1740
|
-
|
|
1798
|
+
const thisHostname = os6.hostname();
|
|
1799
|
+
const thisMachineId = getMachineId2();
|
|
1800
|
+
console.log(` AI tools on this machine ${dim(`(${thisHostname})`)}:`);
|
|
1741
1801
|
console.log();
|
|
1742
1802
|
let foundAny = false;
|
|
1743
1803
|
for (const tool of TOOLS) {
|
|
1744
|
-
const paths = resolveConfigPaths(tool
|
|
1745
|
-
for (const
|
|
1746
|
-
if (!
|
|
1804
|
+
const paths = resolveConfigPaths(tool);
|
|
1805
|
+
for (const { filePath, configKey } of paths) {
|
|
1806
|
+
if (!fs7.existsSync(filePath)) continue;
|
|
1747
1807
|
foundAny = true;
|
|
1748
|
-
const configured = isPiutConfigured(
|
|
1808
|
+
const configured = isPiutConfigured(filePath, configKey);
|
|
1749
1809
|
if (configured) {
|
|
1750
1810
|
toolLine(tool.name, success("connected"), "\u2714");
|
|
1751
1811
|
} else {
|
|
@@ -1759,15 +1819,15 @@ async function statusCommand(options = {}) {
|
|
|
1759
1819
|
console.log(dim(" Run ") + brand("piut setup") + dim(" to configure your AI tools."));
|
|
1760
1820
|
}
|
|
1761
1821
|
console.log();
|
|
1762
|
-
console.log(
|
|
1822
|
+
console.log(` Connected projects on this machine:`);
|
|
1763
1823
|
console.log();
|
|
1764
1824
|
const projects = scanForProjects();
|
|
1765
1825
|
let connectedCount = 0;
|
|
1766
1826
|
for (const project of projects) {
|
|
1767
1827
|
const connectedFiles = [];
|
|
1768
1828
|
for (const file of PIUT_FILES) {
|
|
1769
|
-
const absPath =
|
|
1770
|
-
if (
|
|
1829
|
+
const absPath = path10.join(project.path, file);
|
|
1830
|
+
if (fs7.existsSync(absPath) && hasPiutReference(absPath)) {
|
|
1771
1831
|
connectedFiles.push(file);
|
|
1772
1832
|
}
|
|
1773
1833
|
}
|
|
@@ -1784,6 +1844,33 @@ async function statusCommand(options = {}) {
|
|
|
1784
1844
|
console.log(dim(` ${connectedCount} project(s) connected to your brain.`));
|
|
1785
1845
|
}
|
|
1786
1846
|
console.log();
|
|
1847
|
+
const store = readStore();
|
|
1848
|
+
if (store.apiKey) {
|
|
1849
|
+
const [remoteConnections, remoteProjects] = await Promise.all([
|
|
1850
|
+
fetchRemoteConnections(store.apiKey),
|
|
1851
|
+
fetchRemoteProjects(store.apiKey)
|
|
1852
|
+
]);
|
|
1853
|
+
const otherMachineConns = remoteConnections.filter((c) => c.machine_id !== thisMachineId);
|
|
1854
|
+
const otherMachineProjects = remoteProjects.filter((p) => p.machineId !== thisMachineId);
|
|
1855
|
+
if (otherMachineConns.length > 0 || otherMachineProjects.length > 0) {
|
|
1856
|
+
console.log(` Other machines:`);
|
|
1857
|
+
console.log();
|
|
1858
|
+
if (otherMachineConns.length > 0) {
|
|
1859
|
+
for (const conn of otherMachineConns) {
|
|
1860
|
+
const machine = machineLabel(conn.hostname, conn.machine_id);
|
|
1861
|
+
const age = timeAgo(conn.last_connected_at);
|
|
1862
|
+
console.log(dim(` ${conn.tool_name}`) + dim(` @${machine}`) + dim(` \u2014 ${conn.request_count} requests, ${age}`));
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
if (otherMachineProjects.length > 0) {
|
|
1866
|
+
for (const proj of otherMachineProjects) {
|
|
1867
|
+
const machine = machineLabel(proj.hostname, proj.machineId);
|
|
1868
|
+
console.log(dim(` ${proj.projectName}`) + dim(` @${machine}:${proj.projectPath}`));
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
console.log();
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1787
1874
|
}
|
|
1788
1875
|
async function verifyStatus() {
|
|
1789
1876
|
const store = readStore();
|
|
@@ -1811,19 +1898,19 @@ async function verifyStatus() {
|
|
|
1811
1898
|
console.log();
|
|
1812
1899
|
console.log(" Tool Configurations");
|
|
1813
1900
|
for (const tool of TOOLS) {
|
|
1814
|
-
const paths = resolveConfigPaths(tool
|
|
1815
|
-
for (const
|
|
1816
|
-
if (!
|
|
1817
|
-
const piutConfig = getPiutConfig(
|
|
1901
|
+
const paths = resolveConfigPaths(tool);
|
|
1902
|
+
for (const { filePath, configKey } of paths) {
|
|
1903
|
+
if (!fs7.existsSync(filePath)) continue;
|
|
1904
|
+
const piutConfig = getPiutConfig(filePath, configKey);
|
|
1818
1905
|
if (!piutConfig) {
|
|
1819
1906
|
toolLine(tool.name, dim("installed, not connected"), "\u25CB");
|
|
1820
1907
|
break;
|
|
1821
1908
|
}
|
|
1822
|
-
const
|
|
1823
|
-
if (
|
|
1909
|
+
const extractedKey = extractKeyFromConfig(piutConfig);
|
|
1910
|
+
if (extractedKey && extractedKey === store.apiKey) {
|
|
1824
1911
|
toolLine(tool.name, success("key matches"), "\u2714");
|
|
1825
|
-
} else if (
|
|
1826
|
-
const masked =
|
|
1912
|
+
} else if (extractedKey) {
|
|
1913
|
+
const masked = extractedKey.slice(0, 6) + "...";
|
|
1827
1914
|
toolLine(tool.name, chalk3.red(`key STALE (${masked})`), "\u2717");
|
|
1828
1915
|
issues++;
|
|
1829
1916
|
} else {
|
|
@@ -1856,16 +1943,18 @@ async function verifyStatus() {
|
|
|
1856
1943
|
}
|
|
1857
1944
|
|
|
1858
1945
|
// src/commands/remove.ts
|
|
1859
|
-
|
|
1946
|
+
init_esm_shims();
|
|
1947
|
+
import fs8 from "fs";
|
|
1860
1948
|
import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
|
|
1861
1949
|
async function removeCommand() {
|
|
1862
1950
|
banner();
|
|
1863
1951
|
const configured = [];
|
|
1864
1952
|
for (const tool of TOOLS) {
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1953
|
+
if (!tool.configKey) continue;
|
|
1954
|
+
const paths = resolveConfigPaths(tool);
|
|
1955
|
+
for (const { filePath, configKey } of paths) {
|
|
1956
|
+
if (fs8.existsSync(filePath) && isPiutConfigured(filePath, configKey)) {
|
|
1957
|
+
configured.push({ tool, configPath: filePath, resolvedConfigKey: configKey });
|
|
1869
1958
|
break;
|
|
1870
1959
|
}
|
|
1871
1960
|
}
|
|
@@ -1894,8 +1983,9 @@ async function removeCommand() {
|
|
|
1894
1983
|
if (!proceed) return;
|
|
1895
1984
|
console.log();
|
|
1896
1985
|
const removedNames = [];
|
|
1897
|
-
for (const { tool, configPath } of selected) {
|
|
1898
|
-
|
|
1986
|
+
for (const { tool, configPath, resolvedConfigKey } of selected) {
|
|
1987
|
+
if (!resolvedConfigKey) continue;
|
|
1988
|
+
const removed = removeFromConfig(configPath, resolvedConfigKey);
|
|
1899
1989
|
if (removed) {
|
|
1900
1990
|
removedNames.push(tool.name);
|
|
1901
1991
|
toolLine(tool.name, success("removed"), "\u2714");
|
|
@@ -1914,11 +2004,13 @@ async function removeCommand() {
|
|
|
1914
2004
|
}
|
|
1915
2005
|
|
|
1916
2006
|
// src/commands/build.ts
|
|
1917
|
-
|
|
2007
|
+
init_esm_shims();
|
|
2008
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
1918
2009
|
import chalk5 from "chalk";
|
|
1919
2010
|
import os7 from "os";
|
|
1920
2011
|
|
|
1921
2012
|
// src/lib/auth.ts
|
|
2013
|
+
init_esm_shims();
|
|
1922
2014
|
import { select, input, password as password2 } from "@inquirer/prompts";
|
|
1923
2015
|
import { exec } from "child_process";
|
|
1924
2016
|
import chalk4 from "chalk";
|
|
@@ -2031,66 +2123,53 @@ async function resolveApiKeyWithResult(keyOption) {
|
|
|
2031
2123
|
async function buildCommand(options) {
|
|
2032
2124
|
banner();
|
|
2033
2125
|
const { apiKey, serverUrl } = await resolveApiKeyWithResult(options.key);
|
|
2034
|
-
|
|
2035
|
-
if (options.folders) {
|
|
2036
|
-
scanDirs = options.folders.split(",").map((f) => expandPath(f.trim()));
|
|
2037
|
-
} else {
|
|
2038
|
-
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"));
|
|
2039
|
-
console.log();
|
|
2040
|
-
console.log(dim(" This is a local scan only \u2014 no files leave your machine"));
|
|
2041
|
-
console.log(dim(" until you review and explicitly approve."));
|
|
2042
|
-
console.log();
|
|
2043
|
-
scanDirs = await selectFolders();
|
|
2044
|
-
}
|
|
2045
|
-
if (scanDirs.length === 0) {
|
|
2046
|
-
console.log(chalk5.yellow(" No folders selected."));
|
|
2047
|
-
return;
|
|
2048
|
-
}
|
|
2126
|
+
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"));
|
|
2049
2127
|
console.log();
|
|
2050
|
-
console.log(dim(" Scanning
|
|
2128
|
+
console.log(dim(" Scanning for AI config files..."));
|
|
2051
2129
|
console.log();
|
|
2052
|
-
|
|
2130
|
+
const cwd = process.cwd();
|
|
2131
|
+
const homeDirs = getDefaultScanDirs();
|
|
2132
|
+
const allScanDirs = [cwd, ...homeDirs.filter((d) => !d.startsWith(cwd) && !cwd.startsWith(d))];
|
|
2053
2133
|
const onProgress = (progress) => {
|
|
2054
|
-
if (progress.phase === "
|
|
2055
|
-
console.log(dim(` ${progress.message}`));
|
|
2056
|
-
} else if (progress.phase === "parsing") {
|
|
2057
|
-
fileCount++;
|
|
2058
|
-
console.log(dim(` [${fileCount}] ${progress.message}`));
|
|
2059
|
-
} else if (progress.phase === "projects") {
|
|
2134
|
+
if (progress.phase === "projects") {
|
|
2060
2135
|
console.log(dim(` [project] ${progress.message}`));
|
|
2061
2136
|
} else if (progress.phase === "configs") {
|
|
2062
2137
|
console.log(dim(` [config] ${progress.message}`));
|
|
2063
2138
|
}
|
|
2064
2139
|
};
|
|
2065
|
-
const
|
|
2066
|
-
|
|
2067
|
-
|
|
2140
|
+
const projects = detectProjects(allScanDirs, onProgress);
|
|
2141
|
+
const globalConfigs = collectGlobalConfigFiles(onProgress);
|
|
2142
|
+
const projectConfigs = collectProjectConfigFiles(projects, onProgress);
|
|
2143
|
+
const allConfigs = [...globalConfigs, ...projectConfigs];
|
|
2068
2144
|
console.log();
|
|
2069
|
-
if (
|
|
2070
|
-
console.log(
|
|
2071
|
-
|
|
2145
|
+
if (allConfigs.length > 0) {
|
|
2146
|
+
console.log(success(` \u2713 Found ${allConfigs.length} config file${allConfigs.length === 1 ? "" : "s"} in ${projects.length} project${projects.length === 1 ? "" : "s"}`));
|
|
2147
|
+
} else {
|
|
2148
|
+
console.log(dim(" No AI config files found."));
|
|
2149
|
+
}
|
|
2150
|
+
const totalBytes = allConfigs.reduce((sum, c) => sum + Buffer.byteLength(c.content, "utf-8"), 0);
|
|
2151
|
+
const totalFiles = allConfigs.length;
|
|
2152
|
+
if (totalFiles === 0) {
|
|
2153
|
+
console.log();
|
|
2154
|
+
console.log(chalk5.yellow(" No config files found to build your brain."));
|
|
2155
|
+
console.log(dim(" Add AI config files (CLAUDE.md, .cursorrules, etc.) to your projects,"));
|
|
2156
|
+
console.log(dim(" or upload documents via: piut vault upload <file>"));
|
|
2072
2157
|
console.log();
|
|
2073
2158
|
return;
|
|
2074
2159
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
selectedFolderPaths = await reviewFolders(scanResult);
|
|
2080
|
-
}
|
|
2081
|
-
if (selectedFolderPaths.length === 0 && scanResult.configFiles.length === 0) {
|
|
2082
|
-
console.log(chalk5.yellow(" No folders selected."));
|
|
2160
|
+
if (totalBytes > MAX_BRAIN_INPUT_BYTES) {
|
|
2161
|
+
console.log();
|
|
2162
|
+
console.log(chalk5.yellow(` Total data: ${formatSize(totalBytes)} exceeds the 1MB limit.`));
|
|
2163
|
+
console.log();
|
|
2083
2164
|
return;
|
|
2084
2165
|
}
|
|
2085
|
-
const selectedFolders = scanResult.folders.filter((f) => selectedFolderPaths.includes(f.path));
|
|
2086
|
-
const totalSelectedFiles = selectedFolders.reduce((sum, f) => sum + f.fileCount, 0) + scanResult.configFiles.length;
|
|
2087
2166
|
if (!options.yes) {
|
|
2088
2167
|
console.log();
|
|
2089
2168
|
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"));
|
|
2090
2169
|
console.log();
|
|
2091
|
-
console.log(dim(` ${
|
|
2092
|
-
console.log(dim("
|
|
2093
|
-
console.log(dim(
|
|
2170
|
+
console.log(dim(` ${totalFiles} file${totalFiles === 1 ? "" : "s"} (${formatSize(totalBytes)}) will be processed by Claude Sonnet to design your brain.`));
|
|
2171
|
+
console.log(dim(" File contents are used for brain generation only and"));
|
|
2172
|
+
console.log(dim(` are not retained by p\u0131ut.`));
|
|
2094
2173
|
console.log();
|
|
2095
2174
|
console.log(dim(` Privacy policy: ${brand("https://piut.com/privacy")}`));
|
|
2096
2175
|
console.log();
|
|
@@ -2105,7 +2184,20 @@ async function buildCommand(options) {
|
|
|
2105
2184
|
return;
|
|
2106
2185
|
}
|
|
2107
2186
|
}
|
|
2108
|
-
const
|
|
2187
|
+
const home2 = os7.homedir();
|
|
2188
|
+
const brainInput = {
|
|
2189
|
+
summary: {
|
|
2190
|
+
projects: projects.map((p) => ({
|
|
2191
|
+
name: p.name,
|
|
2192
|
+
path: p.path.replace(home2, "~"),
|
|
2193
|
+
description: p.description
|
|
2194
|
+
})),
|
|
2195
|
+
configFiles: allConfigs
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
await streamBuild(apiKey, serverUrl, brainInput, options);
|
|
2199
|
+
}
|
|
2200
|
+
async function streamBuild(apiKey, serverUrl, brainInput, options) {
|
|
2109
2201
|
const spinner = new Spinner();
|
|
2110
2202
|
spinner.start("Generating brain...");
|
|
2111
2203
|
try {
|
|
@@ -2205,85 +2297,13 @@ async function buildCommand(options) {
|
|
|
2205
2297
|
spinner.stop();
|
|
2206
2298
|
if (err instanceof CliError) throw err;
|
|
2207
2299
|
const msg = err.message || "Unknown error";
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
throw new CliError(hint);
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
async function selectFolders() {
|
|
2214
|
-
const defaults = getDefaultScanDirs();
|
|
2215
|
-
const homeDir = os7.homedir();
|
|
2216
|
-
const ALL_VALUE = "__all__";
|
|
2217
|
-
const CUSTOM_VALUE = "__custom__";
|
|
2218
|
-
const choices = [
|
|
2219
|
-
{ name: `All home folders (~) ${chalk5.dim("(Recommended)")}`, value: ALL_VALUE },
|
|
2220
|
-
{ name: chalk5.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), value: "__sep__", disabled: true },
|
|
2221
|
-
...defaults.map((d) => ({ name: displayPath(d), value: d })),
|
|
2222
|
-
{ name: chalk5.dim("Browse to a folder..."), value: CUSTOM_VALUE }
|
|
2223
|
-
];
|
|
2224
|
-
const selected = await checkbox3({
|
|
2225
|
-
message: "Select folders to scan:",
|
|
2226
|
-
choices,
|
|
2227
|
-
required: true
|
|
2228
|
-
});
|
|
2229
|
-
let scanDirs;
|
|
2230
|
-
if (selected.includes(ALL_VALUE)) {
|
|
2231
|
-
scanDirs = defaults;
|
|
2232
|
-
} else {
|
|
2233
|
-
scanDirs = selected.filter((v) => v !== CUSTOM_VALUE);
|
|
2234
|
-
if (selected.includes(CUSTOM_VALUE)) {
|
|
2235
|
-
const custom = await input2({
|
|
2236
|
-
message: "Enter folder path(s), comma-separated:"
|
|
2237
|
-
});
|
|
2238
|
-
const customPaths = custom.split(",").map((f) => expandPath(f.trim())).filter(Boolean);
|
|
2239
|
-
scanDirs = [...scanDirs, ...customPaths];
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
if (scanDirs.length > 0 && !selected.includes(ALL_VALUE)) {
|
|
2243
|
-
console.log();
|
|
2244
|
-
console.log(dim(" Selected:"));
|
|
2245
|
-
for (const d of scanDirs) {
|
|
2246
|
-
console.log(dim(` ${displayPath(d)}`));
|
|
2247
|
-
}
|
|
2248
|
-
const addMore = await select2({
|
|
2249
|
-
message: "Add more folders or continue?",
|
|
2250
|
-
choices: [
|
|
2251
|
-
{ name: "Continue with scan", value: "continue" },
|
|
2252
|
-
{ name: "Add another folder...", value: "add" }
|
|
2253
|
-
]
|
|
2254
|
-
});
|
|
2255
|
-
if (addMore === "add") {
|
|
2256
|
-
const extra = await input2({
|
|
2257
|
-
message: "Enter folder path(s), comma-separated:"
|
|
2258
|
-
});
|
|
2259
|
-
const extraPaths = extra.split(",").map((f) => expandPath(f.trim())).filter(Boolean);
|
|
2260
|
-
scanDirs = [...scanDirs, ...extraPaths];
|
|
2261
|
-
}
|
|
2300
|
+
console.log(chalk5.red(` \u2717 ${msg}`));
|
|
2301
|
+
throw new CliError(msg);
|
|
2262
2302
|
}
|
|
2263
|
-
return scanDirs;
|
|
2264
|
-
}
|
|
2265
|
-
async function reviewFolders(scanResult) {
|
|
2266
|
-
if (scanResult.folders.length === 0) return [];
|
|
2267
|
-
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"));
|
|
2268
|
-
console.log();
|
|
2269
|
-
console.log(dim(" All folders selected by default. Deselect any you want to exclude."));
|
|
2270
|
-
console.log();
|
|
2271
|
-
const choices = scanResult.folders.map((folder) => ({
|
|
2272
|
-
name: formatFolderChoice(folder),
|
|
2273
|
-
value: folder.path,
|
|
2274
|
-
checked: true
|
|
2275
|
-
}));
|
|
2276
|
-
const selected = await checkbox3({
|
|
2277
|
-
message: "Select folders to include in your brain:",
|
|
2278
|
-
choices
|
|
2279
|
-
});
|
|
2280
|
-
const selectedFolders = scanResult.folders.filter((f) => selected.includes(f.path));
|
|
2281
|
-
console.log();
|
|
2282
|
-
console.log(dim(` ${formatSelectionSummary(selectedFolders)}`));
|
|
2283
|
-
return selected;
|
|
2284
2303
|
}
|
|
2285
2304
|
|
|
2286
2305
|
// src/commands/deploy.ts
|
|
2306
|
+
init_esm_shims();
|
|
2287
2307
|
import chalk6 from "chalk";
|
|
2288
2308
|
async function deployCommand(options) {
|
|
2289
2309
|
banner();
|
|
@@ -2321,33 +2341,34 @@ async function deployCommand(options) {
|
|
|
2321
2341
|
}
|
|
2322
2342
|
|
|
2323
2343
|
// src/commands/connect.ts
|
|
2324
|
-
|
|
2325
|
-
import
|
|
2326
|
-
import
|
|
2344
|
+
init_esm_shims();
|
|
2345
|
+
import fs9 from "fs";
|
|
2346
|
+
import path11 from "path";
|
|
2347
|
+
import { checkbox as checkbox3 } from "@inquirer/prompts";
|
|
2327
2348
|
var RULE_FILES = [
|
|
2328
2349
|
{
|
|
2329
2350
|
tool: "Claude Code",
|
|
2330
2351
|
filePath: "CLAUDE.md",
|
|
2331
2352
|
strategy: "append",
|
|
2332
|
-
detect: (p) => p.hasClaudeMd ||
|
|
2353
|
+
detect: (p) => p.hasClaudeMd || fs9.existsSync(path11.join(p.path, ".claude"))
|
|
2333
2354
|
},
|
|
2334
2355
|
{
|
|
2335
2356
|
tool: "Cursor",
|
|
2336
2357
|
filePath: ".cursor/rules/piut.mdc",
|
|
2337
2358
|
strategy: "create",
|
|
2338
|
-
detect: (p) => p.hasCursorRules ||
|
|
2359
|
+
detect: (p) => p.hasCursorRules || fs9.existsSync(path11.join(p.path, ".cursor"))
|
|
2339
2360
|
},
|
|
2340
2361
|
{
|
|
2341
2362
|
tool: "Windsurf",
|
|
2342
2363
|
filePath: ".windsurf/rules/piut.md",
|
|
2343
2364
|
strategy: "create",
|
|
2344
|
-
detect: (p) => p.hasWindsurfRules ||
|
|
2365
|
+
detect: (p) => p.hasWindsurfRules || fs9.existsSync(path11.join(p.path, ".windsurf"))
|
|
2345
2366
|
},
|
|
2346
2367
|
{
|
|
2347
2368
|
tool: "GitHub Copilot",
|
|
2348
2369
|
filePath: ".github/copilot-instructions.md",
|
|
2349
2370
|
strategy: "append",
|
|
2350
|
-
detect: (p) => p.hasCopilotInstructions ||
|
|
2371
|
+
detect: (p) => p.hasCopilotInstructions || fs9.existsSync(path11.join(p.path, ".github"))
|
|
2351
2372
|
},
|
|
2352
2373
|
{
|
|
2353
2374
|
tool: "Amazon Q",
|
|
@@ -2359,7 +2380,19 @@ var RULE_FILES = [
|
|
|
2359
2380
|
tool: "Zed",
|
|
2360
2381
|
filePath: ".zed/rules.md",
|
|
2361
2382
|
strategy: "create",
|
|
2362
|
-
detect: (p) => p.hasZedRules ||
|
|
2383
|
+
detect: (p) => p.hasZedRules || fs9.existsSync(path11.join(p.path, ".zed"))
|
|
2384
|
+
},
|
|
2385
|
+
{
|
|
2386
|
+
tool: "Gemini CLI",
|
|
2387
|
+
filePath: "GEMINI.md",
|
|
2388
|
+
strategy: "append",
|
|
2389
|
+
detect: (p) => fs9.existsSync(path11.join(p.path, ".gemini"))
|
|
2390
|
+
},
|
|
2391
|
+
{
|
|
2392
|
+
tool: "Paperclip",
|
|
2393
|
+
filePath: "AGENTS.md",
|
|
2394
|
+
strategy: "append",
|
|
2395
|
+
detect: (p) => fs9.existsSync(path11.join(p.path, ".paperclip"))
|
|
2363
2396
|
}
|
|
2364
2397
|
];
|
|
2365
2398
|
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut-context)
|
|
@@ -2394,7 +2427,7 @@ Full skill reference: .piut/skill.md
|
|
|
2394
2427
|
`;
|
|
2395
2428
|
function hasPiutReference2(filePath) {
|
|
2396
2429
|
try {
|
|
2397
|
-
const content =
|
|
2430
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
2398
2431
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
2399
2432
|
} catch {
|
|
2400
2433
|
return false;
|
|
@@ -2417,13 +2450,13 @@ async function connectCommand(options) {
|
|
|
2417
2450
|
console.log();
|
|
2418
2451
|
return;
|
|
2419
2452
|
}
|
|
2420
|
-
let
|
|
2453
|
+
let scanFolders;
|
|
2421
2454
|
if (options.folders) {
|
|
2422
|
-
|
|
2455
|
+
scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
|
|
2423
2456
|
}
|
|
2424
2457
|
console.log();
|
|
2425
2458
|
console.log(dim(" Scanning for projects..."));
|
|
2426
|
-
const projects = scanForProjects(
|
|
2459
|
+
const projects = scanForProjects(scanFolders);
|
|
2427
2460
|
if (projects.length === 0) {
|
|
2428
2461
|
console.log(warning(" No projects found."));
|
|
2429
2462
|
console.log(dim(" Try running from a directory with your projects, or use --folders."));
|
|
@@ -2434,20 +2467,20 @@ async function connectCommand(options) {
|
|
|
2434
2467
|
for (const project of projects) {
|
|
2435
2468
|
for (const rule of RULE_FILES) {
|
|
2436
2469
|
if (!rule.detect(project)) continue;
|
|
2437
|
-
const absPath =
|
|
2438
|
-
if (
|
|
2470
|
+
const absPath = path11.join(project.path, rule.filePath);
|
|
2471
|
+
if (fs9.existsSync(absPath) && hasPiutReference2(absPath)) continue;
|
|
2439
2472
|
actions.push({
|
|
2440
2473
|
project,
|
|
2441
2474
|
tool: rule.tool,
|
|
2442
2475
|
filePath: rule.filePath,
|
|
2443
2476
|
absPath,
|
|
2444
|
-
action: rule.strategy === "create" || !
|
|
2477
|
+
action: rule.strategy === "create" || !fs9.existsSync(absPath) ? "create" : "append"
|
|
2445
2478
|
});
|
|
2446
2479
|
}
|
|
2447
2480
|
const hasAnyAction = actions.some((a) => a.project === project);
|
|
2448
2481
|
if (!hasAnyAction) {
|
|
2449
|
-
const claudeMdPath =
|
|
2450
|
-
if (!
|
|
2482
|
+
const claudeMdPath = path11.join(project.path, "CLAUDE.md");
|
|
2483
|
+
if (!fs9.existsSync(claudeMdPath)) {
|
|
2451
2484
|
actions.push({
|
|
2452
2485
|
project,
|
|
2453
2486
|
tool: "Claude Code",
|
|
@@ -2487,7 +2520,7 @@ async function connectCommand(options) {
|
|
|
2487
2520
|
console.log();
|
|
2488
2521
|
const projectChoices = [];
|
|
2489
2522
|
for (const [projectPath, projectActions] of byProject) {
|
|
2490
|
-
const projectName =
|
|
2523
|
+
const projectName = path11.basename(projectPath);
|
|
2491
2524
|
const desc = projectActions.map((a) => {
|
|
2492
2525
|
const verb = a.action === "create" ? "will create" : "will append to";
|
|
2493
2526
|
return `${verb} ${a.filePath}`;
|
|
@@ -2502,7 +2535,7 @@ async function connectCommand(options) {
|
|
|
2502
2535
|
if (options.yes) {
|
|
2503
2536
|
selectedPaths = Array.from(byProject.keys());
|
|
2504
2537
|
} else {
|
|
2505
|
-
selectedPaths = await
|
|
2538
|
+
selectedPaths = await checkbox3({
|
|
2506
2539
|
message: "Select projects to connect:",
|
|
2507
2540
|
choices: projectChoices
|
|
2508
2541
|
});
|
|
@@ -2516,15 +2549,15 @@ async function connectCommand(options) {
|
|
|
2516
2549
|
const copilotTool = TOOLS.find((t) => t.id === "copilot");
|
|
2517
2550
|
for (const projectPath of selectedPaths) {
|
|
2518
2551
|
const projectActions = byProject.get(projectPath) || [];
|
|
2519
|
-
const projectName =
|
|
2552
|
+
const projectName = path11.basename(projectPath);
|
|
2520
2553
|
writePiutConfig(projectPath, { slug, apiKey, serverUrl });
|
|
2521
2554
|
await writePiutSkill(projectPath, slug, apiKey);
|
|
2522
2555
|
ensureGitignored(projectPath);
|
|
2523
2556
|
console.log(success(` \u2713 ${projectName}/.piut/`) + dim(" \u2014 credentials + skill"));
|
|
2524
2557
|
if (copilotTool) {
|
|
2525
|
-
const hasCopilot =
|
|
2558
|
+
const hasCopilot = fs9.existsSync(path11.join(projectPath, ".github", "copilot-instructions.md")) || fs9.existsSync(path11.join(projectPath, ".github"));
|
|
2526
2559
|
if (hasCopilot) {
|
|
2527
|
-
const vscodeMcpPath =
|
|
2560
|
+
const vscodeMcpPath = path11.join(projectPath, ".vscode", "mcp.json");
|
|
2528
2561
|
const serverConfig = copilotTool.generateConfig(slug, apiKey);
|
|
2529
2562
|
mergeConfig(vscodeMcpPath, copilotTool.configKey, serverConfig);
|
|
2530
2563
|
console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014 Copilot MCP"));
|
|
@@ -2534,26 +2567,28 @@ async function connectCommand(options) {
|
|
|
2534
2567
|
if (action.action === "create") {
|
|
2535
2568
|
const isAppendType = RULE_FILES.find((r) => r.filePath === action.filePath)?.strategy === "append";
|
|
2536
2569
|
const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
|
|
2537
|
-
|
|
2538
|
-
|
|
2570
|
+
fs9.mkdirSync(path11.dirname(action.absPath), { recursive: true });
|
|
2571
|
+
fs9.writeFileSync(action.absPath, content, "utf-8");
|
|
2539
2572
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 created"));
|
|
2540
2573
|
} else {
|
|
2541
|
-
|
|
2574
|
+
fs9.appendFileSync(action.absPath, APPEND_SECTION);
|
|
2542
2575
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 appended"));
|
|
2543
2576
|
}
|
|
2544
2577
|
connected++;
|
|
2545
2578
|
}
|
|
2546
2579
|
}
|
|
2547
2580
|
const machineId = getMachineId();
|
|
2581
|
+
const hostname = getHostname();
|
|
2548
2582
|
for (const projectPath of selectedPaths) {
|
|
2549
2583
|
const projectActions = byProject.get(projectPath) || [];
|
|
2550
|
-
const projectName =
|
|
2584
|
+
const projectName = path11.basename(projectPath);
|
|
2551
2585
|
const toolsDetected = [...new Set(projectActions.map((a) => a.tool))];
|
|
2552
2586
|
const configFilesWritten = projectActions.map((a) => a.filePath);
|
|
2553
2587
|
registerProject(apiKey, {
|
|
2554
2588
|
projectName,
|
|
2555
2589
|
projectPath,
|
|
2556
2590
|
machineId,
|
|
2591
|
+
hostname,
|
|
2557
2592
|
toolsDetected,
|
|
2558
2593
|
configFiles: configFilesWritten
|
|
2559
2594
|
}).catch(() => {
|
|
@@ -2565,9 +2600,10 @@ async function connectCommand(options) {
|
|
|
2565
2600
|
}
|
|
2566
2601
|
|
|
2567
2602
|
// src/commands/disconnect.ts
|
|
2568
|
-
|
|
2569
|
-
import
|
|
2570
|
-
import
|
|
2603
|
+
init_esm_shims();
|
|
2604
|
+
import fs10 from "fs";
|
|
2605
|
+
import path12 from "path";
|
|
2606
|
+
import { checkbox as checkbox4, confirm as confirm5 } from "@inquirer/prompts";
|
|
2571
2607
|
var DEDICATED_FILES = /* @__PURE__ */ new Set([
|
|
2572
2608
|
".cursor/rules/piut.mdc",
|
|
2573
2609
|
".windsurf/rules/piut.md",
|
|
@@ -2576,11 +2612,13 @@ var DEDICATED_FILES = /* @__PURE__ */ new Set([
|
|
|
2576
2612
|
var APPEND_FILES = [
|
|
2577
2613
|
"CLAUDE.md",
|
|
2578
2614
|
".github/copilot-instructions.md",
|
|
2579
|
-
"CONVENTIONS.md"
|
|
2615
|
+
"CONVENTIONS.md",
|
|
2616
|
+
"GEMINI.md",
|
|
2617
|
+
"AGENTS.md"
|
|
2580
2618
|
];
|
|
2581
2619
|
function hasPiutReference3(filePath) {
|
|
2582
2620
|
try {
|
|
2583
|
-
const content =
|
|
2621
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
2584
2622
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
2585
2623
|
} catch {
|
|
2586
2624
|
return false;
|
|
@@ -2588,7 +2626,7 @@ function hasPiutReference3(filePath) {
|
|
|
2588
2626
|
}
|
|
2589
2627
|
function removePiutSection(filePath) {
|
|
2590
2628
|
try {
|
|
2591
|
-
let content =
|
|
2629
|
+
let content = fs10.readFileSync(filePath, "utf-8");
|
|
2592
2630
|
const patterns = [
|
|
2593
2631
|
/\n*## p[ıi]ut Context[\s\S]*?(?=\n## |\n---\n|$)/g
|
|
2594
2632
|
];
|
|
@@ -2602,7 +2640,7 @@ function removePiutSection(filePath) {
|
|
|
2602
2640
|
}
|
|
2603
2641
|
if (changed) {
|
|
2604
2642
|
content = content.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
2605
|
-
|
|
2643
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
2606
2644
|
}
|
|
2607
2645
|
return changed;
|
|
2608
2646
|
} catch {
|
|
@@ -2611,18 +2649,18 @@ function removePiutSection(filePath) {
|
|
|
2611
2649
|
}
|
|
2612
2650
|
async function disconnectCommand(options) {
|
|
2613
2651
|
banner();
|
|
2614
|
-
let
|
|
2652
|
+
let scanFolders;
|
|
2615
2653
|
if (options.folders) {
|
|
2616
|
-
|
|
2654
|
+
scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
|
|
2617
2655
|
}
|
|
2618
2656
|
console.log(dim(" Scanning for connected projects..."));
|
|
2619
|
-
const projects = scanForProjects(
|
|
2657
|
+
const projects = scanForProjects(scanFolders);
|
|
2620
2658
|
const actions = [];
|
|
2621
2659
|
for (const project of projects) {
|
|
2622
|
-
const projectName =
|
|
2660
|
+
const projectName = path12.basename(project.path);
|
|
2623
2661
|
for (const dedicatedFile of DEDICATED_FILES) {
|
|
2624
|
-
const absPath =
|
|
2625
|
-
if (
|
|
2662
|
+
const absPath = path12.join(project.path, dedicatedFile);
|
|
2663
|
+
if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
|
|
2626
2664
|
actions.push({
|
|
2627
2665
|
projectPath: project.path,
|
|
2628
2666
|
projectName,
|
|
@@ -2633,8 +2671,8 @@ async function disconnectCommand(options) {
|
|
|
2633
2671
|
}
|
|
2634
2672
|
}
|
|
2635
2673
|
for (const appendFile of APPEND_FILES) {
|
|
2636
|
-
const absPath =
|
|
2637
|
-
if (
|
|
2674
|
+
const absPath = path12.join(project.path, appendFile);
|
|
2675
|
+
if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
|
|
2638
2676
|
actions.push({
|
|
2639
2677
|
projectPath: project.path,
|
|
2640
2678
|
projectName,
|
|
@@ -2649,12 +2687,12 @@ async function disconnectCommand(options) {
|
|
|
2649
2687
|
projectPath: project.path,
|
|
2650
2688
|
projectName,
|
|
2651
2689
|
filePath: ".piut/",
|
|
2652
|
-
absPath:
|
|
2690
|
+
absPath: path12.join(project.path, ".piut"),
|
|
2653
2691
|
action: "remove-dir"
|
|
2654
2692
|
});
|
|
2655
2693
|
}
|
|
2656
|
-
const vscodeMcpPath =
|
|
2657
|
-
if (
|
|
2694
|
+
const vscodeMcpPath = path12.join(project.path, ".vscode", "mcp.json");
|
|
2695
|
+
if (fs10.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
|
|
2658
2696
|
actions.push({
|
|
2659
2697
|
projectPath: project.path,
|
|
2660
2698
|
projectName,
|
|
@@ -2676,7 +2714,7 @@ async function disconnectCommand(options) {
|
|
|
2676
2714
|
}
|
|
2677
2715
|
console.log();
|
|
2678
2716
|
const projectChoices = Array.from(byProject.entries()).map(([projectPath, projectActions]) => {
|
|
2679
|
-
const name =
|
|
2717
|
+
const name = path12.basename(projectPath);
|
|
2680
2718
|
const files = projectActions.map((a) => a.filePath).join(", ");
|
|
2681
2719
|
return {
|
|
2682
2720
|
name: `${name} ${dim(`(${files})`)}`,
|
|
@@ -2687,7 +2725,7 @@ async function disconnectCommand(options) {
|
|
|
2687
2725
|
if (options.yes) {
|
|
2688
2726
|
selectedPaths = Array.from(byProject.keys());
|
|
2689
2727
|
} else {
|
|
2690
|
-
selectedPaths = await
|
|
2728
|
+
selectedPaths = await checkbox4({
|
|
2691
2729
|
message: "Select projects to disconnect:",
|
|
2692
2730
|
choices: projectChoices
|
|
2693
2731
|
});
|
|
@@ -2705,11 +2743,11 @@ async function disconnectCommand(options) {
|
|
|
2705
2743
|
let disconnected = 0;
|
|
2706
2744
|
for (const projectPath of selectedPaths) {
|
|
2707
2745
|
const projectActions = byProject.get(projectPath) || [];
|
|
2708
|
-
const projectName =
|
|
2746
|
+
const projectName = path12.basename(projectPath);
|
|
2709
2747
|
for (const action of projectActions) {
|
|
2710
2748
|
if (action.action === "delete") {
|
|
2711
2749
|
try {
|
|
2712
|
-
|
|
2750
|
+
fs10.unlinkSync(action.absPath);
|
|
2713
2751
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 deleted"));
|
|
2714
2752
|
disconnected++;
|
|
2715
2753
|
} catch {
|
|
@@ -2753,6 +2791,7 @@ async function disconnectCommand(options) {
|
|
|
2753
2791
|
}
|
|
2754
2792
|
|
|
2755
2793
|
// src/commands/login.ts
|
|
2794
|
+
init_esm_shims();
|
|
2756
2795
|
import chalk7 from "chalk";
|
|
2757
2796
|
async function loginCommand() {
|
|
2758
2797
|
try {
|
|
@@ -2768,6 +2807,7 @@ async function loginCommand() {
|
|
|
2768
2807
|
}
|
|
2769
2808
|
|
|
2770
2809
|
// src/commands/logout.ts
|
|
2810
|
+
init_esm_shims();
|
|
2771
2811
|
async function logoutCommand() {
|
|
2772
2812
|
banner();
|
|
2773
2813
|
const config = readStore();
|
|
@@ -2784,9 +2824,11 @@ async function logoutCommand() {
|
|
|
2784
2824
|
}
|
|
2785
2825
|
|
|
2786
2826
|
// src/commands/update.ts
|
|
2827
|
+
init_esm_shims();
|
|
2787
2828
|
import chalk9 from "chalk";
|
|
2788
2829
|
|
|
2789
2830
|
// src/lib/update-check.ts
|
|
2831
|
+
init_esm_shims();
|
|
2790
2832
|
import { execFile } from "child_process";
|
|
2791
2833
|
import chalk8 from "chalk";
|
|
2792
2834
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
@@ -2885,7 +2927,8 @@ async function updateCommand(currentVersion) {
|
|
|
2885
2927
|
}
|
|
2886
2928
|
|
|
2887
2929
|
// src/commands/doctor.ts
|
|
2888
|
-
|
|
2930
|
+
init_esm_shims();
|
|
2931
|
+
import fs11 from "fs";
|
|
2889
2932
|
import chalk10 from "chalk";
|
|
2890
2933
|
async function doctorCommand(options) {
|
|
2891
2934
|
if (!options.json) banner();
|
|
@@ -2935,10 +2978,10 @@ async function doctorCommand(options) {
|
|
|
2935
2978
|
}
|
|
2936
2979
|
let toolsFixed = 0;
|
|
2937
2980
|
for (const tool of TOOLS) {
|
|
2938
|
-
const paths = resolveConfigPaths(tool
|
|
2939
|
-
for (const configPath of paths) {
|
|
2940
|
-
if (!
|
|
2941
|
-
const piutConfig = getPiutConfig(configPath,
|
|
2981
|
+
const paths = resolveConfigPaths(tool);
|
|
2982
|
+
for (const { filePath: configPath, configKey: resolvedKey } of paths) {
|
|
2983
|
+
if (!fs11.existsSync(configPath)) continue;
|
|
2984
|
+
const piutConfig = getPiutConfig(configPath, resolvedKey);
|
|
2942
2985
|
if (!piutConfig) {
|
|
2943
2986
|
result.tools.push({
|
|
2944
2987
|
name: tool.name,
|
|
@@ -2953,13 +2996,13 @@ async function doctorCommand(options) {
|
|
|
2953
2996
|
}
|
|
2954
2997
|
break;
|
|
2955
2998
|
}
|
|
2956
|
-
const
|
|
2957
|
-
const configPrefix =
|
|
2999
|
+
const extractedKey = extractKeyFromConfig(piutConfig);
|
|
3000
|
+
const configPrefix = extractedKey ? extractedKey.slice(0, 7) + "..." : "(none)";
|
|
2958
3001
|
let keyMatch = "missing";
|
|
2959
|
-
if (!
|
|
3002
|
+
if (!extractedKey) {
|
|
2960
3003
|
keyMatch = "missing";
|
|
2961
3004
|
result.issues++;
|
|
2962
|
-
} else if (apiKey &&
|
|
3005
|
+
} else if (apiKey && extractedKey === apiKey) {
|
|
2963
3006
|
keyMatch = "match";
|
|
2964
3007
|
} else {
|
|
2965
3008
|
keyMatch = "stale";
|
|
@@ -2977,7 +3020,7 @@ async function doctorCommand(options) {
|
|
|
2977
3020
|
};
|
|
2978
3021
|
if (keyMatch === "stale" && options.fix && apiKey && result.key.valid && result.key.slug) {
|
|
2979
3022
|
const serverConfig = tool.generateConfig(result.key.slug, apiKey);
|
|
2980
|
-
mergeConfig(configPath,
|
|
3023
|
+
mergeConfig(configPath, resolvedKey, serverConfig);
|
|
2981
3024
|
toolResult.fixed = true;
|
|
2982
3025
|
toolResult.keyMatch = "match";
|
|
2983
3026
|
result.issues--;
|
|
@@ -3052,13 +3095,156 @@ async function doctorCommand(options) {
|
|
|
3052
3095
|
if (result.issues > 0) throw new CliError(`${result.issues} issue(s) found`);
|
|
3053
3096
|
}
|
|
3054
3097
|
|
|
3098
|
+
// src/commands/vault.ts
|
|
3099
|
+
init_esm_shims();
|
|
3100
|
+
import fs12 from "fs";
|
|
3101
|
+
import path13 from "path";
|
|
3102
|
+
import chalk11 from "chalk";
|
|
3103
|
+
import { confirm as confirm7 } from "@inquirer/prompts";
|
|
3104
|
+
var DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
3105
|
+
"pdf",
|
|
3106
|
+
"docx",
|
|
3107
|
+
"doc",
|
|
3108
|
+
"pptx",
|
|
3109
|
+
"pages",
|
|
3110
|
+
"key",
|
|
3111
|
+
"rtf",
|
|
3112
|
+
"odt",
|
|
3113
|
+
"odp",
|
|
3114
|
+
"eml",
|
|
3115
|
+
"mbox"
|
|
3116
|
+
]);
|
|
3117
|
+
function formatSize2(bytes) {
|
|
3118
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
3119
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
3120
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3121
|
+
}
|
|
3122
|
+
function resolveApiKey(options) {
|
|
3123
|
+
const key = options.key || readStore().apiKey;
|
|
3124
|
+
if (!key) {
|
|
3125
|
+
console.log(warning(" No API key found."));
|
|
3126
|
+
console.log(dim(" Run ") + brand("piut login") + dim(" first, or pass --key."));
|
|
3127
|
+
throw new CliError();
|
|
3128
|
+
}
|
|
3129
|
+
return key;
|
|
3130
|
+
}
|
|
3131
|
+
async function vaultListCommand(options) {
|
|
3132
|
+
const key = resolveApiKey(options);
|
|
3133
|
+
const data = await listVaultFiles(key);
|
|
3134
|
+
if (data.files.length === 0) {
|
|
3135
|
+
console.log(dim(" No files in vault."));
|
|
3136
|
+
console.log(dim(" Upload with: ") + brand("piut vault upload <file>"));
|
|
3137
|
+
console.log();
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3140
|
+
console.log();
|
|
3141
|
+
for (const file of data.files) {
|
|
3142
|
+
const size = dim(`(${formatSize2(file.sizeBytes)})`);
|
|
3143
|
+
console.log(` ${file.filename} ${size}`);
|
|
3144
|
+
if (file.summary) {
|
|
3145
|
+
console.log(dim(` ${file.summary}`));
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
console.log();
|
|
3149
|
+
console.log(dim(` ${data.usage.fileCount} file(s), ${formatSize2(data.usage.totalBytes)} / ${formatSize2(data.usage.maxBytes)} used`));
|
|
3150
|
+
console.log();
|
|
3151
|
+
}
|
|
3152
|
+
async function vaultUploadCommand(filePath, options) {
|
|
3153
|
+
const key = resolveApiKey(options);
|
|
3154
|
+
const resolved = path13.resolve(filePath);
|
|
3155
|
+
if (!fs12.existsSync(resolved)) {
|
|
3156
|
+
console.log(chalk11.red(` File not found: ${filePath}`));
|
|
3157
|
+
throw new CliError();
|
|
3158
|
+
}
|
|
3159
|
+
const stat = fs12.statSync(resolved);
|
|
3160
|
+
if (!stat.isFile()) {
|
|
3161
|
+
console.log(chalk11.red(` Not a file: ${filePath}`));
|
|
3162
|
+
throw new CliError();
|
|
3163
|
+
}
|
|
3164
|
+
const filename = path13.basename(resolved);
|
|
3165
|
+
const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
|
|
3166
|
+
const isDocument = DOCUMENT_EXTENSIONS.has(ext);
|
|
3167
|
+
let content;
|
|
3168
|
+
let encoding;
|
|
3169
|
+
if (isDocument) {
|
|
3170
|
+
content = fs12.readFileSync(resolved).toString("base64");
|
|
3171
|
+
encoding = "base64";
|
|
3172
|
+
} else {
|
|
3173
|
+
content = fs12.readFileSync(resolved, "utf-8");
|
|
3174
|
+
}
|
|
3175
|
+
const spinner = new Spinner();
|
|
3176
|
+
spinner.start(`Uploading ${filename}...`);
|
|
3177
|
+
try {
|
|
3178
|
+
const result = await uploadVaultFile(key, filename, content, encoding);
|
|
3179
|
+
spinner.stop();
|
|
3180
|
+
console.log(success(` Uploaded ${result.filename}`) + dim(` (${formatSize2(result.sizeBytes)})`));
|
|
3181
|
+
if (result.summary) {
|
|
3182
|
+
console.log(dim(` ${result.summary}`));
|
|
3183
|
+
}
|
|
3184
|
+
console.log();
|
|
3185
|
+
} catch (err) {
|
|
3186
|
+
spinner.stop();
|
|
3187
|
+
console.log(chalk11.red(` ${err.message}`));
|
|
3188
|
+
throw new CliError();
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
async function vaultReadCommand(filename, options) {
|
|
3192
|
+
const key = resolveApiKey(options);
|
|
3193
|
+
try {
|
|
3194
|
+
const file = await readVaultFile(key, filename);
|
|
3195
|
+
if (options.output) {
|
|
3196
|
+
const outPath = path13.resolve(options.output);
|
|
3197
|
+
fs12.writeFileSync(outPath, file.content, "utf-8");
|
|
3198
|
+
console.log(success(` Saved to ${outPath}`));
|
|
3199
|
+
console.log();
|
|
3200
|
+
} else {
|
|
3201
|
+
console.log();
|
|
3202
|
+
console.log(file.content);
|
|
3203
|
+
}
|
|
3204
|
+
} catch (err) {
|
|
3205
|
+
console.log(chalk11.red(` ${err.message}`));
|
|
3206
|
+
throw new CliError();
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
async function vaultDeleteCommand(filename, options) {
|
|
3210
|
+
const key = resolveApiKey(options);
|
|
3211
|
+
if (!options.yes) {
|
|
3212
|
+
const confirmed = await confirm7({
|
|
3213
|
+
message: `Delete "${filename}" from vault? This cannot be undone.`,
|
|
3214
|
+
default: false
|
|
3215
|
+
});
|
|
3216
|
+
if (!confirmed) return;
|
|
3217
|
+
}
|
|
3218
|
+
try {
|
|
3219
|
+
await deleteVaultFile(key, filename);
|
|
3220
|
+
console.log(success(` Deleted ${filename}`));
|
|
3221
|
+
console.log();
|
|
3222
|
+
} catch (err) {
|
|
3223
|
+
console.log(chalk11.red(` ${err.message}`));
|
|
3224
|
+
throw new CliError();
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3055
3228
|
// src/commands/interactive.ts
|
|
3056
|
-
|
|
3229
|
+
init_esm_shims();
|
|
3230
|
+
import { select as select2, confirm as confirm8, checkbox as checkbox5, Separator } from "@inquirer/prompts";
|
|
3057
3231
|
import fs14 from "fs";
|
|
3058
3232
|
import path15 from "path";
|
|
3059
3233
|
import { exec as exec2 } from "child_process";
|
|
3060
|
-
import
|
|
3061
|
-
|
|
3234
|
+
import chalk13 from "chalk";
|
|
3235
|
+
var DOCUMENT_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
3236
|
+
"pdf",
|
|
3237
|
+
"docx",
|
|
3238
|
+
"doc",
|
|
3239
|
+
"pptx",
|
|
3240
|
+
"pages",
|
|
3241
|
+
"key",
|
|
3242
|
+
"rtf",
|
|
3243
|
+
"odt",
|
|
3244
|
+
"odp",
|
|
3245
|
+
"eml",
|
|
3246
|
+
"mbox"
|
|
3247
|
+
]);
|
|
3062
3248
|
async function authenticate() {
|
|
3063
3249
|
const config = readStore();
|
|
3064
3250
|
const apiKey = config.apiKey;
|
|
@@ -3097,7 +3283,7 @@ async function interactiveMenu() {
|
|
|
3097
3283
|
console.log(warning(" You haven\u2019t built a brain yet."));
|
|
3098
3284
|
console.log(dim(" Your brain is how AI tools learn about you \u2014 your projects, preferences, and context."));
|
|
3099
3285
|
console.log();
|
|
3100
|
-
const wantBuild = await
|
|
3286
|
+
const wantBuild = await confirm8({
|
|
3101
3287
|
message: "Build your brain now?",
|
|
3102
3288
|
default: true
|
|
3103
3289
|
});
|
|
@@ -3113,7 +3299,7 @@ async function interactiveMenu() {
|
|
|
3113
3299
|
console.log(warning(" Your brain is built but not deployed yet."));
|
|
3114
3300
|
console.log(dim(" Deploy it to make your MCP server live so AI tools can read your brain."));
|
|
3115
3301
|
console.log();
|
|
3116
|
-
const wantDeploy = await
|
|
3302
|
+
const wantDeploy = await confirm8({
|
|
3117
3303
|
message: "Deploy your brain now?",
|
|
3118
3304
|
default: true
|
|
3119
3305
|
});
|
|
@@ -3130,15 +3316,28 @@ async function interactiveMenu() {
|
|
|
3130
3316
|
const isDeployed = currentValidation.status === "active";
|
|
3131
3317
|
let action;
|
|
3132
3318
|
try {
|
|
3133
|
-
action = await
|
|
3319
|
+
action = await select2({
|
|
3134
3320
|
message: "What would you like to do?",
|
|
3135
3321
|
loop: false,
|
|
3136
3322
|
choices: [
|
|
3137
3323
|
{
|
|
3138
|
-
name:
|
|
3324
|
+
name: "My Brain",
|
|
3325
|
+
value: "view-brain",
|
|
3326
|
+
description: "View all 5 brain sections",
|
|
3327
|
+
disabled: !hasBrain && "(build brain first)"
|
|
3328
|
+
},
|
|
3329
|
+
{
|
|
3330
|
+
name: "Build Brain",
|
|
3139
3331
|
value: "build",
|
|
3140
|
-
description: hasBrain ? "
|
|
3332
|
+
description: hasBrain ? "Rebuild your brain from your files" : "Build your brain from your files"
|
|
3333
|
+
},
|
|
3334
|
+
{
|
|
3335
|
+
name: "Edit Brain",
|
|
3336
|
+
value: "edit-brain",
|
|
3337
|
+
description: "Open piut.com to edit your brain",
|
|
3338
|
+
disabled: !hasBrain && "(build brain first)"
|
|
3141
3339
|
},
|
|
3340
|
+
new Separator(),
|
|
3142
3341
|
{
|
|
3143
3342
|
name: isDeployed ? "Undeploy Brain" : "Deploy Brain",
|
|
3144
3343
|
value: "deploy",
|
|
@@ -3159,23 +3358,21 @@ async function interactiveMenu() {
|
|
|
3159
3358
|
},
|
|
3160
3359
|
new Separator(),
|
|
3161
3360
|
{
|
|
3162
|
-
name: "
|
|
3163
|
-
value: "
|
|
3164
|
-
description: "
|
|
3165
|
-
disabled: !hasBrain && "(build brain first)"
|
|
3361
|
+
name: "View Files",
|
|
3362
|
+
value: "vault-view",
|
|
3363
|
+
description: "List and manage files in your vault"
|
|
3166
3364
|
},
|
|
3167
3365
|
{
|
|
3168
|
-
name: "
|
|
3169
|
-
value: "
|
|
3170
|
-
description: "
|
|
3171
|
-
disabled: !hasBrain && "(build brain first)"
|
|
3366
|
+
name: "Upload Files",
|
|
3367
|
+
value: "vault-upload",
|
|
3368
|
+
description: "Upload a file to your vault"
|
|
3172
3369
|
},
|
|
3370
|
+
new Separator(),
|
|
3173
3371
|
{
|
|
3174
3372
|
name: "Status",
|
|
3175
3373
|
value: "status",
|
|
3176
3374
|
description: "Show brain, deployment, and connected tools/projects"
|
|
3177
3375
|
},
|
|
3178
|
-
new Separator(),
|
|
3179
3376
|
{
|
|
3180
3377
|
name: "Logout",
|
|
3181
3378
|
value: "logout",
|
|
@@ -3194,12 +3391,14 @@ async function interactiveMenu() {
|
|
|
3194
3391
|
if (action === "exit") return;
|
|
3195
3392
|
try {
|
|
3196
3393
|
switch (action) {
|
|
3394
|
+
case "view-brain":
|
|
3395
|
+
await handleViewBrain(apiKey);
|
|
3396
|
+
break;
|
|
3197
3397
|
case "build":
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
}
|
|
3398
|
+
await buildCommand({ key: apiKey });
|
|
3399
|
+
break;
|
|
3400
|
+
case "edit-brain":
|
|
3401
|
+
handleEditBrain();
|
|
3203
3402
|
break;
|
|
3204
3403
|
case "deploy":
|
|
3205
3404
|
if (isDeployed) {
|
|
@@ -3214,11 +3413,11 @@ async function interactiveMenu() {
|
|
|
3214
3413
|
case "connect-projects":
|
|
3215
3414
|
await handleManageProjects(apiKey, currentValidation);
|
|
3216
3415
|
break;
|
|
3217
|
-
case "
|
|
3218
|
-
|
|
3416
|
+
case "vault-view":
|
|
3417
|
+
await handleVaultView(apiKey);
|
|
3219
3418
|
break;
|
|
3220
|
-
case "
|
|
3221
|
-
await
|
|
3419
|
+
case "vault-upload":
|
|
3420
|
+
await handleVaultUpload(apiKey);
|
|
3222
3421
|
break;
|
|
3223
3422
|
case "status":
|
|
3224
3423
|
statusCommand();
|
|
@@ -3241,7 +3440,7 @@ async function interactiveMenu() {
|
|
|
3241
3440
|
} else if (err instanceof CliError) {
|
|
3242
3441
|
console.log();
|
|
3243
3442
|
} else {
|
|
3244
|
-
console.log(
|
|
3443
|
+
console.log(chalk13.red(` Error: ${err.message}`));
|
|
3245
3444
|
console.log();
|
|
3246
3445
|
}
|
|
3247
3446
|
}
|
|
@@ -3252,7 +3451,7 @@ async function interactiveMenu() {
|
|
|
3252
3451
|
}
|
|
3253
3452
|
}
|
|
3254
3453
|
async function handleUndeploy(apiKey) {
|
|
3255
|
-
const confirmed = await
|
|
3454
|
+
const confirmed = await confirm8({
|
|
3256
3455
|
message: "Undeploy your brain? AI tools will lose access to your MCP server.",
|
|
3257
3456
|
default: false
|
|
3258
3457
|
});
|
|
@@ -3264,69 +3463,86 @@ async function handleUndeploy(apiKey) {
|
|
|
3264
3463
|
console.log(dim(" Run ") + brand("piut deploy") + dim(" to re-deploy anytime."));
|
|
3265
3464
|
console.log();
|
|
3266
3465
|
} catch (err) {
|
|
3267
|
-
console.log(
|
|
3466
|
+
console.log(chalk13.red(` \u2717 ${err.message}`));
|
|
3268
3467
|
}
|
|
3269
3468
|
}
|
|
3270
3469
|
async function handleConnectTools(apiKey, validation) {
|
|
3271
3470
|
const { slug } = validation;
|
|
3272
3471
|
const detected = [];
|
|
3273
3472
|
for (const tool of TOOLS) {
|
|
3274
|
-
const paths = resolveConfigPaths(tool
|
|
3275
|
-
for (const
|
|
3276
|
-
const exists = fs14.existsSync(
|
|
3277
|
-
const parentExists = fs14.existsSync(path15.dirname(
|
|
3473
|
+
const paths = resolveConfigPaths(tool);
|
|
3474
|
+
for (const { filePath, configKey } of paths) {
|
|
3475
|
+
const exists = fs14.existsSync(filePath);
|
|
3476
|
+
const parentExists = fs14.existsSync(path15.dirname(filePath));
|
|
3278
3477
|
if (exists || parentExists) {
|
|
3279
|
-
const connected = exists && isPiutConfigured(
|
|
3280
|
-
detected.push({ tool, configPath, connected });
|
|
3478
|
+
const connected = exists && !!configKey && isPiutConfigured(filePath, configKey);
|
|
3479
|
+
detected.push({ tool, configPath: filePath, resolvedConfigKey: configKey, connected });
|
|
3281
3480
|
break;
|
|
3282
3481
|
}
|
|
3283
3482
|
}
|
|
3284
3483
|
}
|
|
3285
3484
|
if (detected.length === 0) {
|
|
3286
3485
|
console.log(warning(" No supported AI tools detected."));
|
|
3287
|
-
console.log(dim(" Supported: Claude Code, Claude Desktop, Cursor, Windsurf, GitHub Copilot, Amazon Q, Zed"));
|
|
3288
3486
|
console.log();
|
|
3289
3487
|
return;
|
|
3290
3488
|
}
|
|
3291
|
-
const
|
|
3292
|
-
const
|
|
3489
|
+
const mcpTools = detected.filter((d) => !d.tool.skillOnly);
|
|
3490
|
+
const skillOnlyTools = detected.filter((d) => d.tool.skillOnly);
|
|
3491
|
+
const connectedCount = mcpTools.filter((d) => d.connected).length;
|
|
3492
|
+
const availableCount = mcpTools.length - connectedCount;
|
|
3293
3493
|
if (connectedCount > 0 || availableCount > 0) {
|
|
3294
3494
|
const parts = [];
|
|
3295
3495
|
if (connectedCount > 0) parts.push(`${connectedCount} connected`);
|
|
3296
3496
|
if (availableCount > 0) parts.push(`${availableCount} available`);
|
|
3497
|
+
if (skillOnlyTools.length > 0) parts.push(`${skillOnlyTools.length} skill-only`);
|
|
3297
3498
|
console.log(dim(` ${parts.join(", ")}`));
|
|
3298
3499
|
}
|
|
3299
3500
|
console.log();
|
|
3300
|
-
|
|
3501
|
+
if (mcpTools.length === 0) {
|
|
3502
|
+
console.log(dim(" Detected tools are skill-only (no MCP config to manage)."));
|
|
3503
|
+
if (skillOnlyTools.length > 0) {
|
|
3504
|
+
console.log(dim(` Skill-only: ${skillOnlyTools.map((d) => d.tool.name).join(", ")}`));
|
|
3505
|
+
}
|
|
3506
|
+
console.log(dim(' Use "Connect Projects" to add skill files to your projects.'));
|
|
3507
|
+
console.log();
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
const choices = mcpTools.map((d) => ({
|
|
3301
3511
|
name: `${d.tool.name}${d.connected ? dim(" (connected)") : ""}`,
|
|
3302
3512
|
value: d,
|
|
3303
3513
|
checked: d.connected
|
|
3304
3514
|
}));
|
|
3305
|
-
const selected = await
|
|
3515
|
+
const selected = await checkbox5({
|
|
3306
3516
|
message: "Select tools to keep connected (toggle with space):",
|
|
3307
3517
|
choices
|
|
3308
3518
|
});
|
|
3309
3519
|
const toConnect = selected.filter((s) => !s.connected);
|
|
3310
|
-
const toDisconnect =
|
|
3520
|
+
const toDisconnect = mcpTools.filter((d) => d.connected && !selected.includes(d));
|
|
3311
3521
|
if (toConnect.length === 0 && toDisconnect.length === 0) {
|
|
3312
3522
|
console.log(dim(" No changes."));
|
|
3313
3523
|
console.log();
|
|
3314
3524
|
return;
|
|
3315
3525
|
}
|
|
3316
3526
|
console.log();
|
|
3317
|
-
for (const { tool, configPath } of toConnect) {
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3527
|
+
for (const { tool, configPath, resolvedConfigKey } of toConnect) {
|
|
3528
|
+
if (tool.generateConfig && resolvedConfigKey) {
|
|
3529
|
+
const serverConfig = tool.generateConfig(slug, apiKey);
|
|
3530
|
+
mergeConfig(configPath, resolvedConfigKey, serverConfig);
|
|
3531
|
+
toolLine(tool.name, success("connected"), "\u2714");
|
|
3532
|
+
}
|
|
3321
3533
|
}
|
|
3322
3534
|
const removedNames = [];
|
|
3323
|
-
for (const { tool, configPath } of toDisconnect) {
|
|
3324
|
-
|
|
3535
|
+
for (const { tool, configPath, resolvedConfigKey } of toDisconnect) {
|
|
3536
|
+
if (!resolvedConfigKey) continue;
|
|
3537
|
+
const removed = removeFromConfig(configPath, resolvedConfigKey);
|
|
3325
3538
|
if (removed) {
|
|
3326
3539
|
removedNames.push(tool.name);
|
|
3327
3540
|
toolLine(tool.name, warning("disconnected"), "\u2714");
|
|
3328
3541
|
}
|
|
3329
3542
|
}
|
|
3543
|
+
if (skillOnlyTools.length > 0) {
|
|
3544
|
+
console.log(dim(` Skill-only (use "Connect Projects"): ${skillOnlyTools.map((d) => d.tool.name).join(", ")}`));
|
|
3545
|
+
}
|
|
3330
3546
|
if (toConnect.length > 0 && validation.serverUrl) {
|
|
3331
3547
|
await Promise.all(
|
|
3332
3548
|
toConnect.map(({ tool }) => pingMcp(validation.serverUrl, apiKey, tool.name))
|
|
@@ -3366,7 +3582,7 @@ async function handleManageProjects(apiKey, validation) {
|
|
|
3366
3582
|
value: i,
|
|
3367
3583
|
checked: i.connected
|
|
3368
3584
|
}));
|
|
3369
|
-
const selected = await
|
|
3585
|
+
const selected = await checkbox5({
|
|
3370
3586
|
message: "Select projects to keep connected (toggle with space):",
|
|
3371
3587
|
choices
|
|
3372
3588
|
});
|
|
@@ -3459,96 +3675,103 @@ function handleEditBrain() {
|
|
|
3459
3675
|
console.log(success(" \u2713 Opened in browser."));
|
|
3460
3676
|
console.log();
|
|
3461
3677
|
}
|
|
3462
|
-
function
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
(p) => `- **${p.name}** (${p.path})${p.description ? ` \u2014 ${p.description}` : ""}`
|
|
3474
|
-
);
|
|
3475
|
-
parts.push(`## Projects Found
|
|
3476
|
-
${projectLines.join("\n")}`);
|
|
3477
|
-
}
|
|
3478
|
-
const totalFiles = (summary.configFiles?.length || 0) + (summary.recentDocuments?.length || 0) + (summary.personalDocuments?.length || 0);
|
|
3479
|
-
const fileLimit = totalFiles <= 10 ? 1e4 : totalFiles <= 30 ? 5e3 : totalFiles <= 60 ? 3e3 : 2e3;
|
|
3480
|
-
if (summary.configFiles.length > 0) {
|
|
3481
|
-
const configBlocks = summary.configFiles.map(
|
|
3482
|
-
(f) => `### ${f.name}
|
|
3483
|
-
\`\`\`
|
|
3484
|
-
${f.content.slice(0, fileLimit)}
|
|
3485
|
-
\`\`\``
|
|
3486
|
-
);
|
|
3487
|
-
parts.push(`## AI Config Files
|
|
3488
|
-
${configBlocks.join("\n\n")}`);
|
|
3489
|
-
}
|
|
3490
|
-
if (summary.recentDocuments.length > 0) {
|
|
3491
|
-
const docBlocks = summary.recentDocuments.map(
|
|
3492
|
-
(f) => `### ${f.name}
|
|
3493
|
-
${f.content.slice(0, fileLimit)}`
|
|
3494
|
-
);
|
|
3495
|
-
parts.push(`## Recent Documents
|
|
3496
|
-
${docBlocks.join("\n\n")}`);
|
|
3678
|
+
function formatSize3(bytes) {
|
|
3679
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
3680
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
3681
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3682
|
+
}
|
|
3683
|
+
async function handleVaultView(apiKey) {
|
|
3684
|
+
const data = await listVaultFiles(apiKey);
|
|
3685
|
+
if (data.files.length === 0) {
|
|
3686
|
+
console.log(dim(' Vault is empty. Use "Upload Files" to add files.'));
|
|
3687
|
+
console.log();
|
|
3688
|
+
return;
|
|
3497
3689
|
}
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
${
|
|
3502
|
-
);
|
|
3503
|
-
parts.push(`## Personal Documents
|
|
3504
|
-
${docBlocks.join("\n\n")}`);
|
|
3690
|
+
console.log();
|
|
3691
|
+
for (const file of data.files) {
|
|
3692
|
+
const size = dim(`(${formatSize3(file.sizeBytes)})`);
|
|
3693
|
+
console.log(` ${file.filename} ${size}`);
|
|
3694
|
+
if (file.summary) console.log(dim(` ${file.summary}`));
|
|
3505
3695
|
}
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3696
|
+
console.log();
|
|
3697
|
+
console.log(dim(` ${data.usage.fileCount} file(s), ${formatSize3(data.usage.totalBytes)} / ${formatSize3(data.usage.maxBytes)} used`));
|
|
3698
|
+
console.log();
|
|
3699
|
+
const action = await select2({
|
|
3700
|
+
message: "Actions:",
|
|
3701
|
+
choices: [
|
|
3702
|
+
{ name: "Delete a file", value: "delete" },
|
|
3703
|
+
{ name: "Back", value: "back" }
|
|
3704
|
+
]
|
|
3705
|
+
});
|
|
3706
|
+
if (action === "back") return;
|
|
3707
|
+
if (action === "delete") {
|
|
3708
|
+
const fileChoices = data.files.map((f) => ({
|
|
3709
|
+
name: `${f.filename} ${dim(`(${formatSize3(f.sizeBytes)})`)}`,
|
|
3710
|
+
value: f.filename
|
|
3711
|
+
}));
|
|
3712
|
+
const filename = await select2({
|
|
3713
|
+
message: "Which file to delete?",
|
|
3714
|
+
choices: fileChoices
|
|
3715
|
+
});
|
|
3716
|
+
const confirmed = await confirm8({
|
|
3717
|
+
message: `Delete "${filename}"? This cannot be undone.`,
|
|
3718
|
+
default: false
|
|
3719
|
+
});
|
|
3720
|
+
if (confirmed) {
|
|
3721
|
+
try {
|
|
3722
|
+
await deleteVaultFile(apiKey, filename);
|
|
3723
|
+
console.log(success(` Deleted ${filename}`));
|
|
3724
|
+
console.log();
|
|
3725
|
+
} catch (err) {
|
|
3726
|
+
console.log(chalk13.red(` ${err.message}`));
|
|
3727
|
+
console.log();
|
|
3728
|
+
}
|
|
3729
|
+
}
|
|
3509
3730
|
}
|
|
3510
|
-
return result;
|
|
3511
3731
|
}
|
|
3512
|
-
async function
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3732
|
+
async function handleVaultUpload(apiKey) {
|
|
3733
|
+
const treePrompt2 = (await Promise.resolve().then(() => (init_tree_prompt(), tree_prompt_exports))).default;
|
|
3734
|
+
const files = await treePrompt2({
|
|
3735
|
+
message: "Select files to upload:",
|
|
3736
|
+
mode: "files"
|
|
3737
|
+
});
|
|
3738
|
+
if (files.length === 0) {
|
|
3739
|
+
console.log(dim(" No files selected."));
|
|
3516
3740
|
console.log();
|
|
3517
3741
|
return;
|
|
3518
3742
|
}
|
|
3519
|
-
const cwd = process.cwd();
|
|
3520
|
-
const cwdDisplay = cwd.replace(os8.homedir(), "~");
|
|
3521
|
-
console.log(dim(` Scanning ${cwdDisplay}...`));
|
|
3522
|
-
const scanResult = await scanFolders([cwd]);
|
|
3523
|
-
const allFolderPaths = scanResult.folders.map((f) => f.path);
|
|
3524
|
-
const brainInput = buildBrainInput(scanResult, allFolderPaths);
|
|
3525
|
-
const projCount = brainInput.summary.projects.length;
|
|
3526
|
-
const cfgCount = brainInput.summary.configFiles.length;
|
|
3527
|
-
const dcCount = (brainInput.summary.personalDocuments?.length || 0) + brainInput.summary.recentDocuments.length;
|
|
3528
|
-
console.log(success(` Scanned: ${projCount} projects, ${cfgCount} config files, ${dcCount} docs`));
|
|
3529
3743
|
console.log();
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3744
|
+
let uploaded = 0;
|
|
3745
|
+
for (const filePath of files) {
|
|
3746
|
+
const filename = path15.basename(filePath);
|
|
3747
|
+
const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
|
|
3748
|
+
const isDocument = DOCUMENT_EXTENSIONS2.has(ext);
|
|
3749
|
+
let content;
|
|
3750
|
+
let encoding;
|
|
3751
|
+
if (isDocument) {
|
|
3752
|
+
content = fs14.readFileSync(filePath).toString("base64");
|
|
3753
|
+
encoding = "base64";
|
|
3754
|
+
} else {
|
|
3755
|
+
content = fs14.readFileSync(filePath, "utf-8");
|
|
3756
|
+
}
|
|
3757
|
+
const spinner = new Spinner();
|
|
3758
|
+
spinner.start(`Uploading ${filename}...`);
|
|
3759
|
+
try {
|
|
3760
|
+
const result = await uploadVaultFile(apiKey, filename, content, encoding);
|
|
3761
|
+
spinner.stop();
|
|
3762
|
+
console.log(success(` Uploaded ${result.filename}`) + dim(` (${formatSize3(result.sizeBytes)})`));
|
|
3763
|
+
if (result.summary) console.log(dim(` ${result.summary}`));
|
|
3764
|
+
uploaded++;
|
|
3765
|
+
} catch (err) {
|
|
3766
|
+
spinner.stop();
|
|
3767
|
+
console.log(chalk13.red(` ${err.message}`));
|
|
3768
|
+
}
|
|
3534
3769
|
}
|
|
3535
|
-
|
|
3536
|
-
const spinner = new Spinner();
|
|
3537
|
-
spinner.start("Resyncing brain...");
|
|
3538
|
-
try {
|
|
3539
|
-
const result = await resyncBrain(validation.serverUrl, apiKey, content);
|
|
3540
|
-
spinner.stop();
|
|
3541
|
-
console.log();
|
|
3542
|
-
console.log(success(" \u2713 Brain resynced."));
|
|
3543
|
-
console.log(dim(` ${result.summary}`));
|
|
3544
|
-
console.log();
|
|
3545
|
-
} catch (err) {
|
|
3546
|
-
spinner.stop();
|
|
3547
|
-
const msg = err.message;
|
|
3548
|
-
console.log(chalk11.red(` \u2717 ${msg}`));
|
|
3770
|
+
if (uploaded > 0) {
|
|
3549
3771
|
console.log();
|
|
3550
|
-
|
|
3772
|
+
console.log(success(` ${uploaded} file${uploaded === 1 ? "" : "s"} uploaded to vault.`));
|
|
3551
3773
|
}
|
|
3774
|
+
console.log();
|
|
3552
3775
|
}
|
|
3553
3776
|
async function handleViewBrain(apiKey) {
|
|
3554
3777
|
console.log(dim(" Loading brain..."));
|
|
@@ -3578,7 +3801,7 @@ async function handleViewBrain(apiKey) {
|
|
|
3578
3801
|
if (hasUnpublishedChanges) {
|
|
3579
3802
|
console.log(warning(" You have unpublished changes."));
|
|
3580
3803
|
console.log();
|
|
3581
|
-
const wantPublish = await
|
|
3804
|
+
const wantPublish = await confirm8({
|
|
3582
3805
|
message: "Publish now?",
|
|
3583
3806
|
default: true
|
|
3584
3807
|
});
|
|
@@ -3592,11 +3815,11 @@ async function handleViewBrain(apiKey) {
|
|
|
3592
3815
|
console.log();
|
|
3593
3816
|
const msg = err.message;
|
|
3594
3817
|
if (msg === "REQUIRES_SUBSCRIPTION") {
|
|
3595
|
-
console.log(
|
|
3818
|
+
console.log(chalk13.yellow(" Deploy requires an active subscription ($10/mo)."));
|
|
3596
3819
|
console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
|
|
3597
3820
|
console.log(dim(" 14-day free trial included."));
|
|
3598
3821
|
} else {
|
|
3599
|
-
console.log(
|
|
3822
|
+
console.log(chalk13.red(` \u2717 ${msg}`));
|
|
3600
3823
|
}
|
|
3601
3824
|
console.log();
|
|
3602
3825
|
}
|
|
@@ -3605,7 +3828,7 @@ async function handleViewBrain(apiKey) {
|
|
|
3605
3828
|
}
|
|
3606
3829
|
|
|
3607
3830
|
// src/cli.ts
|
|
3608
|
-
var VERSION = "3.
|
|
3831
|
+
var VERSION = "3.8.0";
|
|
3609
3832
|
function withExit(fn) {
|
|
3610
3833
|
return async (...args2) => {
|
|
3611
3834
|
try {
|
|
@@ -3621,7 +3844,7 @@ program.name("piut").description("Build your AI brain instantly. Deploy it as an
|
|
|
3621
3844
|
if (actionCommand.name() === "update") return;
|
|
3622
3845
|
return checkForUpdate(VERSION);
|
|
3623
3846
|
}).action(interactiveMenu);
|
|
3624
|
-
program.command("build").description("Build or rebuild your brain from your files").option("-k, --key <key>", "API key").option("
|
|
3847
|
+
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));
|
|
3625
3848
|
program.command("deploy").description("Publish your MCP server (requires paid account)").option("-k, --key <key>", "API key").action(withExit(deployCommand));
|
|
3626
3849
|
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));
|
|
3627
3850
|
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));
|
|
@@ -3631,6 +3854,11 @@ program.command("remove").description("Remove all p\u0131ut configurations").act
|
|
|
3631
3854
|
program.command("login").description("Authenticate with p\u0131ut (email, browser, or API key)").action(withExit(loginCommand));
|
|
3632
3855
|
program.command("logout").description("Remove saved API key").action(logoutCommand);
|
|
3633
3856
|
program.command("doctor").description("Diagnose and fix connection issues").option("-k, --key <key>", "API key to verify against").option("--fix", "Auto-fix stale configurations").option("--json", "Output results as JSON").action(withExit(doctorCommand));
|
|
3857
|
+
var vault = program.command("vault").description("Manage your file vault (upload, list, read, delete)");
|
|
3858
|
+
vault.command("list").description("List all files in your vault").option("-k, --key <key>", "API key").action(withExit(vaultListCommand));
|
|
3859
|
+
vault.command("upload <file>").description("Upload a file to your vault").option("-k, --key <key>", "API key").action(withExit(vaultUploadCommand));
|
|
3860
|
+
vault.command("read <filename>").description("Read a file from your vault").option("-k, --key <key>", "API key").option("-o, --output <path>", "Save to a local file instead of printing").action(withExit(vaultReadCommand));
|
|
3861
|
+
vault.command("delete <filename>").description("Delete a file from your vault").option("-k, --key <key>", "API key").option("-y, --yes", "Skip confirmation prompt").action(withExit(vaultDeleteCommand));
|
|
3634
3862
|
program.command("update").description("Check for and install CLI updates").action(() => updateCommand(VERSION));
|
|
3635
3863
|
var args = process.argv.slice(2);
|
|
3636
3864
|
if (args.includes("--version") || args.includes("-V")) {
|