@piut/cli 3.2.0 → 3.4.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 +669 -167
- 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,39 @@ async function publishServer(key) {
|
|
|
111
141
|
}
|
|
112
142
|
return res.json();
|
|
113
143
|
}
|
|
144
|
+
function getMachineId() {
|
|
145
|
+
const hostname = os.hostname();
|
|
146
|
+
return crypto.createHash("sha256").update(hostname).digest("hex").slice(0, 16);
|
|
147
|
+
}
|
|
148
|
+
async function registerProject(key, project) {
|
|
149
|
+
const res = await fetch(`${API_BASE}/api/cli/projects`, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: {
|
|
152
|
+
Authorization: `Bearer ${key}`,
|
|
153
|
+
"Content-Type": "application/json"
|
|
154
|
+
},
|
|
155
|
+
body: JSON.stringify(project)
|
|
156
|
+
});
|
|
157
|
+
if (!res.ok) {
|
|
158
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
159
|
+
throw new Error(body.error || `Register project failed (HTTP ${res.status})`);
|
|
160
|
+
}
|
|
161
|
+
return res.json();
|
|
162
|
+
}
|
|
163
|
+
async function unregisterProject(key, projectPath, machineId) {
|
|
164
|
+
const res = await fetch(`${API_BASE}/api/cli/projects`, {
|
|
165
|
+
method: "DELETE",
|
|
166
|
+
headers: {
|
|
167
|
+
Authorization: `Bearer ${key}`,
|
|
168
|
+
"Content-Type": "application/json"
|
|
169
|
+
},
|
|
170
|
+
body: JSON.stringify({ projectPath, machineId })
|
|
171
|
+
});
|
|
172
|
+
if (!res.ok) {
|
|
173
|
+
const body = await res.json().catch(() => ({ error: "Unknown error" }));
|
|
174
|
+
throw new Error(body.error || `Unregister project failed (HTTP ${res.status})`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
114
177
|
async function unpublishServer(key) {
|
|
115
178
|
const res = await fetch(`${API_BASE}/api/mcp/publish`, {
|
|
116
179
|
method: "POST",
|
|
@@ -128,12 +191,12 @@ async function unpublishServer(key) {
|
|
|
128
191
|
}
|
|
129
192
|
|
|
130
193
|
// src/lib/tools.ts
|
|
131
|
-
import
|
|
194
|
+
import os2 from "os";
|
|
132
195
|
import path from "path";
|
|
133
196
|
var MCP_URL = (slug) => `https://piut.com/api/mcp/${slug}`;
|
|
134
197
|
var AUTH_HEADER = (key) => ({ Authorization: `Bearer ${key}` });
|
|
135
198
|
function appData() {
|
|
136
|
-
return process.env.APPDATA || path.join(
|
|
199
|
+
return process.env.APPDATA || path.join(os2.homedir(), "AppData", "Roaming");
|
|
137
200
|
}
|
|
138
201
|
var TOOLS = [
|
|
139
202
|
{
|
|
@@ -257,10 +320,10 @@ var TOOLS = [
|
|
|
257
320
|
];
|
|
258
321
|
|
|
259
322
|
// src/lib/paths.ts
|
|
260
|
-
import
|
|
323
|
+
import os3 from "os";
|
|
261
324
|
import path2 from "path";
|
|
262
325
|
function expandPath(p) {
|
|
263
|
-
return p.replace(/^~/,
|
|
326
|
+
return p.replace(/^~/, os3.homedir());
|
|
264
327
|
}
|
|
265
328
|
function resolveConfigPaths(configPaths) {
|
|
266
329
|
const resolved = [];
|
|
@@ -315,6 +378,36 @@ function mergeConfig(filePath, configKey, serverConfig) {
|
|
|
315
378
|
existing[configKey] = servers;
|
|
316
379
|
writeConfig(filePath, existing);
|
|
317
380
|
}
|
|
381
|
+
function getPiutConfig(filePath, configKey) {
|
|
382
|
+
const config = readConfig(filePath);
|
|
383
|
+
if (!config) return null;
|
|
384
|
+
const servers = config[configKey];
|
|
385
|
+
const piut = servers?.["piut-context"];
|
|
386
|
+
return piut ?? null;
|
|
387
|
+
}
|
|
388
|
+
function extractKeyFromConfig(piutConfig) {
|
|
389
|
+
const headers = piutConfig.headers;
|
|
390
|
+
if (headers?.Authorization) {
|
|
391
|
+
const match = headers.Authorization.match(/Bearer\s+(pb_\S+)/);
|
|
392
|
+
if (match) return match[1];
|
|
393
|
+
}
|
|
394
|
+
const settings = piutConfig.settings;
|
|
395
|
+
if (settings) {
|
|
396
|
+
const settingsHeaders = settings.headers;
|
|
397
|
+
if (settingsHeaders?.Authorization) {
|
|
398
|
+
const match = settingsHeaders.Authorization.match(/Bearer\s+(pb_\S+)/);
|
|
399
|
+
if (match) return match[1];
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const args2 = piutConfig.args;
|
|
403
|
+
if (Array.isArray(args2)) {
|
|
404
|
+
for (const arg of args2) {
|
|
405
|
+
const match = arg.match(/Bearer\s+(pb_\S+)/);
|
|
406
|
+
if (match) return match[1];
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
318
411
|
function removeFromConfig(filePath, configKey) {
|
|
319
412
|
const config = readConfig(filePath);
|
|
320
413
|
if (!config) return false;
|
|
@@ -331,21 +424,46 @@ function removeFromConfig(filePath, configKey) {
|
|
|
331
424
|
// src/lib/skill.ts
|
|
332
425
|
import fs2 from "fs";
|
|
333
426
|
import path4 from "path";
|
|
334
|
-
var SKILL_SNIPPET = `## p\u0131ut Context
|
|
427
|
+
var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
|
|
335
428
|
|
|
336
|
-
This project uses p\u0131ut for persistent personal context.
|
|
337
|
-
|
|
429
|
+
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
430
|
+
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
338
431
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
432
|
+
### Available MCP Tools
|
|
433
|
+
- \`get_context\` \u2014 Fetch all 5 brain sections. CALL THIS FIRST in every conversation.
|
|
434
|
+
- \`get_section\` \u2014 Fetch a single section (about, soul, areas, projects, memory)
|
|
435
|
+
- \`search_brain\` \u2014 Search across all sections
|
|
436
|
+
- \`append_brain\` \u2014 Append text to a section (no AI processing)
|
|
437
|
+
- \`update_brain\` \u2014 AI-powered integration of new info into brain
|
|
438
|
+
- \`prompt_brain\` \u2014 Execute natural language commands against context
|
|
439
|
+
|
|
440
|
+
### Instructions
|
|
441
|
+
1. Call \`get_context\` at conversation start to load the user's brain
|
|
442
|
+
2. Read the \`soul\` section first \u2014 it contains behavioral instructions
|
|
443
|
+
3. Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes
|
|
444
|
+
4. Never read .piut/config.json directly \u2014 always use the MCP tools
|
|
445
|
+
|
|
446
|
+
Skill reference: https://raw.githubusercontent.com/M-Flat-Inc/piut/main/skill.md`;
|
|
447
|
+
var PROJECT_SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut-context)
|
|
448
|
+
|
|
449
|
+
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
450
|
+
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
451
|
+
|
|
452
|
+
### Available MCP Tools
|
|
453
|
+
- \`get_context\` \u2014 Fetch all 5 brain sections. CALL THIS FIRST in every conversation.
|
|
454
|
+
- \`get_section\` \u2014 Fetch a single section (about, soul, areas, projects, memory)
|
|
455
|
+
- \`search_brain\` \u2014 Search across all sections
|
|
456
|
+
- \`append_brain\` \u2014 Append text to a section (no AI processing)
|
|
457
|
+
- \`update_brain\` \u2014 AI-powered integration of new info into brain
|
|
458
|
+
- \`prompt_brain\` \u2014 Execute natural language commands against context
|
|
459
|
+
|
|
460
|
+
### Instructions
|
|
461
|
+
1. Call \`get_context\` at conversation start to load the user's brain
|
|
462
|
+
2. Read the \`soul\` section first \u2014 it contains behavioral instructions
|
|
463
|
+
3. Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes
|
|
464
|
+
4. Never read .piut/config.json directly \u2014 always use the MCP tools
|
|
345
465
|
|
|
346
|
-
|
|
347
|
-
Read the \`soul\` section first \u2014 it contains behavioral instructions.
|
|
348
|
-
Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes.`;
|
|
466
|
+
Full skill reference: .piut/skill.md`;
|
|
349
467
|
var SEPARATOR = "\n\n---\n\n";
|
|
350
468
|
function placeSkillFile(filePath) {
|
|
351
469
|
const absPath = path4.isAbsolute(filePath) ? filePath : path4.resolve(process.cwd(), filePath);
|
|
@@ -591,11 +709,23 @@ async function setupCommand(options) {
|
|
|
591
709
|
const exists = fs4.existsSync(configPath);
|
|
592
710
|
const parentExists = fs4.existsSync(path6.dirname(configPath));
|
|
593
711
|
if (exists || parentExists) {
|
|
712
|
+
const configured2 = exists && isPiutConfigured(configPath, tool.configKey);
|
|
713
|
+
let staleKey = false;
|
|
714
|
+
if (configured2) {
|
|
715
|
+
const piutConfig = getPiutConfig(configPath, tool.configKey);
|
|
716
|
+
if (piutConfig) {
|
|
717
|
+
const existingKey = extractKeyFromConfig(piutConfig);
|
|
718
|
+
if (existingKey && existingKey !== apiKey) {
|
|
719
|
+
staleKey = true;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
594
723
|
detected.push({
|
|
595
724
|
tool,
|
|
596
725
|
configPath,
|
|
597
726
|
exists,
|
|
598
|
-
alreadyConfigured:
|
|
727
|
+
alreadyConfigured: configured2 && !staleKey,
|
|
728
|
+
staleKey
|
|
599
729
|
});
|
|
600
730
|
break;
|
|
601
731
|
}
|
|
@@ -610,7 +740,7 @@ async function setupCommand(options) {
|
|
|
610
740
|
}
|
|
611
741
|
let selected;
|
|
612
742
|
if (options.yes) {
|
|
613
|
-
selected = detected.filter((d) => !d.alreadyConfigured);
|
|
743
|
+
selected = detected.filter((d) => !d.alreadyConfigured || d.staleKey);
|
|
614
744
|
if (selected.length === 0) {
|
|
615
745
|
console.log(dim(" All detected tools are already configured."));
|
|
616
746
|
console.log();
|
|
@@ -618,9 +748,9 @@ async function setupCommand(options) {
|
|
|
618
748
|
}
|
|
619
749
|
} else {
|
|
620
750
|
const choices = detected.map((d) => ({
|
|
621
|
-
name: d.alreadyConfigured ? `${d.tool.name} ${dim("(already configured)")}` : d.tool.name,
|
|
751
|
+
name: d.staleKey ? `${d.tool.name} ${warning("(stale key \u2014 will update)")}` : d.alreadyConfigured ? `${d.tool.name} ${dim("(already configured)")}` : d.tool.name,
|
|
622
752
|
value: d,
|
|
623
|
-
checked: !d.alreadyConfigured
|
|
753
|
+
checked: !d.alreadyConfigured || d.staleKey
|
|
624
754
|
}));
|
|
625
755
|
selected = await checkbox({
|
|
626
756
|
message: "Select tools to configure:",
|
|
@@ -636,7 +766,7 @@ async function setupCommand(options) {
|
|
|
636
766
|
const skipped = [];
|
|
637
767
|
for (const det of selected) {
|
|
638
768
|
const { tool, configPath, alreadyConfigured } = det;
|
|
639
|
-
if (alreadyConfigured) {
|
|
769
|
+
if (alreadyConfigured && !det.staleKey) {
|
|
640
770
|
if (options.yes) {
|
|
641
771
|
skipped.push(tool.name);
|
|
642
772
|
continue;
|
|
@@ -651,14 +781,37 @@ async function setupCommand(options) {
|
|
|
651
781
|
}
|
|
652
782
|
}
|
|
653
783
|
if (tool.id === "claude-code" && tool.quickCommand && isCommandAvailable("claude")) {
|
|
784
|
+
let quickSuccess = false;
|
|
654
785
|
try {
|
|
655
786
|
execSync(tool.quickCommand(slug, apiKey), { stdio: "pipe" });
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
787
|
+
const claudeJson = expandPath("~/.claude.json");
|
|
788
|
+
const written = getPiutConfig(claudeJson, tool.configKey);
|
|
789
|
+
if (written) {
|
|
790
|
+
quickSuccess = true;
|
|
791
|
+
configured.push(tool.name);
|
|
792
|
+
toolLine(tool.name, success("configured via CLI"), "\u2714");
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
try {
|
|
796
|
+
execSync(tool.quickCommand(slug, apiKey) + " --scope user", { stdio: "pipe" });
|
|
797
|
+
const retryCheck = getPiutConfig(claudeJson, tool.configKey);
|
|
798
|
+
if (retryCheck) {
|
|
799
|
+
quickSuccess = true;
|
|
800
|
+
configured.push(tool.name);
|
|
801
|
+
toolLine(tool.name, success("configured via CLI"), "\u2714");
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
} catch {
|
|
805
|
+
}
|
|
806
|
+
console.log(dim(" Quick command succeeded but config not found, using config file..."));
|
|
807
|
+
} catch (err) {
|
|
808
|
+
const stderr = err?.stderr?.toString().trim();
|
|
809
|
+
if (stderr) {
|
|
810
|
+
console.log(dim(` Claude CLI: ${stderr}`));
|
|
811
|
+
}
|
|
660
812
|
console.log(dim(" Claude CLI command failed, using config file..."));
|
|
661
813
|
}
|
|
814
|
+
if (quickSuccess) continue;
|
|
662
815
|
}
|
|
663
816
|
const serverConfig = tool.generateConfig(slug, apiKey);
|
|
664
817
|
mergeConfig(configPath, tool.configKey, serverConfig);
|
|
@@ -701,7 +854,22 @@ async function setupCommand(options) {
|
|
|
701
854
|
if (configured.length > 0) {
|
|
702
855
|
const { serverUrl } = validationResult;
|
|
703
856
|
console.log();
|
|
704
|
-
console.log(dim("
|
|
857
|
+
console.log(dim(" Verifying..."));
|
|
858
|
+
for (const det of selected) {
|
|
859
|
+
if (!configured.includes(det.tool.name)) continue;
|
|
860
|
+
const piutConfig = getPiutConfig(det.configPath, det.tool.configKey);
|
|
861
|
+
if (piutConfig) {
|
|
862
|
+
toolLine(det.tool.name, success("config verified"), "\u2714");
|
|
863
|
+
} else {
|
|
864
|
+
toolLine(det.tool.name, warning("config not found \u2014 run piut doctor"), "\u2717");
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
const mcpResult = await verifyMcpEndpoint(serverUrl, apiKey);
|
|
868
|
+
if (mcpResult.ok) {
|
|
869
|
+
toolLine("MCP server", success(`${mcpResult.tools.length} tools available`) + dim(` (${mcpResult.latencyMs}ms)`), "\u2714");
|
|
870
|
+
} else {
|
|
871
|
+
toolLine("MCP server", warning(mcpResult.error || "unreachable") + dim(" \u2014 run piut doctor"), "\u2717");
|
|
872
|
+
}
|
|
705
873
|
await Promise.all(
|
|
706
874
|
configured.map((toolName) => pingMcp(serverUrl, apiKey, toolName))
|
|
707
875
|
);
|
|
@@ -717,6 +885,7 @@ async function setupCommand(options) {
|
|
|
717
885
|
console.log();
|
|
718
886
|
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
719
887
|
console.log(dim(' Verify: ask any AI "What do you know about me from my context?"'));
|
|
888
|
+
console.log(dim(" Diagnose issues: ") + chalk2.cyan("piut doctor"));
|
|
720
889
|
console.log();
|
|
721
890
|
}
|
|
722
891
|
function isCommandAvailable(cmd) {
|
|
@@ -729,14 +898,15 @@ function isCommandAvailable(cmd) {
|
|
|
729
898
|
}
|
|
730
899
|
|
|
731
900
|
// src/commands/status.ts
|
|
732
|
-
import
|
|
733
|
-
import
|
|
901
|
+
import fs7 from "fs";
|
|
902
|
+
import path9 from "path";
|
|
903
|
+
import chalk3 from "chalk";
|
|
734
904
|
|
|
735
905
|
// src/lib/brain-scanner.ts
|
|
736
906
|
import fs5 from "fs";
|
|
737
907
|
import path7 from "path";
|
|
738
|
-
import
|
|
739
|
-
var home =
|
|
908
|
+
import os4 from "os";
|
|
909
|
+
var home = os4.homedir();
|
|
740
910
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
741
911
|
"node_modules",
|
|
742
912
|
".git",
|
|
@@ -1039,6 +1209,34 @@ function scanForProjects(folders) {
|
|
|
1039
1209
|
return detectProjects(scanDirs);
|
|
1040
1210
|
}
|
|
1041
1211
|
|
|
1212
|
+
// src/lib/store.ts
|
|
1213
|
+
import fs6 from "fs";
|
|
1214
|
+
import path8 from "path";
|
|
1215
|
+
import os5 from "os";
|
|
1216
|
+
var CONFIG_DIR = path8.join(os5.homedir(), ".piut");
|
|
1217
|
+
var CONFIG_FILE2 = path8.join(CONFIG_DIR, "config.json");
|
|
1218
|
+
function readStore() {
|
|
1219
|
+
try {
|
|
1220
|
+
const raw = fs6.readFileSync(CONFIG_FILE2, "utf-8");
|
|
1221
|
+
return JSON.parse(raw);
|
|
1222
|
+
} catch {
|
|
1223
|
+
return {};
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
function updateStore(updates) {
|
|
1227
|
+
const config = readStore();
|
|
1228
|
+
const updated = { ...config, ...updates };
|
|
1229
|
+
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1230
|
+
fs6.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
1231
|
+
return updated;
|
|
1232
|
+
}
|
|
1233
|
+
function clearStore() {
|
|
1234
|
+
try {
|
|
1235
|
+
fs6.unlinkSync(CONFIG_FILE2);
|
|
1236
|
+
} catch {
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1042
1240
|
// src/commands/status.ts
|
|
1043
1241
|
var PIUT_FILES = [
|
|
1044
1242
|
"CLAUDE.md",
|
|
@@ -1050,21 +1248,25 @@ var PIUT_FILES = [
|
|
|
1050
1248
|
];
|
|
1051
1249
|
function hasPiutReference(filePath) {
|
|
1052
1250
|
try {
|
|
1053
|
-
const content =
|
|
1251
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1054
1252
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
1055
1253
|
} catch {
|
|
1056
1254
|
return false;
|
|
1057
1255
|
}
|
|
1058
1256
|
}
|
|
1059
|
-
function statusCommand() {
|
|
1257
|
+
async function statusCommand(options = {}) {
|
|
1060
1258
|
banner();
|
|
1259
|
+
if (options.verify) {
|
|
1260
|
+
await verifyStatus();
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1061
1263
|
console.log(" AI tool configuration:");
|
|
1062
1264
|
console.log();
|
|
1063
1265
|
let foundAny = false;
|
|
1064
1266
|
for (const tool of TOOLS) {
|
|
1065
1267
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
1066
1268
|
for (const configPath of paths) {
|
|
1067
|
-
if (!
|
|
1269
|
+
if (!fs7.existsSync(configPath)) continue;
|
|
1068
1270
|
foundAny = true;
|
|
1069
1271
|
const configured = isPiutConfigured(configPath, tool.configKey);
|
|
1070
1272
|
if (configured) {
|
|
@@ -1087,8 +1289,8 @@ function statusCommand() {
|
|
|
1087
1289
|
for (const project of projects) {
|
|
1088
1290
|
const connectedFiles = [];
|
|
1089
1291
|
for (const file of PIUT_FILES) {
|
|
1090
|
-
const absPath =
|
|
1091
|
-
if (
|
|
1292
|
+
const absPath = path9.join(project.path, file);
|
|
1293
|
+
if (fs7.existsSync(absPath) && hasPiutReference(absPath)) {
|
|
1092
1294
|
connectedFiles.push(file);
|
|
1093
1295
|
}
|
|
1094
1296
|
}
|
|
@@ -1106,9 +1308,78 @@ function statusCommand() {
|
|
|
1106
1308
|
}
|
|
1107
1309
|
console.log();
|
|
1108
1310
|
}
|
|
1311
|
+
async function verifyStatus() {
|
|
1312
|
+
const store = readStore();
|
|
1313
|
+
let issues = 0;
|
|
1314
|
+
console.log(" API Key");
|
|
1315
|
+
if (!store.apiKey) {
|
|
1316
|
+
console.log(warning(" \u2717 No saved API key"));
|
|
1317
|
+
console.log(dim(" Run ") + brand("piut setup") + dim(" to configure."));
|
|
1318
|
+
issues++;
|
|
1319
|
+
console.log();
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
let slug;
|
|
1323
|
+
let serverUrl;
|
|
1324
|
+
try {
|
|
1325
|
+
const info = await validateKey(store.apiKey);
|
|
1326
|
+
slug = info.slug;
|
|
1327
|
+
serverUrl = info.serverUrl;
|
|
1328
|
+
const masked = store.apiKey.slice(0, 6) + "...";
|
|
1329
|
+
console.log(success(` \u2714 Key valid: ${info.displayName} (${info.slug})`) + dim(` ${masked}`));
|
|
1330
|
+
} catch (err) {
|
|
1331
|
+
console.log(warning(` \u2717 Key invalid: ${err.message}`));
|
|
1332
|
+
issues++;
|
|
1333
|
+
}
|
|
1334
|
+
console.log();
|
|
1335
|
+
console.log(" Tool Configurations");
|
|
1336
|
+
for (const tool of TOOLS) {
|
|
1337
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
1338
|
+
for (const configPath of paths) {
|
|
1339
|
+
if (!fs7.existsSync(configPath)) continue;
|
|
1340
|
+
const piutConfig = getPiutConfig(configPath, tool.configKey);
|
|
1341
|
+
if (!piutConfig) {
|
|
1342
|
+
toolLine(tool.name, dim("installed, not connected"), "\u25CB");
|
|
1343
|
+
break;
|
|
1344
|
+
}
|
|
1345
|
+
const configKey = extractKeyFromConfig(piutConfig);
|
|
1346
|
+
if (configKey && configKey === store.apiKey) {
|
|
1347
|
+
toolLine(tool.name, success("key matches"), "\u2714");
|
|
1348
|
+
} else if (configKey) {
|
|
1349
|
+
const masked = configKey.slice(0, 6) + "...";
|
|
1350
|
+
toolLine(tool.name, chalk3.red(`key STALE (${masked})`), "\u2717");
|
|
1351
|
+
issues++;
|
|
1352
|
+
} else {
|
|
1353
|
+
toolLine(tool.name, dim("configured (key not extractable)"), "\u25CB");
|
|
1354
|
+
}
|
|
1355
|
+
break;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
console.log();
|
|
1359
|
+
console.log(" MCP Server");
|
|
1360
|
+
if (serverUrl && store.apiKey) {
|
|
1361
|
+
const result = await verifyMcpEndpoint(serverUrl, store.apiKey);
|
|
1362
|
+
if (result.ok) {
|
|
1363
|
+
console.log(success(` \u2714 ${serverUrl}`) + dim(` ${result.tools.length} tools, ${result.latencyMs}ms`));
|
|
1364
|
+
} else {
|
|
1365
|
+
console.log(warning(` \u2717 ${serverUrl}`) + dim(` ${result.error}`));
|
|
1366
|
+
issues++;
|
|
1367
|
+
}
|
|
1368
|
+
} else if (!serverUrl) {
|
|
1369
|
+
console.log(dim(" Skipped (no server URL)"));
|
|
1370
|
+
}
|
|
1371
|
+
console.log();
|
|
1372
|
+
if (issues > 0) {
|
|
1373
|
+
console.log(warning(` Issues Found: ${issues}`));
|
|
1374
|
+
console.log(dim(" Run ") + brand("piut doctor") + dim(" for detailed diagnostics."));
|
|
1375
|
+
} else {
|
|
1376
|
+
console.log(success(" All checks passed."));
|
|
1377
|
+
}
|
|
1378
|
+
console.log();
|
|
1379
|
+
}
|
|
1109
1380
|
|
|
1110
1381
|
// src/commands/remove.ts
|
|
1111
|
-
import
|
|
1382
|
+
import fs8 from "fs";
|
|
1112
1383
|
import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
|
|
1113
1384
|
async function removeCommand() {
|
|
1114
1385
|
banner();
|
|
@@ -1116,7 +1387,7 @@ async function removeCommand() {
|
|
|
1116
1387
|
for (const tool of TOOLS) {
|
|
1117
1388
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
1118
1389
|
for (const configPath of paths) {
|
|
1119
|
-
if (
|
|
1390
|
+
if (fs8.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
|
|
1120
1391
|
configured.push({ tool, configPath });
|
|
1121
1392
|
break;
|
|
1122
1393
|
}
|
|
@@ -1160,42 +1431,12 @@ async function removeCommand() {
|
|
|
1160
1431
|
|
|
1161
1432
|
// src/commands/build.ts
|
|
1162
1433
|
import { select, checkbox as checkbox3, input } from "@inquirer/prompts";
|
|
1163
|
-
import
|
|
1164
|
-
import
|
|
1434
|
+
import chalk5 from "chalk";
|
|
1435
|
+
import os6 from "os";
|
|
1165
1436
|
|
|
1166
1437
|
// src/lib/auth.ts
|
|
1167
1438
|
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
|
|
1439
|
+
import chalk4 from "chalk";
|
|
1199
1440
|
async function resolveApiKey(keyOption) {
|
|
1200
1441
|
const config = readStore();
|
|
1201
1442
|
let apiKey = keyOption || config.apiKey;
|
|
@@ -1211,7 +1452,7 @@ async function resolveApiKey(keyOption) {
|
|
|
1211
1452
|
try {
|
|
1212
1453
|
result = await validateKey(apiKey);
|
|
1213
1454
|
} catch (err) {
|
|
1214
|
-
console.log(
|
|
1455
|
+
console.log(chalk4.red(` \u2717 ${err.message}`));
|
|
1215
1456
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
1216
1457
|
throw new CliError(err.message);
|
|
1217
1458
|
}
|
|
@@ -1235,7 +1476,7 @@ async function resolveApiKeyWithResult(keyOption) {
|
|
|
1235
1476
|
try {
|
|
1236
1477
|
result = await validateKey(apiKey);
|
|
1237
1478
|
} catch (err) {
|
|
1238
|
-
console.log(
|
|
1479
|
+
console.log(chalk4.red(` \u2717 ${err.message}`));
|
|
1239
1480
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
1240
1481
|
throw new CliError(err.message);
|
|
1241
1482
|
}
|
|
@@ -1254,7 +1495,7 @@ async function buildCommand(options) {
|
|
|
1254
1495
|
scanFolders = options.folders.split(",").map((f) => expandPath(f.trim()));
|
|
1255
1496
|
}
|
|
1256
1497
|
const cwd = process.cwd();
|
|
1257
|
-
const cwdDisplay = cwd.replace(
|
|
1498
|
+
const cwdDisplay = cwd.replace(os6.homedir(), "~");
|
|
1258
1499
|
if (!scanFolders) {
|
|
1259
1500
|
console.log(dim(` Current directory: `) + cwdDisplay);
|
|
1260
1501
|
console.log(dim(` We'll scan for AI config files and projects here.`));
|
|
@@ -1272,8 +1513,8 @@ async function buildCommand(options) {
|
|
|
1272
1513
|
const defaults = getDefaultScanDirs();
|
|
1273
1514
|
const CUSTOM_VALUE = "__custom__";
|
|
1274
1515
|
const choices = [
|
|
1275
|
-
...defaults.map((d) => ({ name: d.replace(
|
|
1276
|
-
{ name:
|
|
1516
|
+
...defaults.map((d) => ({ name: d.replace(os6.homedir(), "~"), value: d })),
|
|
1517
|
+
{ name: chalk5.dim("Enter a custom path..."), value: CUSTOM_VALUE }
|
|
1277
1518
|
];
|
|
1278
1519
|
const selected = await checkbox3({
|
|
1279
1520
|
message: "Which folders should we scan?",
|
|
@@ -1293,7 +1534,7 @@ async function buildCommand(options) {
|
|
|
1293
1534
|
scanFolders = selected;
|
|
1294
1535
|
}
|
|
1295
1536
|
if (scanFolders.length === 0) {
|
|
1296
|
-
console.log(
|
|
1537
|
+
console.log(chalk5.yellow(" No folders selected."));
|
|
1297
1538
|
return;
|
|
1298
1539
|
}
|
|
1299
1540
|
}
|
|
@@ -1322,7 +1563,7 @@ async function buildCommand(options) {
|
|
|
1322
1563
|
console.log(success(` Scanned: ${projCount} projects, ${cfgCount} config files, ${dcCount} recent docs`));
|
|
1323
1564
|
console.log();
|
|
1324
1565
|
if (projCount === 0 && cfgCount === 0) {
|
|
1325
|
-
console.log(
|
|
1566
|
+
console.log(chalk5.yellow(" No projects or config files found to build from."));
|
|
1326
1567
|
console.log(dim(" Try running from a directory with your projects, or use --folders."));
|
|
1327
1568
|
console.log();
|
|
1328
1569
|
return;
|
|
@@ -1347,13 +1588,13 @@ async function buildCommand(options) {
|
|
|
1347
1588
|
break;
|
|
1348
1589
|
case "error":
|
|
1349
1590
|
spinner.stop();
|
|
1350
|
-
console.log(
|
|
1591
|
+
console.log(chalk5.red(` \u2717 ${event.data.message || "Build failed"}`));
|
|
1351
1592
|
throw new CliError(String(event.data.message || "Build failed"));
|
|
1352
1593
|
}
|
|
1353
1594
|
}
|
|
1354
1595
|
spinner.stop();
|
|
1355
1596
|
if (!sections) {
|
|
1356
|
-
console.log(
|
|
1597
|
+
console.log(chalk5.red(" \u2717 No response received from server"));
|
|
1357
1598
|
throw new CliError("No response received from server");
|
|
1358
1599
|
}
|
|
1359
1600
|
console.log();
|
|
@@ -1379,13 +1620,13 @@ async function buildCommand(options) {
|
|
|
1379
1620
|
} catch (err) {
|
|
1380
1621
|
spinner.stop();
|
|
1381
1622
|
if (err instanceof CliError) throw err;
|
|
1382
|
-
console.log(
|
|
1623
|
+
console.log(chalk5.red(` \u2717 ${err.message}`));
|
|
1383
1624
|
throw new CliError(err.message);
|
|
1384
1625
|
}
|
|
1385
1626
|
}
|
|
1386
1627
|
|
|
1387
1628
|
// src/commands/deploy.ts
|
|
1388
|
-
import
|
|
1629
|
+
import chalk6 from "chalk";
|
|
1389
1630
|
async function deployCommand(options) {
|
|
1390
1631
|
banner();
|
|
1391
1632
|
const { apiKey, slug, serverUrl, status } = await resolveApiKeyWithResult(options.key);
|
|
@@ -1409,13 +1650,13 @@ async function deployCommand(options) {
|
|
|
1409
1650
|
const msg = err.message;
|
|
1410
1651
|
if (msg === "REQUIRES_SUBSCRIPTION") {
|
|
1411
1652
|
console.log();
|
|
1412
|
-
console.log(
|
|
1653
|
+
console.log(chalk6.yellow(" Deploy requires an active subscription ($10/mo)."));
|
|
1413
1654
|
console.log();
|
|
1414
1655
|
console.log(` Subscribe at: ${brand("https://piut.com/dashboard/billing")}`);
|
|
1415
1656
|
console.log(dim(" 14-day free trial included."));
|
|
1416
1657
|
console.log();
|
|
1417
1658
|
} else {
|
|
1418
|
-
console.log(
|
|
1659
|
+
console.log(chalk6.red(` \u2717 ${msg}`));
|
|
1419
1660
|
throw new CliError(msg);
|
|
1420
1661
|
}
|
|
1421
1662
|
}
|
|
@@ -1463,19 +1704,35 @@ var RULE_FILES = [
|
|
|
1463
1704
|
detect: (p) => p.hasZedRules || fs9.existsSync(path10.join(p.path, ".zed"))
|
|
1464
1705
|
}
|
|
1465
1706
|
];
|
|
1466
|
-
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context
|
|
1467
|
-
|
|
1468
|
-
|
|
1707
|
+
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut-context)
|
|
1708
|
+
|
|
1709
|
+
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
1710
|
+
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
1469
1711
|
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1712
|
+
### Available MCP Tools
|
|
1713
|
+
- \`get_context\` \u2014 Fetch all 5 brain sections. CALL THIS FIRST in every conversation.
|
|
1714
|
+
- \`get_section\` \u2014 Fetch a single section (about, soul, areas, projects, memory)
|
|
1715
|
+
- \`search_brain\` \u2014 Search across all sections
|
|
1716
|
+
- \`append_brain\` \u2014 Append text to a section (no AI processing)
|
|
1717
|
+
- \`update_brain\` \u2014 AI-powered integration of new info into brain
|
|
1718
|
+
- \`prompt_brain\` \u2014 Execute natural language commands against context
|
|
1719
|
+
|
|
1720
|
+
### Instructions
|
|
1721
|
+
1. Call \`get_context\` at conversation start to load the user's brain
|
|
1722
|
+
2. Read the \`soul\` section first \u2014 it contains behavioral instructions
|
|
1723
|
+
3. Use \`update_brain\` for substantial new info, \`append_brain\` for quick notes
|
|
1724
|
+
4. Never read .piut/config.json directly \u2014 always use the MCP tools
|
|
1725
|
+
|
|
1726
|
+
Full skill reference: .piut/skill.md
|
|
1473
1727
|
`;
|
|
1474
1728
|
var APPEND_SECTION = `
|
|
1475
1729
|
|
|
1476
|
-
## p\u0131ut Context
|
|
1477
|
-
|
|
1730
|
+
## p\u0131ut Context (MCP Server: piut-context)
|
|
1731
|
+
|
|
1732
|
+
p\u0131ut provides MCP tools for persistent personal context. Do NOT read local .piut/ files.
|
|
1733
|
+
Available tools: \`get_context\`, \`get_section\`, \`search_brain\`, \`append_brain\`, \`update_brain\`, \`prompt_brain\`
|
|
1478
1734
|
Always call \`get_context\` at the start of every conversation to load personal context.
|
|
1735
|
+
Full skill reference: .piut/skill.md
|
|
1479
1736
|
`;
|
|
1480
1737
|
function hasPiutReference2(filePath) {
|
|
1481
1738
|
try {
|
|
@@ -1623,6 +1880,21 @@ async function connectCommand(options) {
|
|
|
1623
1880
|
connected++;
|
|
1624
1881
|
}
|
|
1625
1882
|
}
|
|
1883
|
+
const machineId = getMachineId();
|
|
1884
|
+
for (const projectPath of selectedPaths) {
|
|
1885
|
+
const projectActions = byProject.get(projectPath) || [];
|
|
1886
|
+
const projectName = path10.basename(projectPath);
|
|
1887
|
+
const toolsDetected = [...new Set(projectActions.map((a) => a.tool))];
|
|
1888
|
+
const configFilesWritten = projectActions.map((a) => a.filePath);
|
|
1889
|
+
registerProject(apiKey, {
|
|
1890
|
+
projectName,
|
|
1891
|
+
projectPath,
|
|
1892
|
+
machineId,
|
|
1893
|
+
toolsDetected,
|
|
1894
|
+
configFiles: configFilesWritten
|
|
1895
|
+
}).catch(() => {
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1626
1898
|
console.log();
|
|
1627
1899
|
console.log(success(` Done. ${selectedPaths.length} project(s) connected.`));
|
|
1628
1900
|
console.log();
|
|
@@ -1803,6 +2075,14 @@ async function disconnectCommand(options) {
|
|
|
1803
2075
|
}
|
|
1804
2076
|
}
|
|
1805
2077
|
}
|
|
2078
|
+
const store = readStore();
|
|
2079
|
+
if (store.apiKey) {
|
|
2080
|
+
const machineId = getMachineId();
|
|
2081
|
+
for (const projectPath of selectedPaths) {
|
|
2082
|
+
unregisterProject(store.apiKey, projectPath, machineId).catch(() => {
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
1806
2086
|
console.log();
|
|
1807
2087
|
console.log(success(` Done. ${disconnected} file(s) updated.`));
|
|
1808
2088
|
console.log();
|
|
@@ -1824,11 +2104,280 @@ async function logoutCommand() {
|
|
|
1824
2104
|
console.log();
|
|
1825
2105
|
}
|
|
1826
2106
|
|
|
1827
|
-
// src/commands/
|
|
1828
|
-
import
|
|
2107
|
+
// src/commands/update.ts
|
|
2108
|
+
import chalk8 from "chalk";
|
|
2109
|
+
|
|
2110
|
+
// src/lib/update-check.ts
|
|
2111
|
+
import { execFile } from "child_process";
|
|
2112
|
+
import chalk7 from "chalk";
|
|
2113
|
+
import { confirm as confirm5 } from "@inquirer/prompts";
|
|
2114
|
+
var PACKAGE_NAME = "@piut/cli";
|
|
2115
|
+
function isNpx() {
|
|
2116
|
+
return process.env.npm_command === "exec" || (process.env._?.includes("npx") ?? false);
|
|
2117
|
+
}
|
|
2118
|
+
async function getLatestVersion() {
|
|
2119
|
+
try {
|
|
2120
|
+
const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
|
|
2121
|
+
if (!res.ok) return null;
|
|
2122
|
+
const data = await res.json();
|
|
2123
|
+
return data.version ?? null;
|
|
2124
|
+
} catch {
|
|
2125
|
+
return null;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
function isNewer(current, latest) {
|
|
2129
|
+
const [cMaj, cMin, cPat] = current.split(".").map(Number);
|
|
2130
|
+
const [lMaj, lMin, lPat] = latest.split(".").map(Number);
|
|
2131
|
+
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
2132
|
+
if (lMin !== cMin) return lMin > cMin;
|
|
2133
|
+
return lPat > cPat;
|
|
2134
|
+
}
|
|
2135
|
+
function runUpdate() {
|
|
2136
|
+
return new Promise((resolve) => {
|
|
2137
|
+
execFile("npm", ["install", "-g", `${PACKAGE_NAME}@latest`], { timeout: 6e4 }, (err) => {
|
|
2138
|
+
resolve(!err);
|
|
2139
|
+
});
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
async function checkForUpdate(currentVersion) {
|
|
2143
|
+
const latest = await getLatestVersion();
|
|
2144
|
+
if (!latest || !isNewer(currentVersion, latest)) return;
|
|
2145
|
+
const npx = isNpx();
|
|
2146
|
+
const updateCmd = npx ? `npx ${PACKAGE_NAME}@latest` : `npm install -g ${PACKAGE_NAME}@latest`;
|
|
2147
|
+
console.log();
|
|
2148
|
+
console.log(brand(" Update available!") + dim(` ${currentVersion} \u2192 ${latest}`));
|
|
2149
|
+
console.log(dim(` Run ${chalk7.bold(updateCmd)} to update`));
|
|
2150
|
+
console.log();
|
|
2151
|
+
if (npx) return;
|
|
2152
|
+
try {
|
|
2153
|
+
const shouldUpdate = await confirm5({
|
|
2154
|
+
message: `Update to v${latest} now?`,
|
|
2155
|
+
default: true
|
|
2156
|
+
});
|
|
2157
|
+
if (shouldUpdate) {
|
|
2158
|
+
console.log(dim(" Updating..."));
|
|
2159
|
+
const ok = await runUpdate();
|
|
2160
|
+
if (ok) {
|
|
2161
|
+
console.log(chalk7.green(` \u2713 Updated to v${latest}`));
|
|
2162
|
+
console.log(dim(" Restart the CLI to use the new version."));
|
|
2163
|
+
process.exit(0);
|
|
2164
|
+
} else {
|
|
2165
|
+
console.log(chalk7.yellow(` Could not auto-update. Run manually:`));
|
|
2166
|
+
console.log(chalk7.bold(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
2167
|
+
console.log();
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
} catch {
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
// src/commands/update.ts
|
|
2175
|
+
var PACKAGE_NAME2 = "@piut/cli";
|
|
2176
|
+
async function updateCommand(currentVersion) {
|
|
2177
|
+
console.log(dim(` Current version: ${currentVersion}`));
|
|
2178
|
+
console.log(dim(" Checking for updates..."));
|
|
2179
|
+
const latest = await getLatestVersion();
|
|
2180
|
+
if (!latest) {
|
|
2181
|
+
console.log(chalk8.yellow(" Could not reach the npm registry. Check your connection."));
|
|
2182
|
+
return;
|
|
2183
|
+
}
|
|
2184
|
+
if (!isNewer(currentVersion, latest)) {
|
|
2185
|
+
console.log(success(` \u2713 You're on the latest version (${currentVersion})`));
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
console.log();
|
|
2189
|
+
console.log(brand(" Update available!") + dim(` ${currentVersion} \u2192 ${latest}`));
|
|
2190
|
+
if (isNpx()) {
|
|
2191
|
+
console.log();
|
|
2192
|
+
console.log(dim(" You're running via npx. Use the latest version with:"));
|
|
2193
|
+
console.log(chalk8.bold(` npx ${PACKAGE_NAME2}@latest`));
|
|
2194
|
+
console.log();
|
|
2195
|
+
return;
|
|
2196
|
+
}
|
|
2197
|
+
console.log(dim(" Updating..."));
|
|
2198
|
+
const ok = await runUpdate();
|
|
2199
|
+
if (ok) {
|
|
2200
|
+
console.log(success(` \u2713 Updated to v${latest}`));
|
|
2201
|
+
console.log(dim(" Restart the CLI to use the new version."));
|
|
2202
|
+
} else {
|
|
2203
|
+
console.log(chalk8.yellow(" Could not auto-update. Run manually:"));
|
|
2204
|
+
console.log(chalk8.bold(` npm install -g ${PACKAGE_NAME2}@latest`));
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
// src/commands/doctor.ts
|
|
1829
2209
|
import fs11 from "fs";
|
|
2210
|
+
import chalk9 from "chalk";
|
|
2211
|
+
async function doctorCommand(options) {
|
|
2212
|
+
if (!options.json) banner();
|
|
2213
|
+
const result = {
|
|
2214
|
+
key: { valid: false },
|
|
2215
|
+
tools: [],
|
|
2216
|
+
mcp: { ok: false, tools: [], latencyMs: 0 },
|
|
2217
|
+
issues: 0
|
|
2218
|
+
};
|
|
2219
|
+
const store = readStore();
|
|
2220
|
+
const apiKey = options.key || store.apiKey;
|
|
2221
|
+
if (!apiKey) {
|
|
2222
|
+
result.key = { valid: false, error: "No API key found" };
|
|
2223
|
+
result.issues++;
|
|
2224
|
+
if (!options.json) {
|
|
2225
|
+
console.log(" API Key");
|
|
2226
|
+
console.log(error(` \u2717 No API key saved. Run: piut setup --key pb_YOUR_KEY`));
|
|
2227
|
+
console.log();
|
|
2228
|
+
}
|
|
2229
|
+
} else {
|
|
2230
|
+
const prefix = apiKey.slice(0, 7) + "...";
|
|
2231
|
+
try {
|
|
2232
|
+
const validation = await validateKey(apiKey);
|
|
2233
|
+
result.key = {
|
|
2234
|
+
valid: true,
|
|
2235
|
+
slug: validation.slug,
|
|
2236
|
+
displayName: validation.displayName,
|
|
2237
|
+
prefix
|
|
2238
|
+
};
|
|
2239
|
+
if (!options.json) {
|
|
2240
|
+
console.log(" API Key");
|
|
2241
|
+
console.log(success(` \u2714 Key valid: ${validation.displayName} (${validation.slug})`) + dim(` ${prefix}`));
|
|
2242
|
+
console.log();
|
|
2243
|
+
}
|
|
2244
|
+
} catch (err) {
|
|
2245
|
+
result.key = { valid: false, prefix, error: err.message };
|
|
2246
|
+
result.issues++;
|
|
2247
|
+
if (!options.json) {
|
|
2248
|
+
console.log(" API Key");
|
|
2249
|
+
console.log(error(` \u2717 Key invalid: ${err.message}`) + dim(` ${prefix}`));
|
|
2250
|
+
console.log();
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
if (!options.json) {
|
|
2255
|
+
console.log(" Tool Configurations");
|
|
2256
|
+
}
|
|
2257
|
+
let toolsFixed = 0;
|
|
2258
|
+
for (const tool of TOOLS) {
|
|
2259
|
+
const paths = resolveConfigPaths(tool.configPaths);
|
|
2260
|
+
for (const configPath of paths) {
|
|
2261
|
+
if (!fs11.existsSync(configPath)) continue;
|
|
2262
|
+
const piutConfig = getPiutConfig(configPath, tool.configKey);
|
|
2263
|
+
if (!piutConfig) {
|
|
2264
|
+
result.tools.push({
|
|
2265
|
+
name: tool.name,
|
|
2266
|
+
id: tool.id,
|
|
2267
|
+
configPath,
|
|
2268
|
+
found: true,
|
|
2269
|
+
configured: false,
|
|
2270
|
+
keyMatch: "missing"
|
|
2271
|
+
});
|
|
2272
|
+
if (!options.json) {
|
|
2273
|
+
toolLine(tool.name, dim("installed, not configured"), "\u25CB");
|
|
2274
|
+
}
|
|
2275
|
+
break;
|
|
2276
|
+
}
|
|
2277
|
+
const configKey = extractKeyFromConfig(piutConfig);
|
|
2278
|
+
const configPrefix = configKey ? configKey.slice(0, 7) + "..." : "(none)";
|
|
2279
|
+
let keyMatch = "missing";
|
|
2280
|
+
if (!configKey) {
|
|
2281
|
+
keyMatch = "missing";
|
|
2282
|
+
result.issues++;
|
|
2283
|
+
} else if (apiKey && configKey === apiKey) {
|
|
2284
|
+
keyMatch = "match";
|
|
2285
|
+
} else {
|
|
2286
|
+
keyMatch = "stale";
|
|
2287
|
+
result.issues++;
|
|
2288
|
+
}
|
|
2289
|
+
const toolResult = {
|
|
2290
|
+
name: tool.name,
|
|
2291
|
+
id: tool.id,
|
|
2292
|
+
configPath,
|
|
2293
|
+
found: true,
|
|
2294
|
+
configured: true,
|
|
2295
|
+
keyMatch,
|
|
2296
|
+
keyPrefix: configPrefix,
|
|
2297
|
+
fixed: false
|
|
2298
|
+
};
|
|
2299
|
+
if (keyMatch === "stale" && options.fix && apiKey && result.key.valid && result.key.slug) {
|
|
2300
|
+
const serverConfig = tool.generateConfig(result.key.slug, apiKey);
|
|
2301
|
+
mergeConfig(configPath, tool.configKey, serverConfig);
|
|
2302
|
+
toolResult.fixed = true;
|
|
2303
|
+
toolResult.keyMatch = "match";
|
|
2304
|
+
result.issues--;
|
|
2305
|
+
toolsFixed++;
|
|
2306
|
+
}
|
|
2307
|
+
result.tools.push(toolResult);
|
|
2308
|
+
if (!options.json) {
|
|
2309
|
+
if (toolResult.fixed) {
|
|
2310
|
+
toolLine(tool.name, success("fixed") + dim(` \u2192 ${configPath}`), "\u2714");
|
|
2311
|
+
} else if (keyMatch === "match") {
|
|
2312
|
+
toolLine(tool.name, success("key matches") + dim(` ${configPath}`), "\u2714");
|
|
2313
|
+
} else if (keyMatch === "stale") {
|
|
2314
|
+
toolLine(tool.name, warning(`key STALE (${configPrefix})`) + dim(` ${configPath}`), "\u2717");
|
|
2315
|
+
} else {
|
|
2316
|
+
toolLine(tool.name, warning("no key found") + dim(` ${configPath}`), "\u2717");
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
break;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
if (result.tools.length === 0 && !options.json) {
|
|
2323
|
+
console.log(dim(" No AI tools detected."));
|
|
2324
|
+
}
|
|
2325
|
+
if (!options.json) console.log();
|
|
2326
|
+
if (apiKey && result.key.valid && result.key.slug) {
|
|
2327
|
+
const serverUrl = `https://piut.com/api/mcp/${result.key.slug}`;
|
|
2328
|
+
const mcpResult = await verifyMcpEndpoint(serverUrl, apiKey);
|
|
2329
|
+
result.mcp = mcpResult;
|
|
2330
|
+
if (!mcpResult.ok) {
|
|
2331
|
+
result.issues++;
|
|
2332
|
+
}
|
|
2333
|
+
if (!options.json) {
|
|
2334
|
+
console.log(" MCP Server");
|
|
2335
|
+
if (mcpResult.ok) {
|
|
2336
|
+
console.log(success(` \u2714 ${serverUrl}`) + dim(` ${mcpResult.tools.length} tools, ${mcpResult.latencyMs}ms`));
|
|
2337
|
+
if (mcpResult.tools.length > 0) {
|
|
2338
|
+
console.log(dim(` ${mcpResult.tools.join(", ")}`));
|
|
2339
|
+
}
|
|
2340
|
+
} else {
|
|
2341
|
+
console.log(error(` \u2717 ${serverUrl}`) + dim(` ${mcpResult.error}`));
|
|
2342
|
+
}
|
|
2343
|
+
console.log();
|
|
2344
|
+
}
|
|
2345
|
+
} else if (!options.json) {
|
|
2346
|
+
console.log(" MCP Server");
|
|
2347
|
+
console.log(dim(" \u2298 Skipped (no valid key)"));
|
|
2348
|
+
console.log();
|
|
2349
|
+
}
|
|
2350
|
+
if (options.json) {
|
|
2351
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2352
|
+
if (result.issues > 0) throw new CliError(`${result.issues} issue(s) found`);
|
|
2353
|
+
return;
|
|
2354
|
+
}
|
|
2355
|
+
if (toolsFixed > 0) {
|
|
2356
|
+
console.log(success(` Fixed ${toolsFixed} stale config(s).`));
|
|
2357
|
+
console.log();
|
|
2358
|
+
}
|
|
2359
|
+
if (result.issues === 0) {
|
|
2360
|
+
console.log(success(" All checks passed."));
|
|
2361
|
+
} else {
|
|
2362
|
+
console.log(warning(` ${result.issues} issue(s) found.`));
|
|
2363
|
+
const staleTools = result.tools.filter((t) => t.keyMatch === "stale" && !t.fixed);
|
|
2364
|
+
if (staleTools.length > 0 && !options.fix) {
|
|
2365
|
+
console.log();
|
|
2366
|
+
console.log(dim(" Fix stale configs: ") + chalk9.cyan("piut doctor --fix"));
|
|
2367
|
+
}
|
|
2368
|
+
if (!result.key.valid) {
|
|
2369
|
+
console.log(dim(" Set a valid key: ") + chalk9.cyan("piut setup --key pb_YOUR_KEY"));
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
console.log();
|
|
2373
|
+
if (result.issues > 0) throw new CliError(`${result.issues} issue(s) found`);
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
// src/commands/interactive.ts
|
|
2377
|
+
import { select as select2, confirm as confirm6, checkbox as checkbox6, password as password3 } from "@inquirer/prompts";
|
|
2378
|
+
import fs12 from "fs";
|
|
1830
2379
|
import path12 from "path";
|
|
1831
|
-
import
|
|
2380
|
+
import chalk10 from "chalk";
|
|
1832
2381
|
async function authenticate() {
|
|
1833
2382
|
const config = readStore();
|
|
1834
2383
|
let apiKey = config.apiKey;
|
|
@@ -1856,7 +2405,7 @@ async function authenticate() {
|
|
|
1856
2405
|
try {
|
|
1857
2406
|
result = await validateKey(apiKey);
|
|
1858
2407
|
} catch (err) {
|
|
1859
|
-
console.log(
|
|
2408
|
+
console.log(chalk10.red(` \u2717 ${err.message}`));
|
|
1860
2409
|
console.log(dim(" Get a key at https://piut.com/dashboard/keys"));
|
|
1861
2410
|
process.exit(1);
|
|
1862
2411
|
}
|
|
@@ -1879,7 +2428,7 @@ async function interactiveMenu() {
|
|
|
1879
2428
|
console.log(warning(" You haven\u2019t built a brain yet."));
|
|
1880
2429
|
console.log(dim(" Your brain is how AI tools learn about you \u2014 your projects, preferences, and context."));
|
|
1881
2430
|
console.log();
|
|
1882
|
-
const wantBuild = await
|
|
2431
|
+
const wantBuild = await confirm6({
|
|
1883
2432
|
message: "Build your brain now?",
|
|
1884
2433
|
default: true
|
|
1885
2434
|
});
|
|
@@ -1895,7 +2444,7 @@ async function interactiveMenu() {
|
|
|
1895
2444
|
console.log(warning(" Your brain is built but not deployed yet."));
|
|
1896
2445
|
console.log(dim(" Deploy it to make your MCP server live so AI tools can read your brain."));
|
|
1897
2446
|
console.log();
|
|
1898
|
-
const wantDeploy = await
|
|
2447
|
+
const wantDeploy = await confirm6({
|
|
1899
2448
|
message: "Deploy your brain now?",
|
|
1900
2449
|
default: true
|
|
1901
2450
|
});
|
|
@@ -2015,7 +2564,7 @@ async function interactiveMenu() {
|
|
|
2015
2564
|
} else if (err instanceof CliError) {
|
|
2016
2565
|
console.log();
|
|
2017
2566
|
} else {
|
|
2018
|
-
console.log(
|
|
2567
|
+
console.log(chalk10.red(` Error: ${err.message}`));
|
|
2019
2568
|
console.log();
|
|
2020
2569
|
}
|
|
2021
2570
|
}
|
|
@@ -2026,7 +2575,7 @@ async function interactiveMenu() {
|
|
|
2026
2575
|
}
|
|
2027
2576
|
}
|
|
2028
2577
|
async function handleUndeploy(apiKey) {
|
|
2029
|
-
const confirmed = await
|
|
2578
|
+
const confirmed = await confirm6({
|
|
2030
2579
|
message: "Undeploy your brain? AI tools will lose access to your MCP server.",
|
|
2031
2580
|
default: false
|
|
2032
2581
|
});
|
|
@@ -2038,7 +2587,7 @@ async function handleUndeploy(apiKey) {
|
|
|
2038
2587
|
console.log(dim(" Run ") + brand("piut deploy") + dim(" to re-deploy anytime."));
|
|
2039
2588
|
console.log();
|
|
2040
2589
|
} catch (err) {
|
|
2041
|
-
console.log(
|
|
2590
|
+
console.log(chalk10.red(` \u2717 ${err.message}`));
|
|
2042
2591
|
}
|
|
2043
2592
|
}
|
|
2044
2593
|
async function handleConnectTools(apiKey, validation) {
|
|
@@ -2048,8 +2597,8 @@ async function handleConnectTools(apiKey, validation) {
|
|
|
2048
2597
|
for (const tool of TOOLS) {
|
|
2049
2598
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
2050
2599
|
for (const configPath of paths) {
|
|
2051
|
-
const exists =
|
|
2052
|
-
const parentExists =
|
|
2600
|
+
const exists = fs12.existsSync(configPath);
|
|
2601
|
+
const parentExists = fs12.existsSync(path12.dirname(configPath));
|
|
2053
2602
|
if (exists || parentExists) {
|
|
2054
2603
|
if (exists && isPiutConfigured(configPath, tool.configKey)) {
|
|
2055
2604
|
alreadyConnected.push(tool.name);
|
|
@@ -2102,7 +2651,7 @@ async function handleDisconnectTools() {
|
|
|
2102
2651
|
for (const tool of TOOLS) {
|
|
2103
2652
|
const paths = resolveConfigPaths(tool.configPaths);
|
|
2104
2653
|
for (const configPath of paths) {
|
|
2105
|
-
if (
|
|
2654
|
+
if (fs12.existsSync(configPath) && isPiutConfigured(configPath, tool.configKey)) {
|
|
2106
2655
|
configured.push({ tool, configPath });
|
|
2107
2656
|
break;
|
|
2108
2657
|
}
|
|
@@ -2125,7 +2674,7 @@ async function handleDisconnectTools() {
|
|
|
2125
2674
|
console.log(dim(" No tools selected."));
|
|
2126
2675
|
return;
|
|
2127
2676
|
}
|
|
2128
|
-
const proceed = await
|
|
2677
|
+
const proceed = await confirm6({
|
|
2129
2678
|
message: `Disconnect p\u0131ut from ${selected.length} tool(s)?`,
|
|
2130
2679
|
default: false
|
|
2131
2680
|
});
|
|
@@ -2144,70 +2693,12 @@ async function handleDisconnectTools() {
|
|
|
2144
2693
|
console.log();
|
|
2145
2694
|
}
|
|
2146
2695
|
|
|
2147
|
-
// src/lib/update-check.ts
|
|
2148
|
-
import { execFile } from "child_process";
|
|
2149
|
-
import chalk7 from "chalk";
|
|
2150
|
-
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
2151
|
-
var PACKAGE_NAME = "@piut/cli";
|
|
2152
|
-
async function getLatestVersion() {
|
|
2153
|
-
try {
|
|
2154
|
-
const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`);
|
|
2155
|
-
if (!res.ok) return null;
|
|
2156
|
-
const data = await res.json();
|
|
2157
|
-
return data.version ?? null;
|
|
2158
|
-
} catch {
|
|
2159
|
-
return null;
|
|
2160
|
-
}
|
|
2161
|
-
}
|
|
2162
|
-
function isNewer(current, latest) {
|
|
2163
|
-
const [cMaj, cMin, cPat] = current.split(".").map(Number);
|
|
2164
|
-
const [lMaj, lMin, lPat] = latest.split(".").map(Number);
|
|
2165
|
-
if (lMaj !== cMaj) return lMaj > cMaj;
|
|
2166
|
-
if (lMin !== cMin) return lMin > cMin;
|
|
2167
|
-
return lPat > cPat;
|
|
2168
|
-
}
|
|
2169
|
-
function runUpdate() {
|
|
2170
|
-
return new Promise((resolve) => {
|
|
2171
|
-
execFile("npm", ["install", "-g", `${PACKAGE_NAME}@latest`], { timeout: 6e4 }, (err) => {
|
|
2172
|
-
resolve(!err);
|
|
2173
|
-
});
|
|
2174
|
-
});
|
|
2175
|
-
}
|
|
2176
|
-
async function checkForUpdate(currentVersion) {
|
|
2177
|
-
const latest = await getLatestVersion();
|
|
2178
|
-
if (!latest || !isNewer(currentVersion, latest)) return;
|
|
2179
|
-
console.log();
|
|
2180
|
-
console.log(brand(" Update available!") + dim(` ${currentVersion} \u2192 ${latest}`));
|
|
2181
|
-
console.log(dim(` Run ${chalk7.bold(`npm install -g ${PACKAGE_NAME}@latest`)} to update`));
|
|
2182
|
-
console.log();
|
|
2183
|
-
try {
|
|
2184
|
-
const shouldUpdate = await confirm6({
|
|
2185
|
-
message: `Update to v${latest} now?`,
|
|
2186
|
-
default: true
|
|
2187
|
-
});
|
|
2188
|
-
if (shouldUpdate) {
|
|
2189
|
-
console.log(dim(" Updating..."));
|
|
2190
|
-
const ok = await runUpdate();
|
|
2191
|
-
if (ok) {
|
|
2192
|
-
console.log(chalk7.green(` \u2713 Updated to v${latest}`));
|
|
2193
|
-
console.log(dim(" Restart the CLI to use the new version."));
|
|
2194
|
-
process.exit(0);
|
|
2195
|
-
} else {
|
|
2196
|
-
console.log(chalk7.yellow(` Could not auto-update. Run manually:`));
|
|
2197
|
-
console.log(chalk7.bold(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
2198
|
-
console.log();
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
} catch {
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2205
2696
|
// src/cli.ts
|
|
2206
|
-
var VERSION = "3.
|
|
2697
|
+
var VERSION = "3.4.0";
|
|
2207
2698
|
function withExit(fn) {
|
|
2208
|
-
return async (...
|
|
2699
|
+
return async (...args2) => {
|
|
2209
2700
|
try {
|
|
2210
|
-
await fn(...
|
|
2701
|
+
await fn(...args2);
|
|
2211
2702
|
} catch (err) {
|
|
2212
2703
|
if (err instanceof CliError) process.exit(1);
|
|
2213
2704
|
throw err;
|
|
@@ -2215,13 +2706,24 @@ function withExit(fn) {
|
|
|
2215
2706
|
};
|
|
2216
2707
|
}
|
|
2217
2708
|
var program = new Command();
|
|
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", () =>
|
|
2709
|
+
program.name("piut").description("Build your AI brain instantly. Deploy it as an MCP server. Connect it to every project.").version(VERSION).hook("preAction", (thisCommand, actionCommand) => {
|
|
2710
|
+
if (actionCommand.name() === "update") return;
|
|
2711
|
+
return checkForUpdate(VERSION);
|
|
2712
|
+
}).action(interactiveMenu);
|
|
2219
2713
|
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
2714
|
program.command("deploy").description("Publish your MCP server (requires paid account)").option("-k, --key <key>", "API key").action(withExit(deployCommand));
|
|
2221
2715
|
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
2716
|
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
2717
|
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));
|
|
2224
|
-
program.command("status").description("Show brain, deployment, and connected projects").action(statusCommand);
|
|
2718
|
+
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));
|
|
2225
2719
|
program.command("remove").description("Remove all p\u0131ut configurations").action(withExit(removeCommand));
|
|
2226
2720
|
program.command("logout").description("Remove saved API key").action(logoutCommand);
|
|
2721
|
+
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));
|
|
2722
|
+
program.command("update").description("Check for and install CLI updates").action(() => updateCommand(VERSION));
|
|
2723
|
+
var args = process.argv.slice(2);
|
|
2724
|
+
if (args.includes("--version") || args.includes("-V")) {
|
|
2725
|
+
console.log(VERSION);
|
|
2726
|
+
await checkForUpdate(VERSION);
|
|
2727
|
+
process.exit(0);
|
|
2728
|
+
}
|
|
2227
2729
|
program.parse();
|