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