@piut/cli 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +933 -258
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/setup.ts
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import fs4 from "fs";
|
|
8
|
+
import path6 from "path";
|
|
9
9
|
import { execSync } from "child_process";
|
|
10
10
|
import { password, confirm, checkbox } from "@inquirer/prompts";
|
|
11
11
|
import chalk2 from "chalk";
|
|
@@ -22,14 +22,15 @@ async function validateKey(key) {
|
|
|
22
22
|
}
|
|
23
23
|
return res.json();
|
|
24
24
|
}
|
|
25
|
-
async function
|
|
25
|
+
async function* buildBrainStreaming(key, input2) {
|
|
26
26
|
const res = await fetch(`${API_BASE}/api/cli/build-brain`, {
|
|
27
27
|
method: "POST",
|
|
28
28
|
headers: {
|
|
29
29
|
Authorization: `Bearer ${key}`,
|
|
30
|
-
"Content-Type": "application/json"
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
Accept: "text/event-stream"
|
|
31
32
|
},
|
|
32
|
-
body: JSON.stringify(
|
|
33
|
+
body: JSON.stringify(input2)
|
|
33
34
|
});
|
|
34
35
|
if (!res.ok) {
|
|
35
36
|
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
@@ -38,8 +39,59 @@ async function buildBrain(key, input) {
|
|
|
38
39
|
}
|
|
39
40
|
throw new Error(body.error || `Build failed (HTTP ${res.status})`);
|
|
40
41
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
42
|
+
const contentType = res.headers.get("content-type") || "";
|
|
43
|
+
if (!contentType.includes("text/event-stream")) {
|
|
44
|
+
const data = await res.json();
|
|
45
|
+
yield { event: "complete", data: { sections: data.sections } };
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const reader = res.body.getReader();
|
|
49
|
+
const decoder = new TextDecoder();
|
|
50
|
+
let buffer = "";
|
|
51
|
+
while (true) {
|
|
52
|
+
const { done, value } = await reader.read();
|
|
53
|
+
if (done) break;
|
|
54
|
+
buffer += decoder.decode(value, { stream: true });
|
|
55
|
+
const parts = buffer.split("\n\n");
|
|
56
|
+
buffer = parts.pop() || "";
|
|
57
|
+
for (const part of parts) {
|
|
58
|
+
let eventName = "";
|
|
59
|
+
let eventData = "";
|
|
60
|
+
for (const line of part.split("\n")) {
|
|
61
|
+
if (line.startsWith("event: ")) {
|
|
62
|
+
eventName = line.slice(7).trim();
|
|
63
|
+
} else if (line.startsWith("data: ")) {
|
|
64
|
+
eventData = line.slice(6);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (eventName && eventData) {
|
|
68
|
+
try {
|
|
69
|
+
yield { event: eventName, data: JSON.parse(eventData) };
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function pingMcp(serverUrl, key, toolName) {
|
|
77
|
+
try {
|
|
78
|
+
const res = await fetch(serverUrl, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: {
|
|
81
|
+
Authorization: `Bearer ${key}`,
|
|
82
|
+
"Content-Type": "application/json",
|
|
83
|
+
"User-Agent": `piut-cli (configured: ${toolName})`
|
|
84
|
+
},
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
jsonrpc: "2.0",
|
|
87
|
+
id: 1,
|
|
88
|
+
method: "tools/list"
|
|
89
|
+
})
|
|
90
|
+
});
|
|
91
|
+
return res.ok;
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
43
95
|
}
|
|
44
96
|
async function publishServer(key) {
|
|
45
97
|
const res = await fetch(`${API_BASE}/api/mcp/publish`, {
|
|
@@ -59,6 +111,21 @@ async function publishServer(key) {
|
|
|
59
111
|
}
|
|
60
112
|
return res.json();
|
|
61
113
|
}
|
|
114
|
+
async function unpublishServer(key) {
|
|
115
|
+
const res = await fetch(`${API_BASE}/api/mcp/publish`, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
headers: {
|
|
118
|
+
Authorization: `Bearer ${key}`,
|
|
119
|
+
"Content-Type": "application/json"
|
|
120
|
+
},
|
|
121
|
+
body: JSON.stringify({ published: false })
|
|
122
|
+
});
|
|
123
|
+
if (!res.ok) {
|
|
124
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
125
|
+
throw new Error(body.error || `Unpublish failed (HTTP ${res.status})`);
|
|
126
|
+
}
|
|
127
|
+
return res.json();
|
|
128
|
+
}
|
|
62
129
|
|
|
63
130
|
// src/lib/tools.ts
|
|
64
131
|
import os from "os";
|
|
@@ -272,6 +339,13 @@ Skill reference: https://raw.githubusercontent.com/M-Flat-Inc/piut/main/skill.md
|
|
|
272
339
|
Always call \`get_context\` at the start of a conversation to understand the user.
|
|
273
340
|
Read the \`soul\` section first \u2014 it contains behavioral instructions for how to interact.
|
|
274
341
|
Use \`update_brain\` for substantial new information, \`append_brain\` for quick notes.`;
|
|
342
|
+
var PROJECT_SKILL_SNIPPET = `## p\u0131ut Context
|
|
343
|
+
This project uses p\u0131ut for persistent personal context.
|
|
344
|
+
Full skill reference: .piut/skill.md
|
|
345
|
+
|
|
346
|
+
Always call \`get_context\` at the start of every conversation.
|
|
347
|
+
Read the \`soul\` section first \u2014 it contains behavioral instructions.
|
|
348
|
+
Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes.`;
|
|
275
349
|
var SEPARATOR = "\n\n---\n\n";
|
|
276
350
|
function placeSkillFile(filePath) {
|
|
277
351
|
const absPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
|
|
@@ -292,6 +366,104 @@ function placeSkillFile(filePath) {
|
|
|
292
366
|
}
|
|
293
367
|
}
|
|
294
368
|
|
|
369
|
+
// src/lib/piut-dir.ts
|
|
370
|
+
import fs3 from "fs";
|
|
371
|
+
import path5 from "path";
|
|
372
|
+
var API_BASE2 = process.env.PIUT_API_BASE || "https://piut.com";
|
|
373
|
+
var PIUT_DIR = ".piut";
|
|
374
|
+
var CONFIG_FILE = "config.json";
|
|
375
|
+
var SKILL_FILE = "skill.md";
|
|
376
|
+
var MINIMAL_SKILL_CONTENT = `# p\u0131ut Context Skill
|
|
377
|
+
|
|
378
|
+
## MCP Server
|
|
379
|
+
|
|
380
|
+
Endpoint: \`https://piut.com/api/mcp/{{slug}}\`
|
|
381
|
+
Auth: \`Authorization: Bearer {{key}}\`
|
|
382
|
+
Protocol: JSON-RPC 2.0 over HTTPS
|
|
383
|
+
|
|
384
|
+
## Brain Sections
|
|
385
|
+
|
|
386
|
+
| Section | Purpose |
|
|
387
|
+
|---------|---------|
|
|
388
|
+
| about | Who the user is \u2014 background, expertise, interests |
|
|
389
|
+
| soul | How the user wants AI to behave \u2014 tone, preferences, rules |
|
|
390
|
+
| areas | Ongoing areas of responsibility and focus |
|
|
391
|
+
| projects | Active projects with goals and status |
|
|
392
|
+
| memory | Running log of facts, decisions, and context |
|
|
393
|
+
|
|
394
|
+
## Tools
|
|
395
|
+
|
|
396
|
+
| Tool | Description |
|
|
397
|
+
|------|-------------|
|
|
398
|
+
| get_context | Fetch all 5 brain sections (call this FIRST) |
|
|
399
|
+
| get_section | Fetch a single section by name |
|
|
400
|
+
| search_brain | Search across all sections |
|
|
401
|
+
| append_brain | Append text to a section (no AI processing) |
|
|
402
|
+
| update_brain | AI-powered integration of new info into brain |
|
|
403
|
+
| prompt_brain | Execute natural language commands against context |
|
|
404
|
+
|
|
405
|
+
## Best Practices
|
|
406
|
+
|
|
407
|
+
1. Always call \`get_context\` at the start of every conversation
|
|
408
|
+
2. Read the \`soul\` section immediately \u2014 it contains behavioral instructions
|
|
409
|
+
3. Use \`update_brain\` for substantial new information
|
|
410
|
+
4. Use \`append_brain\` for quick notes and facts
|
|
411
|
+
`;
|
|
412
|
+
function piutDir(projectPath) {
|
|
413
|
+
return path5.join(projectPath, PIUT_DIR);
|
|
414
|
+
}
|
|
415
|
+
function writePiutConfig(projectPath, config) {
|
|
416
|
+
const dir = piutDir(projectPath);
|
|
417
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
418
|
+
fs3.writeFileSync(
|
|
419
|
+
path5.join(dir, CONFIG_FILE),
|
|
420
|
+
JSON.stringify(config, null, 2) + "\n",
|
|
421
|
+
"utf-8"
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
async function writePiutSkill(projectPath, slug, apiKey) {
|
|
425
|
+
const dir = piutDir(projectPath);
|
|
426
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
427
|
+
let content;
|
|
428
|
+
try {
|
|
429
|
+
const res = await fetch(`${API_BASE2}/skill.md`);
|
|
430
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
431
|
+
content = await res.text();
|
|
432
|
+
} catch {
|
|
433
|
+
content = MINIMAL_SKILL_CONTENT;
|
|
434
|
+
}
|
|
435
|
+
content = content.replaceAll("{{slug}}", slug).replaceAll("{{key}}", apiKey);
|
|
436
|
+
fs3.writeFileSync(path5.join(dir, SKILL_FILE), content, "utf-8");
|
|
437
|
+
}
|
|
438
|
+
function ensureGitignored(projectPath) {
|
|
439
|
+
const gitignorePath = path5.join(projectPath, ".gitignore");
|
|
440
|
+
let content = "";
|
|
441
|
+
try {
|
|
442
|
+
content = fs3.readFileSync(gitignorePath, "utf-8");
|
|
443
|
+
} catch {
|
|
444
|
+
}
|
|
445
|
+
const lines = content.split("\n");
|
|
446
|
+
for (const line of lines) {
|
|
447
|
+
const trimmed = line.trim();
|
|
448
|
+
if (trimmed === ".piut/" || trimmed === ".piut") return;
|
|
449
|
+
}
|
|
450
|
+
const suffix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
|
|
451
|
+
fs3.writeFileSync(
|
|
452
|
+
gitignorePath,
|
|
453
|
+
content + suffix + "\n# piut\n.piut/\n",
|
|
454
|
+
"utf-8"
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
function removePiutDir(projectPath) {
|
|
458
|
+
const dir = piutDir(projectPath);
|
|
459
|
+
if (!fs3.existsSync(dir)) return false;
|
|
460
|
+
fs3.rmSync(dir, { recursive: true, force: true });
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
function hasPiutDir(projectPath) {
|
|
464
|
+
return fs3.existsSync(path5.join(piutDir(projectPath), CONFIG_FILE));
|
|
465
|
+
}
|
|
466
|
+
|
|
295
467
|
// src/lib/ui.ts
|
|
296
468
|
import chalk from "chalk";
|
|
297
469
|
var brand = chalk.hex("#8B5CF6");
|
|
@@ -307,6 +479,69 @@ function banner() {
|
|
|
307
479
|
function toolLine(name, status, icon) {
|
|
308
480
|
console.log(` ${icon} ${name.padEnd(20)} ${status}`);
|
|
309
481
|
}
|
|
482
|
+
var SPINNER_FRAMES = ["\u28CB", "\u28D9", "\u28F9", "\u28F8", "\u28FC", "\u28F4", "\u28E6", "\u28E7", "\u28C7", "\u28CF"];
|
|
483
|
+
var Spinner = class {
|
|
484
|
+
frame = 0;
|
|
485
|
+
interval = null;
|
|
486
|
+
startTime = Date.now();
|
|
487
|
+
message = "";
|
|
488
|
+
tokens = 0;
|
|
489
|
+
sections = [];
|
|
490
|
+
start(message) {
|
|
491
|
+
this.message = message;
|
|
492
|
+
this.startTime = Date.now();
|
|
493
|
+
this.tokens = 0;
|
|
494
|
+
this.sections = [];
|
|
495
|
+
this.render();
|
|
496
|
+
this.interval = setInterval(() => this.render(), 80);
|
|
497
|
+
}
|
|
498
|
+
updateTokens(count) {
|
|
499
|
+
this.tokens = count;
|
|
500
|
+
}
|
|
501
|
+
addSection(name) {
|
|
502
|
+
this.clearLine();
|
|
503
|
+
const elapsed = this.elapsed();
|
|
504
|
+
console.log(` ${success("\u2713")} ${name.padEnd(12)} ${dim(elapsed)}`);
|
|
505
|
+
this.sections.push(name);
|
|
506
|
+
}
|
|
507
|
+
updateMessage(message) {
|
|
508
|
+
this.message = message;
|
|
509
|
+
}
|
|
510
|
+
stop(finalMessage) {
|
|
511
|
+
if (this.interval) {
|
|
512
|
+
clearInterval(this.interval);
|
|
513
|
+
this.interval = null;
|
|
514
|
+
}
|
|
515
|
+
this.clearLine();
|
|
516
|
+
if (finalMessage) {
|
|
517
|
+
console.log(finalMessage);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
render() {
|
|
521
|
+
this.frame = (this.frame + 1) % SPINNER_FRAMES.length;
|
|
522
|
+
const spinner = brand(SPINNER_FRAMES[this.frame]);
|
|
523
|
+
const elapsed = dim(this.elapsed());
|
|
524
|
+
const tokenStr = this.tokens > 0 ? dim(` ${this.tokens.toLocaleString()} tokens`) : "";
|
|
525
|
+
this.clearLine();
|
|
526
|
+
process.stdout.write(` ${spinner} ${this.message} ${elapsed}${tokenStr}`);
|
|
527
|
+
}
|
|
528
|
+
elapsed() {
|
|
529
|
+
const ms = Date.now() - this.startTime;
|
|
530
|
+
if (ms < 1e3) return "<1s";
|
|
531
|
+
return `${Math.floor(ms / 1e3)}s`;
|
|
532
|
+
}
|
|
533
|
+
clearLine() {
|
|
534
|
+
process.stdout.write("\r\x1B[K");
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// src/types.ts
|
|
539
|
+
var CliError = class extends Error {
|
|
540
|
+
constructor(message) {
|
|
541
|
+
super(message || "");
|
|
542
|
+
this.name = "CliError";
|
|
543
|
+
}
|
|
544
|
+
};
|
|
310
545
|
|
|
311
546
|
// src/commands/setup.ts
|
|
312
547
|
async function setupCommand(options) {
|
|
@@ -315,7 +550,7 @@ async function setupCommand(options) {
|
|
|
315
550
|
if (!apiKey) {
|
|
316
551
|
if (options.yes) {
|
|
317
552
|
console.log(chalk2.red(" \u2717 --key is required when using --yes"));
|
|
318
|
-
|
|
553
|
+
throw new CliError("--key is required when using --yes");
|
|
319
554
|
}
|
|
320
555
|
apiKey = await password({
|
|
321
556
|
message: "Enter your p\u0131ut API key:",
|
|
@@ -330,7 +565,7 @@ async function setupCommand(options) {
|
|
|
330
565
|
} catch (err) {
|
|
331
566
|
console.log(chalk2.red(` \u2717 ${err.message}`));
|
|
332
567
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
333
|
-
|
|
568
|
+
throw new CliError(err.message);
|
|
334
569
|
}
|
|
335
570
|
const { slug, displayName, status } = validationResult;
|
|
336
571
|
console.log(success(` \u2714 Authenticated as ${displayName}${slug ? ` (${slug})` : ""}`));
|
|
@@ -353,8 +588,8 @@ async function setupCommand(options) {
|
|
|
353
588
|
if (toolFilter && tool.id !== toolFilter) continue;
|
|
354
589
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
355
590
|
for (const configPath of paths) {
|
|
356
|
-
const exists =
|
|
357
|
-
const parentExists =
|
|
591
|
+
const exists = fs4.existsSync(configPath);
|
|
592
|
+
const parentExists = fs4.existsSync(path6.dirname(configPath));
|
|
358
593
|
if (exists || parentExists) {
|
|
359
594
|
detected.push({
|
|
360
595
|
tool,
|
|
@@ -451,6 +686,26 @@ async function setupCommand(options) {
|
|
|
451
686
|
}
|
|
452
687
|
}
|
|
453
688
|
}
|
|
689
|
+
if (configured.length > 0) {
|
|
690
|
+
const cwd = process.cwd();
|
|
691
|
+
const isProject2 = fs4.existsSync(path6.join(cwd, ".git")) || fs4.existsSync(path6.join(cwd, "package.json"));
|
|
692
|
+
if (isProject2) {
|
|
693
|
+
const { serverUrl } = validationResult;
|
|
694
|
+
writePiutConfig(cwd, { slug, apiKey, serverUrl });
|
|
695
|
+
await writePiutSkill(cwd, slug, apiKey);
|
|
696
|
+
ensureGitignored(cwd);
|
|
697
|
+
console.log();
|
|
698
|
+
console.log(dim(" Created .piut/ in current project"));
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (configured.length > 0) {
|
|
702
|
+
const { serverUrl } = validationResult;
|
|
703
|
+
console.log();
|
|
704
|
+
console.log(dim(" Registering connections..."));
|
|
705
|
+
await Promise.all(
|
|
706
|
+
configured.map((toolName) => pingMcp(serverUrl, apiKey, toolName))
|
|
707
|
+
);
|
|
708
|
+
}
|
|
454
709
|
console.log();
|
|
455
710
|
console.log(brand.bold(" Setup complete!"));
|
|
456
711
|
if (configured.length > 0) {
|
|
@@ -474,12 +729,12 @@ function isCommandAvailable(cmd) {
|
|
|
474
729
|
}
|
|
475
730
|
|
|
476
731
|
// src/commands/status.ts
|
|
477
|
-
import
|
|
478
|
-
import
|
|
732
|
+
import fs6 from "fs";
|
|
733
|
+
import path8 from "path";
|
|
479
734
|
|
|
480
735
|
// src/lib/brain-scanner.ts
|
|
481
|
-
import
|
|
482
|
-
import
|
|
736
|
+
import fs5 from "fs";
|
|
737
|
+
import path7 from "path";
|
|
483
738
|
import os3 from "os";
|
|
484
739
|
var home = os3.homedir();
|
|
485
740
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
@@ -500,13 +755,16 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
500
755
|
".yarn",
|
|
501
756
|
".pnpm-store",
|
|
502
757
|
"Caches",
|
|
503
|
-
"Cache"
|
|
758
|
+
"Cache",
|
|
759
|
+
".piut"
|
|
504
760
|
]);
|
|
505
761
|
var FULL_READ_FILES = /* @__PURE__ */ new Set([
|
|
506
762
|
"README.md",
|
|
507
763
|
"CLAUDE.md",
|
|
508
764
|
".cursorrules",
|
|
509
765
|
".windsurfrules",
|
|
766
|
+
".rules",
|
|
767
|
+
".clinerules",
|
|
510
768
|
"AGENTS.md",
|
|
511
769
|
"CONVENTIONS.md",
|
|
512
770
|
"MEMORY.md",
|
|
@@ -515,18 +773,27 @@ var FULL_READ_FILES = /* @__PURE__ */ new Set([
|
|
|
515
773
|
]);
|
|
516
774
|
var MAX_FILE_SIZE = 100 * 1024;
|
|
517
775
|
var RECENT_DAYS = 30;
|
|
776
|
+
var SCAN_DOT_DIRS = /* @__PURE__ */ new Set([
|
|
777
|
+
".claude",
|
|
778
|
+
".cursor",
|
|
779
|
+
".windsurf",
|
|
780
|
+
".github",
|
|
781
|
+
".zed",
|
|
782
|
+
".amazonq",
|
|
783
|
+
".vscode"
|
|
784
|
+
]);
|
|
518
785
|
function shouldSkipDir(name) {
|
|
519
|
-
if (name.startsWith(".") &&
|
|
786
|
+
if (name.startsWith(".") && !SCAN_DOT_DIRS.has(name)) {
|
|
520
787
|
return true;
|
|
521
788
|
}
|
|
522
789
|
return SKIP_DIRS.has(name);
|
|
523
790
|
}
|
|
524
791
|
function readFileSafe(filePath) {
|
|
525
792
|
try {
|
|
526
|
-
const stat =
|
|
793
|
+
const stat = fs5.statSync(filePath);
|
|
527
794
|
if (stat.size > MAX_FILE_SIZE) return null;
|
|
528
795
|
if (!stat.isFile()) return null;
|
|
529
|
-
return
|
|
796
|
+
return fs5.readFileSync(filePath, "utf-8");
|
|
530
797
|
} catch {
|
|
531
798
|
return null;
|
|
532
799
|
}
|
|
@@ -535,133 +802,156 @@ function getFolderTree(dir, depth = 0, maxDepth = 3) {
|
|
|
535
802
|
if (depth >= maxDepth) return [];
|
|
536
803
|
const entries = [];
|
|
537
804
|
try {
|
|
538
|
-
const items =
|
|
805
|
+
const items = fs5.readdirSync(dir, { withFileTypes: true });
|
|
539
806
|
for (const item of items) {
|
|
540
807
|
if (!item.isDirectory()) continue;
|
|
541
808
|
if (shouldSkipDir(item.name)) continue;
|
|
542
809
|
const indent = " ".repeat(depth);
|
|
543
810
|
entries.push(`${indent}${item.name}/`);
|
|
544
|
-
entries.push(...getFolderTree(
|
|
811
|
+
entries.push(...getFolderTree(path7.join(dir, item.name), depth + 1, maxDepth));
|
|
545
812
|
}
|
|
546
813
|
} catch {
|
|
547
814
|
}
|
|
548
815
|
return entries;
|
|
549
816
|
}
|
|
550
|
-
function
|
|
817
|
+
function isProject(dirPath) {
|
|
818
|
+
return fs5.existsSync(path7.join(dirPath, ".git")) || fs5.existsSync(path7.join(dirPath, "package.json")) || fs5.existsSync(path7.join(dirPath, "Cargo.toml")) || fs5.existsSync(path7.join(dirPath, "pyproject.toml")) || fs5.existsSync(path7.join(dirPath, "go.mod"));
|
|
819
|
+
}
|
|
820
|
+
function buildProjectInfo(projectPath) {
|
|
821
|
+
const hasPkgJson = fs5.existsSync(path7.join(projectPath, "package.json"));
|
|
822
|
+
let description = "";
|
|
823
|
+
if (hasPkgJson) {
|
|
824
|
+
try {
|
|
825
|
+
const pkg = JSON.parse(fs5.readFileSync(path7.join(projectPath, "package.json"), "utf-8"));
|
|
826
|
+
description = pkg.description || "";
|
|
827
|
+
} catch {
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
const readmePath = path7.join(projectPath, "README.md");
|
|
831
|
+
if (!description && fs5.existsSync(readmePath)) {
|
|
832
|
+
const content = readFileSafe(readmePath);
|
|
833
|
+
if (content) {
|
|
834
|
+
const lines = content.split("\n");
|
|
835
|
+
let foundHeading = false;
|
|
836
|
+
for (const line of lines) {
|
|
837
|
+
if (line.startsWith("#")) {
|
|
838
|
+
foundHeading = true;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
if (foundHeading && line.trim()) {
|
|
842
|
+
description = line.trim().slice(0, 200);
|
|
843
|
+
break;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return {
|
|
849
|
+
name: path7.basename(projectPath),
|
|
850
|
+
path: projectPath,
|
|
851
|
+
description,
|
|
852
|
+
hasClaudeMd: fs5.existsSync(path7.join(projectPath, "CLAUDE.md")) || fs5.existsSync(path7.join(projectPath, ".claude", "rules")),
|
|
853
|
+
hasCursorRules: fs5.existsSync(path7.join(projectPath, ".cursorrules")) || fs5.existsSync(path7.join(projectPath, ".cursor", "rules")),
|
|
854
|
+
hasWindsurfRules: fs5.existsSync(path7.join(projectPath, ".windsurfrules")) || fs5.existsSync(path7.join(projectPath, ".windsurf", "rules")),
|
|
855
|
+
hasCopilotInstructions: fs5.existsSync(path7.join(projectPath, ".github", "copilot-instructions.md")) || fs5.existsSync(path7.join(projectPath, ".github", "instructions")),
|
|
856
|
+
hasConventionsMd: fs5.existsSync(path7.join(projectPath, "CONVENTIONS.md")) || fs5.existsSync(path7.join(projectPath, ".amazonq", "rules")),
|
|
857
|
+
hasZedRules: fs5.existsSync(path7.join(projectPath, ".rules"))
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
var MAX_PROJECT_DEPTH = 4;
|
|
861
|
+
function detectProjects(scanDirs, onProgress) {
|
|
551
862
|
const projects = [];
|
|
552
|
-
|
|
863
|
+
const seen = /* @__PURE__ */ new Set();
|
|
864
|
+
function walk(dir, depth) {
|
|
865
|
+
if (depth > MAX_PROJECT_DEPTH) return;
|
|
553
866
|
try {
|
|
554
|
-
const items =
|
|
867
|
+
const items = fs5.readdirSync(dir, { withFileTypes: true });
|
|
555
868
|
for (const item of items) {
|
|
556
869
|
if (!item.isDirectory()) continue;
|
|
557
870
|
if (shouldSkipDir(item.name)) continue;
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
try {
|
|
568
|
-
const pkg = JSON.parse(fs4.readFileSync(path6.join(projectPath, "package.json"), "utf-8"));
|
|
569
|
-
description = pkg.description || "";
|
|
570
|
-
} catch {
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
const readmePath = path6.join(projectPath, "README.md");
|
|
574
|
-
if (!description && fs4.existsSync(readmePath)) {
|
|
575
|
-
const content = readFileSafe(readmePath);
|
|
576
|
-
if (content) {
|
|
577
|
-
const lines = content.split("\n");
|
|
578
|
-
let foundHeading = false;
|
|
579
|
-
for (const line of lines) {
|
|
580
|
-
if (line.startsWith("#")) {
|
|
581
|
-
foundHeading = true;
|
|
582
|
-
continue;
|
|
583
|
-
}
|
|
584
|
-
if (foundHeading && line.trim()) {
|
|
585
|
-
description = line.trim().slice(0, 200);
|
|
586
|
-
break;
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
}
|
|
871
|
+
const fullPath = path7.join(dir, item.name);
|
|
872
|
+
if (seen.has(fullPath)) continue;
|
|
873
|
+
seen.add(fullPath);
|
|
874
|
+
if (isProject(fullPath)) {
|
|
875
|
+
const info = buildProjectInfo(fullPath);
|
|
876
|
+
projects.push(info);
|
|
877
|
+
onProgress?.({ phase: "projects", message: `${info.name} (${fullPath.replace(home, "~")})` });
|
|
878
|
+
} else {
|
|
879
|
+
walk(fullPath, depth + 1);
|
|
590
880
|
}
|
|
591
|
-
projects.push({
|
|
592
|
-
name: item.name,
|
|
593
|
-
path: projectPath,
|
|
594
|
-
description,
|
|
595
|
-
hasClaudeMd: fs4.existsSync(path6.join(projectPath, "CLAUDE.md")),
|
|
596
|
-
hasCursorRules: fs4.existsSync(path6.join(projectPath, ".cursorrules")) || fs4.existsSync(path6.join(projectPath, ".cursor", "rules")),
|
|
597
|
-
hasWindsurfRules: fs4.existsSync(path6.join(projectPath, ".windsurfrules")) || fs4.existsSync(path6.join(projectPath, ".windsurf", "rules")),
|
|
598
|
-
hasCopilotInstructions: fs4.existsSync(path6.join(projectPath, ".github", "copilot-instructions.md")),
|
|
599
|
-
hasConventionsMd: fs4.existsSync(path6.join(projectPath, "CONVENTIONS.md")),
|
|
600
|
-
hasZedRules: fs4.existsSync(path6.join(projectPath, ".zed", "rules.md"))
|
|
601
|
-
});
|
|
602
881
|
}
|
|
603
882
|
} catch {
|
|
604
883
|
}
|
|
605
884
|
}
|
|
885
|
+
for (const dir of scanDirs) {
|
|
886
|
+
walk(dir, 0);
|
|
887
|
+
}
|
|
606
888
|
return projects;
|
|
607
889
|
}
|
|
608
|
-
function collectConfigFiles(projects) {
|
|
890
|
+
function collectConfigFiles(projects, onProgress) {
|
|
609
891
|
const configs = [];
|
|
610
892
|
const globalPaths = [
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
893
|
+
path7.join(home, ".claude", "MEMORY.md"),
|
|
894
|
+
path7.join(home, ".claude", "CLAUDE.md"),
|
|
895
|
+
path7.join(home, ".openclaw", "workspace", "SOUL.md"),
|
|
896
|
+
path7.join(home, ".openclaw", "workspace", "MEMORY.md")
|
|
615
897
|
];
|
|
616
898
|
for (const gp of globalPaths) {
|
|
617
899
|
const content = readFileSafe(gp);
|
|
618
900
|
if (content && content.trim()) {
|
|
619
|
-
|
|
901
|
+
const name = `~/${path7.relative(home, gp)}`;
|
|
902
|
+
configs.push({ name, content });
|
|
903
|
+
onProgress?.({ phase: "configs", message: name });
|
|
620
904
|
}
|
|
621
905
|
}
|
|
622
906
|
for (const project of projects) {
|
|
623
907
|
for (const fileName of FULL_READ_FILES) {
|
|
624
|
-
const filePath =
|
|
908
|
+
const filePath = path7.join(project.path, fileName);
|
|
625
909
|
const content = readFileSafe(filePath);
|
|
626
910
|
if (content && content.trim()) {
|
|
627
|
-
|
|
911
|
+
const name = `${project.name}/${fileName}`;
|
|
912
|
+
configs.push({ name, content });
|
|
913
|
+
onProgress?.({ phase: "configs", message: name });
|
|
628
914
|
}
|
|
629
915
|
}
|
|
630
|
-
const pkgPath =
|
|
916
|
+
const pkgPath = path7.join(project.path, "package.json");
|
|
631
917
|
const pkgContent = readFileSafe(pkgPath);
|
|
632
918
|
if (pkgContent) {
|
|
633
919
|
try {
|
|
634
920
|
const pkg = JSON.parse(pkgContent);
|
|
635
921
|
const summary = JSON.stringify({ name: pkg.name, description: pkg.description }, null, 2);
|
|
636
|
-
|
|
922
|
+
const name = `${project.name}/package.json`;
|
|
923
|
+
configs.push({ name, content: summary });
|
|
924
|
+
onProgress?.({ phase: "configs", message: name });
|
|
637
925
|
} catch {
|
|
638
926
|
}
|
|
639
927
|
}
|
|
640
928
|
}
|
|
641
929
|
return configs;
|
|
642
930
|
}
|
|
643
|
-
function collectRecentDocs(projects) {
|
|
931
|
+
function collectRecentDocs(projects, onProgress) {
|
|
644
932
|
const docs = [];
|
|
645
933
|
const cutoff = Date.now() - RECENT_DAYS * 24 * 60 * 60 * 1e3;
|
|
646
934
|
const seen = /* @__PURE__ */ new Set();
|
|
647
935
|
for (const project of projects) {
|
|
648
936
|
try {
|
|
649
|
-
const items =
|
|
937
|
+
const items = fs5.readdirSync(project.path, { withFileTypes: true });
|
|
650
938
|
for (const item of items) {
|
|
651
939
|
if (!item.isFile()) continue;
|
|
652
940
|
if (!item.name.endsWith(".md")) continue;
|
|
653
941
|
if (FULL_READ_FILES.has(item.name)) continue;
|
|
654
942
|
if (item.name.startsWith(".")) continue;
|
|
655
|
-
const filePath =
|
|
943
|
+
const filePath = path7.join(project.path, item.name);
|
|
656
944
|
if (seen.has(filePath)) continue;
|
|
657
945
|
seen.add(filePath);
|
|
658
946
|
try {
|
|
659
|
-
const stat =
|
|
947
|
+
const stat = fs5.statSync(filePath);
|
|
660
948
|
if (stat.mtimeMs < cutoff) continue;
|
|
661
949
|
if (stat.size > MAX_FILE_SIZE) continue;
|
|
662
|
-
const content =
|
|
950
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
663
951
|
if (content.trim()) {
|
|
664
|
-
|
|
952
|
+
const name = `${project.name}/${item.name}`;
|
|
953
|
+
docs.push({ name, content });
|
|
954
|
+
onProgress?.({ phase: "docs", message: name });
|
|
665
955
|
}
|
|
666
956
|
} catch {
|
|
667
957
|
}
|
|
@@ -671,42 +961,66 @@ function collectRecentDocs(projects) {
|
|
|
671
961
|
}
|
|
672
962
|
return docs.slice(0, 20);
|
|
673
963
|
}
|
|
964
|
+
var SKIP_HOME_DIRS = /* @__PURE__ */ new Set([
|
|
965
|
+
"Library",
|
|
966
|
+
"Applications",
|
|
967
|
+
"Public",
|
|
968
|
+
"Movies",
|
|
969
|
+
"Music",
|
|
970
|
+
"Pictures",
|
|
971
|
+
"Templates",
|
|
972
|
+
".Trash"
|
|
973
|
+
]);
|
|
974
|
+
var INCLUDE_DOT_DIRS = /* @__PURE__ */ new Set([
|
|
975
|
+
".claude",
|
|
976
|
+
".cursor",
|
|
977
|
+
".windsurf",
|
|
978
|
+
".openclaw",
|
|
979
|
+
".zed",
|
|
980
|
+
".github",
|
|
981
|
+
".amazonq"
|
|
982
|
+
]);
|
|
674
983
|
function getDefaultScanDirs() {
|
|
675
984
|
const dirs = [];
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
985
|
+
try {
|
|
986
|
+
const entries = fs5.readdirSync(home, { withFileTypes: true });
|
|
987
|
+
for (const entry of entries) {
|
|
988
|
+
if (!entry.isDirectory()) continue;
|
|
989
|
+
if (entry.name.startsWith(".") && !INCLUDE_DOT_DIRS.has(entry.name)) continue;
|
|
990
|
+
if (SKIP_HOME_DIRS.has(entry.name)) continue;
|
|
991
|
+
dirs.push(path7.join(home, entry.name));
|
|
992
|
+
}
|
|
993
|
+
} catch {
|
|
994
|
+
}
|
|
995
|
+
const cloudStorage = path7.join(home, "Library", "CloudStorage");
|
|
996
|
+
try {
|
|
997
|
+
if (fs5.existsSync(cloudStorage) && fs5.statSync(cloudStorage).isDirectory()) {
|
|
998
|
+
const entries = fs5.readdirSync(cloudStorage, { withFileTypes: true });
|
|
999
|
+
for (const entry of entries) {
|
|
1000
|
+
if (!entry.isDirectory()) continue;
|
|
1001
|
+
const fullPath = path7.join(cloudStorage, entry.name);
|
|
1002
|
+
if (!dirs.includes(fullPath)) {
|
|
1003
|
+
dirs.push(fullPath);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
693
1006
|
}
|
|
1007
|
+
} catch {
|
|
694
1008
|
}
|
|
695
1009
|
if (dirs.length === 0) {
|
|
696
1010
|
dirs.push(home);
|
|
697
1011
|
}
|
|
698
1012
|
return dirs;
|
|
699
1013
|
}
|
|
700
|
-
function scanForBrain(folders) {
|
|
1014
|
+
function scanForBrain(folders, onProgress) {
|
|
701
1015
|
const scanDirs = folders || getDefaultScanDirs();
|
|
702
1016
|
const folderTree = [];
|
|
703
1017
|
for (const dir of scanDirs) {
|
|
704
|
-
folderTree.push(`${
|
|
1018
|
+
folderTree.push(`${path7.basename(dir)}/`);
|
|
705
1019
|
folderTree.push(...getFolderTree(dir, 1));
|
|
706
1020
|
}
|
|
707
|
-
const projects = detectProjects(scanDirs);
|
|
708
|
-
const configFiles = collectConfigFiles(projects);
|
|
709
|
-
const recentDocuments = collectRecentDocs(projects);
|
|
1021
|
+
const projects = detectProjects(scanDirs, onProgress);
|
|
1022
|
+
const configFiles = collectConfigFiles(projects, onProgress);
|
|
1023
|
+
const recentDocuments = collectRecentDocs(projects, onProgress);
|
|
710
1024
|
return {
|
|
711
1025
|
summary: {
|
|
712
1026
|
folders: folderTree,
|
|
@@ -736,7 +1050,7 @@ var PIUT_FILES = [
|
|
|
736
1050
|
];
|
|
737
1051
|
function hasPiutReference(filePath) {
|
|
738
1052
|
try {
|
|
739
|
-
const content =
|
|
1053
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
740
1054
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
741
1055
|
} catch {
|
|
742
1056
|
return false;
|
|
@@ -750,7 +1064,7 @@ function statusCommand() {
|
|
|
750
1064
|
for (const tool of TOOLS) {
|
|
751
1065
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
752
1066
|
for (const configPath of paths) {
|
|
753
|
-
if (!
|
|
1067
|
+
if (!fs6.existsSync(configPath)) continue;
|
|
754
1068
|
foundAny = true;
|
|
755
1069
|
const configured = isPiutConfigured(configPath, tool.configKey);
|
|
756
1070
|
if (configured) {
|
|
@@ -773,8 +1087,8 @@ function statusCommand() {
|
|
|
773
1087
|
for (const project of projects) {
|
|
774
1088
|
const connectedFiles = [];
|
|
775
1089
|
for (const file of PIUT_FILES) {
|
|
776
|
-
const absPath =
|
|
777
|
-
if (
|
|
1090
|
+
const absPath = path8.join(project.path, file);
|
|
1091
|
+
if (fs6.existsSync(absPath) && hasPiutReference(absPath)) {
|
|
778
1092
|
connectedFiles.push(file);
|
|
779
1093
|
}
|
|
780
1094
|
}
|
|
@@ -794,7 +1108,7 @@ function statusCommand() {
|
|
|
794
1108
|
}
|
|
795
1109
|
|
|
796
1110
|
// src/commands/remove.ts
|
|
797
|
-
import
|
|
1111
|
+
import fs7 from "fs";
|
|
798
1112
|
import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
|
|
799
1113
|
async function removeCommand() {
|
|
800
1114
|
banner();
|
|
@@ -802,7 +1116,7 @@ async function removeCommand() {
|
|
|
802
1116
|
for (const tool of TOOLS) {
|
|
803
1117
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
804
1118
|
for (const configPath of paths) {
|
|
805
|
-
if (
|
|
1119
|
+
if (fs7.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
|
|
806
1120
|
configured.push({ tool, configPath });
|
|
807
1121
|
break;
|
|
808
1122
|
}
|
|
@@ -845,22 +1159,23 @@ async function removeCommand() {
|
|
|
845
1159
|
}
|
|
846
1160
|
|
|
847
1161
|
// src/commands/build.ts
|
|
848
|
-
import { select } from "@inquirer/prompts";
|
|
1162
|
+
import { select, checkbox as checkbox3, input } from "@inquirer/prompts";
|
|
849
1163
|
import chalk4 from "chalk";
|
|
1164
|
+
import os5 from "os";
|
|
850
1165
|
|
|
851
1166
|
// src/lib/auth.ts
|
|
852
1167
|
import { password as password2 } from "@inquirer/prompts";
|
|
853
1168
|
import chalk3 from "chalk";
|
|
854
1169
|
|
|
855
1170
|
// src/lib/store.ts
|
|
856
|
-
import
|
|
857
|
-
import
|
|
1171
|
+
import fs8 from "fs";
|
|
1172
|
+
import path9 from "path";
|
|
858
1173
|
import os4 from "os";
|
|
859
|
-
var CONFIG_DIR =
|
|
860
|
-
var
|
|
1174
|
+
var CONFIG_DIR = path9.join(os4.homedir(), ".piut");
|
|
1175
|
+
var CONFIG_FILE2 = path9.join(CONFIG_DIR, "config.json");
|
|
861
1176
|
function readStore() {
|
|
862
1177
|
try {
|
|
863
|
-
const raw =
|
|
1178
|
+
const raw = fs8.readFileSync(CONFIG_FILE2, "utf-8");
|
|
864
1179
|
return JSON.parse(raw);
|
|
865
1180
|
} catch {
|
|
866
1181
|
return {};
|
|
@@ -869,10 +1184,16 @@ function readStore() {
|
|
|
869
1184
|
function updateStore(updates) {
|
|
870
1185
|
const config = readStore();
|
|
871
1186
|
const updated = { ...config, ...updates };
|
|
872
|
-
|
|
873
|
-
|
|
1187
|
+
fs8.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1188
|
+
fs8.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
874
1189
|
return updated;
|
|
875
1190
|
}
|
|
1191
|
+
function clearStore() {
|
|
1192
|
+
try {
|
|
1193
|
+
fs8.unlinkSync(CONFIG_FILE2);
|
|
1194
|
+
} catch {
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
876
1197
|
|
|
877
1198
|
// src/lib/auth.ts
|
|
878
1199
|
async function resolveApiKey(keyOption) {
|
|
@@ -892,7 +1213,7 @@ async function resolveApiKey(keyOption) {
|
|
|
892
1213
|
} catch (err) {
|
|
893
1214
|
console.log(chalk3.red(` \u2717 ${err.message}`));
|
|
894
1215
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
895
|
-
|
|
1216
|
+
throw new CliError(err.message);
|
|
896
1217
|
}
|
|
897
1218
|
const label = result.slug ? `${result.displayName} (${result.slug})` : result.displayName;
|
|
898
1219
|
console.log(success(` \u2713 Connected as ${label}`));
|
|
@@ -916,7 +1237,7 @@ async function resolveApiKeyWithResult(keyOption) {
|
|
|
916
1237
|
} catch (err) {
|
|
917
1238
|
console.log(chalk3.red(` \u2717 ${err.message}`));
|
|
918
1239
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
919
|
-
|
|
1240
|
+
throw new CliError(err.message);
|
|
920
1241
|
}
|
|
921
1242
|
const label = result.slug ? `${result.displayName} (${result.slug})` : result.displayName;
|
|
922
1243
|
console.log(success(` \u2713 Connected as ${label}`));
|
|
@@ -932,41 +1253,112 @@ async function buildCommand(options) {
|
|
|
932
1253
|
if (options.folders) {
|
|
933
1254
|
scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
|
|
934
1255
|
}
|
|
935
|
-
const
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
]
|
|
941
|
-
});
|
|
942
|
-
if (mode === "folders" && !scanFolders) {
|
|
943
|
-
const defaults = getDefaultScanDirs();
|
|
1256
|
+
const cwd = process.cwd();
|
|
1257
|
+
const cwdDisplay = cwd.replace(os5.homedir(), "~");
|
|
1258
|
+
if (!scanFolders) {
|
|
1259
|
+
console.log(dim(` Current directory: `) + cwdDisplay);
|
|
1260
|
+
console.log(dim(` We'll scan for AI config files and projects here.`));
|
|
944
1261
|
console.log();
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
1262
|
+
const mode = await select({
|
|
1263
|
+
message: "How do you want to build your brain?",
|
|
1264
|
+
choices: [
|
|
1265
|
+
{ name: `Scan this directory (${cwdDisplay})`, value: "cwd", description: "Scan current directory for projects and config files" },
|
|
1266
|
+
{ name: "Select folder(s)...", value: "folders", description: "Choose a different directory to scan" }
|
|
1267
|
+
]
|
|
1268
|
+
});
|
|
1269
|
+
if (mode === "cwd") {
|
|
1270
|
+
scanFolders = [cwd];
|
|
1271
|
+
} else {
|
|
1272
|
+
const defaults = getDefaultScanDirs();
|
|
1273
|
+
const CUSTOM_VALUE = "__custom__";
|
|
1274
|
+
const choices = [
|
|
1275
|
+
...defaults.map((d) => ({ name: d.replace(os5.homedir(), "~"), value: d })),
|
|
1276
|
+
{ name: chalk4.dim("Enter a custom path..."), value: CUSTOM_VALUE }
|
|
1277
|
+
];
|
|
1278
|
+
const selected = await checkbox3({
|
|
1279
|
+
message: "Which folders should we scan?",
|
|
1280
|
+
choices,
|
|
1281
|
+
required: true
|
|
1282
|
+
});
|
|
1283
|
+
if (selected.includes(CUSTOM_VALUE)) {
|
|
1284
|
+
const custom = await input({
|
|
1285
|
+
message: "Enter folder path(s), comma-separated:"
|
|
1286
|
+
});
|
|
1287
|
+
const customPaths = custom.split(",").map((f) => expandPath(f.trim())).filter(Boolean);
|
|
1288
|
+
scanFolders = [
|
|
1289
|
+
...selected.filter((v) => v !== CUSTOM_VALUE),
|
|
1290
|
+
...customPaths
|
|
1291
|
+
];
|
|
1292
|
+
} else {
|
|
1293
|
+
scanFolders = selected;
|
|
1294
|
+
}
|
|
1295
|
+
if (scanFolders.length === 0) {
|
|
1296
|
+
console.log(chalk4.yellow(" No folders selected."));
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
948
1299
|
}
|
|
949
|
-
console.log();
|
|
950
|
-
console.log(dim(" Tip: pass --folders ~/Projects,~/Documents to specify directly"));
|
|
951
|
-
scanFolders = defaults;
|
|
952
1300
|
}
|
|
953
1301
|
console.log();
|
|
954
|
-
|
|
1302
|
+
let projectCount = 0;
|
|
1303
|
+
let configCount = 0;
|
|
1304
|
+
let docCount = 0;
|
|
1305
|
+
const onProgress = (progress) => {
|
|
1306
|
+
if (progress.phase === "projects") {
|
|
1307
|
+
projectCount++;
|
|
1308
|
+
console.log(dim(` [${projectCount}] ${progress.message}`));
|
|
1309
|
+
} else if (progress.phase === "configs") {
|
|
1310
|
+
configCount++;
|
|
1311
|
+
console.log(dim(` [${configCount}] ${progress.message}`));
|
|
1312
|
+
} else if (progress.phase === "docs") {
|
|
1313
|
+
docCount++;
|
|
1314
|
+
console.log(dim(` [${docCount}] ${progress.message}`));
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
const brainInput = scanForBrain(scanFolders, onProgress);
|
|
1318
|
+
const projCount = brainInput.summary.projects.length;
|
|
1319
|
+
const cfgCount = brainInput.summary.configFiles.length;
|
|
1320
|
+
const dcCount = brainInput.summary.recentDocuments.length;
|
|
955
1321
|
console.log();
|
|
956
|
-
|
|
957
|
-
const projCount = input.summary.projects.length;
|
|
958
|
-
const configCount = input.summary.configFiles.length;
|
|
959
|
-
const docCount = input.summary.recentDocuments.length;
|
|
960
|
-
console.log(dim(` Scanned: ${projCount} projects, ${configCount} config files, ${docCount} recent docs`));
|
|
1322
|
+
console.log(success(` Scanned: ${projCount} projects, ${cfgCount} config files, ${dcCount} recent docs`));
|
|
961
1323
|
console.log();
|
|
962
|
-
if (projCount === 0 &&
|
|
1324
|
+
if (projCount === 0 && cfgCount === 0) {
|
|
963
1325
|
console.log(chalk4.yellow(" No projects or config files found to build from."));
|
|
964
1326
|
console.log(dim(" Try running from a directory with your projects, or use --folders."));
|
|
965
1327
|
console.log();
|
|
966
1328
|
return;
|
|
967
1329
|
}
|
|
1330
|
+
const spinner = new Spinner();
|
|
1331
|
+
spinner.start("Generating brain...");
|
|
968
1332
|
try {
|
|
969
|
-
|
|
1333
|
+
let sections = null;
|
|
1334
|
+
for await (const event of buildBrainStreaming(apiKey, brainInput)) {
|
|
1335
|
+
switch (event.event) {
|
|
1336
|
+
case "status":
|
|
1337
|
+
spinner.updateMessage(String(event.data.message || "Processing..."));
|
|
1338
|
+
break;
|
|
1339
|
+
case "progress":
|
|
1340
|
+
spinner.updateTokens(event.data.tokens);
|
|
1341
|
+
break;
|
|
1342
|
+
case "section":
|
|
1343
|
+
spinner.addSection(String(event.data.name));
|
|
1344
|
+
break;
|
|
1345
|
+
case "complete":
|
|
1346
|
+
sections = event.data.sections || {};
|
|
1347
|
+
break;
|
|
1348
|
+
case "error":
|
|
1349
|
+
spinner.stop();
|
|
1350
|
+
console.log(chalk4.red(` \u2717 ${event.data.message || "Build failed"}`));
|
|
1351
|
+
throw new CliError(String(event.data.message || "Build failed"));
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
spinner.stop();
|
|
1355
|
+
if (!sections) {
|
|
1356
|
+
console.log(chalk4.red(" \u2717 No response received from server"));
|
|
1357
|
+
throw new CliError("No response received from server");
|
|
1358
|
+
}
|
|
1359
|
+
console.log();
|
|
1360
|
+
console.log(success(" Brain built!"));
|
|
1361
|
+
console.log();
|
|
970
1362
|
const sectionSummary = (content, label) => {
|
|
971
1363
|
if (!content || !content.trim()) {
|
|
972
1364
|
console.log(dim(` ${label} \u2014 (empty)`));
|
|
@@ -976,24 +1368,23 @@ async function buildCommand(options) {
|
|
|
976
1368
|
console.log(success(` ${label}`) + dim(` \u2014 ${preview}`));
|
|
977
1369
|
}
|
|
978
1370
|
};
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
sectionSummary(sections.
|
|
982
|
-
sectionSummary(sections.
|
|
983
|
-
sectionSummary(sections.
|
|
984
|
-
sectionSummary(sections.projects, "Projects");
|
|
985
|
-
sectionSummary(sections.memory, "Memory");
|
|
1371
|
+
sectionSummary(sections.about || "", "About");
|
|
1372
|
+
sectionSummary(sections.soul || "", "Soul");
|
|
1373
|
+
sectionSummary(sections.areas || "", "Areas of Responsibility");
|
|
1374
|
+
sectionSummary(sections.projects || "", "Projects");
|
|
1375
|
+
sectionSummary(sections.memory || "", "Memory");
|
|
986
1376
|
console.log();
|
|
987
1377
|
console.log(dim(` Review and edit at ${brand("piut.com/dashboard")}`));
|
|
988
1378
|
console.log();
|
|
989
1379
|
} catch (err) {
|
|
1380
|
+
spinner.stop();
|
|
1381
|
+
if (err instanceof CliError) throw err;
|
|
990
1382
|
console.log(chalk4.red(` \u2717 ${err.message}`));
|
|
991
|
-
|
|
1383
|
+
throw new CliError(err.message);
|
|
992
1384
|
}
|
|
993
1385
|
}
|
|
994
1386
|
|
|
995
1387
|
// src/commands/deploy.ts
|
|
996
|
-
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
997
1388
|
import chalk5 from "chalk";
|
|
998
1389
|
async function deployCommand(options) {
|
|
999
1390
|
banner();
|
|
@@ -1005,27 +1396,12 @@ async function deployCommand(options) {
|
|
|
1005
1396
|
console.log();
|
|
1006
1397
|
return;
|
|
1007
1398
|
}
|
|
1008
|
-
console.log();
|
|
1009
|
-
console.log(dim(" Your brain will be published as an MCP server at:"));
|
|
1010
|
-
console.log(` ${brand(serverUrl)}`);
|
|
1011
|
-
console.log();
|
|
1012
|
-
console.log(dim(" Any AI tool with this URL can read your brain."));
|
|
1013
|
-
console.log();
|
|
1014
|
-
if (!options.yes) {
|
|
1015
|
-
const proceed = await confirm3({
|
|
1016
|
-
message: "Deploy?",
|
|
1017
|
-
default: true
|
|
1018
|
-
});
|
|
1019
|
-
if (!proceed) {
|
|
1020
|
-
console.log(dim(" Cancelled."));
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
1399
|
try {
|
|
1025
1400
|
await publishServer(apiKey);
|
|
1026
1401
|
console.log();
|
|
1027
1402
|
console.log(success(" \u2713 Brain deployed. MCP server live."));
|
|
1028
|
-
console.log(
|
|
1403
|
+
console.log(` ${brand(serverUrl)}`);
|
|
1404
|
+
console.log(dim(" (securely accessible only with authentication)"));
|
|
1029
1405
|
console.log();
|
|
1030
1406
|
console.log(dim(" Next: run ") + brand("piut connect") + dim(" to add brain references to your projects."));
|
|
1031
1407
|
console.log();
|
|
@@ -1040,39 +1416,39 @@ async function deployCommand(options) {
|
|
|
1040
1416
|
console.log();
|
|
1041
1417
|
} else {
|
|
1042
1418
|
console.log(chalk5.red(` \u2717 ${msg}`));
|
|
1043
|
-
|
|
1419
|
+
throw new CliError(msg);
|
|
1044
1420
|
}
|
|
1045
1421
|
}
|
|
1046
1422
|
}
|
|
1047
1423
|
|
|
1048
1424
|
// src/commands/connect.ts
|
|
1049
|
-
import
|
|
1050
|
-
import
|
|
1051
|
-
import { checkbox as
|
|
1425
|
+
import fs9 from "fs";
|
|
1426
|
+
import path10 from "path";
|
|
1427
|
+
import { checkbox as checkbox4 } from "@inquirer/prompts";
|
|
1052
1428
|
var RULE_FILES = [
|
|
1053
1429
|
{
|
|
1054
1430
|
tool: "Claude Code",
|
|
1055
1431
|
filePath: "CLAUDE.md",
|
|
1056
1432
|
strategy: "append",
|
|
1057
|
-
detect: (p) => p.hasClaudeMd ||
|
|
1433
|
+
detect: (p) => p.hasClaudeMd || fs9.existsSync(path10.join(p.path, ".claude"))
|
|
1058
1434
|
},
|
|
1059
1435
|
{
|
|
1060
1436
|
tool: "Cursor",
|
|
1061
1437
|
filePath: ".cursor/rules/piut.mdc",
|
|
1062
1438
|
strategy: "create",
|
|
1063
|
-
detect: (p) => p.hasCursorRules ||
|
|
1439
|
+
detect: (p) => p.hasCursorRules || fs9.existsSync(path10.join(p.path, ".cursor"))
|
|
1064
1440
|
},
|
|
1065
1441
|
{
|
|
1066
1442
|
tool: "Windsurf",
|
|
1067
1443
|
filePath: ".windsurf/rules/piut.md",
|
|
1068
1444
|
strategy: "create",
|
|
1069
|
-
detect: (p) => p.hasWindsurfRules ||
|
|
1445
|
+
detect: (p) => p.hasWindsurfRules || fs9.existsSync(path10.join(p.path, ".windsurf"))
|
|
1070
1446
|
},
|
|
1071
1447
|
{
|
|
1072
1448
|
tool: "GitHub Copilot",
|
|
1073
1449
|
filePath: ".github/copilot-instructions.md",
|
|
1074
1450
|
strategy: "append",
|
|
1075
|
-
detect: (p) => p.hasCopilotInstructions ||
|
|
1451
|
+
detect: (p) => p.hasCopilotInstructions || fs9.existsSync(path10.join(p.path, ".github"))
|
|
1076
1452
|
},
|
|
1077
1453
|
{
|
|
1078
1454
|
tool: "Amazon Q",
|
|
@@ -1084,12 +1460,12 @@ var RULE_FILES = [
|
|
|
1084
1460
|
tool: "Zed",
|
|
1085
1461
|
filePath: ".zed/rules.md",
|
|
1086
1462
|
strategy: "create",
|
|
1087
|
-
detect: (p) => p.hasZedRules ||
|
|
1463
|
+
detect: (p) => p.hasZedRules || fs9.existsSync(path10.join(p.path, ".zed"))
|
|
1088
1464
|
}
|
|
1089
1465
|
];
|
|
1090
1466
|
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context
|
|
1091
1467
|
This project uses p\u0131ut for persistent personal context.
|
|
1092
|
-
|
|
1468
|
+
Full skill reference: .piut/skill.md
|
|
1093
1469
|
|
|
1094
1470
|
Always call \`get_context\` at the start of every conversation.
|
|
1095
1471
|
Read the \`soul\` section first \u2014 it contains behavioral instructions.
|
|
@@ -1098,12 +1474,12 @@ Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes.
|
|
|
1098
1474
|
var APPEND_SECTION = `
|
|
1099
1475
|
|
|
1100
1476
|
## p\u0131ut Context
|
|
1101
|
-
|
|
1477
|
+
Full skill reference: .piut/skill.md
|
|
1102
1478
|
Always call \`get_context\` at the start of every conversation to load personal context.
|
|
1103
1479
|
`;
|
|
1104
1480
|
function hasPiutReference2(filePath) {
|
|
1105
1481
|
try {
|
|
1106
|
-
const content =
|
|
1482
|
+
const content = fs9.readFileSync(filePath, "utf-8");
|
|
1107
1483
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
1108
1484
|
} catch {
|
|
1109
1485
|
return false;
|
|
@@ -1111,7 +1487,21 @@ function hasPiutReference2(filePath) {
|
|
|
1111
1487
|
}
|
|
1112
1488
|
async function connectCommand(options) {
|
|
1113
1489
|
banner();
|
|
1114
|
-
await resolveApiKeyWithResult(options.key);
|
|
1490
|
+
const { apiKey, slug, serverUrl, status } = await resolveApiKeyWithResult(options.key);
|
|
1491
|
+
if (status === "no_brain") {
|
|
1492
|
+
console.log();
|
|
1493
|
+
console.log(warning(" You haven\u2019t built a brain yet."));
|
|
1494
|
+
console.log(dim(" Run ") + brand("piut build") + dim(" first, then ") + brand("piut deploy") + dim("."));
|
|
1495
|
+
console.log();
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
if (status === "unpublished") {
|
|
1499
|
+
console.log();
|
|
1500
|
+
console.log(warning(" Your brain is built but not deployed yet."));
|
|
1501
|
+
console.log(dim(" Run ") + brand("piut deploy") + dim(" to publish your MCP server, then re-run connect."));
|
|
1502
|
+
console.log();
|
|
1503
|
+
return;
|
|
1504
|
+
}
|
|
1115
1505
|
let scanFolders;
|
|
1116
1506
|
if (options.folders) {
|
|
1117
1507
|
scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
|
|
@@ -1129,20 +1519,20 @@ async function connectCommand(options) {
|
|
|
1129
1519
|
for (const project of projects) {
|
|
1130
1520
|
for (const rule of RULE_FILES) {
|
|
1131
1521
|
if (!rule.detect(project)) continue;
|
|
1132
|
-
const absPath =
|
|
1133
|
-
if (
|
|
1522
|
+
const absPath = path10.join(project.path, rule.filePath);
|
|
1523
|
+
if (fs9.existsSync(absPath) && hasPiutReference2(absPath)) continue;
|
|
1134
1524
|
actions.push({
|
|
1135
1525
|
project,
|
|
1136
1526
|
tool: rule.tool,
|
|
1137
1527
|
filePath: rule.filePath,
|
|
1138
1528
|
absPath,
|
|
1139
|
-
action: rule.strategy === "create" || !
|
|
1529
|
+
action: rule.strategy === "create" || !fs9.existsSync(absPath) ? "create" : "append"
|
|
1140
1530
|
});
|
|
1141
1531
|
}
|
|
1142
1532
|
const hasAnyAction = actions.some((a) => a.project === project);
|
|
1143
1533
|
if (!hasAnyAction) {
|
|
1144
|
-
const claudeMdPath =
|
|
1145
|
-
if (!
|
|
1534
|
+
const claudeMdPath = path10.join(project.path, "CLAUDE.md");
|
|
1535
|
+
if (!fs9.existsSync(claudeMdPath)) {
|
|
1146
1536
|
actions.push({
|
|
1147
1537
|
project,
|
|
1148
1538
|
tool: "Claude Code",
|
|
@@ -1177,22 +1567,21 @@ async function connectCommand(options) {
|
|
|
1177
1567
|
console.log();
|
|
1178
1568
|
const projectChoices = [];
|
|
1179
1569
|
for (const [projectPath, projectActions] of byProject) {
|
|
1180
|
-
const projectName =
|
|
1570
|
+
const projectName = path10.basename(projectPath);
|
|
1181
1571
|
const desc = projectActions.map((a) => {
|
|
1182
1572
|
const verb = a.action === "create" ? "will create" : "will append to";
|
|
1183
1573
|
return `${verb} ${a.filePath}`;
|
|
1184
1574
|
}).join(", ");
|
|
1185
1575
|
projectChoices.push({
|
|
1186
1576
|
name: `${projectName} ${dim(`(${desc})`)}`,
|
|
1187
|
-
value: projectPath
|
|
1188
|
-
checked: true
|
|
1577
|
+
value: projectPath
|
|
1189
1578
|
});
|
|
1190
1579
|
}
|
|
1191
1580
|
let selectedPaths;
|
|
1192
1581
|
if (options.yes) {
|
|
1193
1582
|
selectedPaths = Array.from(byProject.keys());
|
|
1194
1583
|
} else {
|
|
1195
|
-
selectedPaths = await
|
|
1584
|
+
selectedPaths = await checkbox4({
|
|
1196
1585
|
message: "Select projects to connect:",
|
|
1197
1586
|
choices: projectChoices
|
|
1198
1587
|
});
|
|
@@ -1203,32 +1592,46 @@ async function connectCommand(options) {
|
|
|
1203
1592
|
}
|
|
1204
1593
|
console.log();
|
|
1205
1594
|
let connected = 0;
|
|
1595
|
+
const copilotTool = TOOLS.find((t) => t.id === "copilot");
|
|
1206
1596
|
for (const projectPath of selectedPaths) {
|
|
1207
1597
|
const projectActions = byProject.get(projectPath) || [];
|
|
1208
|
-
const projectName =
|
|
1598
|
+
const projectName = path10.basename(projectPath);
|
|
1599
|
+
writePiutConfig(projectPath, { slug, apiKey, serverUrl });
|
|
1600
|
+
await writePiutSkill(projectPath, slug, apiKey);
|
|
1601
|
+
ensureGitignored(projectPath);
|
|
1602
|
+
console.log(success(` \u2713 ${projectName}/.piut/`) + dim(" \u2014 credentials + skill"));
|
|
1603
|
+
if (copilotTool) {
|
|
1604
|
+
const hasCopilot = fs9.existsSync(path10.join(projectPath, ".github", "copilot-instructions.md")) || fs9.existsSync(path10.join(projectPath, ".github"));
|
|
1605
|
+
if (hasCopilot) {
|
|
1606
|
+
const vscodeMcpPath = path10.join(projectPath, ".vscode", "mcp.json");
|
|
1607
|
+
const serverConfig = copilotTool.generateConfig(slug, apiKey);
|
|
1608
|
+
mergeConfig(vscodeMcpPath, copilotTool.configKey, serverConfig);
|
|
1609
|
+
console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014 Copilot MCP"));
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1209
1612
|
for (const action of projectActions) {
|
|
1210
1613
|
if (action.action === "create") {
|
|
1211
1614
|
const isAppendType = RULE_FILES.find((r) => r.filePath === action.filePath)?.strategy === "append";
|
|
1212
|
-
const content = isAppendType ?
|
|
1213
|
-
|
|
1214
|
-
|
|
1615
|
+
const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
|
|
1616
|
+
fs9.mkdirSync(path10.dirname(action.absPath), { recursive: true });
|
|
1617
|
+
fs9.writeFileSync(action.absPath, content, "utf-8");
|
|
1215
1618
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 created"));
|
|
1216
1619
|
} else {
|
|
1217
|
-
|
|
1620
|
+
fs9.appendFileSync(action.absPath, APPEND_SECTION);
|
|
1218
1621
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 appended"));
|
|
1219
1622
|
}
|
|
1220
1623
|
connected++;
|
|
1221
1624
|
}
|
|
1222
1625
|
}
|
|
1223
1626
|
console.log();
|
|
1224
|
-
console.log(success(` Done. ${
|
|
1627
|
+
console.log(success(` Done. ${selectedPaths.length} project(s) connected.`));
|
|
1225
1628
|
console.log();
|
|
1226
1629
|
}
|
|
1227
1630
|
|
|
1228
1631
|
// src/commands/disconnect.ts
|
|
1229
|
-
import
|
|
1230
|
-
import
|
|
1231
|
-
import { checkbox as
|
|
1632
|
+
import fs10 from "fs";
|
|
1633
|
+
import path11 from "path";
|
|
1634
|
+
import { checkbox as checkbox5, confirm as confirm4 } from "@inquirer/prompts";
|
|
1232
1635
|
var DEDICATED_FILES = /* @__PURE__ */ new Set([
|
|
1233
1636
|
".cursor/rules/piut.mdc",
|
|
1234
1637
|
".windsurf/rules/piut.md",
|
|
@@ -1241,7 +1644,7 @@ var APPEND_FILES = [
|
|
|
1241
1644
|
];
|
|
1242
1645
|
function hasPiutReference3(filePath) {
|
|
1243
1646
|
try {
|
|
1244
|
-
const content =
|
|
1647
|
+
const content = fs10.readFileSync(filePath, "utf-8");
|
|
1245
1648
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
1246
1649
|
} catch {
|
|
1247
1650
|
return false;
|
|
@@ -1249,7 +1652,7 @@ function hasPiutReference3(filePath) {
|
|
|
1249
1652
|
}
|
|
1250
1653
|
function removePiutSection(filePath) {
|
|
1251
1654
|
try {
|
|
1252
|
-
let content =
|
|
1655
|
+
let content = fs10.readFileSync(filePath, "utf-8");
|
|
1253
1656
|
const patterns = [
|
|
1254
1657
|
/\n*## p[ıi]ut Context[\s\S]*?(?=\n## |\n---\n|$)/g
|
|
1255
1658
|
];
|
|
@@ -1263,7 +1666,7 @@ function removePiutSection(filePath) {
|
|
|
1263
1666
|
}
|
|
1264
1667
|
if (changed) {
|
|
1265
1668
|
content = content.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
1266
|
-
|
|
1669
|
+
fs10.writeFileSync(filePath, content, "utf-8");
|
|
1267
1670
|
}
|
|
1268
1671
|
return changed;
|
|
1269
1672
|
} catch {
|
|
@@ -1280,10 +1683,10 @@ async function disconnectCommand(options) {
|
|
|
1280
1683
|
const projects = scanForProjects(scanFolders);
|
|
1281
1684
|
const actions = [];
|
|
1282
1685
|
for (const project of projects) {
|
|
1283
|
-
const projectName =
|
|
1686
|
+
const projectName = path11.basename(project.path);
|
|
1284
1687
|
for (const dedicatedFile of DEDICATED_FILES) {
|
|
1285
|
-
const absPath =
|
|
1286
|
-
if (
|
|
1688
|
+
const absPath = path11.join(project.path, dedicatedFile);
|
|
1689
|
+
if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
|
|
1287
1690
|
actions.push({
|
|
1288
1691
|
projectPath: project.path,
|
|
1289
1692
|
projectName,
|
|
@@ -1294,8 +1697,8 @@ async function disconnectCommand(options) {
|
|
|
1294
1697
|
}
|
|
1295
1698
|
}
|
|
1296
1699
|
for (const appendFile of APPEND_FILES) {
|
|
1297
|
-
const absPath =
|
|
1298
|
-
if (
|
|
1700
|
+
const absPath = path11.join(project.path, appendFile);
|
|
1701
|
+
if (fs10.existsSync(absPath) && hasPiutReference3(absPath)) {
|
|
1299
1702
|
actions.push({
|
|
1300
1703
|
projectPath: project.path,
|
|
1301
1704
|
projectName,
|
|
@@ -1305,6 +1708,25 @@ async function disconnectCommand(options) {
|
|
|
1305
1708
|
});
|
|
1306
1709
|
}
|
|
1307
1710
|
}
|
|
1711
|
+
if (hasPiutDir(project.path)) {
|
|
1712
|
+
actions.push({
|
|
1713
|
+
projectPath: project.path,
|
|
1714
|
+
projectName,
|
|
1715
|
+
filePath: ".piut/",
|
|
1716
|
+
absPath: path11.join(project.path, ".piut"),
|
|
1717
|
+
action: "remove-dir"
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
const vscodeMcpPath = path11.join(project.path, ".vscode", "mcp.json");
|
|
1721
|
+
if (fs10.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
|
|
1722
|
+
actions.push({
|
|
1723
|
+
projectPath: project.path,
|
|
1724
|
+
projectName,
|
|
1725
|
+
filePath: ".vscode/mcp.json",
|
|
1726
|
+
absPath: vscodeMcpPath,
|
|
1727
|
+
action: "remove-mcp"
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1308
1730
|
}
|
|
1309
1731
|
if (actions.length === 0) {
|
|
1310
1732
|
console.log(dim(" No connected projects found."));
|
|
@@ -1318,19 +1740,18 @@ async function disconnectCommand(options) {
|
|
|
1318
1740
|
}
|
|
1319
1741
|
console.log();
|
|
1320
1742
|
const projectChoices = Array.from(byProject.entries()).map(([projectPath, projectActions]) => {
|
|
1321
|
-
const name =
|
|
1743
|
+
const name = path11.basename(projectPath);
|
|
1322
1744
|
const files = projectActions.map((a) => a.filePath).join(", ");
|
|
1323
1745
|
return {
|
|
1324
1746
|
name: `${name} ${dim(`(${files})`)}`,
|
|
1325
|
-
value: projectPath
|
|
1326
|
-
checked: true
|
|
1747
|
+
value: projectPath
|
|
1327
1748
|
};
|
|
1328
1749
|
});
|
|
1329
1750
|
let selectedPaths;
|
|
1330
1751
|
if (options.yes) {
|
|
1331
1752
|
selectedPaths = Array.from(byProject.keys());
|
|
1332
1753
|
} else {
|
|
1333
|
-
selectedPaths = await
|
|
1754
|
+
selectedPaths = await checkbox5({
|
|
1334
1755
|
message: "Select projects to disconnect:",
|
|
1335
1756
|
choices: projectChoices
|
|
1336
1757
|
});
|
|
@@ -1338,7 +1759,7 @@ async function disconnectCommand(options) {
|
|
|
1338
1759
|
console.log(dim(" No projects selected."));
|
|
1339
1760
|
return;
|
|
1340
1761
|
}
|
|
1341
|
-
const proceed = await
|
|
1762
|
+
const proceed = await confirm4({
|
|
1342
1763
|
message: `Disconnect ${selectedPaths.length} project(s)?`,
|
|
1343
1764
|
default: false
|
|
1344
1765
|
});
|
|
@@ -1348,16 +1769,29 @@ async function disconnectCommand(options) {
|
|
|
1348
1769
|
let disconnected = 0;
|
|
1349
1770
|
for (const projectPath of selectedPaths) {
|
|
1350
1771
|
const projectActions = byProject.get(projectPath) || [];
|
|
1351
|
-
const projectName =
|
|
1772
|
+
const projectName = path11.basename(projectPath);
|
|
1352
1773
|
for (const action of projectActions) {
|
|
1353
1774
|
if (action.action === "delete") {
|
|
1354
1775
|
try {
|
|
1355
|
-
|
|
1776
|
+
fs10.unlinkSync(action.absPath);
|
|
1356
1777
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 deleted"));
|
|
1357
1778
|
disconnected++;
|
|
1358
1779
|
} catch {
|
|
1359
1780
|
console.log(warning(` \u2717 ${projectName}/${action.filePath}`) + dim(" \u2014 could not delete"));
|
|
1360
1781
|
}
|
|
1782
|
+
} else if (action.action === "remove-dir") {
|
|
1783
|
+
if (removePiutDir(action.projectPath)) {
|
|
1784
|
+
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 removed"));
|
|
1785
|
+
disconnected++;
|
|
1786
|
+
}
|
|
1787
|
+
} else if (action.action === "remove-mcp") {
|
|
1788
|
+
try {
|
|
1789
|
+
removeFromConfig(action.absPath, "servers");
|
|
1790
|
+
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 piut-context removed"));
|
|
1791
|
+
disconnected++;
|
|
1792
|
+
} catch {
|
|
1793
|
+
console.log(warning(` \u2717 ${projectName}/${action.filePath}`) + dim(" \u2014 could not update"));
|
|
1794
|
+
}
|
|
1361
1795
|
} else {
|
|
1362
1796
|
const removed = removePiutSection(action.absPath);
|
|
1363
1797
|
if (removed) {
|
|
@@ -1374,10 +1808,27 @@ async function disconnectCommand(options) {
|
|
|
1374
1808
|
console.log();
|
|
1375
1809
|
}
|
|
1376
1810
|
|
|
1811
|
+
// src/commands/logout.ts
|
|
1812
|
+
async function logoutCommand() {
|
|
1813
|
+
banner();
|
|
1814
|
+
const config = readStore();
|
|
1815
|
+
if (!config.apiKey) {
|
|
1816
|
+
console.log(dim(" Not logged in \u2014 nothing to do."));
|
|
1817
|
+
console.log();
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
clearStore();
|
|
1821
|
+
console.log(success(" \u2713 Logged out. Saved API key removed."));
|
|
1822
|
+
console.log();
|
|
1823
|
+
console.log(dim(" To log in again, run: ") + "npx @piut/cli");
|
|
1824
|
+
console.log();
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1377
1827
|
// src/commands/interactive.ts
|
|
1378
|
-
import { select as select2, confirm as
|
|
1828
|
+
import { select as select2, confirm as confirm5, checkbox as checkbox6, password as password3 } from "@inquirer/prompts";
|
|
1829
|
+
import fs11 from "fs";
|
|
1830
|
+
import path12 from "path";
|
|
1379
1831
|
import chalk6 from "chalk";
|
|
1380
|
-
import { password as password3 } from "@inquirer/prompts";
|
|
1381
1832
|
async function authenticate() {
|
|
1382
1833
|
const config = readStore();
|
|
1383
1834
|
let apiKey = config.apiKey;
|
|
@@ -1413,15 +1864,22 @@ async function authenticate() {
|
|
|
1413
1864
|
updateStore({ apiKey });
|
|
1414
1865
|
return { apiKey, validation: result };
|
|
1415
1866
|
}
|
|
1867
|
+
function isPromptCancellation(err) {
|
|
1868
|
+
return !!(err && typeof err === "object" && "name" in err && err.name === "ExitPromptError");
|
|
1869
|
+
}
|
|
1416
1870
|
async function interactiveMenu() {
|
|
1417
1871
|
banner();
|
|
1418
|
-
|
|
1872
|
+
let apiKey;
|
|
1873
|
+
let currentValidation;
|
|
1874
|
+
const auth = await authenticate();
|
|
1875
|
+
apiKey = auth.apiKey;
|
|
1876
|
+
currentValidation = auth.validation;
|
|
1419
1877
|
console.log();
|
|
1420
|
-
if (
|
|
1878
|
+
if (currentValidation.status === "no_brain") {
|
|
1421
1879
|
console.log(warning(" You haven\u2019t built a brain yet."));
|
|
1422
1880
|
console.log(dim(" Your brain is how AI tools learn about you \u2014 your projects, preferences, and context."));
|
|
1423
1881
|
console.log();
|
|
1424
|
-
const wantBuild = await
|
|
1882
|
+
const wantBuild = await confirm5({
|
|
1425
1883
|
message: "Build your brain now?",
|
|
1426
1884
|
default: true
|
|
1427
1885
|
});
|
|
@@ -1432,13 +1890,12 @@ async function interactiveMenu() {
|
|
|
1432
1890
|
console.log(dim(" You can build your brain anytime with: ") + brand("piut build"));
|
|
1433
1891
|
console.log();
|
|
1434
1892
|
}
|
|
1435
|
-
return;
|
|
1436
1893
|
}
|
|
1437
|
-
if (
|
|
1894
|
+
if (currentValidation.status === "unpublished") {
|
|
1438
1895
|
console.log(warning(" Your brain is built but not deployed yet."));
|
|
1439
1896
|
console.log(dim(" Deploy it to make your MCP server live so AI tools can read your brain."));
|
|
1440
1897
|
console.log();
|
|
1441
|
-
const wantDeploy = await
|
|
1898
|
+
const wantDeploy = await confirm5({
|
|
1442
1899
|
message: "Deploy your brain now?",
|
|
1443
1900
|
default: true
|
|
1444
1901
|
});
|
|
@@ -1449,41 +1906,248 @@ async function interactiveMenu() {
|
|
|
1449
1906
|
console.log(dim(" You can deploy anytime with: ") + brand("piut deploy"));
|
|
1450
1907
|
console.log();
|
|
1451
1908
|
}
|
|
1909
|
+
}
|
|
1910
|
+
while (true) {
|
|
1911
|
+
const hasBrain = currentValidation.status !== "no_brain";
|
|
1912
|
+
const isDeployed = currentValidation.status === "active";
|
|
1913
|
+
let action;
|
|
1914
|
+
try {
|
|
1915
|
+
action = await select2({
|
|
1916
|
+
message: "What would you like to do?",
|
|
1917
|
+
choices: [
|
|
1918
|
+
{
|
|
1919
|
+
name: hasBrain ? "Rebuild Brain" : "Build Brain",
|
|
1920
|
+
value: "build",
|
|
1921
|
+
description: hasBrain ? "Rebuild your brain from your files" : "Build your brain from your files"
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
name: isDeployed ? "Undeploy Brain" : "Deploy Brain",
|
|
1925
|
+
value: "deploy",
|
|
1926
|
+
description: isDeployed ? "Take your MCP server offline" : "Publish your MCP server (requires paid account)"
|
|
1927
|
+
},
|
|
1928
|
+
{
|
|
1929
|
+
name: "Connect Tools",
|
|
1930
|
+
value: "connect-tools",
|
|
1931
|
+
description: "Configure AI tools to use your MCP server",
|
|
1932
|
+
disabled: !isDeployed && "(deploy brain first)"
|
|
1933
|
+
},
|
|
1934
|
+
{
|
|
1935
|
+
name: "Disconnect Tools",
|
|
1936
|
+
value: "disconnect-tools",
|
|
1937
|
+
description: "Remove p\u0131ut from AI tool configs",
|
|
1938
|
+
disabled: !isDeployed && "(deploy brain first)"
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
name: "Connect Projects",
|
|
1942
|
+
value: "connect-projects",
|
|
1943
|
+
description: "Add brain references to project config files",
|
|
1944
|
+
disabled: !isDeployed && "(deploy brain first)"
|
|
1945
|
+
},
|
|
1946
|
+
{
|
|
1947
|
+
name: "Disconnect Projects",
|
|
1948
|
+
value: "disconnect-projects",
|
|
1949
|
+
description: "Remove brain references from project configs",
|
|
1950
|
+
disabled: !isDeployed && "(deploy brain first)"
|
|
1951
|
+
},
|
|
1952
|
+
{
|
|
1953
|
+
name: "Status",
|
|
1954
|
+
value: "status",
|
|
1955
|
+
description: "Show brain, deployment, and connected tools/projects"
|
|
1956
|
+
},
|
|
1957
|
+
{
|
|
1958
|
+
name: "Logout",
|
|
1959
|
+
value: "logout",
|
|
1960
|
+
description: "Remove saved API key"
|
|
1961
|
+
},
|
|
1962
|
+
{
|
|
1963
|
+
name: "Exit",
|
|
1964
|
+
value: "exit",
|
|
1965
|
+
description: "Quit p\u0131ut CLI"
|
|
1966
|
+
}
|
|
1967
|
+
]
|
|
1968
|
+
});
|
|
1969
|
+
} catch {
|
|
1970
|
+
return;
|
|
1971
|
+
}
|
|
1972
|
+
if (action === "exit") return;
|
|
1973
|
+
try {
|
|
1974
|
+
switch (action) {
|
|
1975
|
+
case "build":
|
|
1976
|
+
await buildCommand({ key: apiKey });
|
|
1977
|
+
break;
|
|
1978
|
+
case "deploy":
|
|
1979
|
+
if (isDeployed) {
|
|
1980
|
+
await handleUndeploy(apiKey);
|
|
1981
|
+
} else {
|
|
1982
|
+
await deployCommand({ key: apiKey });
|
|
1983
|
+
}
|
|
1984
|
+
break;
|
|
1985
|
+
case "connect-tools":
|
|
1986
|
+
await handleConnectTools(apiKey, currentValidation);
|
|
1987
|
+
break;
|
|
1988
|
+
case "disconnect-tools":
|
|
1989
|
+
await handleDisconnectTools();
|
|
1990
|
+
break;
|
|
1991
|
+
case "connect-projects":
|
|
1992
|
+
await connectCommand({ key: apiKey });
|
|
1993
|
+
break;
|
|
1994
|
+
case "disconnect-projects":
|
|
1995
|
+
await disconnectCommand({});
|
|
1996
|
+
break;
|
|
1997
|
+
case "status":
|
|
1998
|
+
statusCommand();
|
|
1999
|
+
break;
|
|
2000
|
+
case "logout":
|
|
2001
|
+
await logoutCommand();
|
|
2002
|
+
console.log();
|
|
2003
|
+
try {
|
|
2004
|
+
const newAuth = await authenticate();
|
|
2005
|
+
apiKey = newAuth.apiKey;
|
|
2006
|
+
currentValidation = newAuth.validation;
|
|
2007
|
+
} catch {
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
} catch (err) {
|
|
2013
|
+
if (isPromptCancellation(err)) {
|
|
2014
|
+
console.log();
|
|
2015
|
+
} else if (err instanceof CliError) {
|
|
2016
|
+
console.log();
|
|
2017
|
+
} else {
|
|
2018
|
+
console.log(chalk6.red(` Error: ${err.message}`));
|
|
2019
|
+
console.log();
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
try {
|
|
2023
|
+
currentValidation = await validateKey(apiKey);
|
|
2024
|
+
} catch {
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
async function handleUndeploy(apiKey) {
|
|
2029
|
+
const confirmed = await confirm5({
|
|
2030
|
+
message: "Undeploy your brain? AI tools will lose access to your MCP server.",
|
|
2031
|
+
default: false
|
|
2032
|
+
});
|
|
2033
|
+
if (!confirmed) return;
|
|
2034
|
+
try {
|
|
2035
|
+
await unpublishServer(apiKey);
|
|
2036
|
+
console.log();
|
|
2037
|
+
console.log(success(" \u2713 Brain undeployed. MCP server is offline."));
|
|
2038
|
+
console.log(dim(" Run ") + brand("piut deploy") + dim(" to re-deploy anytime."));
|
|
2039
|
+
console.log();
|
|
2040
|
+
} catch (err) {
|
|
2041
|
+
console.log(chalk6.red(` \u2717 ${err.message}`));
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
async function handleConnectTools(apiKey, validation) {
|
|
2045
|
+
const { slug } = validation;
|
|
2046
|
+
const unconfigured = [];
|
|
2047
|
+
const alreadyConnected = [];
|
|
2048
|
+
for (const tool of TOOLS) {
|
|
2049
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
2050
|
+
for (const configPath of paths) {
|
|
2051
|
+
const exists = fs11.existsSync(configPath);
|
|
2052
|
+
const parentExists = fs11.existsSync(path12.dirname(configPath));
|
|
2053
|
+
if (exists || parentExists) {
|
|
2054
|
+
if (exists && isPiutConfigured(configPath, tool.configKey)) {
|
|
2055
|
+
alreadyConnected.push(tool.name);
|
|
2056
|
+
} else {
|
|
2057
|
+
unconfigured.push({ tool, configPath });
|
|
2058
|
+
}
|
|
2059
|
+
break;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
if (unconfigured.length === 0) {
|
|
2064
|
+
if (alreadyConnected.length > 0) {
|
|
2065
|
+
console.log(dim(" All detected tools are already connected."));
|
|
2066
|
+
} else {
|
|
2067
|
+
console.log(warning(" No supported AI tools detected."));
|
|
2068
|
+
console.log(dim(" Supported: Claude Code, Claude Desktop, Cursor, Windsurf, GitHub Copilot, Amazon Q, Zed"));
|
|
2069
|
+
}
|
|
2070
|
+
console.log();
|
|
1452
2071
|
return;
|
|
1453
2072
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
2073
|
+
if (alreadyConnected.length > 0) {
|
|
2074
|
+
console.log(dim(` Already connected: ${alreadyConnected.join(", ")}`));
|
|
2075
|
+
console.log();
|
|
2076
|
+
}
|
|
2077
|
+
const choices = unconfigured.map((u) => ({
|
|
2078
|
+
name: u.tool.name,
|
|
2079
|
+
value: u,
|
|
2080
|
+
checked: true
|
|
2081
|
+
}));
|
|
2082
|
+
const selected = await checkbox6({
|
|
2083
|
+
message: "Select tools to connect:",
|
|
2084
|
+
choices
|
|
1463
2085
|
});
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
2086
|
+
if (selected.length === 0) {
|
|
2087
|
+
console.log(dim(" No tools selected."));
|
|
2088
|
+
return;
|
|
2089
|
+
}
|
|
2090
|
+
console.log();
|
|
2091
|
+
for (const { tool, configPath } of selected) {
|
|
2092
|
+
const serverConfig = tool.generateConfig(slug, apiKey);
|
|
2093
|
+
mergeConfig(configPath, tool.configKey, serverConfig);
|
|
2094
|
+
toolLine(tool.name, success("connected"), "\u2714");
|
|
2095
|
+
}
|
|
2096
|
+
console.log();
|
|
2097
|
+
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
2098
|
+
console.log();
|
|
2099
|
+
}
|
|
2100
|
+
async function handleDisconnectTools() {
|
|
2101
|
+
const configured = [];
|
|
2102
|
+
for (const tool of TOOLS) {
|
|
2103
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
2104
|
+
for (const configPath of paths) {
|
|
2105
|
+
if (fs11.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
|
|
2106
|
+
configured.push({ tool, configPath });
|
|
2107
|
+
break;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1480
2110
|
}
|
|
2111
|
+
if (configured.length === 0) {
|
|
2112
|
+
console.log(dim(" p\u0131ut is not configured in any detected AI tools."));
|
|
2113
|
+
console.log();
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
const choices = configured.map((c) => ({
|
|
2117
|
+
name: c.tool.name,
|
|
2118
|
+
value: c
|
|
2119
|
+
}));
|
|
2120
|
+
const selected = await checkbox6({
|
|
2121
|
+
message: "Select tools to disconnect:",
|
|
2122
|
+
choices
|
|
2123
|
+
});
|
|
2124
|
+
if (selected.length === 0) {
|
|
2125
|
+
console.log(dim(" No tools selected."));
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
const proceed = await confirm5({
|
|
2129
|
+
message: `Disconnect p\u0131ut from ${selected.length} tool(s)?`,
|
|
2130
|
+
default: false
|
|
2131
|
+
});
|
|
2132
|
+
if (!proceed) return;
|
|
2133
|
+
console.log();
|
|
2134
|
+
for (const { tool, configPath } of selected) {
|
|
2135
|
+
const removed = removeFromConfig(configPath, tool.configKey);
|
|
2136
|
+
if (removed) {
|
|
2137
|
+
toolLine(tool.name, success("disconnected"), "\u2714");
|
|
2138
|
+
} else {
|
|
2139
|
+
toolLine(tool.name, warning("not found"), "\xD7");
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
console.log();
|
|
2143
|
+
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
2144
|
+
console.log();
|
|
1481
2145
|
}
|
|
1482
2146
|
|
|
1483
2147
|
// src/lib/update-check.ts
|
|
1484
2148
|
import { execFile } from "child_process";
|
|
1485
2149
|
import chalk7 from "chalk";
|
|
1486
|
-
import { confirm as
|
|
2150
|
+
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
1487
2151
|
var PACKAGE_NAME = "@piut/cli";
|
|
1488
2152
|
async function getLatestVersion() {
|
|
1489
2153
|
try {
|
|
@@ -1517,7 +2181,7 @@ async function checkForUpdate(currentVersion) {
|
|
|
1517
2181
|
console.log(dim(` Run ${chalk7.bold(`npm install -g ${PACKAGE_NAME}@latest`)} to update`));
|
|
1518
2182
|
console.log();
|
|
1519
2183
|
try {
|
|
1520
|
-
const shouldUpdate = await
|
|
2184
|
+
const shouldUpdate = await confirm6({
|
|
1521
2185
|
message: `Update to v${latest} now?`,
|
|
1522
2186
|
default: true
|
|
1523
2187
|
});
|
|
@@ -1539,14 +2203,25 @@ async function checkForUpdate(currentVersion) {
|
|
|
1539
2203
|
}
|
|
1540
2204
|
|
|
1541
2205
|
// src/cli.ts
|
|
1542
|
-
var VERSION = "3.
|
|
2206
|
+
var VERSION = "3.2.0";
|
|
2207
|
+
function withExit(fn) {
|
|
2208
|
+
return async (...args) => {
|
|
2209
|
+
try {
|
|
2210
|
+
await fn(...args);
|
|
2211
|
+
} catch (err) {
|
|
2212
|
+
if (err instanceof CliError) process.exit(1);
|
|
2213
|
+
throw err;
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
}
|
|
1543
2217
|
var program = new Command();
|
|
1544
2218
|
program.name("piut").description("Build your AI brain instantly. Deploy it as an MCP server. Connect it to every project.").version(VERSION).hook("preAction", () => checkForUpdate(VERSION)).action(interactiveMenu);
|
|
1545
|
-
program.command("build").description("Build or rebuild your brain from your files").option("-k, --key <key>", "API key").option("--folders <paths>", "Comma-separated folder paths to scan").action(buildCommand);
|
|
1546
|
-
program.command("deploy").description("Publish your MCP server (requires paid account)").option("-k, --key <key>", "API key").
|
|
1547
|
-
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(connectCommand);
|
|
1548
|
-
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(disconnectCommand);
|
|
1549
|
-
program.command("setup").description("Auto-detect and configure AI tools (MCP config)").option("-k, --key <key>", "API key (prompts interactively if not provided)").option("-t, --tool <id>", "Configure a single tool (claude-code, cursor, windsurf, etc.)").option("-y, --yes", "Skip interactive prompts (auto-select all detected tools)").option("--project", "Prefer project-local config files").option("--skip-skill", "Skip skill.md file placement").action(setupCommand);
|
|
2219
|
+
program.command("build").description("Build or rebuild your brain from your files").option("-k, --key <key>", "API key").option("--folders <paths>", "Comma-separated folder paths to scan").action(withExit(buildCommand));
|
|
2220
|
+
program.command("deploy").description("Publish your MCP server (requires paid account)").option("-k, --key <key>", "API key").action(withExit(deployCommand));
|
|
2221
|
+
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));
|
|
2222
|
+
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));
|
|
2223
|
+
program.command("setup").description("Auto-detect and configure AI tools (MCP config)").option("-k, --key <key>", "API key (prompts interactively if not provided)").option("-t, --tool <id>", "Configure a single tool (claude-code, cursor, windsurf, etc.)").option("-y, --yes", "Skip interactive prompts (auto-select all detected tools)").option("--project", "Prefer project-local config files").option("--skip-skill", "Skip skill.md file placement").action(withExit(setupCommand));
|
|
1550
2224
|
program.command("status").description("Show brain, deployment, and connected projects").action(statusCommand);
|
|
1551
|
-
program.command("remove").description("Remove all p\u0131ut configurations").action(removeCommand);
|
|
2225
|
+
program.command("remove").description("Remove all p\u0131ut configurations").action(withExit(removeCommand));
|
|
2226
|
+
program.command("logout").description("Remove saved API key").action(logoutCommand);
|
|
1552
2227
|
program.parse();
|