@piut/cli 3.8.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli.js +583 -236
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -28,9 +28,9 @@ __export(tree_prompt_exports, {
|
|
|
28
28
|
loadChildren: () => loadChildren,
|
|
29
29
|
shouldShowInTree: () => shouldShowInTree
|
|
30
30
|
});
|
|
31
|
-
import
|
|
32
|
-
import
|
|
33
|
-
import
|
|
31
|
+
import fs15 from "fs";
|
|
32
|
+
import path16 from "path";
|
|
33
|
+
import os9 from "os";
|
|
34
34
|
import {
|
|
35
35
|
createPrompt,
|
|
36
36
|
useState,
|
|
@@ -50,19 +50,19 @@ function shouldShowInTree(name) {
|
|
|
50
50
|
}
|
|
51
51
|
function shouldShowFile(name) {
|
|
52
52
|
if (name.startsWith(".")) return false;
|
|
53
|
-
const ext =
|
|
53
|
+
const ext = path16.extname(name).toLowerCase();
|
|
54
54
|
return !HIDDEN_FILE_EXTENSIONS.has(ext);
|
|
55
55
|
}
|
|
56
56
|
function loadChildren(parentPath, parentDepth, includeFiles = false) {
|
|
57
57
|
try {
|
|
58
|
-
const entries =
|
|
58
|
+
const entries = fs15.readdirSync(parentPath, { withFileTypes: true });
|
|
59
59
|
const dirs = [];
|
|
60
60
|
const files = [];
|
|
61
61
|
for (const entry of entries) {
|
|
62
62
|
if (entry.isDirectory()) {
|
|
63
63
|
if (!shouldShowInTree(entry.name)) continue;
|
|
64
64
|
dirs.push({
|
|
65
|
-
path:
|
|
65
|
+
path: path16.join(parentPath, entry.name),
|
|
66
66
|
name: entry.name,
|
|
67
67
|
depth: parentDepth + 1,
|
|
68
68
|
expanded: false,
|
|
@@ -72,7 +72,7 @@ function loadChildren(parentPath, parentDepth, includeFiles = false) {
|
|
|
72
72
|
} else if (includeFiles && entry.isFile()) {
|
|
73
73
|
if (!shouldShowFile(entry.name)) continue;
|
|
74
74
|
files.push({
|
|
75
|
-
path:
|
|
75
|
+
path: path16.join(parentPath, entry.name),
|
|
76
76
|
name: entry.name,
|
|
77
77
|
depth: parentDepth + 1,
|
|
78
78
|
expanded: false,
|
|
@@ -214,7 +214,7 @@ var init_tree_prompt = __esm({
|
|
|
214
214
|
".map"
|
|
215
215
|
]);
|
|
216
216
|
treePrompt = createPrompt((config, done) => {
|
|
217
|
-
const root = config.root ??
|
|
217
|
+
const root = config.root ?? os9.homedir();
|
|
218
218
|
const pageSize = config.pageSize ?? 15;
|
|
219
219
|
const mode = config.mode ?? "folders";
|
|
220
220
|
const includeFiles = mode === "files";
|
|
@@ -325,9 +325,9 @@ import { Command } from "commander";
|
|
|
325
325
|
|
|
326
326
|
// src/commands/setup.ts
|
|
327
327
|
init_esm_shims();
|
|
328
|
-
import
|
|
329
|
-
import
|
|
330
|
-
import { execSync } from "child_process";
|
|
328
|
+
import fs6 from "fs";
|
|
329
|
+
import path9 from "path";
|
|
330
|
+
import { execSync as execSync2 } from "child_process";
|
|
331
331
|
import { password, confirm, checkbox } from "@inquirer/prompts";
|
|
332
332
|
import chalk2 from "chalk";
|
|
333
333
|
|
|
@@ -384,29 +384,33 @@ async function* buildBrainStreaming(key, input2) {
|
|
|
384
384
|
const reader = res.body.getReader();
|
|
385
385
|
const decoder = new TextDecoder();
|
|
386
386
|
let buffer = "";
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
387
|
+
try {
|
|
388
|
+
while (true) {
|
|
389
|
+
const { done, value } = await reader.read();
|
|
390
|
+
if (done) break;
|
|
391
|
+
buffer += decoder.decode(value, { stream: true });
|
|
392
|
+
const parts = buffer.split("\n\n");
|
|
393
|
+
buffer = parts.pop() || "";
|
|
394
|
+
for (const part of parts) {
|
|
395
|
+
let eventName = "";
|
|
396
|
+
let eventData = "";
|
|
397
|
+
for (const line of part.split("\n")) {
|
|
398
|
+
if (line.startsWith("event: ")) {
|
|
399
|
+
eventName = line.slice(7).trim();
|
|
400
|
+
} else if (line.startsWith("data: ")) {
|
|
401
|
+
eventData = line.slice(6);
|
|
402
|
+
}
|
|
401
403
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
404
|
+
if (eventName && eventData) {
|
|
405
|
+
try {
|
|
406
|
+
yield { event: eventName, data: JSON.parse(eventData) };
|
|
407
|
+
} catch {
|
|
408
|
+
}
|
|
407
409
|
}
|
|
408
410
|
}
|
|
409
411
|
}
|
|
412
|
+
} catch {
|
|
413
|
+
yield { event: "error", data: { message: "Connection lost. The build may still complete \u2014 run `piut status` to check." } };
|
|
410
414
|
}
|
|
411
415
|
}
|
|
412
416
|
async function verifyMcpEndpoint(serverUrl, key) {
|
|
@@ -637,7 +641,7 @@ var TOOLS = [
|
|
|
637
641
|
project: [".mcp.json"]
|
|
638
642
|
},
|
|
639
643
|
skillFilePath: "CLAUDE.md",
|
|
640
|
-
quickCommand: (slug, key) => `claude mcp add-json piut
|
|
644
|
+
quickCommand: (slug, key) => `claude mcp add-json piut '${JSON.stringify({
|
|
641
645
|
type: "http",
|
|
642
646
|
url: MCP_URL(slug),
|
|
643
647
|
headers: { ...AUTH_HEADER(key), ...machineHeaders("Claude Code") }
|
|
@@ -700,21 +704,20 @@ var TOOLS = [
|
|
|
700
704
|
})
|
|
701
705
|
},
|
|
702
706
|
{
|
|
703
|
-
id: "
|
|
704
|
-
name: "
|
|
707
|
+
id: "vscode",
|
|
708
|
+
name: "VS Code",
|
|
705
709
|
configKey: "servers",
|
|
706
|
-
globalConfigKey: "mcp.servers",
|
|
707
710
|
configPaths: {
|
|
708
|
-
darwin: ["~/Library/Application Support/Code/User/
|
|
709
|
-
win32: [path2.join(appData(), "Code", "User", "
|
|
710
|
-
linux: ["~/.config/Code/User/
|
|
711
|
+
darwin: ["~/Library/Application Support/Code/User/mcp.json"],
|
|
712
|
+
win32: [path2.join(appData(), "Code", "User", "mcp.json")],
|
|
713
|
+
linux: ["~/.config/Code/User/mcp.json"],
|
|
711
714
|
project: [".vscode/mcp.json"]
|
|
712
715
|
},
|
|
713
716
|
skillFilePath: ".github/copilot-instructions.md",
|
|
714
717
|
generateConfig: (slug, key) => ({
|
|
715
718
|
type: "http",
|
|
716
719
|
url: MCP_URL(slug),
|
|
717
|
-
headers: { ...AUTH_HEADER(key), ...machineHeaders("
|
|
720
|
+
headers: { ...AUTH_HEADER(key), ...machineHeaders("VS Code") }
|
|
718
721
|
})
|
|
719
722
|
},
|
|
720
723
|
{
|
|
@@ -773,7 +776,7 @@ var TOOLS = [
|
|
|
773
776
|
win32: ["~/.mcporter/mcporter.json", "~/.openclaw/workspace/config/mcporter.json"],
|
|
774
777
|
linux: ["~/.mcporter/mcporter.json", "~/.openclaw/workspace/config/mcporter.json"]
|
|
775
778
|
},
|
|
776
|
-
quickCommand: (slug, key) => `npx mcporter config add piut
|
|
779
|
+
quickCommand: (slug, key) => `npx mcporter config add piut ${MCP_URL(slug)} --header "Authorization=Bearer ${key}"`,
|
|
777
780
|
generateConfig: (slug, key) => ({
|
|
778
781
|
url: MCP_URL(slug),
|
|
779
782
|
headers: { ...AUTH_HEADER(key), ...machineHeaders("OpenClaw") }
|
|
@@ -817,6 +820,8 @@ function resolveConfigPaths(tool) {
|
|
|
817
820
|
init_esm_shims();
|
|
818
821
|
import fs from "fs";
|
|
819
822
|
import path4 from "path";
|
|
823
|
+
var SERVER_KEY = "piut";
|
|
824
|
+
var LEGACY_SERVER_KEY = "piut-context";
|
|
820
825
|
function readConfig(filePath) {
|
|
821
826
|
let raw;
|
|
822
827
|
try {
|
|
@@ -864,12 +869,13 @@ function isPiutConfigured(filePath, configKey) {
|
|
|
864
869
|
const config = readConfig(filePath);
|
|
865
870
|
if (!config) return false;
|
|
866
871
|
const servers = resolveKeyPath(config, configKey);
|
|
867
|
-
return !!servers?.[
|
|
872
|
+
return !!(servers?.[SERVER_KEY] || servers?.[LEGACY_SERVER_KEY]);
|
|
868
873
|
}
|
|
869
874
|
function mergeConfig(filePath, configKey, serverConfig) {
|
|
870
875
|
const existing = readConfig(filePath) || {};
|
|
871
876
|
const servers = resolveKeyPath(existing, configKey) || {};
|
|
872
|
-
servers[
|
|
877
|
+
delete servers[LEGACY_SERVER_KEY];
|
|
878
|
+
servers[SERVER_KEY] = serverConfig;
|
|
873
879
|
setAtKeyPath(existing, configKey, servers);
|
|
874
880
|
writeConfig(filePath, existing);
|
|
875
881
|
}
|
|
@@ -877,7 +883,7 @@ function getPiutConfig(filePath, configKey) {
|
|
|
877
883
|
const config = readConfig(filePath);
|
|
878
884
|
if (!config) return null;
|
|
879
885
|
const servers = resolveKeyPath(config, configKey);
|
|
880
|
-
const piut = servers?.[
|
|
886
|
+
const piut = servers?.[SERVER_KEY] || servers?.[LEGACY_SERVER_KEY];
|
|
881
887
|
return piut ?? null;
|
|
882
888
|
}
|
|
883
889
|
function extractKeyFromConfig(piutConfig) {
|
|
@@ -929,8 +935,11 @@ function removeFromConfig(filePath, configKey) {
|
|
|
929
935
|
const config = readConfig(filePath);
|
|
930
936
|
if (!config) return false;
|
|
931
937
|
const servers = resolveKeyPath(config, configKey);
|
|
932
|
-
|
|
933
|
-
|
|
938
|
+
const hasNew = !!servers?.[SERVER_KEY];
|
|
939
|
+
const hasLegacy = !!servers?.[LEGACY_SERVER_KEY];
|
|
940
|
+
if (!hasNew && !hasLegacy) return false;
|
|
941
|
+
delete servers[SERVER_KEY];
|
|
942
|
+
delete servers[LEGACY_SERVER_KEY];
|
|
934
943
|
if (Object.keys(servers).length === 0 && !configKey.includes(".")) {
|
|
935
944
|
delete config[configKey];
|
|
936
945
|
}
|
|
@@ -942,7 +951,7 @@ function removeFromConfig(filePath, configKey) {
|
|
|
942
951
|
init_esm_shims();
|
|
943
952
|
import fs2 from "fs";
|
|
944
953
|
import path5 from "path";
|
|
945
|
-
var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut
|
|
954
|
+
var SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut)
|
|
946
955
|
|
|
947
956
|
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
948
957
|
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
@@ -962,7 +971,7 @@ p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use
|
|
|
962
971
|
4. Never read .piut/config.json directly \u2014 always use the MCP tools
|
|
963
972
|
|
|
964
973
|
Skill reference: https://raw.githubusercontent.com/M-Flat-Inc/piut/main/skill.md`;
|
|
965
|
-
var PROJECT_SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut
|
|
974
|
+
var PROJECT_SKILL_SNIPPET = `## p\u0131ut Context (MCP Server: piut)
|
|
966
975
|
|
|
967
976
|
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
968
977
|
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
@@ -1058,6 +1067,16 @@ function writePiutConfig(projectPath, config) {
|
|
|
1058
1067
|
"utf-8"
|
|
1059
1068
|
);
|
|
1060
1069
|
}
|
|
1070
|
+
function readPiutConfig(projectPath) {
|
|
1071
|
+
try {
|
|
1072
|
+
const raw = fs3.readFileSync(path6.join(piutDir(projectPath), CONFIG_FILE), "utf-8");
|
|
1073
|
+
const parsed = JSON.parse(raw);
|
|
1074
|
+
if (parsed.slug && parsed.apiKey && parsed.serverUrl) return parsed;
|
|
1075
|
+
return null;
|
|
1076
|
+
} catch {
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1061
1080
|
async function writePiutSkill(projectPath, slug, apiKey) {
|
|
1062
1081
|
const dir = piutDir(projectPath);
|
|
1063
1082
|
fs3.mkdirSync(dir, { recursive: true });
|
|
@@ -1101,6 +1120,42 @@ function hasPiutDir(projectPath) {
|
|
|
1101
1120
|
return fs3.existsSync(path6.join(piutDir(projectPath), CONFIG_FILE));
|
|
1102
1121
|
}
|
|
1103
1122
|
|
|
1123
|
+
// src/lib/global-install.ts
|
|
1124
|
+
init_esm_shims();
|
|
1125
|
+
import { execSync } from "child_process";
|
|
1126
|
+
import fs5 from "fs";
|
|
1127
|
+
import path8 from "path";
|
|
1128
|
+
import os5 from "os";
|
|
1129
|
+
|
|
1130
|
+
// src/lib/store.ts
|
|
1131
|
+
init_esm_shims();
|
|
1132
|
+
import fs4 from "fs";
|
|
1133
|
+
import path7 from "path";
|
|
1134
|
+
import os4 from "os";
|
|
1135
|
+
var CONFIG_DIR = path7.join(os4.homedir(), ".piut");
|
|
1136
|
+
var CONFIG_FILE2 = path7.join(CONFIG_DIR, "config.json");
|
|
1137
|
+
function readStore() {
|
|
1138
|
+
try {
|
|
1139
|
+
const raw = fs4.readFileSync(CONFIG_FILE2, "utf-8");
|
|
1140
|
+
return JSON.parse(raw);
|
|
1141
|
+
} catch {
|
|
1142
|
+
return {};
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
function updateStore(updates) {
|
|
1146
|
+
const config = readStore();
|
|
1147
|
+
const updated = { ...config, ...updates };
|
|
1148
|
+
fs4.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1149
|
+
fs4.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
1150
|
+
return updated;
|
|
1151
|
+
}
|
|
1152
|
+
function clearStore() {
|
|
1153
|
+
try {
|
|
1154
|
+
fs4.unlinkSync(CONFIG_FILE2);
|
|
1155
|
+
} catch {
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1104
1159
|
// src/lib/ui.ts
|
|
1105
1160
|
init_esm_shims();
|
|
1106
1161
|
import chalk from "chalk";
|
|
@@ -1187,6 +1242,65 @@ var Spinner = class {
|
|
|
1187
1242
|
}
|
|
1188
1243
|
};
|
|
1189
1244
|
|
|
1245
|
+
// src/lib/global-install.ts
|
|
1246
|
+
function isPiutInPath() {
|
|
1247
|
+
try {
|
|
1248
|
+
execSync(process.platform === "win32" ? "where piut" : "which piut", { stdio: "pipe" });
|
|
1249
|
+
return true;
|
|
1250
|
+
} catch {
|
|
1251
|
+
return false;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
function getShellProfile() {
|
|
1255
|
+
const shell = process.env.SHELL || "";
|
|
1256
|
+
const home2 = os5.homedir();
|
|
1257
|
+
if (shell.includes("zsh")) {
|
|
1258
|
+
return path8.join(home2, ".zshrc");
|
|
1259
|
+
}
|
|
1260
|
+
if (shell.includes("bash")) {
|
|
1261
|
+
const profile = path8.join(home2, ".bash_profile");
|
|
1262
|
+
if (process.platform === "darwin" && fs5.existsSync(profile)) {
|
|
1263
|
+
return profile;
|
|
1264
|
+
}
|
|
1265
|
+
return path8.join(home2, ".bashrc");
|
|
1266
|
+
}
|
|
1267
|
+
return null;
|
|
1268
|
+
}
|
|
1269
|
+
function addShellAlias(profilePath) {
|
|
1270
|
+
try {
|
|
1271
|
+
const content = fs5.existsSync(profilePath) ? fs5.readFileSync(profilePath, "utf-8") : "";
|
|
1272
|
+
if (content.includes("alias piut=") || content.includes("piut()")) {
|
|
1273
|
+
return true;
|
|
1274
|
+
}
|
|
1275
|
+
const alias = '\n# p\u0131ut CLI shortcut\nalias piut="npx @piut/cli"\n';
|
|
1276
|
+
fs5.appendFileSync(profilePath, alias);
|
|
1277
|
+
return true;
|
|
1278
|
+
} catch {
|
|
1279
|
+
return false;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
async function offerGlobalInstall() {
|
|
1283
|
+
if (isPiutInPath()) return;
|
|
1284
|
+
const store = readStore();
|
|
1285
|
+
if (store.globalInstallOffered) return;
|
|
1286
|
+
updateStore({ globalInstallOffered: true });
|
|
1287
|
+
try {
|
|
1288
|
+
execSync("npm install -g @piut/cli", { stdio: "pipe", timeout: 3e4 });
|
|
1289
|
+
if (isPiutInPath()) {
|
|
1290
|
+
console.log(dim(" Installed `piut` command for quick access"));
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
} catch {
|
|
1294
|
+
}
|
|
1295
|
+
const profile = getShellProfile();
|
|
1296
|
+
if (profile) {
|
|
1297
|
+
if (addShellAlias(profile)) {
|
|
1298
|
+
console.log(dim(` Added \`piut\` shortcut to ${path8.basename(profile)}`));
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1190
1304
|
// src/types.ts
|
|
1191
1305
|
init_esm_shims();
|
|
1192
1306
|
var CliError = class extends Error {
|
|
@@ -1242,8 +1356,8 @@ async function setupCommand(options) {
|
|
|
1242
1356
|
if (tool.skillOnly) continue;
|
|
1243
1357
|
const paths = resolveConfigPaths(tool);
|
|
1244
1358
|
for (const { filePath, configKey } of paths) {
|
|
1245
|
-
const exists =
|
|
1246
|
-
const parentExists =
|
|
1359
|
+
const exists = fs6.existsSync(filePath);
|
|
1360
|
+
const parentExists = fs6.existsSync(path9.dirname(filePath));
|
|
1247
1361
|
if (exists || parentExists) {
|
|
1248
1362
|
const configured2 = exists && !!configKey && isPiutConfigured(filePath, configKey);
|
|
1249
1363
|
let staleKey = false;
|
|
@@ -1323,7 +1437,7 @@ async function setupCommand(options) {
|
|
|
1323
1437
|
if (tool.id === "claude-code" && tool.quickCommand && isCommandAvailable("claude")) {
|
|
1324
1438
|
let quickSuccess = false;
|
|
1325
1439
|
try {
|
|
1326
|
-
|
|
1440
|
+
execSync2(tool.quickCommand(slug, apiKey), { stdio: "pipe" });
|
|
1327
1441
|
const claudeJson = expandPath("~/.claude.json");
|
|
1328
1442
|
const written = tool.configKey ? getPiutConfig(claudeJson, tool.configKey) : null;
|
|
1329
1443
|
if (written) {
|
|
@@ -1333,7 +1447,7 @@ async function setupCommand(options) {
|
|
|
1333
1447
|
continue;
|
|
1334
1448
|
}
|
|
1335
1449
|
try {
|
|
1336
|
-
|
|
1450
|
+
execSync2(tool.quickCommand(slug, apiKey) + " --scope user", { stdio: "pipe" });
|
|
1337
1451
|
const retryCheck = tool.configKey ? getPiutConfig(claudeJson, tool.configKey) : null;
|
|
1338
1452
|
if (retryCheck) {
|
|
1339
1453
|
quickSuccess = true;
|
|
@@ -1383,7 +1497,7 @@ async function setupCommand(options) {
|
|
|
1383
1497
|
}
|
|
1384
1498
|
if (configured.length > 0) {
|
|
1385
1499
|
const cwd = process.cwd();
|
|
1386
|
-
const isProject2 =
|
|
1500
|
+
const isProject2 = fs6.existsSync(path9.join(cwd, ".git")) || fs6.existsSync(path9.join(cwd, "package.json"));
|
|
1387
1501
|
if (isProject2) {
|
|
1388
1502
|
const { serverUrl } = validationResult;
|
|
1389
1503
|
writePiutConfig(cwd, { slug, apiKey, serverUrl });
|
|
@@ -1425,6 +1539,7 @@ async function setupCommand(options) {
|
|
|
1425
1539
|
if (skipped.length > 0) {
|
|
1426
1540
|
console.log(dim(` Skipped: ${skipped.join(", ")}`));
|
|
1427
1541
|
}
|
|
1542
|
+
await offerGlobalInstall();
|
|
1428
1543
|
console.log();
|
|
1429
1544
|
console.log(dim(" Restart your AI tools for changes to take effect."));
|
|
1430
1545
|
console.log(dim(' Verify: ask any AI "What do you know about me from my context?"'));
|
|
@@ -1433,7 +1548,7 @@ async function setupCommand(options) {
|
|
|
1433
1548
|
}
|
|
1434
1549
|
function isCommandAvailable(cmd) {
|
|
1435
1550
|
try {
|
|
1436
|
-
|
|
1551
|
+
execSync2(process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`, { stdio: "pipe" });
|
|
1437
1552
|
return true;
|
|
1438
1553
|
} catch {
|
|
1439
1554
|
return false;
|
|
@@ -1442,16 +1557,16 @@ function isCommandAvailable(cmd) {
|
|
|
1442
1557
|
|
|
1443
1558
|
// src/commands/status.ts
|
|
1444
1559
|
init_esm_shims();
|
|
1445
|
-
import
|
|
1446
|
-
import
|
|
1447
|
-
import
|
|
1560
|
+
import fs8 from "fs";
|
|
1561
|
+
import os7 from "os";
|
|
1562
|
+
import path11 from "path";
|
|
1448
1563
|
import chalk3 from "chalk";
|
|
1449
1564
|
|
|
1450
1565
|
// src/lib/brain-scanner.ts
|
|
1451
1566
|
init_esm_shims();
|
|
1452
|
-
import
|
|
1453
|
-
import
|
|
1454
|
-
import
|
|
1567
|
+
import fs7 from "fs";
|
|
1568
|
+
import path10 from "path";
|
|
1569
|
+
import os6 from "os";
|
|
1455
1570
|
|
|
1456
1571
|
// src/lib/file-types.ts
|
|
1457
1572
|
init_esm_shims();
|
|
@@ -1470,7 +1585,7 @@ var AI_CONFIG_FILENAMES = /* @__PURE__ */ new Set([
|
|
|
1470
1585
|
]);
|
|
1471
1586
|
|
|
1472
1587
|
// src/lib/brain-scanner.ts
|
|
1473
|
-
var home =
|
|
1588
|
+
var home = os6.homedir();
|
|
1474
1589
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1475
1590
|
"node_modules",
|
|
1476
1591
|
".git",
|
|
@@ -1534,22 +1649,22 @@ var INCLUDE_DOT_DIRS = /* @__PURE__ */ new Set([
|
|
|
1534
1649
|
function getDefaultScanDirs() {
|
|
1535
1650
|
const dirs = [];
|
|
1536
1651
|
try {
|
|
1537
|
-
const entries =
|
|
1652
|
+
const entries = fs7.readdirSync(home, { withFileTypes: true });
|
|
1538
1653
|
for (const entry of entries) {
|
|
1539
1654
|
if (!entry.isDirectory()) continue;
|
|
1540
1655
|
if (entry.name.startsWith(".") && !INCLUDE_DOT_DIRS.has(entry.name)) continue;
|
|
1541
1656
|
if (SKIP_HOME_DIRS.has(entry.name)) continue;
|
|
1542
|
-
dirs.push(
|
|
1657
|
+
dirs.push(path10.join(home, entry.name));
|
|
1543
1658
|
}
|
|
1544
1659
|
} catch {
|
|
1545
1660
|
}
|
|
1546
|
-
const cloudStorage =
|
|
1661
|
+
const cloudStorage = path10.join(home, "Library", "CloudStorage");
|
|
1547
1662
|
try {
|
|
1548
|
-
if (
|
|
1549
|
-
const entries =
|
|
1663
|
+
if (fs7.existsSync(cloudStorage) && fs7.statSync(cloudStorage).isDirectory()) {
|
|
1664
|
+
const entries = fs7.readdirSync(cloudStorage, { withFileTypes: true });
|
|
1550
1665
|
for (const entry of entries) {
|
|
1551
1666
|
if (!entry.isDirectory()) continue;
|
|
1552
|
-
const fullPath =
|
|
1667
|
+
const fullPath = path10.join(cloudStorage, entry.name);
|
|
1553
1668
|
if (!dirs.includes(fullPath)) {
|
|
1554
1669
|
dirs.push(fullPath);
|
|
1555
1670
|
}
|
|
@@ -1561,22 +1676,22 @@ function getDefaultScanDirs() {
|
|
|
1561
1676
|
return dirs;
|
|
1562
1677
|
}
|
|
1563
1678
|
function isProject(dirPath) {
|
|
1564
|
-
return
|
|
1679
|
+
return fs7.existsSync(path10.join(dirPath, ".git")) || fs7.existsSync(path10.join(dirPath, "package.json")) || fs7.existsSync(path10.join(dirPath, "Cargo.toml")) || fs7.existsSync(path10.join(dirPath, "pyproject.toml")) || fs7.existsSync(path10.join(dirPath, "go.mod"));
|
|
1565
1680
|
}
|
|
1566
1681
|
function buildProjectInfo(projectPath) {
|
|
1567
|
-
const hasPkgJson =
|
|
1682
|
+
const hasPkgJson = fs7.existsSync(path10.join(projectPath, "package.json"));
|
|
1568
1683
|
let description = "";
|
|
1569
1684
|
if (hasPkgJson) {
|
|
1570
1685
|
try {
|
|
1571
|
-
const pkg = JSON.parse(
|
|
1686
|
+
const pkg = JSON.parse(fs7.readFileSync(path10.join(projectPath, "package.json"), "utf-8"));
|
|
1572
1687
|
description = pkg.description || "";
|
|
1573
1688
|
} catch {
|
|
1574
1689
|
}
|
|
1575
1690
|
}
|
|
1576
|
-
const readmePath =
|
|
1577
|
-
if (!description &&
|
|
1691
|
+
const readmePath = path10.join(projectPath, "README.md");
|
|
1692
|
+
if (!description && fs7.existsSync(readmePath)) {
|
|
1578
1693
|
try {
|
|
1579
|
-
const content =
|
|
1694
|
+
const content = fs7.readFileSync(readmePath, "utf-8");
|
|
1580
1695
|
const lines = content.split("\n");
|
|
1581
1696
|
let foundHeading = false;
|
|
1582
1697
|
for (const line of lines) {
|
|
@@ -1593,15 +1708,15 @@ function buildProjectInfo(projectPath) {
|
|
|
1593
1708
|
}
|
|
1594
1709
|
}
|
|
1595
1710
|
return {
|
|
1596
|
-
name:
|
|
1711
|
+
name: path10.basename(projectPath),
|
|
1597
1712
|
path: projectPath,
|
|
1598
1713
|
description,
|
|
1599
|
-
hasClaudeMd:
|
|
1600
|
-
hasCursorRules:
|
|
1601
|
-
hasWindsurfRules:
|
|
1602
|
-
hasCopilotInstructions:
|
|
1603
|
-
hasConventionsMd:
|
|
1604
|
-
hasZedRules:
|
|
1714
|
+
hasClaudeMd: fs7.existsSync(path10.join(projectPath, "CLAUDE.md")) || fs7.existsSync(path10.join(projectPath, ".claude", "rules")),
|
|
1715
|
+
hasCursorRules: fs7.existsSync(path10.join(projectPath, ".cursorrules")) || fs7.existsSync(path10.join(projectPath, ".cursor", "rules")),
|
|
1716
|
+
hasWindsurfRules: fs7.existsSync(path10.join(projectPath, ".windsurfrules")) || fs7.existsSync(path10.join(projectPath, ".windsurf", "rules")),
|
|
1717
|
+
hasCopilotInstructions: fs7.existsSync(path10.join(projectPath, ".github", "copilot-instructions.md")) || fs7.existsSync(path10.join(projectPath, ".github", "instructions")),
|
|
1718
|
+
hasConventionsMd: fs7.existsSync(path10.join(projectPath, "CONVENTIONS.md")) || fs7.existsSync(path10.join(projectPath, ".amazonq", "rules")),
|
|
1719
|
+
hasZedRules: fs7.existsSync(path10.join(projectPath, ".rules"))
|
|
1605
1720
|
};
|
|
1606
1721
|
}
|
|
1607
1722
|
var MAX_PROJECT_DEPTH = 4;
|
|
@@ -1611,11 +1726,11 @@ function detectProjects(scanDirs, onProgress) {
|
|
|
1611
1726
|
function walk(dir, depth) {
|
|
1612
1727
|
if (depth > MAX_PROJECT_DEPTH) return;
|
|
1613
1728
|
try {
|
|
1614
|
-
const items =
|
|
1729
|
+
const items = fs7.readdirSync(dir, { withFileTypes: true });
|
|
1615
1730
|
for (const item of items) {
|
|
1616
1731
|
if (!item.isDirectory()) continue;
|
|
1617
1732
|
if (shouldSkipDir(item.name)) continue;
|
|
1618
|
-
const fullPath =
|
|
1733
|
+
const fullPath = path10.join(dir, item.name);
|
|
1619
1734
|
if (seen.has(fullPath)) continue;
|
|
1620
1735
|
seen.add(fullPath);
|
|
1621
1736
|
if (isProject(fullPath)) {
|
|
@@ -1639,20 +1754,20 @@ var MAX_BRAIN_INPUT_BYTES = 1e6;
|
|
|
1639
1754
|
function collectGlobalConfigFiles(onProgress) {
|
|
1640
1755
|
const configs = [];
|
|
1641
1756
|
const globalPaths = [
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1757
|
+
path10.join(home, ".claude", "MEMORY.md"),
|
|
1758
|
+
path10.join(home, ".claude", "CLAUDE.md"),
|
|
1759
|
+
path10.join(home, ".openclaw", "workspace", "SOUL.md"),
|
|
1760
|
+
path10.join(home, ".openclaw", "workspace", "MEMORY.md"),
|
|
1761
|
+
path10.join(home, ".gemini", "MEMORY.md"),
|
|
1762
|
+
path10.join(home, ".paperclip", "IDENTITY.md")
|
|
1648
1763
|
];
|
|
1649
1764
|
for (const gp of globalPaths) {
|
|
1650
1765
|
try {
|
|
1651
|
-
const stat =
|
|
1766
|
+
const stat = fs7.statSync(gp);
|
|
1652
1767
|
if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
|
|
1653
|
-
const content =
|
|
1768
|
+
const content = fs7.readFileSync(gp, "utf-8");
|
|
1654
1769
|
if (content.trim()) {
|
|
1655
|
-
const name =
|
|
1770
|
+
const name = path10.relative(home, gp);
|
|
1656
1771
|
configs.push({ name, content });
|
|
1657
1772
|
onProgress?.({ phase: "configs", message: name });
|
|
1658
1773
|
}
|
|
@@ -1665,11 +1780,11 @@ function collectProjectConfigFiles(projects, onProgress) {
|
|
|
1665
1780
|
const configs = [];
|
|
1666
1781
|
for (const project of projects) {
|
|
1667
1782
|
for (const fileName of AI_CONFIG_FILENAMES) {
|
|
1668
|
-
const filePath =
|
|
1783
|
+
const filePath = path10.join(project.path, fileName);
|
|
1669
1784
|
try {
|
|
1670
|
-
const stat =
|
|
1785
|
+
const stat = fs7.statSync(filePath);
|
|
1671
1786
|
if (!stat.isFile() || stat.size > MAX_CONFIG_SIZE) continue;
|
|
1672
|
-
const content =
|
|
1787
|
+
const content = fs7.readFileSync(filePath, "utf-8");
|
|
1673
1788
|
if (content.trim()) {
|
|
1674
1789
|
const name = `${project.name}/${fileName}`;
|
|
1675
1790
|
configs.push({ name, content });
|
|
@@ -1678,11 +1793,11 @@ function collectProjectConfigFiles(projects, onProgress) {
|
|
|
1678
1793
|
} catch {
|
|
1679
1794
|
}
|
|
1680
1795
|
}
|
|
1681
|
-
const pkgPath =
|
|
1796
|
+
const pkgPath = path10.join(project.path, "package.json");
|
|
1682
1797
|
try {
|
|
1683
|
-
const stat =
|
|
1798
|
+
const stat = fs7.statSync(pkgPath);
|
|
1684
1799
|
if (stat.isFile() && stat.size <= MAX_CONFIG_SIZE) {
|
|
1685
|
-
const pkg = JSON.parse(
|
|
1800
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
1686
1801
|
const summary = JSON.stringify({ name: pkg.name, description: pkg.description }, null, 2);
|
|
1687
1802
|
configs.push({ name: `${project.name}/package.json`, content: summary });
|
|
1688
1803
|
onProgress?.({ phase: "configs", message: `${project.name}/package.json` });
|
|
@@ -1702,35 +1817,6 @@ function formatSize(bytes) {
|
|
|
1702
1817
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1703
1818
|
}
|
|
1704
1819
|
|
|
1705
|
-
// src/lib/store.ts
|
|
1706
|
-
init_esm_shims();
|
|
1707
|
-
import fs6 from "fs";
|
|
1708
|
-
import path9 from "path";
|
|
1709
|
-
import os5 from "os";
|
|
1710
|
-
var CONFIG_DIR = path9.join(os5.homedir(), ".piut");
|
|
1711
|
-
var CONFIG_FILE2 = path9.join(CONFIG_DIR, "config.json");
|
|
1712
|
-
function readStore() {
|
|
1713
|
-
try {
|
|
1714
|
-
const raw = fs6.readFileSync(CONFIG_FILE2, "utf-8");
|
|
1715
|
-
return JSON.parse(raw);
|
|
1716
|
-
} catch {
|
|
1717
|
-
return {};
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
function updateStore(updates) {
|
|
1721
|
-
const config = readStore();
|
|
1722
|
-
const updated = { ...config, ...updates };
|
|
1723
|
-
fs6.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1724
|
-
fs6.writeFileSync(CONFIG_FILE2, JSON.stringify(updated, null, 2) + "\n", "utf-8");
|
|
1725
|
-
return updated;
|
|
1726
|
-
}
|
|
1727
|
-
function clearStore() {
|
|
1728
|
-
try {
|
|
1729
|
-
fs6.unlinkSync(CONFIG_FILE2);
|
|
1730
|
-
} catch {
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
1820
|
// src/commands/status.ts
|
|
1735
1821
|
var API_BASE3 = process.env.PIUT_API_BASE || "https://piut.com";
|
|
1736
1822
|
var PIUT_FILES = [
|
|
@@ -1743,7 +1829,7 @@ var PIUT_FILES = [
|
|
|
1743
1829
|
];
|
|
1744
1830
|
function hasPiutReference(filePath) {
|
|
1745
1831
|
try {
|
|
1746
|
-
const content =
|
|
1832
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
1747
1833
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
1748
1834
|
} catch {
|
|
1749
1835
|
return false;
|
|
@@ -1795,7 +1881,7 @@ async function statusCommand(options = {}) {
|
|
|
1795
1881
|
await verifyStatus();
|
|
1796
1882
|
return;
|
|
1797
1883
|
}
|
|
1798
|
-
const thisHostname =
|
|
1884
|
+
const thisHostname = os7.hostname();
|
|
1799
1885
|
const thisMachineId = getMachineId2();
|
|
1800
1886
|
console.log(` AI tools on this machine ${dim(`(${thisHostname})`)}:`);
|
|
1801
1887
|
console.log();
|
|
@@ -1803,7 +1889,7 @@ async function statusCommand(options = {}) {
|
|
|
1803
1889
|
for (const tool of TOOLS) {
|
|
1804
1890
|
const paths = resolveConfigPaths(tool);
|
|
1805
1891
|
for (const { filePath, configKey } of paths) {
|
|
1806
|
-
if (!
|
|
1892
|
+
if (!fs8.existsSync(filePath)) continue;
|
|
1807
1893
|
foundAny = true;
|
|
1808
1894
|
const configured = isPiutConfigured(filePath, configKey);
|
|
1809
1895
|
if (configured) {
|
|
@@ -1826,8 +1912,8 @@ async function statusCommand(options = {}) {
|
|
|
1826
1912
|
for (const project of projects) {
|
|
1827
1913
|
const connectedFiles = [];
|
|
1828
1914
|
for (const file of PIUT_FILES) {
|
|
1829
|
-
const absPath =
|
|
1830
|
-
if (
|
|
1915
|
+
const absPath = path11.join(project.path, file);
|
|
1916
|
+
if (fs8.existsSync(absPath) && hasPiutReference(absPath)) {
|
|
1831
1917
|
connectedFiles.push(file);
|
|
1832
1918
|
}
|
|
1833
1919
|
}
|
|
@@ -1900,7 +1986,7 @@ async function verifyStatus() {
|
|
|
1900
1986
|
for (const tool of TOOLS) {
|
|
1901
1987
|
const paths = resolveConfigPaths(tool);
|
|
1902
1988
|
for (const { filePath, configKey } of paths) {
|
|
1903
|
-
if (!
|
|
1989
|
+
if (!fs8.existsSync(filePath)) continue;
|
|
1904
1990
|
const piutConfig = getPiutConfig(filePath, configKey);
|
|
1905
1991
|
if (!piutConfig) {
|
|
1906
1992
|
toolLine(tool.name, dim("installed, not connected"), "\u25CB");
|
|
@@ -1944,7 +2030,7 @@ async function verifyStatus() {
|
|
|
1944
2030
|
|
|
1945
2031
|
// src/commands/remove.ts
|
|
1946
2032
|
init_esm_shims();
|
|
1947
|
-
import
|
|
2033
|
+
import fs9 from "fs";
|
|
1948
2034
|
import { checkbox as checkbox2, confirm as confirm2 } from "@inquirer/prompts";
|
|
1949
2035
|
async function removeCommand() {
|
|
1950
2036
|
banner();
|
|
@@ -1953,7 +2039,7 @@ async function removeCommand() {
|
|
|
1953
2039
|
if (!tool.configKey) continue;
|
|
1954
2040
|
const paths = resolveConfigPaths(tool);
|
|
1955
2041
|
for (const { filePath, configKey } of paths) {
|
|
1956
|
-
if (
|
|
2042
|
+
if (fs9.existsSync(filePath) && isPiutConfigured(filePath, configKey)) {
|
|
1957
2043
|
configured.push({ tool, configPath: filePath, resolvedConfigKey: configKey });
|
|
1958
2044
|
break;
|
|
1959
2045
|
}
|
|
@@ -2007,13 +2093,16 @@ async function removeCommand() {
|
|
|
2007
2093
|
init_esm_shims();
|
|
2008
2094
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
2009
2095
|
import chalk5 from "chalk";
|
|
2010
|
-
import
|
|
2096
|
+
import os8 from "os";
|
|
2011
2097
|
|
|
2012
2098
|
// src/lib/auth.ts
|
|
2013
2099
|
init_esm_shims();
|
|
2014
2100
|
import { select, input, password as password2 } from "@inquirer/prompts";
|
|
2015
2101
|
import { exec } from "child_process";
|
|
2102
|
+
import http from "http";
|
|
2103
|
+
import crypto3 from "crypto";
|
|
2016
2104
|
import chalk4 from "chalk";
|
|
2105
|
+
var API_BASE4 = process.env.PIUT_API_BASE || "https://piut.com";
|
|
2017
2106
|
function openBrowser(url) {
|
|
2018
2107
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2019
2108
|
exec(`${cmd} "${url}"`);
|
|
@@ -2052,12 +2141,101 @@ async function pasteKeyFlow() {
|
|
|
2052
2141
|
return { apiKey, validation };
|
|
2053
2142
|
}
|
|
2054
2143
|
async function browserFlow() {
|
|
2055
|
-
const
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2144
|
+
const state = crypto3.randomBytes(16).toString("hex");
|
|
2145
|
+
return new Promise((resolve, reject) => {
|
|
2146
|
+
let settled = false;
|
|
2147
|
+
const server = http.createServer((req, res) => {
|
|
2148
|
+
const url = new URL(req.url, `http://localhost`);
|
|
2149
|
+
if (url.pathname !== "/callback") {
|
|
2150
|
+
res.writeHead(404);
|
|
2151
|
+
res.end();
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
const key = url.searchParams.get("key");
|
|
2155
|
+
const returnedState = url.searchParams.get("state");
|
|
2156
|
+
if (returnedState !== state) {
|
|
2157
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
2158
|
+
res.end(errorPage("State mismatch. Please try again from the CLI."));
|
|
2159
|
+
cleanup();
|
|
2160
|
+
if (!settled) {
|
|
2161
|
+
settled = true;
|
|
2162
|
+
reject(new CliError("Browser auth state mismatch"));
|
|
2163
|
+
}
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
if (!key || !key.startsWith("pb_")) {
|
|
2167
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
2168
|
+
res.end(errorPage("No valid API key received."));
|
|
2169
|
+
cleanup();
|
|
2170
|
+
if (!settled) {
|
|
2171
|
+
settled = true;
|
|
2172
|
+
reject(new CliError("No API key received from browser"));
|
|
2173
|
+
}
|
|
2174
|
+
return;
|
|
2175
|
+
}
|
|
2176
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
2177
|
+
res.end(successPage());
|
|
2178
|
+
cleanup();
|
|
2179
|
+
validateKey(key).then((validation) => {
|
|
2180
|
+
if (!settled) {
|
|
2181
|
+
settled = true;
|
|
2182
|
+
resolve({ apiKey: key, validation });
|
|
2183
|
+
}
|
|
2184
|
+
}).catch((err) => {
|
|
2185
|
+
if (!settled) {
|
|
2186
|
+
settled = true;
|
|
2187
|
+
reject(err);
|
|
2188
|
+
}
|
|
2189
|
+
});
|
|
2190
|
+
});
|
|
2191
|
+
const timer = setTimeout(() => {
|
|
2192
|
+
cleanup();
|
|
2193
|
+
if (!settled) {
|
|
2194
|
+
settled = true;
|
|
2195
|
+
reject(new CliError("Browser login timed out after 2 minutes. Please try again."));
|
|
2196
|
+
}
|
|
2197
|
+
}, 12e4);
|
|
2198
|
+
function cleanup() {
|
|
2199
|
+
clearTimeout(timer);
|
|
2200
|
+
server.close();
|
|
2201
|
+
}
|
|
2202
|
+
server.listen(0, "127.0.0.1", () => {
|
|
2203
|
+
const { port } = server.address();
|
|
2204
|
+
const authUrl = `${API_BASE4}/cli/auth?port=${port}&state=${state}`;
|
|
2205
|
+
console.log(dim(` Opening ${brand("piut.com")} in your browser...`));
|
|
2206
|
+
openBrowser(authUrl);
|
|
2207
|
+
console.log(dim(" Waiting for browser authorization..."));
|
|
2208
|
+
});
|
|
2209
|
+
server.on("error", (err) => {
|
|
2210
|
+
cleanup();
|
|
2211
|
+
if (!settled) {
|
|
2212
|
+
settled = true;
|
|
2213
|
+
reject(new CliError(`Failed to start callback server: ${err.message}`));
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
function successPage() {
|
|
2219
|
+
return `<!DOCTYPE html>
|
|
2220
|
+
<html><head><title>p\u0131ut CLI</title></head>
|
|
2221
|
+
<body style="background:#0a0a0a;color:white;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
|
|
2222
|
+
<div style="text-align:center">
|
|
2223
|
+
<div style="font-size:48px;color:#4ade80;margin-bottom:16px">✓</div>
|
|
2224
|
+
<h2 style="margin:0 0 8px">CLI Authorized</h2>
|
|
2225
|
+
<p style="color:#a3a3a3;margin:0">You can close this tab and return to your terminal.</p>
|
|
2226
|
+
</div>
|
|
2227
|
+
</body></html>`;
|
|
2228
|
+
}
|
|
2229
|
+
function errorPage(message) {
|
|
2230
|
+
return `<!DOCTYPE html>
|
|
2231
|
+
<html><head><title>p\u0131ut CLI</title></head>
|
|
2232
|
+
<body style="background:#0a0a0a;color:white;font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0">
|
|
2233
|
+
<div style="text-align:center">
|
|
2234
|
+
<div style="font-size:48px;color:#f87171;margin-bottom:16px">✗</div>
|
|
2235
|
+
<h2 style="margin:0 0 8px">Authorization Failed</h2>
|
|
2236
|
+
<p style="color:#a3a3a3;margin:0">${message}</p>
|
|
2237
|
+
</div>
|
|
2238
|
+
</body></html>`;
|
|
2061
2239
|
}
|
|
2062
2240
|
async function promptLogin() {
|
|
2063
2241
|
const method = await select({
|
|
@@ -2071,7 +2249,7 @@ async function promptLogin() {
|
|
|
2071
2249
|
{
|
|
2072
2250
|
name: "Log in with browser",
|
|
2073
2251
|
value: "browser",
|
|
2074
|
-
description: "
|
|
2252
|
+
description: "Sign in via piut.com (opens browser)"
|
|
2075
2253
|
},
|
|
2076
2254
|
{
|
|
2077
2255
|
name: "Paste API key",
|
|
@@ -2119,10 +2297,95 @@ async function resolveApiKeyWithResult(keyOption) {
|
|
|
2119
2297
|
}
|
|
2120
2298
|
}
|
|
2121
2299
|
|
|
2300
|
+
// src/lib/sync.ts
|
|
2301
|
+
init_esm_shims();
|
|
2302
|
+
import fs10 from "fs";
|
|
2303
|
+
import path12 from "path";
|
|
2304
|
+
function syncStaleConfigs(slug, apiKey, serverUrl) {
|
|
2305
|
+
const updated = [];
|
|
2306
|
+
for (const tool of TOOLS) {
|
|
2307
|
+
if (tool.skillOnly || !tool.generateConfig || !tool.configKey) continue;
|
|
2308
|
+
const paths = resolveConfigPaths(tool);
|
|
2309
|
+
for (const { filePath, configKey } of paths) {
|
|
2310
|
+
if (!fs10.existsSync(filePath)) continue;
|
|
2311
|
+
const piutConfig = getPiutConfig(filePath, configKey);
|
|
2312
|
+
if (!piutConfig) continue;
|
|
2313
|
+
const existingKey = extractKeyFromConfig(piutConfig);
|
|
2314
|
+
const existingSlug = extractSlugFromConfig(piutConfig);
|
|
2315
|
+
const keyStale = !!existingKey && existingKey !== apiKey;
|
|
2316
|
+
const slugStale = !!existingSlug && existingSlug !== slug;
|
|
2317
|
+
if (keyStale || slugStale) {
|
|
2318
|
+
const newConfig = tool.generateConfig(slug, apiKey);
|
|
2319
|
+
mergeConfig(filePath, configKey, newConfig);
|
|
2320
|
+
updated.push(tool.name);
|
|
2321
|
+
}
|
|
2322
|
+
break;
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
const cwd = process.cwd();
|
|
2326
|
+
const existing = readPiutConfig(cwd);
|
|
2327
|
+
if (existing && (existing.apiKey !== apiKey || existing.slug !== slug)) {
|
|
2328
|
+
writePiutConfig(cwd, { slug, apiKey, serverUrl });
|
|
2329
|
+
updated.push(".piut/config.json");
|
|
2330
|
+
}
|
|
2331
|
+
return updated;
|
|
2332
|
+
}
|
|
2333
|
+
function getConfiguredToolNames() {
|
|
2334
|
+
const names = [];
|
|
2335
|
+
for (const tool of TOOLS) {
|
|
2336
|
+
if (tool.skillOnly || !tool.configKey) continue;
|
|
2337
|
+
const paths = resolveConfigPaths(tool);
|
|
2338
|
+
for (const { filePath, configKey } of paths) {
|
|
2339
|
+
if (!fs10.existsSync(filePath)) continue;
|
|
2340
|
+
if (getPiutConfig(filePath, configKey)) {
|
|
2341
|
+
names.push(tool.name);
|
|
2342
|
+
break;
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
return names;
|
|
2347
|
+
}
|
|
2348
|
+
async function cycleConfigEntry(filePath, configKey, freshConfig) {
|
|
2349
|
+
removeFromConfig(filePath, configKey);
|
|
2350
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2351
|
+
mergeConfig(filePath, configKey, freshConfig);
|
|
2352
|
+
}
|
|
2353
|
+
async function cycleMcpConfigs(slug, apiKey) {
|
|
2354
|
+
for (const tool of TOOLS) {
|
|
2355
|
+
if (tool.skillOnly || !tool.generateConfig || !tool.configKey) continue;
|
|
2356
|
+
const paths = resolveConfigPaths(tool);
|
|
2357
|
+
for (const { filePath, configKey } of paths) {
|
|
2358
|
+
if (!fs10.existsSync(filePath)) continue;
|
|
2359
|
+
if (!getPiutConfig(filePath, configKey)) continue;
|
|
2360
|
+
await cycleConfigEntry(filePath, configKey, tool.generateConfig(slug, apiKey));
|
|
2361
|
+
break;
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
async function cycleProjectConfigs(slug, apiKey, serverUrl) {
|
|
2366
|
+
const projects = scanForProjects();
|
|
2367
|
+
const refreshed = [];
|
|
2368
|
+
const vscodeTool = TOOLS.find((t) => t.id === "vscode");
|
|
2369
|
+
for (const project of projects) {
|
|
2370
|
+
if (!hasPiutDir(project.path)) continue;
|
|
2371
|
+
const projectName = path12.basename(project.path);
|
|
2372
|
+
writePiutConfig(project.path, { slug, apiKey, serverUrl });
|
|
2373
|
+
await writePiutSkill(project.path, slug, apiKey);
|
|
2374
|
+
if (vscodeTool?.generateConfig && vscodeTool.configKey) {
|
|
2375
|
+
const vscodeMcpPath = path12.join(project.path, ".vscode", "mcp.json");
|
|
2376
|
+
if (fs10.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
|
|
2377
|
+
await cycleConfigEntry(vscodeMcpPath, "servers", vscodeTool.generateConfig(slug, apiKey));
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
refreshed.push(projectName);
|
|
2381
|
+
}
|
|
2382
|
+
return refreshed;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2122
2385
|
// src/commands/build.ts
|
|
2123
2386
|
async function buildCommand(options) {
|
|
2124
2387
|
banner();
|
|
2125
|
-
const { apiKey, serverUrl } = await resolveApiKeyWithResult(options.key);
|
|
2388
|
+
const { apiKey, serverUrl, slug } = await resolveApiKeyWithResult(options.key);
|
|
2126
2389
|
console.log(dim(" \u2501\u2501\u2501 BUILD YOUR BRAIN \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
2127
2390
|
console.log();
|
|
2128
2391
|
console.log(dim(" Scanning for AI config files..."));
|
|
@@ -2184,7 +2447,7 @@ async function buildCommand(options) {
|
|
|
2184
2447
|
return;
|
|
2185
2448
|
}
|
|
2186
2449
|
}
|
|
2187
|
-
const home2 =
|
|
2450
|
+
const home2 = os8.homedir();
|
|
2188
2451
|
const brainInput = {
|
|
2189
2452
|
summary: {
|
|
2190
2453
|
projects: projects.map((p) => ({
|
|
@@ -2195,9 +2458,9 @@ async function buildCommand(options) {
|
|
|
2195
2458
|
configFiles: allConfigs
|
|
2196
2459
|
}
|
|
2197
2460
|
};
|
|
2198
|
-
await streamBuild(apiKey, serverUrl, brainInput, options);
|
|
2461
|
+
await streamBuild(apiKey, serverUrl, slug, brainInput, options);
|
|
2199
2462
|
}
|
|
2200
|
-
async function streamBuild(apiKey, serverUrl, brainInput, options) {
|
|
2463
|
+
async function streamBuild(apiKey, serverUrl, slug, brainInput, options) {
|
|
2201
2464
|
const spinner = new Spinner();
|
|
2202
2465
|
spinner.start("Generating brain...");
|
|
2203
2466
|
try {
|
|
@@ -2276,6 +2539,7 @@ async function streamBuild(apiKey, serverUrl, brainInput, options) {
|
|
|
2276
2539
|
console.log(` ${brand(serverUrl)}`);
|
|
2277
2540
|
console.log(dim(" (accessible only with secure authentication)"));
|
|
2278
2541
|
console.log();
|
|
2542
|
+
await cycleMcpConfigs(slug, apiKey);
|
|
2279
2543
|
} catch (err) {
|
|
2280
2544
|
console.log();
|
|
2281
2545
|
const msg = err.message;
|
|
@@ -2322,6 +2586,7 @@ async function deployCommand(options) {
|
|
|
2322
2586
|
console.log(` ${brand(serverUrl)}`);
|
|
2323
2587
|
console.log(dim(" (securely accessible only with authentication)"));
|
|
2324
2588
|
console.log();
|
|
2589
|
+
await cycleMcpConfigs(slug, apiKey);
|
|
2325
2590
|
console.log(dim(" Next: run ") + brand("piut connect") + dim(" to add brain references to your projects."));
|
|
2326
2591
|
console.log();
|
|
2327
2592
|
} catch (err) {
|
|
@@ -2342,33 +2607,33 @@ async function deployCommand(options) {
|
|
|
2342
2607
|
|
|
2343
2608
|
// src/commands/connect.ts
|
|
2344
2609
|
init_esm_shims();
|
|
2345
|
-
import
|
|
2346
|
-
import
|
|
2610
|
+
import fs11 from "fs";
|
|
2611
|
+
import path13 from "path";
|
|
2347
2612
|
import { checkbox as checkbox3 } from "@inquirer/prompts";
|
|
2348
2613
|
var RULE_FILES = [
|
|
2349
2614
|
{
|
|
2350
2615
|
tool: "Claude Code",
|
|
2351
2616
|
filePath: "CLAUDE.md",
|
|
2352
2617
|
strategy: "append",
|
|
2353
|
-
detect: (p) => p.hasClaudeMd ||
|
|
2618
|
+
detect: (p) => p.hasClaudeMd || fs11.existsSync(path13.join(p.path, ".claude"))
|
|
2354
2619
|
},
|
|
2355
2620
|
{
|
|
2356
2621
|
tool: "Cursor",
|
|
2357
2622
|
filePath: ".cursor/rules/piut.mdc",
|
|
2358
2623
|
strategy: "create",
|
|
2359
|
-
detect: (p) => p.hasCursorRules ||
|
|
2624
|
+
detect: (p) => p.hasCursorRules || fs11.existsSync(path13.join(p.path, ".cursor"))
|
|
2360
2625
|
},
|
|
2361
2626
|
{
|
|
2362
2627
|
tool: "Windsurf",
|
|
2363
2628
|
filePath: ".windsurf/rules/piut.md",
|
|
2364
2629
|
strategy: "create",
|
|
2365
|
-
detect: (p) => p.hasWindsurfRules ||
|
|
2630
|
+
detect: (p) => p.hasWindsurfRules || fs11.existsSync(path13.join(p.path, ".windsurf"))
|
|
2366
2631
|
},
|
|
2367
2632
|
{
|
|
2368
|
-
tool: "
|
|
2633
|
+
tool: "VS Code",
|
|
2369
2634
|
filePath: ".github/copilot-instructions.md",
|
|
2370
2635
|
strategy: "append",
|
|
2371
|
-
detect: (p) => p.hasCopilotInstructions ||
|
|
2636
|
+
detect: (p) => p.hasCopilotInstructions || fs11.existsSync(path13.join(p.path, ".github"))
|
|
2372
2637
|
},
|
|
2373
2638
|
{
|
|
2374
2639
|
tool: "Amazon Q",
|
|
@@ -2380,22 +2645,22 @@ var RULE_FILES = [
|
|
|
2380
2645
|
tool: "Zed",
|
|
2381
2646
|
filePath: ".zed/rules.md",
|
|
2382
2647
|
strategy: "create",
|
|
2383
|
-
detect: (p) => p.hasZedRules ||
|
|
2648
|
+
detect: (p) => p.hasZedRules || fs11.existsSync(path13.join(p.path, ".zed"))
|
|
2384
2649
|
},
|
|
2385
2650
|
{
|
|
2386
2651
|
tool: "Gemini CLI",
|
|
2387
2652
|
filePath: "GEMINI.md",
|
|
2388
2653
|
strategy: "append",
|
|
2389
|
-
detect: (p) =>
|
|
2654
|
+
detect: (p) => fs11.existsSync(path13.join(p.path, ".gemini"))
|
|
2390
2655
|
},
|
|
2391
2656
|
{
|
|
2392
2657
|
tool: "Paperclip",
|
|
2393
2658
|
filePath: "AGENTS.md",
|
|
2394
2659
|
strategy: "append",
|
|
2395
|
-
detect: (p) =>
|
|
2660
|
+
detect: (p) => fs11.existsSync(path13.join(p.path, ".paperclip"))
|
|
2396
2661
|
}
|
|
2397
2662
|
];
|
|
2398
|
-
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut
|
|
2663
|
+
var DEDICATED_FILE_CONTENT = `## p\u0131ut Context (MCP Server: piut)
|
|
2399
2664
|
|
|
2400
2665
|
This project uses p\u0131ut for persistent personal context via MCP (Model Context Protocol).
|
|
2401
2666
|
p\u0131ut provides MCP tools \u2014 do NOT read local .piut/ files directly. Use the MCP tools.
|
|
@@ -2418,7 +2683,7 @@ Full skill reference: .piut/skill.md
|
|
|
2418
2683
|
`;
|
|
2419
2684
|
var APPEND_SECTION = `
|
|
2420
2685
|
|
|
2421
|
-
## p\u0131ut Context (MCP Server: piut
|
|
2686
|
+
## p\u0131ut Context (MCP Server: piut)
|
|
2422
2687
|
|
|
2423
2688
|
p\u0131ut provides MCP tools for persistent personal context. Do NOT read local .piut/ files.
|
|
2424
2689
|
Available tools: \`get_context\`, \`get_section\`, \`search_brain\`, \`append_brain\`, \`update_brain\`, \`prompt_brain\`
|
|
@@ -2427,7 +2692,7 @@ Full skill reference: .piut/skill.md
|
|
|
2427
2692
|
`;
|
|
2428
2693
|
function hasPiutReference2(filePath) {
|
|
2429
2694
|
try {
|
|
2430
|
-
const content =
|
|
2695
|
+
const content = fs11.readFileSync(filePath, "utf-8");
|
|
2431
2696
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
2432
2697
|
} catch {
|
|
2433
2698
|
return false;
|
|
@@ -2467,20 +2732,20 @@ async function connectCommand(options) {
|
|
|
2467
2732
|
for (const project of projects) {
|
|
2468
2733
|
for (const rule of RULE_FILES) {
|
|
2469
2734
|
if (!rule.detect(project)) continue;
|
|
2470
|
-
const absPath =
|
|
2471
|
-
if (
|
|
2735
|
+
const absPath = path13.join(project.path, rule.filePath);
|
|
2736
|
+
if (fs11.existsSync(absPath) && hasPiutReference2(absPath)) continue;
|
|
2472
2737
|
actions.push({
|
|
2473
2738
|
project,
|
|
2474
2739
|
tool: rule.tool,
|
|
2475
2740
|
filePath: rule.filePath,
|
|
2476
2741
|
absPath,
|
|
2477
|
-
action: rule.strategy === "create" || !
|
|
2742
|
+
action: rule.strategy === "create" || !fs11.existsSync(absPath) ? "create" : "append"
|
|
2478
2743
|
});
|
|
2479
2744
|
}
|
|
2480
2745
|
const hasAnyAction = actions.some((a) => a.project === project);
|
|
2481
2746
|
if (!hasAnyAction) {
|
|
2482
|
-
const claudeMdPath =
|
|
2483
|
-
if (!
|
|
2747
|
+
const claudeMdPath = path13.join(project.path, "CLAUDE.md");
|
|
2748
|
+
if (!fs11.existsSync(claudeMdPath)) {
|
|
2484
2749
|
actions.push({
|
|
2485
2750
|
project,
|
|
2486
2751
|
tool: "Claude Code",
|
|
@@ -2520,7 +2785,7 @@ async function connectCommand(options) {
|
|
|
2520
2785
|
console.log();
|
|
2521
2786
|
const projectChoices = [];
|
|
2522
2787
|
for (const [projectPath, projectActions] of byProject) {
|
|
2523
|
-
const projectName =
|
|
2788
|
+
const projectName = path13.basename(projectPath);
|
|
2524
2789
|
const desc = projectActions.map((a) => {
|
|
2525
2790
|
const verb = a.action === "create" ? "will create" : "will append to";
|
|
2526
2791
|
return `${verb} ${a.filePath}`;
|
|
@@ -2546,32 +2811,32 @@ async function connectCommand(options) {
|
|
|
2546
2811
|
}
|
|
2547
2812
|
console.log();
|
|
2548
2813
|
let connected = 0;
|
|
2549
|
-
const
|
|
2814
|
+
const vscodeTool = TOOLS.find((t) => t.id === "vscode");
|
|
2550
2815
|
for (const projectPath of selectedPaths) {
|
|
2551
2816
|
const projectActions = byProject.get(projectPath) || [];
|
|
2552
|
-
const projectName =
|
|
2817
|
+
const projectName = path13.basename(projectPath);
|
|
2553
2818
|
writePiutConfig(projectPath, { slug, apiKey, serverUrl });
|
|
2554
2819
|
await writePiutSkill(projectPath, slug, apiKey);
|
|
2555
2820
|
ensureGitignored(projectPath);
|
|
2556
2821
|
console.log(success(` \u2713 ${projectName}/.piut/`) + dim(" \u2014 credentials + skill"));
|
|
2557
|
-
if (
|
|
2558
|
-
const
|
|
2559
|
-
if (
|
|
2560
|
-
const vscodeMcpPath =
|
|
2561
|
-
const serverConfig =
|
|
2562
|
-
mergeConfig(vscodeMcpPath,
|
|
2563
|
-
console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014
|
|
2822
|
+
if (vscodeTool) {
|
|
2823
|
+
const hasVscode = fs11.existsSync(path13.join(projectPath, ".github", "copilot-instructions.md")) || fs11.existsSync(path13.join(projectPath, ".github"));
|
|
2824
|
+
if (hasVscode) {
|
|
2825
|
+
const vscodeMcpPath = path13.join(projectPath, ".vscode", "mcp.json");
|
|
2826
|
+
const serverConfig = vscodeTool.generateConfig(slug, apiKey);
|
|
2827
|
+
mergeConfig(vscodeMcpPath, vscodeTool.configKey, serverConfig);
|
|
2828
|
+
console.log(success(` \u2713 ${projectName}/.vscode/mcp.json`) + dim(" \u2014 VS Code MCP"));
|
|
2564
2829
|
}
|
|
2565
2830
|
}
|
|
2566
2831
|
for (const action of projectActions) {
|
|
2567
2832
|
if (action.action === "create") {
|
|
2568
2833
|
const isAppendType = RULE_FILES.find((r) => r.filePath === action.filePath)?.strategy === "append";
|
|
2569
2834
|
const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
|
|
2570
|
-
|
|
2571
|
-
|
|
2835
|
+
fs11.mkdirSync(path13.dirname(action.absPath), { recursive: true });
|
|
2836
|
+
fs11.writeFileSync(action.absPath, content, "utf-8");
|
|
2572
2837
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 created"));
|
|
2573
2838
|
} else {
|
|
2574
|
-
|
|
2839
|
+
fs11.appendFileSync(action.absPath, APPEND_SECTION);
|
|
2575
2840
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 appended"));
|
|
2576
2841
|
}
|
|
2577
2842
|
connected++;
|
|
@@ -2581,7 +2846,7 @@ async function connectCommand(options) {
|
|
|
2581
2846
|
const hostname = getHostname();
|
|
2582
2847
|
for (const projectPath of selectedPaths) {
|
|
2583
2848
|
const projectActions = byProject.get(projectPath) || [];
|
|
2584
|
-
const projectName =
|
|
2849
|
+
const projectName = path13.basename(projectPath);
|
|
2585
2850
|
const toolsDetected = [...new Set(projectActions.map((a) => a.tool))];
|
|
2586
2851
|
const configFilesWritten = projectActions.map((a) => a.filePath);
|
|
2587
2852
|
registerProject(apiKey, {
|
|
@@ -2601,8 +2866,8 @@ async function connectCommand(options) {
|
|
|
2601
2866
|
|
|
2602
2867
|
// src/commands/disconnect.ts
|
|
2603
2868
|
init_esm_shims();
|
|
2604
|
-
import
|
|
2605
|
-
import
|
|
2869
|
+
import fs12 from "fs";
|
|
2870
|
+
import path14 from "path";
|
|
2606
2871
|
import { checkbox as checkbox4, confirm as confirm5 } from "@inquirer/prompts";
|
|
2607
2872
|
var DEDICATED_FILES = /* @__PURE__ */ new Set([
|
|
2608
2873
|
".cursor/rules/piut.mdc",
|
|
@@ -2618,7 +2883,7 @@ var APPEND_FILES = [
|
|
|
2618
2883
|
];
|
|
2619
2884
|
function hasPiutReference3(filePath) {
|
|
2620
2885
|
try {
|
|
2621
|
-
const content =
|
|
2886
|
+
const content = fs12.readFileSync(filePath, "utf-8");
|
|
2622
2887
|
return content.includes("p\u0131ut Context") || content.includes("piut Context");
|
|
2623
2888
|
} catch {
|
|
2624
2889
|
return false;
|
|
@@ -2626,7 +2891,7 @@ function hasPiutReference3(filePath) {
|
|
|
2626
2891
|
}
|
|
2627
2892
|
function removePiutSection(filePath) {
|
|
2628
2893
|
try {
|
|
2629
|
-
let content =
|
|
2894
|
+
let content = fs12.readFileSync(filePath, "utf-8");
|
|
2630
2895
|
const patterns = [
|
|
2631
2896
|
/\n*## p[ıi]ut Context[\s\S]*?(?=\n## |\n---\n|$)/g
|
|
2632
2897
|
];
|
|
@@ -2640,7 +2905,7 @@ function removePiutSection(filePath) {
|
|
|
2640
2905
|
}
|
|
2641
2906
|
if (changed) {
|
|
2642
2907
|
content = content.replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
|
|
2643
|
-
|
|
2908
|
+
fs12.writeFileSync(filePath, content, "utf-8");
|
|
2644
2909
|
}
|
|
2645
2910
|
return changed;
|
|
2646
2911
|
} catch {
|
|
@@ -2657,10 +2922,10 @@ async function disconnectCommand(options) {
|
|
|
2657
2922
|
const projects = scanForProjects(scanFolders);
|
|
2658
2923
|
const actions = [];
|
|
2659
2924
|
for (const project of projects) {
|
|
2660
|
-
const projectName =
|
|
2925
|
+
const projectName = path14.basename(project.path);
|
|
2661
2926
|
for (const dedicatedFile of DEDICATED_FILES) {
|
|
2662
|
-
const absPath =
|
|
2663
|
-
if (
|
|
2927
|
+
const absPath = path14.join(project.path, dedicatedFile);
|
|
2928
|
+
if (fs12.existsSync(absPath) && hasPiutReference3(absPath)) {
|
|
2664
2929
|
actions.push({
|
|
2665
2930
|
projectPath: project.path,
|
|
2666
2931
|
projectName,
|
|
@@ -2671,8 +2936,8 @@ async function disconnectCommand(options) {
|
|
|
2671
2936
|
}
|
|
2672
2937
|
}
|
|
2673
2938
|
for (const appendFile of APPEND_FILES) {
|
|
2674
|
-
const absPath =
|
|
2675
|
-
if (
|
|
2939
|
+
const absPath = path14.join(project.path, appendFile);
|
|
2940
|
+
if (fs12.existsSync(absPath) && hasPiutReference3(absPath)) {
|
|
2676
2941
|
actions.push({
|
|
2677
2942
|
projectPath: project.path,
|
|
2678
2943
|
projectName,
|
|
@@ -2687,12 +2952,12 @@ async function disconnectCommand(options) {
|
|
|
2687
2952
|
projectPath: project.path,
|
|
2688
2953
|
projectName,
|
|
2689
2954
|
filePath: ".piut/",
|
|
2690
|
-
absPath:
|
|
2955
|
+
absPath: path14.join(project.path, ".piut"),
|
|
2691
2956
|
action: "remove-dir"
|
|
2692
2957
|
});
|
|
2693
2958
|
}
|
|
2694
|
-
const vscodeMcpPath =
|
|
2695
|
-
if (
|
|
2959
|
+
const vscodeMcpPath = path14.join(project.path, ".vscode", "mcp.json");
|
|
2960
|
+
if (fs12.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
|
|
2696
2961
|
actions.push({
|
|
2697
2962
|
projectPath: project.path,
|
|
2698
2963
|
projectName,
|
|
@@ -2714,7 +2979,7 @@ async function disconnectCommand(options) {
|
|
|
2714
2979
|
}
|
|
2715
2980
|
console.log();
|
|
2716
2981
|
const projectChoices = Array.from(byProject.entries()).map(([projectPath, projectActions]) => {
|
|
2717
|
-
const name =
|
|
2982
|
+
const name = path14.basename(projectPath);
|
|
2718
2983
|
const files = projectActions.map((a) => a.filePath).join(", ");
|
|
2719
2984
|
return {
|
|
2720
2985
|
name: `${name} ${dim(`(${files})`)}`,
|
|
@@ -2743,11 +3008,11 @@ async function disconnectCommand(options) {
|
|
|
2743
3008
|
let disconnected = 0;
|
|
2744
3009
|
for (const projectPath of selectedPaths) {
|
|
2745
3010
|
const projectActions = byProject.get(projectPath) || [];
|
|
2746
|
-
const projectName =
|
|
3011
|
+
const projectName = path14.basename(projectPath);
|
|
2747
3012
|
for (const action of projectActions) {
|
|
2748
3013
|
if (action.action === "delete") {
|
|
2749
3014
|
try {
|
|
2750
|
-
|
|
3015
|
+
fs12.unlinkSync(action.absPath);
|
|
2751
3016
|
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 deleted"));
|
|
2752
3017
|
disconnected++;
|
|
2753
3018
|
} catch {
|
|
@@ -2761,7 +3026,7 @@ async function disconnectCommand(options) {
|
|
|
2761
3026
|
} else if (action.action === "remove-mcp") {
|
|
2762
3027
|
try {
|
|
2763
3028
|
removeFromConfig(action.absPath, "servers");
|
|
2764
|
-
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 piut
|
|
3029
|
+
console.log(success(` \u2713 ${projectName}/${action.filePath}`) + dim(" \u2014 piut removed"));
|
|
2765
3030
|
disconnected++;
|
|
2766
3031
|
} catch {
|
|
2767
3032
|
console.log(warning(` \u2717 ${projectName}/${action.filePath}`) + dim(" \u2014 could not update"));
|
|
@@ -2928,7 +3193,7 @@ async function updateCommand(currentVersion) {
|
|
|
2928
3193
|
|
|
2929
3194
|
// src/commands/doctor.ts
|
|
2930
3195
|
init_esm_shims();
|
|
2931
|
-
import
|
|
3196
|
+
import fs13 from "fs";
|
|
2932
3197
|
import chalk10 from "chalk";
|
|
2933
3198
|
async function doctorCommand(options) {
|
|
2934
3199
|
if (!options.json) banner();
|
|
@@ -2980,7 +3245,7 @@ async function doctorCommand(options) {
|
|
|
2980
3245
|
for (const tool of TOOLS) {
|
|
2981
3246
|
const paths = resolveConfigPaths(tool);
|
|
2982
3247
|
for (const { filePath: configPath, configKey: resolvedKey } of paths) {
|
|
2983
|
-
if (!
|
|
3248
|
+
if (!fs13.existsSync(configPath)) continue;
|
|
2984
3249
|
const piutConfig = getPiutConfig(configPath, resolvedKey);
|
|
2985
3250
|
if (!piutConfig) {
|
|
2986
3251
|
result.tools.push({
|
|
@@ -3080,6 +3345,12 @@ async function doctorCommand(options) {
|
|
|
3080
3345
|
}
|
|
3081
3346
|
if (result.issues === 0) {
|
|
3082
3347
|
console.log(success(" All checks passed."));
|
|
3348
|
+
if (result.mcp.ok && result.tools.some((t) => t.configured)) {
|
|
3349
|
+
console.log();
|
|
3350
|
+
console.log(dim(" Tip: If your AI tool can't access your brain but everything"));
|
|
3351
|
+
console.log(dim(" checks out here, try toggling the MCP server off and on in"));
|
|
3352
|
+
console.log(dim(" your tool's settings. MCP connections can go stale."));
|
|
3353
|
+
}
|
|
3083
3354
|
} else {
|
|
3084
3355
|
console.log(warning(` ${result.issues} issue(s) found.`));
|
|
3085
3356
|
const staleTools = result.tools.filter((t) => t.keyMatch === "stale" && !t.fixed);
|
|
@@ -3095,10 +3366,49 @@ async function doctorCommand(options) {
|
|
|
3095
3366
|
if (result.issues > 0) throw new CliError(`${result.issues} issue(s) found`);
|
|
3096
3367
|
}
|
|
3097
3368
|
|
|
3369
|
+
// src/commands/refresh.ts
|
|
3370
|
+
init_esm_shims();
|
|
3371
|
+
async function refreshCommand(options) {
|
|
3372
|
+
banner();
|
|
3373
|
+
const { apiKey, slug, serverUrl, status } = await resolveApiKeyWithResult(options.key);
|
|
3374
|
+
const configuredTools = getConfiguredToolNames();
|
|
3375
|
+
const isDeployed = status === "active";
|
|
3376
|
+
if (configuredTools.length === 0 && !isDeployed) {
|
|
3377
|
+
console.log();
|
|
3378
|
+
console.log(warning(" Nothing to refresh."));
|
|
3379
|
+
console.log(dim(" Run ") + brand("piut setup") + dim(" first to configure your AI tools."));
|
|
3380
|
+
console.log();
|
|
3381
|
+
return;
|
|
3382
|
+
}
|
|
3383
|
+
console.log(dim(" Refreshing..."));
|
|
3384
|
+
if (isDeployed) {
|
|
3385
|
+
try {
|
|
3386
|
+
await publishServer(apiKey);
|
|
3387
|
+
console.log(success(" \u2713 Server republished"));
|
|
3388
|
+
} catch {
|
|
3389
|
+
console.log(warning(" \u2717 Could not republish server"));
|
|
3390
|
+
}
|
|
3391
|
+
}
|
|
3392
|
+
if (configuredTools.length > 0) {
|
|
3393
|
+
await cycleMcpConfigs(slug, apiKey);
|
|
3394
|
+
console.log(success(" \u2713 Refreshed MCP connections"));
|
|
3395
|
+
console.log(dim(` Cycled: ${configuredTools.join(", ")}`));
|
|
3396
|
+
}
|
|
3397
|
+
const refreshedProjects = await cycleProjectConfigs(slug, apiKey, serverUrl);
|
|
3398
|
+
if (refreshedProjects.length > 0) {
|
|
3399
|
+
console.log(success(" \u2713 Refreshed connected projects"));
|
|
3400
|
+
console.log(dim(` Updated: ${refreshedProjects.join(", ")}`));
|
|
3401
|
+
}
|
|
3402
|
+
console.log();
|
|
3403
|
+
console.log(dim(" Tools that watch config files will reconnect automatically."));
|
|
3404
|
+
console.log(dim(" If a tool doesn't refresh, restart it manually."));
|
|
3405
|
+
console.log();
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3098
3408
|
// src/commands/vault.ts
|
|
3099
3409
|
init_esm_shims();
|
|
3100
|
-
import
|
|
3101
|
-
import
|
|
3410
|
+
import fs14 from "fs";
|
|
3411
|
+
import path15 from "path";
|
|
3102
3412
|
import chalk11 from "chalk";
|
|
3103
3413
|
import { confirm as confirm7 } from "@inquirer/prompts";
|
|
3104
3414
|
var DOCUMENT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -3151,26 +3461,26 @@ async function vaultListCommand(options) {
|
|
|
3151
3461
|
}
|
|
3152
3462
|
async function vaultUploadCommand(filePath, options) {
|
|
3153
3463
|
const key = resolveApiKey(options);
|
|
3154
|
-
const resolved =
|
|
3155
|
-
if (!
|
|
3464
|
+
const resolved = path15.resolve(filePath);
|
|
3465
|
+
if (!fs14.existsSync(resolved)) {
|
|
3156
3466
|
console.log(chalk11.red(` File not found: ${filePath}`));
|
|
3157
3467
|
throw new CliError();
|
|
3158
3468
|
}
|
|
3159
|
-
const stat =
|
|
3469
|
+
const stat = fs14.statSync(resolved);
|
|
3160
3470
|
if (!stat.isFile()) {
|
|
3161
3471
|
console.log(chalk11.red(` Not a file: ${filePath}`));
|
|
3162
3472
|
throw new CliError();
|
|
3163
3473
|
}
|
|
3164
|
-
const filename =
|
|
3474
|
+
const filename = path15.basename(resolved);
|
|
3165
3475
|
const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
|
|
3166
3476
|
const isDocument = DOCUMENT_EXTENSIONS.has(ext);
|
|
3167
3477
|
let content;
|
|
3168
3478
|
let encoding;
|
|
3169
3479
|
if (isDocument) {
|
|
3170
|
-
content =
|
|
3480
|
+
content = fs14.readFileSync(resolved).toString("base64");
|
|
3171
3481
|
encoding = "base64";
|
|
3172
3482
|
} else {
|
|
3173
|
-
content =
|
|
3483
|
+
content = fs14.readFileSync(resolved, "utf-8");
|
|
3174
3484
|
}
|
|
3175
3485
|
const spinner = new Spinner();
|
|
3176
3486
|
spinner.start(`Uploading ${filename}...`);
|
|
@@ -3193,8 +3503,8 @@ async function vaultReadCommand(filename, options) {
|
|
|
3193
3503
|
try {
|
|
3194
3504
|
const file = await readVaultFile(key, filename);
|
|
3195
3505
|
if (options.output) {
|
|
3196
|
-
const outPath =
|
|
3197
|
-
|
|
3506
|
+
const outPath = path15.resolve(options.output);
|
|
3507
|
+
fs14.writeFileSync(outPath, file.content, "utf-8");
|
|
3198
3508
|
console.log(success(` Saved to ${outPath}`));
|
|
3199
3509
|
console.log();
|
|
3200
3510
|
} else {
|
|
@@ -3228,8 +3538,8 @@ async function vaultDeleteCommand(filename, options) {
|
|
|
3228
3538
|
// src/commands/interactive.ts
|
|
3229
3539
|
init_esm_shims();
|
|
3230
3540
|
import { select as select2, confirm as confirm8, checkbox as checkbox5, Separator } from "@inquirer/prompts";
|
|
3231
|
-
import
|
|
3232
|
-
import
|
|
3541
|
+
import fs16 from "fs";
|
|
3542
|
+
import path17 from "path";
|
|
3233
3543
|
import { exec as exec2 } from "child_process";
|
|
3234
3544
|
import chalk13 from "chalk";
|
|
3235
3545
|
var DOCUMENT_EXTENSIONS2 = /* @__PURE__ */ new Set([
|
|
@@ -3278,6 +3588,42 @@ async function interactiveMenu() {
|
|
|
3278
3588
|
const auth = await authenticate();
|
|
3279
3589
|
apiKey = auth.apiKey;
|
|
3280
3590
|
currentValidation = auth.validation;
|
|
3591
|
+
const synced = syncStaleConfigs(
|
|
3592
|
+
currentValidation.slug,
|
|
3593
|
+
apiKey,
|
|
3594
|
+
currentValidation.serverUrl
|
|
3595
|
+
);
|
|
3596
|
+
if (synced.length > 0) {
|
|
3597
|
+
console.log(dim(` Updated ${synced.length} stale config(s): ${synced.join(", ")}`));
|
|
3598
|
+
}
|
|
3599
|
+
await offerGlobalInstall();
|
|
3600
|
+
const configuredTools = getConfiguredToolNames();
|
|
3601
|
+
const isDeployed = currentValidation.status === "active";
|
|
3602
|
+
if (configuredTools.length > 0 || isDeployed) {
|
|
3603
|
+
const parts = [];
|
|
3604
|
+
if (isDeployed) {
|
|
3605
|
+
try {
|
|
3606
|
+
await publishServer(apiKey);
|
|
3607
|
+
parts.push("server");
|
|
3608
|
+
} catch {
|
|
3609
|
+
}
|
|
3610
|
+
}
|
|
3611
|
+
if (configuredTools.length > 0) {
|
|
3612
|
+
await cycleMcpConfigs(currentValidation.slug, apiKey);
|
|
3613
|
+
parts.push(`${configuredTools.length} tool(s)`);
|
|
3614
|
+
}
|
|
3615
|
+
const refreshedProjects = await cycleProjectConfigs(
|
|
3616
|
+
currentValidation.slug,
|
|
3617
|
+
apiKey,
|
|
3618
|
+
currentValidation.serverUrl
|
|
3619
|
+
);
|
|
3620
|
+
if (refreshedProjects.length > 0) {
|
|
3621
|
+
parts.push(`${refreshedProjects.length} project(s)`);
|
|
3622
|
+
}
|
|
3623
|
+
if (parts.length > 0) {
|
|
3624
|
+
console.log(dim(` Refreshed: ${parts.join(", ")}`));
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3281
3627
|
console.log();
|
|
3282
3628
|
if (currentValidation.status === "no_brain") {
|
|
3283
3629
|
console.log(warning(" You haven\u2019t built a brain yet."));
|
|
@@ -3313,7 +3659,7 @@ async function interactiveMenu() {
|
|
|
3313
3659
|
}
|
|
3314
3660
|
while (true) {
|
|
3315
3661
|
const hasBrain = currentValidation.status !== "no_brain";
|
|
3316
|
-
const
|
|
3662
|
+
const isDeployed2 = currentValidation.status === "active";
|
|
3317
3663
|
let action;
|
|
3318
3664
|
try {
|
|
3319
3665
|
action = await select2({
|
|
@@ -3339,22 +3685,22 @@ async function interactiveMenu() {
|
|
|
3339
3685
|
},
|
|
3340
3686
|
new Separator(),
|
|
3341
3687
|
{
|
|
3342
|
-
name:
|
|
3688
|
+
name: isDeployed2 ? "Undeploy Brain" : "Deploy Brain",
|
|
3343
3689
|
value: "deploy",
|
|
3344
|
-
description:
|
|
3690
|
+
description: isDeployed2 ? "Take your MCP server offline" : "Publish your MCP server (requires paid account)"
|
|
3345
3691
|
},
|
|
3346
3692
|
new Separator(),
|
|
3347
3693
|
{
|
|
3348
3694
|
name: "Connect Tools",
|
|
3349
3695
|
value: "connect-tools",
|
|
3350
3696
|
description: "Manage which AI tools use your MCP server",
|
|
3351
|
-
disabled: !
|
|
3697
|
+
disabled: !isDeployed2 && "(deploy brain first)"
|
|
3352
3698
|
},
|
|
3353
3699
|
{
|
|
3354
3700
|
name: "Connect Projects",
|
|
3355
3701
|
value: "connect-projects",
|
|
3356
3702
|
description: "Manage brain references in project config files",
|
|
3357
|
-
disabled: !
|
|
3703
|
+
disabled: !isDeployed2 && "(deploy brain first)"
|
|
3358
3704
|
},
|
|
3359
3705
|
new Separator(),
|
|
3360
3706
|
{
|
|
@@ -3401,7 +3747,7 @@ async function interactiveMenu() {
|
|
|
3401
3747
|
handleEditBrain();
|
|
3402
3748
|
break;
|
|
3403
3749
|
case "deploy":
|
|
3404
|
-
if (
|
|
3750
|
+
if (isDeployed2) {
|
|
3405
3751
|
await handleUndeploy(apiKey);
|
|
3406
3752
|
} else {
|
|
3407
3753
|
await deployCommand({ key: apiKey });
|
|
@@ -3472,8 +3818,8 @@ async function handleConnectTools(apiKey, validation) {
|
|
|
3472
3818
|
for (const tool of TOOLS) {
|
|
3473
3819
|
const paths = resolveConfigPaths(tool);
|
|
3474
3820
|
for (const { filePath, configKey } of paths) {
|
|
3475
|
-
const exists =
|
|
3476
|
-
const parentExists =
|
|
3821
|
+
const exists = fs16.existsSync(filePath);
|
|
3822
|
+
const parentExists = fs16.existsSync(path17.dirname(filePath));
|
|
3477
3823
|
if (exists || parentExists) {
|
|
3478
3824
|
const connected = exists && !!configKey && isPiutConfigured(filePath, configKey);
|
|
3479
3825
|
detected.push({ tool, configPath: filePath, resolvedConfigKey: configKey, connected });
|
|
@@ -3596,29 +3942,29 @@ async function handleManageProjects(apiKey, validation) {
|
|
|
3596
3942
|
console.log();
|
|
3597
3943
|
const copilotTool = TOOLS.find((t) => t.id === "copilot");
|
|
3598
3944
|
for (const { project } of toConnect) {
|
|
3599
|
-
const projectName =
|
|
3945
|
+
const projectName = path17.basename(project.path);
|
|
3600
3946
|
writePiutConfig(project.path, { slug, apiKey, serverUrl });
|
|
3601
3947
|
await writePiutSkill(project.path, slug, apiKey);
|
|
3602
3948
|
ensureGitignored(project.path);
|
|
3603
3949
|
if (copilotTool) {
|
|
3604
|
-
const hasCopilot =
|
|
3950
|
+
const hasCopilot = fs16.existsSync(path17.join(project.path, ".github", "copilot-instructions.md")) || fs16.existsSync(path17.join(project.path, ".github"));
|
|
3605
3951
|
if (hasCopilot) {
|
|
3606
|
-
const vscodeMcpPath =
|
|
3952
|
+
const vscodeMcpPath = path17.join(project.path, ".vscode", "mcp.json");
|
|
3607
3953
|
const serverConfig = copilotTool.generateConfig(slug, apiKey);
|
|
3608
3954
|
mergeConfig(vscodeMcpPath, copilotTool.configKey, serverConfig);
|
|
3609
3955
|
}
|
|
3610
3956
|
}
|
|
3611
3957
|
for (const rule of RULE_FILES) {
|
|
3612
3958
|
if (!rule.detect(project)) continue;
|
|
3613
|
-
const absPath =
|
|
3614
|
-
if (
|
|
3615
|
-
if (rule.strategy === "create" || !
|
|
3959
|
+
const absPath = path17.join(project.path, rule.filePath);
|
|
3960
|
+
if (fs16.existsSync(absPath) && hasPiutReference2(absPath)) continue;
|
|
3961
|
+
if (rule.strategy === "create" || !fs16.existsSync(absPath)) {
|
|
3616
3962
|
const isAppendType = rule.strategy === "append";
|
|
3617
3963
|
const content = isAppendType ? PROJECT_SKILL_SNIPPET + "\n" : DEDICATED_FILE_CONTENT;
|
|
3618
|
-
|
|
3619
|
-
|
|
3964
|
+
fs16.mkdirSync(path17.dirname(absPath), { recursive: true });
|
|
3965
|
+
fs16.writeFileSync(absPath, content, "utf-8");
|
|
3620
3966
|
} else {
|
|
3621
|
-
|
|
3967
|
+
fs16.appendFileSync(absPath, APPEND_SECTION);
|
|
3622
3968
|
}
|
|
3623
3969
|
}
|
|
3624
3970
|
toolLine(projectName, success("connected"), "\u2714");
|
|
@@ -3634,24 +3980,24 @@ async function handleManageProjects(apiKey, validation) {
|
|
|
3634
3980
|
});
|
|
3635
3981
|
}
|
|
3636
3982
|
for (const { project } of toDisconnect) {
|
|
3637
|
-
const projectName =
|
|
3983
|
+
const projectName = path17.basename(project.path);
|
|
3638
3984
|
for (const dedicatedFile of DEDICATED_FILES) {
|
|
3639
|
-
const absPath =
|
|
3640
|
-
if (
|
|
3985
|
+
const absPath = path17.join(project.path, dedicatedFile);
|
|
3986
|
+
if (fs16.existsSync(absPath) && hasPiutReference2(absPath)) {
|
|
3641
3987
|
try {
|
|
3642
|
-
|
|
3988
|
+
fs16.unlinkSync(absPath);
|
|
3643
3989
|
} catch {
|
|
3644
3990
|
}
|
|
3645
3991
|
}
|
|
3646
3992
|
}
|
|
3647
3993
|
for (const appendFile of APPEND_FILES) {
|
|
3648
|
-
const absPath =
|
|
3649
|
-
if (
|
|
3994
|
+
const absPath = path17.join(project.path, appendFile);
|
|
3995
|
+
if (fs16.existsSync(absPath) && hasPiutReference2(absPath)) {
|
|
3650
3996
|
removePiutSection(absPath);
|
|
3651
3997
|
}
|
|
3652
3998
|
}
|
|
3653
|
-
const vscodeMcpPath =
|
|
3654
|
-
if (
|
|
3999
|
+
const vscodeMcpPath = path17.join(project.path, ".vscode", "mcp.json");
|
|
4000
|
+
if (fs16.existsSync(vscodeMcpPath) && isPiutConfigured(vscodeMcpPath, "servers")) {
|
|
3655
4001
|
removeFromConfig(vscodeMcpPath, "servers");
|
|
3656
4002
|
}
|
|
3657
4003
|
removePiutDir(project.path);
|
|
@@ -3743,16 +4089,16 @@ async function handleVaultUpload(apiKey) {
|
|
|
3743
4089
|
console.log();
|
|
3744
4090
|
let uploaded = 0;
|
|
3745
4091
|
for (const filePath of files) {
|
|
3746
|
-
const filename =
|
|
4092
|
+
const filename = path17.basename(filePath);
|
|
3747
4093
|
const ext = filename.includes(".") ? filename.split(".").pop()?.toLowerCase() || "" : "";
|
|
3748
4094
|
const isDocument = DOCUMENT_EXTENSIONS2.has(ext);
|
|
3749
4095
|
let content;
|
|
3750
4096
|
let encoding;
|
|
3751
4097
|
if (isDocument) {
|
|
3752
|
-
content =
|
|
4098
|
+
content = fs16.readFileSync(filePath).toString("base64");
|
|
3753
4099
|
encoding = "base64";
|
|
3754
4100
|
} else {
|
|
3755
|
-
content =
|
|
4101
|
+
content = fs16.readFileSync(filePath, "utf-8");
|
|
3756
4102
|
}
|
|
3757
4103
|
const spinner = new Spinner();
|
|
3758
4104
|
spinner.start(`Uploading ${filename}...`);
|
|
@@ -3828,7 +4174,7 @@ async function handleViewBrain(apiKey) {
|
|
|
3828
4174
|
}
|
|
3829
4175
|
|
|
3830
4176
|
// src/cli.ts
|
|
3831
|
-
var VERSION = "3.
|
|
4177
|
+
var VERSION = "3.10.0";
|
|
3832
4178
|
function withExit(fn) {
|
|
3833
4179
|
return async (...args2) => {
|
|
3834
4180
|
try {
|
|
@@ -3854,6 +4200,7 @@ program.command("remove").description("Remove all p\u0131ut configurations").act
|
|
|
3854
4200
|
program.command("login").description("Authenticate with p\u0131ut (email, browser, or API key)").action(withExit(loginCommand));
|
|
3855
4201
|
program.command("logout").description("Remove saved API key").action(logoutCommand);
|
|
3856
4202
|
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));
|
|
4203
|
+
program.command("refresh").description("Force all configured tools to reconnect to your MCP server").option("-k, --key <key>", "API key").action(withExit(refreshCommand));
|
|
3857
4204
|
var vault = program.command("vault").description("Manage your file vault (upload, list, read, delete)");
|
|
3858
4205
|
vault.command("list").description("List all files in your vault").option("-k, --key <key>", "API key").action(withExit(vaultListCommand));
|
|
3859
4206
|
vault.command("upload <file>").description("Upload a file to your vault").option("-k, --key <key>", "API key").action(withExit(vaultUploadCommand));
|