@piut/cli 3.3.0 → 3.5.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 +709 -146
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,6 +11,8 @@ import { password, confirm, checkbox } from "@inquirer/prompts";
|
|
|
11
11
|
import chalk2 from "chalk";
|
|
12
12
|
|
|
13
13
|
// src/lib/api.ts
|
|
14
|
+
import os from "os";
|
|
15
|
+
import crypto from "crypto";
|
|
14
16
|
var API_BASE = process.env.PIUT_API_BASE || "https://piut.com";
|
|
15
17
|
async function validateKey(key) {
|
|
16
18
|
const res = await fetch(`${API_BASE}/api/cli/validate`, {
|
|
@@ -73,6 +75,34 @@ async function* buildBrainStreaming(key, input2) {
|
|
|
73
75
|
}
|
|
74
76
|
}
|
|
75
77
|
}
|
|
78
|
+
async function verifyMcpEndpoint(serverUrl, key) {
|
|
79
|
+
const start = Date.now();
|
|
80
|
+
try {
|
|
81
|
+
const res = await fetch(serverUrl, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
Authorization: `Bearer ${key}`,
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
"User-Agent": "piut-cli (verify)"
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
jsonrpc: "2.0",
|
|
90
|
+
id: 1,
|
|
91
|
+
method: "tools/list"
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
const latencyMs = Date.now() - start;
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
return { ok: false, tools: [], latencyMs, error: `HTTP ${res.status}` };
|
|
97
|
+
}
|
|
98
|
+
const data = await res.json();
|
|
99
|
+
const result = data?.result;
|
|
100
|
+
const tools = Array.isArray(result?.tools) ? result.tools.map((t) => t.name).filter(Boolean) : [];
|
|
101
|
+
return { ok: true, tools, latencyMs };
|
|
102
|
+
} catch (err) {
|
|
103
|
+
return { ok: false, tools: [], latencyMs: Date.now() - start, error: err.message };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
76
106
|
async function pingMcp(serverUrl, key, toolName) {
|
|
77
107
|
try {
|
|
78
108
|
const res = await fetch(serverUrl, {
|
|
@@ -111,6 +141,49 @@ async function publishServer(key) {
|
|
|
111
141
|
}
|
|
112
142
|
return res.json();
|
|
113
143
|
}
|
|
144
|
+
async function getBrain(key) {
|
|
145
|
+
const res = await fetch(`${API_BASE}/api/cli/brain`, {
|
|
146
|
+
headers: { Authorization: `Bearer ${key}` }
|
|
147
|
+
});
|
|
148
|
+
if (!res.ok) {
|
|
149
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
150
|
+
throw new Error(body.error || `Failed to fetch brain (HTTP ${res.status})`);
|
|
151
|
+
}
|
|
152
|
+
return res.json();
|
|
153
|
+
}
|
|
154
|
+
function getMachineId() {
|
|
155
|
+
const hostname = os.hostname();
|
|
156
|
+
return crypto.createHash("sha256").update(hostname).digest("hex").slice(0, 16);
|
|
157
|
+
}
|
|
158
|
+
async function registerProject(key, project) {
|
|
159
|
+
const res = await fetch(`${API_BASE}/api/cli/projects`, {
|
|
160
|
+
method: "POST",
|
|
161
|
+
headers: {
|
|
162
|
+
Authorization: `Bearer ${key}`,
|
|
163
|
+
"Content-Type": "application/json"
|
|
164
|
+
},
|
|
165
|
+
body: JSON.stringify(project)
|
|
166
|
+
});
|
|
167
|
+
if (!res.ok) {
|
|
168
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
169
|
+
throw new Error(body.error || `Register project failed (HTTP ${res.status})`);
|
|
170
|
+
}
|
|
171
|
+
return res.json();
|
|
172
|
+
}
|
|
173
|
+
async function unregisterProject(key, projectPath, machineId) {
|
|
174
|
+
const res = await fetch(`${API_BASE}/api/cli/projects`, {
|
|
175
|
+
method: "DELETE",
|
|
176
|
+
headers: {
|
|
177
|
+
Authorization: `Bearer ${key}`,
|
|
178
|
+
"Content-Type": "application/json"
|
|
179
|
+
},
|
|
180
|
+
body: JSON.stringify({ projectPath, machineId })
|
|
181
|
+
});
|
|
182
|
+
if (!res.ok) {
|
|
183
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
184
|
+
throw new Error(body.error || `Unregister project failed (HTTP ${res.status})`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
114
187
|
async function unpublishServer(key) {
|
|
115
188
|
const res = await fetch(`${API_BASE}/api/mcp/publish`, {
|
|
116
189
|
method: "POST",
|
|
@@ -128,12 +201,12 @@ async function unpublishServer(key) {
|
|
|
128
201
|
}
|
|
129
202
|
|
|
130
203
|
// src/lib/tools.ts
|
|
131
|
-
import
|
|
204
|
+
import os2 from "os";
|
|
132
205
|
import path from "path";
|
|
133
206
|
var MCP_URL = (slug) => `https://piut.com/api/mcp/${slug}`;
|
|
134
207
|
var AUTH_HEADER = (key) => ({ Authorization: `Bearer ${key}` });
|
|
135
208
|
function appData() {
|
|
136
|
-
return process.env.APPDATA || path.join(
|
|
209
|
+
return process.env.APPDATA || path.join(os2.homedir(), "AppData", "Roaming");
|
|
137
210
|
}
|
|
138
211
|
var TOOLS = [
|
|
139
212
|
{
|
|
@@ -257,10 +330,10 @@ var TOOLS = [
|
|
|
257
330
|
];
|
|
258
331
|
|
|
259
332
|
// src/lib/paths.ts
|
|
260
|
-
import
|
|
333
|
+
import os3 from "os";
|
|
261
334
|
import path2 from "path";
|
|
262
335
|
function expandPath(p) {
|
|
263
|
-
return p.replace(/^~/,
|
|
336
|
+
return p.replace(/^~/, os3.homedir());
|
|
264
337
|
}
|
|
265
338
|
function resolveConfigPaths(configPaths) {
|
|
266
339
|
const resolved = [];
|
|
@@ -315,6 +388,36 @@ function mergeConfig(filePath, configKey, serverConfig) {
|
|
|
315
388
|
existing[configKey] = servers;
|
|
316
389
|
writeConfig(filePath, existing);
|
|
317
390
|
}
|
|
391
|
+
function getPiutConfig(filePath, configKey) {
|
|
392
|
+
const config = readConfig(filePath);
|
|
393
|
+
if (!config) return null;
|
|
394
|
+
const servers = config[configKey];
|
|
395
|
+
const piut = servers?.["piut-context"];
|
|
396
|
+
return piut ?? null;
|
|
397
|
+
}
|
|
398
|
+
function extractKeyFromConfig(piutConfig) {
|
|
399
|
+
const headers = piutConfig.headers;
|
|
400
|
+
if (headers?.Authorization) {
|
|
401
|
+
const match = headers.Authorization.match(/Bearer\s+(pb_\S+)/);
|
|
402
|
+
if (match) return match[1];
|
|
403
|
+
}
|
|
404
|
+
const settings = piutConfig.settings;
|
|
405
|
+
if (settings) {
|
|
406
|
+
const settingsHeaders = settings.headers;
|
|
407
|
+
if (settingsHeaders?.Authorization) {
|
|
408
|
+
const match = settingsHeaders.Authorization.match(/Bearer\s+(pb_\S+)/);
|
|
409
|
+
if (match) return match[1];
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const args2 = piutConfig.args;
|
|
413
|
+
if (Array.isArray(args2)) {
|
|
414
|
+
for (const arg of args2) {
|
|
415
|
+
const match = arg.match(/Bearer\s+(pb_\S+)/);
|
|
416
|
+
if (match) return match[1];
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
318
421
|
function removeFromConfig(filePath, configKey) {
|
|
319
422
|
const config = readConfig(filePath);
|
|
320
423
|
if (!config) return false;
|
|
@@ -331,21 +434,46 @@ function removeFromConfig(filePath, configKey) {
|
|
|
331
434
|
// src/lib/skill.ts
|
|
332
435
|
import fs2 from "fs";
|
|
333
436
|
import path4 from "path";
|
|
334
|
-
var SKILL_SNIPPET = `## p\u0131ut Context
|
|
437
|
+
var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
|
|
335
438
|
|
|
336
|
-
This project uses p\u0131ut for persistent personal context.
|
|
337
|
-
|
|
439
|
+
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
440
|
+
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
338
441
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
442
|
+
### Available MCP Tools
|
|
443
|
+
- \`get_context\` \u2014 Fetch all 5 brain sections. CALL THIS FIRST in every conversation.
|
|
444
|
+
- \`get_section\` \u2014 Fetch a single section (about, soul, areas, projects, memory)
|
|
445
|
+
- \`search_brain\` \u2014 Search across all sections
|
|
446
|
+
- \`append_brain\` \u2014 Append text to a section (no AI processing)
|
|
447
|
+
- \`update_brain\` \u2014 AI-powered integration of new info into brain
|
|
448
|
+
- \`prompt_brain\` \u2014 Execute natural language commands against context
|
|
449
|
+
|
|
450
|
+
### Instructions
|
|
451
|
+
1. Call \`get_context\` at conversation start to load the user's brain
|
|
452
|
+
2. Read the \`soul\` section first \u2014 it contains behavioral instructions
|
|
453
|
+
3. Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes
|
|
454
|
+
4. Never read .piut/config.json directly \u2014 always use the MCP tools
|
|
455
|
+
|
|
456
|
+
Skill reference: https://raw.githubusercontent.com/M-Flat-Inc/piut/main/skill.md`;
|
|
457
|
+
var PROJECT_SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
|
|
458
|
+
|
|
459
|
+
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
460
|
+
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
345
461
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
462
|
+
### Available MCP Tools
|
|
463
|
+
- \`get_context\` \u2014 Fetch all 5 brain sections. CALL THIS FIRST in every conversation.
|
|
464
|
+
- \`get_section\` \u2014 Fetch a single section (about, soul, areas, projects, memory)
|
|
465
|
+
- \`search_brain\` \u2014 Search across all sections
|
|
466
|
+
- \`append_brain\` \u2014 Append text to a section (no AI processing)
|
|
467
|
+
- \`update_brain\` \u2014 AI-powered integration of new info into brain
|
|
468
|
+
- \`prompt_brain\` \u2014 Execute natural language commands against context
|
|
469
|
+
|
|
470
|
+
### Instructions
|
|
471
|
+
1. Call \`get_context\` at conversation start to load the user's brain
|
|
472
|
+
2. Read the \`soul\` section first \u2014 it contains behavioral instructions
|
|
473
|
+
3. Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes
|
|
474
|
+
4. Never read .piut/config.json directly \u2014 always use the MCP tools
|
|
475
|
+
|
|
476
|
+
Full skill reference: .piut/skill.md`;
|
|
349
477
|
var SEPARATOR = "\n\n---\n\n";
|
|
350
478
|
function placeSkillFile(filePath) {
|
|
351
479
|
const absPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
|
|
@@ -485,19 +613,14 @@ var Spinner = class {
|
|
|
485
613
|
interval = null;
|
|
486
614
|
startTime = Date.now();
|
|
487
615
|
message = "";
|
|
488
|
-
tokens = 0;
|
|
489
616
|
sections = [];
|
|
490
617
|
start(message) {
|
|
491
618
|
this.message = message;
|
|
492
619
|
this.startTime = Date.now();
|
|
493
|
-
this.tokens = 0;
|
|
494
620
|
this.sections = [];
|
|
495
621
|
this.render();
|
|
496
622
|
this.interval = setInterval(() => this.render(), 80);
|
|
497
623
|
}
|
|
498
|
-
updateTokens(count) {
|
|
499
|
-
this.tokens = count;
|
|
500
|
-
}
|
|
501
624
|
addSection(name) {
|
|
502
625
|
this.clearLine();
|
|
503
626
|
const elapsed = this.elapsed();
|
|
@@ -521,9 +644,8 @@ var Spinner = class {
|
|
|
521
644
|
this.frame = (this.frame + 1) % SPINNER_FRAMES.length;
|
|
522
645
|
const spinner = brand(SPINNER_FRAMES[this.frame]);
|
|
523
646
|
const elapsed = dim(this.elapsed());
|
|
524
|
-
const tokenStr = this.tokens > 0 ? dim(` ${this.tokens.toLocaleString()} tokens`) : "";
|
|
525
647
|
this.clearLine();
|
|
526
|
-
process.stdout.write(` ${spinner} ${this.message} ${elapsed}
|
|
648
|
+
process.stdout.write(` ${spinner} ${this.message} ${elapsed}`);
|
|
527
649
|
}
|
|
528
650
|
elapsed() {
|
|
529
651
|
const ms = Date.now() - this.startTime;
|
|
@@ -591,11 +713,23 @@ async function setupCommand(options) {
|
|
|
591
713
|
const exists = fs4.existsSync(configPath);
|
|
592
714
|
const parentExists = fs4.existsSync(path6.dirname(configPath));
|
|
593
715
|
if (exists || parentExists) {
|
|
716
|
+
const configured2 = exists && isPiutConfigured(configPath, tool.configKey);
|
|
717
|
+
let staleKey = false;
|
|
718
|
+
if (configured2) {
|
|
719
|
+
const piutConfig = getPiutConfig(configPath, tool.configKey);
|
|
720
|
+
if (piutConfig) {
|
|
721
|
+
const existingKey = extractKeyFromConfig(piutConfig);
|
|
722
|
+
if (existingKey && existingKey !== apiKey) {
|
|
723
|
+
staleKey = true;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
594
727
|
detected.push({
|
|
595
728
|
tool,
|
|
596
729
|
configPath,
|
|
597
730
|
exists,
|
|
598
|
-
alreadyConfigured:
|
|
731
|
+
alreadyConfigured: configured2 && !staleKey,
|
|
732
|
+
staleKey
|
|
599
733
|
});
|
|
600
734
|
break;
|
|
601
735
|
}
|
|
@@ -610,7 +744,7 @@ async function setupCommand(options) {
|
|
|
610
744
|
}
|
|
611
745
|
let selected;
|
|
612
746
|
if (options.yes) {
|
|
613
|
-
selected = detected.filter((d) => !d.alreadyConfigured);
|
|
747
|
+
selected = detected.filter((d) => !d.alreadyConfigured || d.staleKey);
|
|
614
748
|
if (selected.length === 0) {
|
|
615
749
|
console.log(dim(" All detected tools are already configured."));
|
|
616
750
|
console.log();
|
|
@@ -618,9 +752,9 @@ async function setupCommand(options) {
|
|
|
618
752
|
}
|
|
619
753
|
} else {
|
|
620
754
|
const choices = detected.map((d) => ({
|
|
621
|
-
name: d.alreadyConfigured ? `${d.tool.name} ${dim("(already configured)")}` : d.tool.name,
|
|
755
|
+
name: d.staleKey ? `${d.tool.name} ${warning("(stale key \u2014 will update)")}` : d.alreadyConfigured ? `${d.tool.name} ${dim("(already configured)")}` : d.tool.name,
|
|
622
756
|
value: d,
|
|
623
|
-
checked: !d.alreadyConfigured
|
|
757
|
+
checked: !d.alreadyConfigured || d.staleKey
|
|
624
758
|
}));
|
|
625
759
|
selected = await checkbox({
|
|
626
760
|
message: "Select tools to configure:",
|
|
@@ -636,7 +770,7 @@ async function setupCommand(options) {
|
|
|
636
770
|
const skipped = [];
|
|
637
771
|
for (const det of selected) {
|
|
638
772
|
const { tool, configPath, alreadyConfigured } = det;
|
|
639
|
-
if (alreadyConfigured) {
|
|
773
|
+
if (alreadyConfigured && !det.staleKey) {
|
|
640
774
|
if (options.yes) {
|
|
641
775
|
skipped.push(tool.name);
|
|
642
776
|
continue;
|
|
@@ -651,14 +785,37 @@ async function setupCommand(options) {
|
|
|
651
785
|
}
|
|
652
786
|
}
|
|
653
787
|
if (tool.id === "claude-code" && tool.quickCommand && isCommandAvailable("claude")) {
|
|
788
|
+
let quickSuccess = false;
|
|
654
789
|
try {
|
|
655
790
|
execSync(tool.quickCommand(slug, apiKey), { stdio: "pipe" });
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
791
|
+
const claudeJson = expandPath("~/.claude.json");
|
|
792
|
+
const written = getPiutConfig(claudeJson, tool.configKey);
|
|
793
|
+
if (written) {
|
|
794
|
+
quickSuccess = true;
|
|
795
|
+
configured.push(tool.name);
|
|
796
|
+
toolLine(tool.name, success("configured via CLI"), "\u2714");
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
try {
|
|
800
|
+
execSync(tool.quickCommand(slug, apiKey) + " --scope user", { stdio: "pipe" });
|
|
801
|
+
const retryCheck = getPiutConfig(claudeJson, tool.configKey);
|
|
802
|
+
if (retryCheck) {
|
|
803
|
+
quickSuccess = true;
|
|
804
|
+
configured.push(tool.name);
|
|
805
|
+
toolLine(tool.name, success("configured via CLI"), "\u2714");
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
} catch {
|
|
809
|
+
}
|
|
810
|
+
console.log(dim(" Quick command succeeded but config not found, using config file..."));
|
|
811
|
+
} catch (err) {
|
|
812
|
+
const stderr = err?.stderr?.toString().trim();
|
|
813
|
+
if (stderr) {
|
|
814
|
+
console.log(dim(` Claude CLI: ${stderr}`));
|
|
815
|
+
}
|
|
660
816
|
console.log(dim(" Claude CLI command failed, using config file..."));
|
|
661
817
|
}
|
|
818
|
+
if (quickSuccess) continue;
|
|
662
819
|
}
|
|
663
820
|
const serverConfig = tool.generateConfig(slug, apiKey);
|
|
664
821
|
mergeConfig(configPath, tool.configKey, serverConfig);
|
|
@@ -701,7 +858,22 @@ async function setupCommand(options) {
|
|
|
701
858
|
if (configured.length > 0) {
|
|
702
859
|
const { serverUrl } = validationResult;
|
|
703
860
|
console.log();
|
|
704
|
-
console.log(dim("
|
|
861
|
+
console.log(dim(" Verifying..."));
|
|
862
|
+
for (const det of selected) {
|
|
863
|
+
if (!configured.includes(det.tool.name)) continue;
|
|
864
|
+
const piutConfig = getPiutConfig(det.configPath, det.tool.configKey);
|
|
865
|
+
if (piutConfig) {
|
|
866
|
+
toolLine(det.tool.name, success("config verified"), "\u2714");
|
|
867
|
+
} else {
|
|
868
|
+
toolLine(det.tool.name, warning("config not found \u2014 run piut doctor"), "\u2717");
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
const mcpResult = await verifyMcpEndpoint(serverUrl, apiKey);
|
|
872
|
+
if (mcpResult.ok) {
|
|
873
|
+
toolLine("MCP server", success(`${mcpResult.tools.length} tools available`) + dim(` (${mcpResult.latencyMs}ms)`), "\u2714");
|
|
874
|
+
} else {
|
|
875
|
+
toolLine("MCP server", warning(mcpResult.error || "unreachable") + dim(" \u2014 run piut doctor"), "\u2717");
|
|
876
|
+
}
|
|
705
877
|
await Promise.all(
|
|
706
878
|
configured.map((toolName) => pingMcp(serverUrl, apiKey, toolName))
|
|
707
879
|
);
|
|
@@ -717,6 +889,7 @@ async function setupCommand(options) {
|
|
|
717
889
|
console.log();
|
|
718
890
|
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
719
891
|
console.log(dim(' Verify: ask any AI "What do you know about me from my context?"'));
|
|
892
|
+
console.log(dim(" Diagnose issues: ") + chalk2.cyan("piut doctor"));
|
|
720
893
|
console.log();
|
|
721
894
|
}
|
|
722
895
|
function isCommandAvailable(cmd) {
|
|
@@ -729,14 +902,15 @@ function isCommandAvailable(cmd) {
|
|
|
729
902
|
}
|
|
730
903
|
|
|
731
904
|
// src/commands/status.ts
|
|
732
|
-
import
|
|
733
|
-
import
|
|
905
|
+
import fs7 from "fs";
|
|
906
|
+
import path9 from "path";
|
|
907
|
+
import chalk3 from "chalk";
|
|
734
908
|
|
|
735
909
|
// src/lib/brain-scanner.ts
|
|
736
910
|
import fs5 from "fs";
|
|
737
911
|
import path7 from "path";
|
|
738
|
-
import
|
|
739
|
-
var home =
|
|
912
|
+
import os4 from "os";
|
|
913
|
+
var home = os4.homedir();
|
|
740
914
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
741
915
|
"node_modules",
|
|
742
916
|
".git",
|
|
@@ -1039,6 +1213,34 @@ function scanForProjects(folders) {
|
|
|
1039
1213
|
return detectProjects(scanDirs);
|
|
1040
1214
|
}
|
|
1041
1215
|
|
|
1216
|
+
// src/lib/store.ts
|
|
1217
|
+
import fs6 from "fs";
|
|
1218
|
+
import path8 from "path";
|
|
1219
|
+
import os5 from "os";
|
|
1220
|
+
var CONFIG_DIR = path8.join(os5.homedir(), ".piut");
|
|
1221
|
+
var CONFIG_FILE2 = path8.join(CONFIG_DIR, "config.json");
|
|
1222
|
+
function readStore() {
|
|
1223
|
+
try {
|
|
1224
|
+
const raw = fs6.readFileSync(CONFIG_FILE2, "utf-8");
|
|
1225
|
+
return JSON.parse(raw);
|
|
1226
|
+
} catch {
|
|
1227
|
+
return {};
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
function updateStore(updates) {
|
|
1231
|
+
const config = readStore();
|
|
1232
|
+
const updated = { ...config, ...updates };
|
|
1233
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1234
|
+
fs6.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
1235
|
+
return updated;
|
|
1236
|
+
}
|
|
1237
|
+
function clearStore() {
|
|
1238
|
+
try {
|
|
1239
|
+
fs6.unlinkSync(CONFIG_FILE2);
|
|
1240
|
+
} catch {
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1042
1244
|
// src/commands/status.ts
|
|
1043
1245
|
var PIUT_FILES = [
|
|
1044
1246
|
"CLAUDE.md",
|
|
@@ -1050,21 +1252,25 @@ var PIUT_FILES = [
|
|
|
1050
1252
|
];
|
|
1051
1253
|
function hasPiutReference(filePath) {
|
|
1052
1254
|
try {
|
|
1053
|
-
const content =
|
|
1255
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1054
1256
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
1055
1257
|
} catch {
|
|
1056
1258
|
return false;
|
|
1057
1259
|
}
|
|
1058
1260
|
}
|
|
1059
|
-
function statusCommand() {
|
|
1261
|
+
async function statusCommand(options = {}) {
|
|
1060
1262
|
banner();
|
|
1263
|
+
if (options.verify) {
|
|
1264
|
+
await verifyStatus();
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1061
1267
|
console.log(" AI tool configuration:");
|
|
1062
1268
|
console.log();
|
|
1063
1269
|
let foundAny = false;
|
|
1064
1270
|
for (const tool of TOOLS) {
|
|
1065
1271
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
1066
1272
|
for (const configPath of paths) {
|
|
1067
|
-
if (!
|
|
1273
|
+
if (!fs7.existsSync(configPath)) continue;
|
|
1068
1274
|
foundAny = true;
|
|
1069
1275
|
const configured = isPiutConfigured(configPath, tool.configKey);
|
|
1070
1276
|
if (configured) {
|
|
@@ -1087,8 +1293,8 @@ function statusCommand() {
|
|
|
1087
1293
|
for (const project of projects) {
|
|
1088
1294
|
const connectedFiles = [];
|
|
1089
1295
|
for (const file of PIUT_FILES) {
|
|
1090
|
-
const absPath =
|
|
1091
|
-
if (
|
|
1296
|
+
const absPath = path9.join(project.path, file);
|
|
1297
|
+
if (fs7.existsSync(absPath) && hasPiutReference(absPath)) {
|
|
1092
1298
|
connectedFiles.push(file);
|
|
1093
1299
|
}
|
|
1094
1300
|
}
|
|
@@ -1106,9 +1312,78 @@ function statusCommand() {
|
|
|
1106
1312
|
}
|
|
1107
1313
|
console.log();
|
|
1108
1314
|
}
|
|
1315
|
+
async function verifyStatus() {
|
|
1316
|
+
const store = readStore();
|
|
1317
|
+
let issues = 0;
|
|
1318
|
+
console.log(" API Key");
|
|
1319
|
+
if (!store.apiKey) {
|
|
1320
|
+
console.log(warning(" \u2717 No saved API key"));
|
|
1321
|
+
console.log(dim(" Run ") + brand("piut setup") + dim(" to configure."));
|
|
1322
|
+
issues++;
|
|
1323
|
+
console.log();
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
let slug;
|
|
1327
|
+
let serverUrl;
|
|
1328
|
+
try {
|
|
1329
|
+
const info = await validateKey(store.apiKey);
|
|
1330
|
+
slug = info.slug;
|
|
1331
|
+
serverUrl = info.serverUrl;
|
|
1332
|
+
const masked = store.apiKey.slice(0, 6) + "...";
|
|
1333
|
+
console.log(success(` \u2714 Key valid: ${info.displayName} (${info.slug})`) + dim(` ${masked}`));
|
|
1334
|
+
} catch (err) {
|
|
1335
|
+
console.log(warning(` \u2717 Key invalid: ${err.message}`));
|
|
1336
|
+
issues++;
|
|
1337
|
+
}
|
|
1338
|
+
console.log();
|
|
1339
|
+
console.log(" Tool Configurations");
|
|
1340
|
+
for (const tool of TOOLS) {
|
|
1341
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
1342
|
+
for (const configPath of paths) {
|
|
1343
|
+
if (!fs7.existsSync(configPath)) continue;
|
|
1344
|
+
const piutConfig = getPiutConfig(configPath, tool.configKey);
|
|
1345
|
+
if (!piutConfig) {
|
|
1346
|
+
toolLine(tool.name, dim("installed, not connected"), "\u25CB");
|
|
1347
|
+
break;
|
|
1348
|
+
}
|
|
1349
|
+
const configKey = extractKeyFromConfig(piutConfig);
|
|
1350
|
+
if (configKey && configKey === store.apiKey) {
|
|
1351
|
+
toolLine(tool.name, success("key matches"), "\u2714");
|
|
1352
|
+
} else if (configKey) {
|
|
1353
|
+
const masked = configKey.slice(0, 6) + "...";
|
|
1354
|
+
toolLine(tool.name, chalk3.red(`key STALE (${masked})`), "\u2717");
|
|
1355
|
+
issues++;
|
|
1356
|
+
} else {
|
|
1357
|
+
toolLine(tool.name, dim("configured (key not extractable)"), "\u25CB");
|
|
1358
|
+
}
|
|
1359
|
+
break;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
console.log();
|
|
1363
|
+
console.log(" MCP Server");
|
|
1364
|
+
if (serverUrl && store.apiKey) {
|
|
1365
|
+
const result = await verifyMcpEndpoint(serverUrl, store.apiKey);
|
|
1366
|
+
if (result.ok) {
|
|
1367
|
+
console.log(success(` \u2714 ${serverUrl}`) + dim(` ${result.tools.length} tools, ${result.latencyMs}ms`));
|
|
1368
|
+
} else {
|
|
1369
|
+
console.log(warning(` \u2717 ${serverUrl}`) + dim(` ${result.error}`));
|
|
1370
|
+
issues++;
|
|
1371
|
+
}
|
|
1372
|
+
} else if (!serverUrl) {
|
|
1373
|
+
console.log(dim(" Skipped (no server URL)"));
|
|
1374
|
+
}
|
|
1375
|
+
console.log();
|
|
1376
|
+
if (issues > 0) {
|
|
1377
|
+
console.log(warning(` Issues Found: ${issues}`));
|
|
1378
|
+
console.log(dim(" Run ") + brand("piut doctor") + dim(" for detailed diagnostics."));
|
|
1379
|
+
} else {
|
|
1380
|
+
console.log(success(" All checks passed."));
|
|
1381
|
+
}
|
|
1382
|
+
console.log();
|
|
1383
|
+
}
|
|
1109
1384
|
|
|
1110
1385
|
// src/commands/remove.ts
|
|
1111
|
-
import
|
|
1386
|
+
import fs8 from "fs";
|
|
1112
1387
|
import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
|
|
1113
1388
|
async function removeCommand() {
|
|
1114
1389
|
banner();
|
|
@@ -1116,7 +1391,7 @@ async function removeCommand() {
|
|
|
1116
1391
|
for (const tool of TOOLS) {
|
|
1117
1392
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
1118
1393
|
for (const configPath of paths) {
|
|
1119
|
-
if (
|
|
1394
|
+
if (fs8.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
|
|
1120
1395
|
configured.push({ tool, configPath });
|
|
1121
1396
|
break;
|
|
1122
1397
|
}
|
|
@@ -1159,43 +1434,13 @@ async function removeCommand() {
|
|
|
1159
1434
|
}
|
|
1160
1435
|
|
|
1161
1436
|
// src/commands/build.ts
|
|
1162
|
-
import { select, checkbox as checkbox3, input } from "@inquirer/prompts";
|
|
1163
|
-
import
|
|
1164
|
-
import
|
|
1437
|
+
import { select, checkbox as checkbox3, input, confirm as confirm3 } from "@inquirer/prompts";
|
|
1438
|
+
import chalk5 from "chalk";
|
|
1439
|
+
import os6 from "os";
|
|
1165
1440
|
|
|
1166
1441
|
// src/lib/auth.ts
|
|
1167
1442
|
import { password as password2 } from "@inquirer/prompts";
|
|
1168
|
-
import
|
|
1169
|
-
|
|
1170
|
-
// src/lib/store.ts
|
|
1171
|
-
import fs8 from "fs";
|
|
1172
|
-
import path9 from "path";
|
|
1173
|
-
import os4 from "os";
|
|
1174
|
-
var CONFIG_DIR = path9.join(os4.homedir(), ".piut");
|
|
1175
|
-
var CONFIG_FILE2 = path9.join(CONFIG_DIR, "config.json");
|
|
1176
|
-
function readStore() {
|
|
1177
|
-
try {
|
|
1178
|
-
const raw = fs8.readFileSync(CONFIG_FILE2, "utf-8");
|
|
1179
|
-
return JSON.parse(raw);
|
|
1180
|
-
} catch {
|
|
1181
|
-
return {};
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
function updateStore(updates) {
|
|
1185
|
-
const config = readStore();
|
|
1186
|
-
const updated = { ...config, ...updates };
|
|
1187
|
-
fs8.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1188
|
-
fs8.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
1189
|
-
return updated;
|
|
1190
|
-
}
|
|
1191
|
-
function clearStore() {
|
|
1192
|
-
try {
|
|
1193
|
-
fs8.unlinkSync(CONFIG_FILE2);
|
|
1194
|
-
} catch {
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
// src/lib/auth.ts
|
|
1443
|
+
import chalk4 from "chalk";
|
|
1199
1444
|
async function resolveApiKey(keyOption) {
|
|
1200
1445
|
const config = readStore();
|
|
1201
1446
|
let apiKey = keyOption || config.apiKey;
|
|
@@ -1211,7 +1456,7 @@ async function resolveApiKey(keyOption) {
|
|
|
1211
1456
|
try {
|
|
1212
1457
|
result = await validateKey(apiKey);
|
|
1213
1458
|
} catch (err) {
|
|
1214
|
-
console.log(
|
|
1459
|
+
console.log(chalk4.red(` \u2717 ${err.message}`));
|
|
1215
1460
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
1216
1461
|
throw new CliError(err.message);
|
|
1217
1462
|
}
|
|
@@ -1235,7 +1480,7 @@ async function resolveApiKeyWithResult(keyOption) {
|
|
|
1235
1480
|
try {
|
|
1236
1481
|
result = await validateKey(apiKey);
|
|
1237
1482
|
} catch (err) {
|
|
1238
|
-
console.log(
|
|
1483
|
+
console.log(chalk4.red(` \u2717 ${err.message}`));
|
|
1239
1484
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
1240
1485
|
throw new CliError(err.message);
|
|
1241
1486
|
}
|
|
@@ -1254,7 +1499,7 @@ async function buildCommand(options) {
|
|
|
1254
1499
|
scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
|
|
1255
1500
|
}
|
|
1256
1501
|
const cwd = process.cwd();
|
|
1257
|
-
const cwdDisplay = cwd.replace(
|
|
1502
|
+
const cwdDisplay = cwd.replace(os6.homedir(), "~");
|
|
1258
1503
|
if (!scanFolders) {
|
|
1259
1504
|
console.log(dim(` Current directory: `) + cwdDisplay);
|
|
1260
1505
|
console.log(dim(` We'll scan for AI config files and projects here.`));
|
|
@@ -1272,8 +1517,8 @@ async function buildCommand(options) {
|
|
|
1272
1517
|
const defaults = getDefaultScanDirs();
|
|
1273
1518
|
const CUSTOM_VALUE = "__custom__";
|
|
1274
1519
|
const choices = [
|
|
1275
|
-
...defaults.map((d) => ({ name: d.replace(
|
|
1276
|
-
{ name:
|
|
1520
|
+
...defaults.map((d) => ({ name: d.replace(os6.homedir(), "~"), value: d })),
|
|
1521
|
+
{ name: chalk5.dim("Enter a custom path..."), value: CUSTOM_VALUE }
|
|
1277
1522
|
];
|
|
1278
1523
|
const selected = await checkbox3({
|
|
1279
1524
|
message: "Which folders should we scan?",
|
|
@@ -1293,7 +1538,7 @@ async function buildCommand(options) {
|
|
|
1293
1538
|
scanFolders = selected;
|
|
1294
1539
|
}
|
|
1295
1540
|
if (scanFolders.length === 0) {
|
|
1296
|
-
console.log(
|
|
1541
|
+
console.log(chalk5.yellow(" No folders selected."));
|
|
1297
1542
|
return;
|
|
1298
1543
|
}
|
|
1299
1544
|
}
|
|
@@ -1322,7 +1567,7 @@ async function buildCommand(options) {
|
|
|
1322
1567
|
console.log(success(` Scanned: ${projCount} projects, ${cfgCount} config files, ${dcCount} recent docs`));
|
|
1323
1568
|
console.log();
|
|
1324
1569
|
if (projCount === 0 && cfgCount === 0) {
|
|
1325
|
-
console.log(
|
|
1570
|
+
console.log(chalk5.yellow(" No projects or config files found to build from."));
|
|
1326
1571
|
console.log(dim(" Try running from a directory with your projects, or use --folders."));
|
|
1327
1572
|
console.log();
|
|
1328
1573
|
return;
|
|
@@ -1337,7 +1582,6 @@ async function buildCommand(options) {
|
|
|
1337
1582
|
spinner.updateMessage(String(event.data.message || "Processing..."));
|
|
1338
1583
|
break;
|
|
1339
1584
|
case "progress":
|
|
1340
|
-
spinner.updateTokens(event.data.tokens);
|
|
1341
1585
|
break;
|
|
1342
1586
|
case "section":
|
|
1343
1587
|
spinner.addSection(String(event.data.name));
|
|
@@ -1347,45 +1591,83 @@ async function buildCommand(options) {
|
|
|
1347
1591
|
break;
|
|
1348
1592
|
case "error":
|
|
1349
1593
|
spinner.stop();
|
|
1350
|
-
console.log(
|
|
1594
|
+
console.log(chalk5.red(` \u2717 ${event.data.message || "Build failed"}`));
|
|
1351
1595
|
throw new CliError(String(event.data.message || "Build failed"));
|
|
1352
1596
|
}
|
|
1353
1597
|
}
|
|
1354
1598
|
spinner.stop();
|
|
1355
1599
|
if (!sections) {
|
|
1356
|
-
console.log(
|
|
1600
|
+
console.log(chalk5.red(" \u2717 No response received from server"));
|
|
1357
1601
|
throw new CliError("No response received from server");
|
|
1358
1602
|
}
|
|
1359
1603
|
console.log();
|
|
1360
1604
|
console.log(success(" Brain built!"));
|
|
1361
1605
|
console.log();
|
|
1362
|
-
const
|
|
1363
|
-
|
|
1606
|
+
const SECTION_LABELS = {
|
|
1607
|
+
about: "About",
|
|
1608
|
+
soul: "Soul",
|
|
1609
|
+
areas: "Areas of Responsibility",
|
|
1610
|
+
projects: "Projects",
|
|
1611
|
+
memory: "Memory"
|
|
1612
|
+
};
|
|
1613
|
+
for (const [key, label] of Object.entries(SECTION_LABELS)) {
|
|
1614
|
+
const content = sections[key] || "";
|
|
1615
|
+
if (!content.trim()) {
|
|
1364
1616
|
console.log(dim(` ${label} \u2014 (empty)`));
|
|
1365
1617
|
} else {
|
|
1366
|
-
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1618
|
+
console.log(success(` ${label}`));
|
|
1619
|
+
const lines = content.split("\n").filter((l) => l.trim()).slice(0, 5);
|
|
1620
|
+
for (const line of lines) {
|
|
1621
|
+
console.log(dim(` ${line.length > 80 ? line.slice(0, 80) + "..." : line}`));
|
|
1622
|
+
}
|
|
1623
|
+
const totalLines = content.split("\n").filter((l) => l.trim()).length;
|
|
1624
|
+
if (totalLines > 5) {
|
|
1625
|
+
console.log(dim(` ... (${totalLines - 5} more lines)`));
|
|
1626
|
+
}
|
|
1369
1627
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
sectionSummary(sections.soul || "", "Soul");
|
|
1373
|
-
sectionSummary(sections.areas || "", "Areas of Responsibility");
|
|
1374
|
-
sectionSummary(sections.projects || "", "Projects");
|
|
1375
|
-
sectionSummary(sections.memory || "", "Memory");
|
|
1376
|
-
console.log();
|
|
1628
|
+
console.log();
|
|
1629
|
+
}
|
|
1377
1630
|
console.log(dim(` Review and edit at ${brand("piut.com/dashboard")}`));
|
|
1378
1631
|
console.log();
|
|
1632
|
+
const wantPublish = options.publish === false ? false : options.yes ? true : await confirm3({
|
|
1633
|
+
message: "Publish your brain now?",
|
|
1634
|
+
default: true
|
|
1635
|
+
});
|
|
1636
|
+
if (wantPublish) {
|
|
1637
|
+
try {
|
|
1638
|
+
await publishServer(apiKey);
|
|
1639
|
+
console.log();
|
|
1640
|
+
console.log(success(" \u2713 Brain published. MCP server is live."));
|
|
1641
|
+
console.log();
|
|
1642
|
+
} catch (err) {
|
|
1643
|
+
console.log();
|
|
1644
|
+
const msg = err.message;
|
|
1645
|
+
if (msg === "REQUIRES_SUBSCRIPTION") {
|
|
1646
|
+
console.log(chalk5.yellow(" Deploy requires an active subscription ($10/mo)."));
|
|
1647
|
+
console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
|
|
1648
|
+
console.log(dim(" 14-day free trial included."));
|
|
1649
|
+
} else {
|
|
1650
|
+
console.log(chalk5.red(` \u2717 ${msg}`));
|
|
1651
|
+
}
|
|
1652
|
+
console.log();
|
|
1653
|
+
}
|
|
1654
|
+
} else {
|
|
1655
|
+
console.log();
|
|
1656
|
+
console.log(dim(" You can publish anytime with: ") + brand("piut deploy"));
|
|
1657
|
+
console.log();
|
|
1658
|
+
}
|
|
1379
1659
|
} catch (err) {
|
|
1380
1660
|
spinner.stop();
|
|
1381
1661
|
if (err instanceof CliError) throw err;
|
|
1382
|
-
|
|
1383
|
-
|
|
1662
|
+
const msg = err.message || "Unknown error";
|
|
1663
|
+
const hint = msg === "terminated" || msg.includes("network") || msg.includes("fetch") ? "The build was interrupted. This can happen if your scan data is very large. Try using --folders to limit which directories are scanned." : msg;
|
|
1664
|
+
console.log(chalk5.red(` \u2717 ${hint}`));
|
|
1665
|
+
throw new CliError(hint);
|
|
1384
1666
|
}
|
|
1385
1667
|
}
|
|
1386
1668
|
|
|
1387
1669
|
// src/commands/deploy.ts
|
|
1388
|
-
import
|
|
1670
|
+
import chalk6 from "chalk";
|
|
1389
1671
|
async function deployCommand(options) {
|
|
1390
1672
|
banner();
|
|
1391
1673
|
const { apiKey, slug, serverUrl, status } = await resolveApiKeyWithResult(options.key);
|
|
@@ -1409,13 +1691,13 @@ async function deployCommand(options) {
|
|
|
1409
1691
|
const msg = err.message;
|
|
1410
1692
|
if (msg === "REQUIRES_SUBSCRIPTION") {
|
|
1411
1693
|
console.log();
|
|
1412
|
-
console.log(
|
|
1694
|
+
console.log(chalk6.yellow(" Deploy requires an active subscription ($10/mo)."));
|
|
1413
1695
|
console.log();
|
|
1414
1696
|
console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
|
|
1415
1697
|
console.log(dim(" 14-day free trial included."));
|
|
1416
1698
|
console.log();
|
|
1417
1699
|
} else {
|
|
1418
|
-
console.log(
|
|
1700
|
+
console.log(chalk6.red(` \u2717 ${msg}`));
|
|
1419
1701
|
throw new CliError(msg);
|
|
1420
1702
|
}
|
|
1421
1703
|
}
|
|
@@ -1463,19 +1745,35 @@ var RULE_FILES = [
|
|
|
1463
1745
|
detect: (p) => p.hasZedRules || fs9.existsSync(path10.join(p.path, ".zed"))
|
|
1464
1746
|
}
|
|
1465
1747
|
];
|
|
1466
|
-
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context
|
|
1467
|
-
|
|
1468
|
-
|
|
1748
|
+
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut-context)
|
|
1749
|
+
|
|
1750
|
+
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
1751
|
+
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
1469
1752
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1753
|
+
### Available MCP Tools
|
|
1754
|
+
- \`get_context\` \u2014 Fetch all 5 brain sections. CALL THIS FIRST in every conversation.
|
|
1755
|
+
- \`get_section\` \u2014 Fetch a single section (about, soul, areas, projects, memory)
|
|
1756
|
+
- \`search_brain\` \u2014 Search across all sections
|
|
1757
|
+
- \`append_brain\` \u2014 Append text to a section (no AI processing)
|
|
1758
|
+
- \`update_brain\` \u2014 AI-powered integration of new info into brain
|
|
1759
|
+
- \`prompt_brain\` \u2014 Execute natural language commands against context
|
|
1760
|
+
|
|
1761
|
+
### Instructions
|
|
1762
|
+
1. Call \`get_context\` at conversation start to load the user's brain
|
|
1763
|
+
2. Read the \`soul\` section first \u2014 it contains behavioral instructions
|
|
1764
|
+
3. Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes
|
|
1765
|
+
4. Never read .piut/config.json directly \u2014 always use the MCP tools
|
|
1766
|
+
|
|
1767
|
+
Full skill reference: .piut/skill.md
|
|
1473
1768
|
`;
|
|
1474
1769
|
var APPEND_SECTION = `
|
|
1475
1770
|
|
|
1476
|
-
## p\u0131ut Context
|
|
1477
|
-
|
|
1771
|
+
## p\u0131ut Context (MCP Server: piut-context)
|
|
1772
|
+
|
|
1773
|
+
p\u0131ut provides MCP tools for persistent personal context. Do NOT read local .piut/ files.
|
|
1774
|
+
Available tools: \`get_context\`, \`get_section\`, \`search_brain\`, \`append_brain\`, \`update_brain\`, \`prompt_brain\`
|
|
1478
1775
|
Always call \`get_context\` at the start of every conversation to load personal context.
|
|
1776
|
+
Full skill reference: .piut/skill.md
|
|
1479
1777
|
`;
|
|
1480
1778
|
function hasPiutReference2(filePath) {
|
|
1481
1779
|
try {
|
|
@@ -1551,8 +1849,10 @@ async function connectCommand(options) {
|
|
|
1551
1849
|
}
|
|
1552
1850
|
}
|
|
1553
1851
|
}
|
|
1852
|
+
const projectsWithActions = new Set(actions.map((a) => a.project.path));
|
|
1853
|
+
const alreadyConnectedCount = projects.filter((p) => !projectsWithActions.has(p.path)).length;
|
|
1554
1854
|
if (actions.length === 0) {
|
|
1555
|
-
console.log(
|
|
1855
|
+
console.log(success(` All ${projects.length} project(s) are already connected.`));
|
|
1556
1856
|
console.log();
|
|
1557
1857
|
return;
|
|
1558
1858
|
}
|
|
@@ -1563,7 +1863,10 @@ async function connectCommand(options) {
|
|
|
1563
1863
|
byProject.get(key).push(action);
|
|
1564
1864
|
}
|
|
1565
1865
|
console.log();
|
|
1566
|
-
|
|
1866
|
+
if (alreadyConnectedCount > 0) {
|
|
1867
|
+
console.log(dim(` ${alreadyConnectedCount} project(s) already connected.`));
|
|
1868
|
+
}
|
|
1869
|
+
console.log(` Found ${brand.bold(String(byProject.size))} project(s) with new connections available:`);
|
|
1567
1870
|
console.log();
|
|
1568
1871
|
const projectChoices = [];
|
|
1569
1872
|
for (const [projectPath, projectActions] of byProject) {
|
|
@@ -1574,7 +1877,8 @@ async function connectCommand(options) {
|
|
|
1574
1877
|
}).join(", ");
|
|
1575
1878
|
projectChoices.push({
|
|
1576
1879
|
name: `${projectName} ${dim(`(${desc})`)}`,
|
|
1577
|
-
value: projectPath
|
|
1880
|
+
value: projectPath,
|
|
1881
|
+
checked: true
|
|
1578
1882
|
});
|
|
1579
1883
|
}
|
|
1580
1884
|
let selectedPaths;
|
|
@@ -1623,6 +1927,21 @@ async function connectCommand(options) {
|
|
|
1623
1927
|
connected++;
|
|
1624
1928
|
}
|
|
1625
1929
|
}
|
|
1930
|
+
const machineId = getMachineId();
|
|
1931
|
+
for (const projectPath of selectedPaths) {
|
|
1932
|
+
const projectActions = byProject.get(projectPath) || [];
|
|
1933
|
+
const projectName = path10.basename(projectPath);
|
|
1934
|
+
const toolsDetected = [...new Set(projectActions.map((a) => a.tool))];
|
|
1935
|
+
const configFilesWritten = projectActions.map((a) => a.filePath);
|
|
1936
|
+
registerProject(apiKey, {
|
|
1937
|
+
projectName,
|
|
1938
|
+
projectPath,
|
|
1939
|
+
machineId,
|
|
1940
|
+
toolsDetected,
|
|
1941
|
+
configFiles: configFilesWritten
|
|
1942
|
+
}).catch(() => {
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1626
1945
|
console.log();
|
|
1627
1946
|
console.log(success(` Done. ${selectedPaths.length} project(s) connected.`));
|
|
1628
1947
|
console.log();
|
|
@@ -1631,7 +1950,7 @@ async function connectCommand(options) {
|
|
|
1631
1950
|
// src/commands/disconnect.ts
|
|
1632
1951
|
import fs10 from "fs";
|
|
1633
1952
|
import path11 from "path";
|
|
1634
|
-
import { checkbox as checkbox5, confirm as
|
|
1953
|
+
import { checkbox as checkbox5, confirm as confirm5 } from "@inquirer/prompts";
|
|
1635
1954
|
var DEDICATED_FILES = /* @__PURE__ */ new Set([
|
|
1636
1955
|
".cursor/rules/piut.mdc",
|
|
1637
1956
|
".windsurf/rules/piut.md",
|
|
@@ -1759,7 +2078,7 @@ async function disconnectCommand(options) {
|
|
|
1759
2078
|
console.log(dim(" No projects selected."));
|
|
1760
2079
|
return;
|
|
1761
2080
|
}
|
|
1762
|
-
const proceed = await
|
|
2081
|
+
const proceed = await confirm5({
|
|
1763
2082
|
message: `Disconnect ${selectedPaths.length} project(s)?`,
|
|
1764
2083
|
default: false
|
|
1765
2084
|
});
|
|
@@ -1803,6 +2122,14 @@ async function disconnectCommand(options) {
|
|
|
1803
2122
|
}
|
|
1804
2123
|
}
|
|
1805
2124
|
}
|
|
2125
|
+
const store = readStore();
|
|
2126
|
+
if (store.apiKey) {
|
|
2127
|
+
const machineId = getMachineId();
|
|
2128
|
+
for (const projectPath of selectedPaths) {
|
|
2129
|
+
unregisterProject(store.apiKey, projectPath, machineId).catch(() => {
|
|
2130
|
+
});
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
1806
2133
|
console.log();
|
|
1807
2134
|
console.log(success(` Done. ${disconnected} file(s) updated.`));
|
|
1808
2135
|
console.log();
|
|
@@ -1825,12 +2152,12 @@ async function logoutCommand() {
|
|
|
1825
2152
|
}
|
|
1826
2153
|
|
|
1827
2154
|
// src/commands/update.ts
|
|
1828
|
-
import
|
|
2155
|
+
import chalk8 from "chalk";
|
|
1829
2156
|
|
|
1830
2157
|
// src/lib/update-check.ts
|
|
1831
2158
|
import { execFile } from "child_process";
|
|
1832
|
-
import
|
|
1833
|
-
import { confirm as
|
|
2159
|
+
import chalk7 from "chalk";
|
|
2160
|
+
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
1834
2161
|
var PACKAGE_NAME = "@piut/cli";
|
|
1835
2162
|
function isNpx() {
|
|
1836
2163
|
return process.env.npm_command === "exec" || (process.env._?.includes("npx") ?? false);
|
|
@@ -1866,11 +2193,11 @@ async function checkForUpdate(currentVersion) {
|
|
|
1866
2193
|
const updateCmd = npx ? `npx ${PACKAGE_NAME}@latest` : `npm install -g ${PACKAGE_NAME}@latest`;
|
|
1867
2194
|
console.log();
|
|
1868
2195
|
console.log(brand(" Update available!") + dim(` ${currentVersion} \u2192 ${latest}`));
|
|
1869
|
-
console.log(dim(` Run ${
|
|
2196
|
+
console.log(dim(` Run ${chalk7.bold(updateCmd)} to update`));
|
|
1870
2197
|
console.log();
|
|
1871
2198
|
if (npx) return;
|
|
1872
2199
|
try {
|
|
1873
|
-
const shouldUpdate = await
|
|
2200
|
+
const shouldUpdate = await confirm6({
|
|
1874
2201
|
message: `Update to v${latest} now?`,
|
|
1875
2202
|
default: true
|
|
1876
2203
|
});
|
|
@@ -1878,12 +2205,12 @@ async function checkForUpdate(currentVersion) {
|
|
|
1878
2205
|
console.log(dim(" Updating..."));
|
|
1879
2206
|
const ok = await runUpdate();
|
|
1880
2207
|
if (ok) {
|
|
1881
|
-
console.log(
|
|
2208
|
+
console.log(chalk7.green(` \u2713 Updated to v${latest}`));
|
|
1882
2209
|
console.log(dim(" Restart the CLI to use the new version."));
|
|
1883
2210
|
process.exit(0);
|
|
1884
2211
|
} else {
|
|
1885
|
-
console.log(
|
|
1886
|
-
console.log(
|
|
2212
|
+
console.log(chalk7.yellow(` Could not auto-update. Run manually:`));
|
|
2213
|
+
console.log(chalk7.bold(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
1887
2214
|
console.log();
|
|
1888
2215
|
}
|
|
1889
2216
|
}
|
|
@@ -1898,7 +2225,7 @@ async function updateCommand(currentVersion) {
|
|
|
1898
2225
|
console.log(dim(" Checking for updates..."));
|
|
1899
2226
|
const latest = await getLatestVersion();
|
|
1900
2227
|
if (!latest) {
|
|
1901
|
-
console.log(
|
|
2228
|
+
console.log(chalk8.yellow(" Could not reach the npm registry. Check your connection."));
|
|
1902
2229
|
return;
|
|
1903
2230
|
}
|
|
1904
2231
|
if (!isNewer(currentVersion, latest)) {
|
|
@@ -1910,7 +2237,7 @@ async function updateCommand(currentVersion) {
|
|
|
1910
2237
|
if (isNpx()) {
|
|
1911
2238
|
console.log();
|
|
1912
2239
|
console.log(dim(" You're running via npx. Use the latest version with:"));
|
|
1913
|
-
console.log(
|
|
2240
|
+
console.log(chalk8.bold(` npx ${PACKAGE_NAME2}@latest`));
|
|
1914
2241
|
console.log();
|
|
1915
2242
|
return;
|
|
1916
2243
|
}
|
|
@@ -1920,16 +2247,184 @@ async function updateCommand(currentVersion) {
|
|
|
1920
2247
|
console.log(success(` \u2713 Updated to v${latest}`));
|
|
1921
2248
|
console.log(dim(" Restart the CLI to use the new version."));
|
|
1922
2249
|
} else {
|
|
1923
|
-
console.log(
|
|
1924
|
-
console.log(
|
|
2250
|
+
console.log(chalk8.yellow(" Could not auto-update. Run manually:"));
|
|
2251
|
+
console.log(chalk8.bold(` npm install -g ${PACKAGE_NAME2}@latest`));
|
|
1925
2252
|
}
|
|
1926
2253
|
}
|
|
1927
2254
|
|
|
1928
|
-
// src/commands/
|
|
1929
|
-
import { select as select2, confirm as confirm6, checkbox as checkbox6, password as password3 } from "@inquirer/prompts";
|
|
2255
|
+
// src/commands/doctor.ts
|
|
1930
2256
|
import fs11 from "fs";
|
|
2257
|
+
import chalk9 from "chalk";
|
|
2258
|
+
async function doctorCommand(options) {
|
|
2259
|
+
if (!options.json) banner();
|
|
2260
|
+
const result = {
|
|
2261
|
+
key: { valid: false },
|
|
2262
|
+
tools: [],
|
|
2263
|
+
mcp: { ok: false, tools: [], latencyMs: 0 },
|
|
2264
|
+
issues: 0
|
|
2265
|
+
};
|
|
2266
|
+
const store = readStore();
|
|
2267
|
+
const apiKey = options.key || store.apiKey;
|
|
2268
|
+
if (!apiKey) {
|
|
2269
|
+
result.key = { valid: false, error: "No API key found" };
|
|
2270
|
+
result.issues++;
|
|
2271
|
+
if (!options.json) {
|
|
2272
|
+
console.log(" API Key");
|
|
2273
|
+
console.log(error(` \u2717 No API key saved. Run: piut setup --key pb_YOUR_KEY`));
|
|
2274
|
+
console.log();
|
|
2275
|
+
}
|
|
2276
|
+
} else {
|
|
2277
|
+
const prefix = apiKey.slice(0, 7) + "...";
|
|
2278
|
+
try {
|
|
2279
|
+
const validation = await validateKey(apiKey);
|
|
2280
|
+
result.key = {
|
|
2281
|
+
valid: true,
|
|
2282
|
+
slug: validation.slug,
|
|
2283
|
+
displayName: validation.displayName,
|
|
2284
|
+
prefix
|
|
2285
|
+
};
|
|
2286
|
+
if (!options.json) {
|
|
2287
|
+
console.log(" API Key");
|
|
2288
|
+
console.log(success(` \u2714 Key valid: ${validation.displayName} (${validation.slug})`) + dim(` ${prefix}`));
|
|
2289
|
+
console.log();
|
|
2290
|
+
}
|
|
2291
|
+
} catch (err) {
|
|
2292
|
+
result.key = { valid: false, prefix, error: err.message };
|
|
2293
|
+
result.issues++;
|
|
2294
|
+
if (!options.json) {
|
|
2295
|
+
console.log(" API Key");
|
|
2296
|
+
console.log(error(` \u2717 Key invalid: ${err.message}`) + dim(` ${prefix}`));
|
|
2297
|
+
console.log();
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
if (!options.json) {
|
|
2302
|
+
console.log(" Tool Configurations");
|
|
2303
|
+
}
|
|
2304
|
+
let toolsFixed = 0;
|
|
2305
|
+
for (const tool of TOOLS) {
|
|
2306
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
2307
|
+
for (const configPath of paths) {
|
|
2308
|
+
if (!fs11.existsSync(configPath)) continue;
|
|
2309
|
+
const piutConfig = getPiutConfig(configPath, tool.configKey);
|
|
2310
|
+
if (!piutConfig) {
|
|
2311
|
+
result.tools.push({
|
|
2312
|
+
name: tool.name,
|
|
2313
|
+
id: tool.id,
|
|
2314
|
+
configPath,
|
|
2315
|
+
found: true,
|
|
2316
|
+
configured: false,
|
|
2317
|
+
keyMatch: "missing"
|
|
2318
|
+
});
|
|
2319
|
+
if (!options.json) {
|
|
2320
|
+
toolLine(tool.name, dim("installed, not configured"), "\u25CB");
|
|
2321
|
+
}
|
|
2322
|
+
break;
|
|
2323
|
+
}
|
|
2324
|
+
const configKey = extractKeyFromConfig(piutConfig);
|
|
2325
|
+
const configPrefix = configKey ? configKey.slice(0, 7) + "..." : "(none)";
|
|
2326
|
+
let keyMatch = "missing";
|
|
2327
|
+
if (!configKey) {
|
|
2328
|
+
keyMatch = "missing";
|
|
2329
|
+
result.issues++;
|
|
2330
|
+
} else if (apiKey && configKey === apiKey) {
|
|
2331
|
+
keyMatch = "match";
|
|
2332
|
+
} else {
|
|
2333
|
+
keyMatch = "stale";
|
|
2334
|
+
result.issues++;
|
|
2335
|
+
}
|
|
2336
|
+
const toolResult = {
|
|
2337
|
+
name: tool.name,
|
|
2338
|
+
id: tool.id,
|
|
2339
|
+
configPath,
|
|
2340
|
+
found: true,
|
|
2341
|
+
configured: true,
|
|
2342
|
+
keyMatch,
|
|
2343
|
+
keyPrefix: configPrefix,
|
|
2344
|
+
fixed: false
|
|
2345
|
+
};
|
|
2346
|
+
if (keyMatch === "stale" && options.fix && apiKey && result.key.valid && result.key.slug) {
|
|
2347
|
+
const serverConfig = tool.generateConfig(result.key.slug, apiKey);
|
|
2348
|
+
mergeConfig(configPath, tool.configKey, serverConfig);
|
|
2349
|
+
toolResult.fixed = true;
|
|
2350
|
+
toolResult.keyMatch = "match";
|
|
2351
|
+
result.issues--;
|
|
2352
|
+
toolsFixed++;
|
|
2353
|
+
}
|
|
2354
|
+
result.tools.push(toolResult);
|
|
2355
|
+
if (!options.json) {
|
|
2356
|
+
if (toolResult.fixed) {
|
|
2357
|
+
toolLine(tool.name, success("fixed") + dim(` \u2192 ${configPath}`), "\u2714");
|
|
2358
|
+
} else if (keyMatch === "match") {
|
|
2359
|
+
toolLine(tool.name, success("key matches") + dim(` ${configPath}`), "\u2714");
|
|
2360
|
+
} else if (keyMatch === "stale") {
|
|
2361
|
+
toolLine(tool.name, warning(`key STALE (${configPrefix})`) + dim(` ${configPath}`), "\u2717");
|
|
2362
|
+
} else {
|
|
2363
|
+
toolLine(tool.name, warning("no key found") + dim(` ${configPath}`), "\u2717");
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
break;
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
if (result.tools.length === 0 && !options.json) {
|
|
2370
|
+
console.log(dim(" No AI tools detected."));
|
|
2371
|
+
}
|
|
2372
|
+
if (!options.json) console.log();
|
|
2373
|
+
if (apiKey && result.key.valid && result.key.slug) {
|
|
2374
|
+
const serverUrl = `https://piut.com/api/mcp/${result.key.slug}`;
|
|
2375
|
+
const mcpResult = await verifyMcpEndpoint(serverUrl, apiKey);
|
|
2376
|
+
result.mcp = mcpResult;
|
|
2377
|
+
if (!mcpResult.ok) {
|
|
2378
|
+
result.issues++;
|
|
2379
|
+
}
|
|
2380
|
+
if (!options.json) {
|
|
2381
|
+
console.log(" MCP Server");
|
|
2382
|
+
if (mcpResult.ok) {
|
|
2383
|
+
console.log(success(` \u2714 ${serverUrl}`) + dim(` ${mcpResult.tools.length} tools, ${mcpResult.latencyMs}ms`));
|
|
2384
|
+
if (mcpResult.tools.length > 0) {
|
|
2385
|
+
console.log(dim(` ${mcpResult.tools.join(", ")}`));
|
|
2386
|
+
}
|
|
2387
|
+
} else {
|
|
2388
|
+
console.log(error(` \u2717 ${serverUrl}`) + dim(` ${mcpResult.error}`));
|
|
2389
|
+
}
|
|
2390
|
+
console.log();
|
|
2391
|
+
}
|
|
2392
|
+
} else if (!options.json) {
|
|
2393
|
+
console.log(" MCP Server");
|
|
2394
|
+
console.log(dim(" \u2298 Skipped (no valid key)"));
|
|
2395
|
+
console.log();
|
|
2396
|
+
}
|
|
2397
|
+
if (options.json) {
|
|
2398
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2399
|
+
if (result.issues > 0) throw new CliError(`${result.issues} issue(s) found`);
|
|
2400
|
+
return;
|
|
2401
|
+
}
|
|
2402
|
+
if (toolsFixed > 0) {
|
|
2403
|
+
console.log(success(` Fixed ${toolsFixed} stale config(s).`));
|
|
2404
|
+
console.log();
|
|
2405
|
+
}
|
|
2406
|
+
if (result.issues === 0) {
|
|
2407
|
+
console.log(success(" All checks passed."));
|
|
2408
|
+
} else {
|
|
2409
|
+
console.log(warning(` ${result.issues} issue(s) found.`));
|
|
2410
|
+
const staleTools = result.tools.filter((t) => t.keyMatch === "stale" && !t.fixed);
|
|
2411
|
+
if (staleTools.length > 0 && !options.fix) {
|
|
2412
|
+
console.log();
|
|
2413
|
+
console.log(dim(" Fix stale configs: ") + chalk9.cyan("piut doctor --fix"));
|
|
2414
|
+
}
|
|
2415
|
+
if (!result.key.valid) {
|
|
2416
|
+
console.log(dim(" Set a valid key: ") + chalk9.cyan("piut setup --key pb_YOUR_KEY"));
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
console.log();
|
|
2420
|
+
if (result.issues > 0) throw new CliError(`${result.issues} issue(s) found`);
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
// src/commands/interactive.ts
|
|
2424
|
+
import { select as select2, confirm as confirm7, checkbox as checkbox6, password as password3 } from "@inquirer/prompts";
|
|
2425
|
+
import fs12 from "fs";
|
|
1931
2426
|
import path12 from "path";
|
|
1932
|
-
import
|
|
2427
|
+
import chalk10 from "chalk";
|
|
1933
2428
|
async function authenticate() {
|
|
1934
2429
|
const config = readStore();
|
|
1935
2430
|
let apiKey = config.apiKey;
|
|
@@ -1957,7 +2452,7 @@ async function authenticate() {
|
|
|
1957
2452
|
try {
|
|
1958
2453
|
result = await validateKey(apiKey);
|
|
1959
2454
|
} catch (err) {
|
|
1960
|
-
console.log(
|
|
2455
|
+
console.log(chalk10.red(` \u2717 ${err.message}`));
|
|
1961
2456
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
1962
2457
|
process.exit(1);
|
|
1963
2458
|
}
|
|
@@ -1980,7 +2475,7 @@ async function interactiveMenu() {
|
|
|
1980
2475
|
console.log(warning(" You haven\u2019t built a brain yet."));
|
|
1981
2476
|
console.log(dim(" Your brain is how AI tools learn about you \u2014 your projects, preferences, and context."));
|
|
1982
2477
|
console.log();
|
|
1983
|
-
const wantBuild = await
|
|
2478
|
+
const wantBuild = await confirm7({
|
|
1984
2479
|
message: "Build your brain now?",
|
|
1985
2480
|
default: true
|
|
1986
2481
|
});
|
|
@@ -1996,7 +2491,7 @@ async function interactiveMenu() {
|
|
|
1996
2491
|
console.log(warning(" Your brain is built but not deployed yet."));
|
|
1997
2492
|
console.log(dim(" Deploy it to make your MCP server live so AI tools can read your brain."));
|
|
1998
2493
|
console.log();
|
|
1999
|
-
const wantDeploy = await
|
|
2494
|
+
const wantDeploy = await confirm7({
|
|
2000
2495
|
message: "Deploy your brain now?",
|
|
2001
2496
|
default: true
|
|
2002
2497
|
});
|
|
@@ -2050,6 +2545,12 @@ async function interactiveMenu() {
|
|
|
2050
2545
|
description: "Remove brain references from project configs",
|
|
2051
2546
|
disabled: !isDeployed && "(deploy brain first)"
|
|
2052
2547
|
},
|
|
2548
|
+
{
|
|
2549
|
+
name: "View Brain",
|
|
2550
|
+
value: "view-brain",
|
|
2551
|
+
description: "View all 5 brain sections",
|
|
2552
|
+
disabled: !hasBrain && "(build brain first)"
|
|
2553
|
+
},
|
|
2053
2554
|
{
|
|
2054
2555
|
name: "Status",
|
|
2055
2556
|
value: "status",
|
|
@@ -2095,6 +2596,9 @@ async function interactiveMenu() {
|
|
|
2095
2596
|
case "disconnect-projects":
|
|
2096
2597
|
await disconnectCommand({});
|
|
2097
2598
|
break;
|
|
2599
|
+
case "view-brain":
|
|
2600
|
+
await handleViewBrain(apiKey);
|
|
2601
|
+
break;
|
|
2098
2602
|
case "status":
|
|
2099
2603
|
statusCommand();
|
|
2100
2604
|
break;
|
|
@@ -2116,7 +2620,7 @@ async function interactiveMenu() {
|
|
|
2116
2620
|
} else if (err instanceof CliError) {
|
|
2117
2621
|
console.log();
|
|
2118
2622
|
} else {
|
|
2119
|
-
console.log(
|
|
2623
|
+
console.log(chalk10.red(` Error: ${err.message}`));
|
|
2120
2624
|
console.log();
|
|
2121
2625
|
}
|
|
2122
2626
|
}
|
|
@@ -2127,7 +2631,7 @@ async function interactiveMenu() {
|
|
|
2127
2631
|
}
|
|
2128
2632
|
}
|
|
2129
2633
|
async function handleUndeploy(apiKey) {
|
|
2130
|
-
const confirmed = await
|
|
2634
|
+
const confirmed = await confirm7({
|
|
2131
2635
|
message: "Undeploy your brain? AI tools will lose access to your MCP server.",
|
|
2132
2636
|
default: false
|
|
2133
2637
|
});
|
|
@@ -2139,7 +2643,7 @@ async function handleUndeploy(apiKey) {
|
|
|
2139
2643
|
console.log(dim(" Run ") + brand("piut deploy") + dim(" to re-deploy anytime."));
|
|
2140
2644
|
console.log();
|
|
2141
2645
|
} catch (err) {
|
|
2142
|
-
console.log(
|
|
2646
|
+
console.log(chalk10.red(` \u2717 ${err.message}`));
|
|
2143
2647
|
}
|
|
2144
2648
|
}
|
|
2145
2649
|
async function handleConnectTools(apiKey, validation) {
|
|
@@ -2149,8 +2653,8 @@ async function handleConnectTools(apiKey, validation) {
|
|
|
2149
2653
|
for (const tool of TOOLS) {
|
|
2150
2654
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
2151
2655
|
for (const configPath of paths) {
|
|
2152
|
-
const exists =
|
|
2153
|
-
const parentExists =
|
|
2656
|
+
const exists = fs12.existsSync(configPath);
|
|
2657
|
+
const parentExists = fs12.existsSync(path12.dirname(configPath));
|
|
2154
2658
|
if (exists || parentExists) {
|
|
2155
2659
|
if (exists && isPiutConfigured(configPath, tool.configKey)) {
|
|
2156
2660
|
alreadyConnected.push(tool.name);
|
|
@@ -2194,6 +2698,11 @@ async function handleConnectTools(apiKey, validation) {
|
|
|
2194
2698
|
mergeConfig(configPath, tool.configKey, serverConfig);
|
|
2195
2699
|
toolLine(tool.name, success("connected"), "\u2714");
|
|
2196
2700
|
}
|
|
2701
|
+
if (validation.serverUrl) {
|
|
2702
|
+
await Promise.all(
|
|
2703
|
+
selected.map(({ tool }) => pingMcp(validation.serverUrl, apiKey, tool.name))
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2197
2706
|
console.log();
|
|
2198
2707
|
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
2199
2708
|
console.log();
|
|
@@ -2203,7 +2712,7 @@ async function handleDisconnectTools() {
|
|
|
2203
2712
|
for (const tool of TOOLS) {
|
|
2204
2713
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
2205
2714
|
for (const configPath of paths) {
|
|
2206
|
-
if (
|
|
2715
|
+
if (fs12.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
|
|
2207
2716
|
configured.push({ tool, configPath });
|
|
2208
2717
|
break;
|
|
2209
2718
|
}
|
|
@@ -2226,7 +2735,7 @@ async function handleDisconnectTools() {
|
|
|
2226
2735
|
console.log(dim(" No tools selected."));
|
|
2227
2736
|
return;
|
|
2228
2737
|
}
|
|
2229
|
-
const proceed = await
|
|
2738
|
+
const proceed = await confirm7({
|
|
2230
2739
|
message: `Disconnect p\u0131ut from ${selected.length} tool(s)?`,
|
|
2231
2740
|
default: false
|
|
2232
2741
|
});
|
|
@@ -2244,9 +2753,62 @@ async function handleDisconnectTools() {
|
|
|
2244
2753
|
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
2245
2754
|
console.log();
|
|
2246
2755
|
}
|
|
2756
|
+
async function handleViewBrain(apiKey) {
|
|
2757
|
+
console.log(dim(" Loading brain..."));
|
|
2758
|
+
const { sections, hasUnpublishedChanges } = await getBrain(apiKey);
|
|
2759
|
+
const SECTION_LABELS = {
|
|
2760
|
+
about: "About",
|
|
2761
|
+
soul: "Soul",
|
|
2762
|
+
areas: "Areas of Responsibility",
|
|
2763
|
+
projects: "Projects",
|
|
2764
|
+
memory: "Memory"
|
|
2765
|
+
};
|
|
2766
|
+
console.log();
|
|
2767
|
+
for (const [key, label] of Object.entries(SECTION_LABELS)) {
|
|
2768
|
+
const content = sections[key] || "";
|
|
2769
|
+
if (!content.trim()) {
|
|
2770
|
+
console.log(dim(` ${label} \u2014 (empty)`));
|
|
2771
|
+
} else {
|
|
2772
|
+
console.log(success(` ${label}`));
|
|
2773
|
+
for (const line of content.split("\n")) {
|
|
2774
|
+
console.log(` ${line}`);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
console.log();
|
|
2778
|
+
}
|
|
2779
|
+
console.log(dim(` Edit at ${brand("piut.com/dashboard")}`));
|
|
2780
|
+
console.log();
|
|
2781
|
+
if (hasUnpublishedChanges) {
|
|
2782
|
+
console.log(warning(" You have unpublished changes."));
|
|
2783
|
+
console.log();
|
|
2784
|
+
const wantPublish = await confirm7({
|
|
2785
|
+
message: "Publish now?",
|
|
2786
|
+
default: true
|
|
2787
|
+
});
|
|
2788
|
+
if (wantPublish) {
|
|
2789
|
+
try {
|
|
2790
|
+
await publishServer(apiKey);
|
|
2791
|
+
console.log();
|
|
2792
|
+
console.log(success(" \u2713 Brain published."));
|
|
2793
|
+
console.log();
|
|
2794
|
+
} catch (err) {
|
|
2795
|
+
console.log();
|
|
2796
|
+
const msg = err.message;
|
|
2797
|
+
if (msg === "REQUIRES_SUBSCRIPTION") {
|
|
2798
|
+
console.log(chalk10.yellow(" Deploy requires an active subscription ($10/mo)."));
|
|
2799
|
+
console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
|
|
2800
|
+
console.log(dim(" 14-day free trial included."));
|
|
2801
|
+
} else {
|
|
2802
|
+
console.log(chalk10.red(` \u2717 ${msg}`));
|
|
2803
|
+
}
|
|
2804
|
+
console.log();
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2247
2809
|
|
|
2248
2810
|
// src/cli.ts
|
|
2249
|
-
var VERSION = "3.
|
|
2811
|
+
var VERSION = "3.5.0";
|
|
2250
2812
|
function withExit(fn) {
|
|
2251
2813
|
return async (...args2) => {
|
|
2252
2814
|
try {
|
|
@@ -2262,14 +2824,15 @@ program.name("piut").description("Build your AI brain instantly. Deploy it as an
|
|
|
2262
2824
|
if (actionCommand.name() === "update") return;
|
|
2263
2825
|
return checkForUpdate(VERSION);
|
|
2264
2826
|
}).action(interactiveMenu);
|
|
2265
|
-
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));
|
|
2827
|
+
program.command("build").description("Build or rebuild your brain from your files").option("-k, --key <key>", "API key").option("--folders <paths>", "Comma-separated folder paths to scan").option("-y, --yes", "Auto-publish after build").option("--no-publish", "Skip publish prompt after build").action(withExit(buildCommand));
|
|
2266
2828
|
program.command("deploy").description("Publish your MCP server (requires paid account)").option("-k, --key <key>", "API key").action(withExit(deployCommand));
|
|
2267
2829
|
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));
|
|
2268
2830
|
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));
|
|
2269
2831
|
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));
|
|
2270
|
-
program.command("status").description("Show brain, deployment, and connected projects").action(statusCommand);
|
|
2832
|
+
program.command("status").description("Show brain, deployment, and connected projects").option("--verify", "Validate API key, check tool configs, and test MCP endpoint").action(withExit(statusCommand));
|
|
2271
2833
|
program.command("remove").description("Remove all p\u0131ut configurations").action(withExit(removeCommand));
|
|
2272
2834
|
program.command("logout").description("Remove saved API key").action(logoutCommand);
|
|
2835
|
+
program.command("doctor").description("Diagnose and fix connection issues").option("-k, --key <key>", "API key to verify against").option("--fix", "Auto-fix stale configurations").option("--json", "Output results as JSON").action(withExit(doctorCommand));
|
|
2273
2836
|
program.command("update").description("Check for and install CLI updates").action(() => updateCommand(VERSION));
|
|
2274
2837
|
var args = process.argv.slice(2);
|
|
2275
2838
|
if (args.includes("--version") || args.includes("-V")) {
|