indelible-mcp 4.5.1 → 4.6.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/package.json +1 -1
- package/src/index.js +303 -35
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -257,11 +257,42 @@ async function checkHealth() {
|
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
async function getUtxos(address) {
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
|
|
260
|
+
const path = `/api/address/${address}/unspent`;
|
|
261
|
+
const res = await spvFetch(path);
|
|
262
|
+
const first = await res.json();
|
|
263
|
+
if (Array.isArray(first) && first.length > 0) {
|
|
264
|
+
process.stderr.write(`[MCP] UTXOs from bridge: ${first.length}
|
|
263
265
|
`);
|
|
264
|
-
|
|
266
|
+
return first;
|
|
267
|
+
}
|
|
268
|
+
let reached = 0;
|
|
269
|
+
try {
|
|
270
|
+
const bridges = await getBridges();
|
|
271
|
+
const settled = await Promise.allSettled(bridges.map(async (bridge) => {
|
|
272
|
+
const r = await fetch(`${bridge.url}${path}`, {
|
|
273
|
+
signal: AbortSignal.timeout(6e3),
|
|
274
|
+
headers: { "X-API-Key": bridge.apiKey }
|
|
275
|
+
});
|
|
276
|
+
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
277
|
+
return { name: bridge.name, utxos: await r.json() };
|
|
278
|
+
}));
|
|
279
|
+
for (const s of settled) {
|
|
280
|
+
if (s.status !== "fulfilled") continue;
|
|
281
|
+
reached++;
|
|
282
|
+
const u = s.value.utxos;
|
|
283
|
+
if (Array.isArray(u) && u.length > 0) {
|
|
284
|
+
process.stderr.write(`[MCP] UTXOs recovered via fan-out from ${s.value.name}: ${u.length}
|
|
285
|
+
`);
|
|
286
|
+
return u;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
} catch (e) {
|
|
290
|
+
process.stderr.write(`[MCP] UTXO fan-out error: ${e.message}
|
|
291
|
+
`);
|
|
292
|
+
}
|
|
293
|
+
process.stderr.write(`[MCP] UTXOs: 0 across ${reached} bridge(s)
|
|
294
|
+
`);
|
|
295
|
+
return Array.isArray(first) ? first : [];
|
|
265
296
|
}
|
|
266
297
|
async function getAddressHistory(address) {
|
|
267
298
|
const res = await spvFetch(`/api/address/${address}/history`);
|
|
@@ -1081,10 +1112,78 @@ var init_wall_stub = __esm({
|
|
|
1081
1112
|
init_config_customer();
|
|
1082
1113
|
import { createInterface as createInterface3 } from "node:readline";
|
|
1083
1114
|
import { execSync as execSync2 } from "node:child_process";
|
|
1084
|
-
import { homedir as
|
|
1085
|
-
import { join as
|
|
1086
|
-
import { fileURLToPath } from "node:url";
|
|
1087
|
-
import { readFileSync as
|
|
1115
|
+
import { homedir as homedir13 } from "node:os";
|
|
1116
|
+
import { join as join14, dirname as dirname5, resolve } from "node:path";
|
|
1117
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1118
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync15, mkdirSync as mkdirSync6, readdirSync, statSync } from "node:fs";
|
|
1119
|
+
|
|
1120
|
+
// mcp-server/lib/guardrails-run.js
|
|
1121
|
+
function contextFromInput(input) {
|
|
1122
|
+
const tool = input?.tool_name;
|
|
1123
|
+
const ti = input?.tool_input || {};
|
|
1124
|
+
return {
|
|
1125
|
+
tool,
|
|
1126
|
+
filePath: ti.file_path || "",
|
|
1127
|
+
// For Write the new content is `content`; for Edit it's `new_string`.
|
|
1128
|
+
added: tool === "Write" ? ti.content || "" : ti.new_string || "",
|
|
1129
|
+
command: ti.command || ""
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function runRules(input, rules) {
|
|
1133
|
+
const ctx = contextFromInput(input);
|
|
1134
|
+
for (const rule of rules) {
|
|
1135
|
+
let reason = null;
|
|
1136
|
+
try {
|
|
1137
|
+
reason = rule.test(ctx);
|
|
1138
|
+
} catch {
|
|
1139
|
+
reason = null;
|
|
1140
|
+
}
|
|
1141
|
+
if (reason) return { id: rule.id, severity: rule.severity, reason };
|
|
1142
|
+
}
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// mcp-server/lib/guardrails.customer.js
|
|
1147
|
+
var RULES = [
|
|
1148
|
+
{
|
|
1149
|
+
// Reading a private key or seed into the chat sends it to the model and into
|
|
1150
|
+
// the saved transcript. The key controls real funds — this is the one worth
|
|
1151
|
+
// hard-blocking. If the agent needs a non-secret field, it can read just that.
|
|
1152
|
+
id: "credential-pull",
|
|
1153
|
+
severity: "BLOCK",
|
|
1154
|
+
test: ({ command }) => {
|
|
1155
|
+
if (!command) return null;
|
|
1156
|
+
const READ = /\b(cat|less|more|head|tail|type|Get-Content|gc)\b/i;
|
|
1157
|
+
const SECRET = /(config\.json|\.env\b|\bwif\b|\bseed\b|private[-_ ]?key|\.indelible[\\/])/i;
|
|
1158
|
+
return READ.test(command) && SECRET.test(command) ? "That command would print your wallet key or seed into the conversation \u2014 which hands it to the model and writes it into the saved transcript. Your private key controls your funds; never read it into the chat. If you only need a non-secret value, read just that field." : null;
|
|
1159
|
+
}
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
// The irreversible-command guard. Advisory, never blocked — your repo, your
|
|
1163
|
+
// machine, your call — but a force-push / recursive delete / hard reset has no
|
|
1164
|
+
// undo, so make the blast radius visible before it runs.
|
|
1165
|
+
id: "destructive-op",
|
|
1166
|
+
severity: "WARN",
|
|
1167
|
+
test: ({ command }) => {
|
|
1168
|
+
if (!command) return null;
|
|
1169
|
+
const hits = [
|
|
1170
|
+
/git\s+push\b[^\n]*(--force\b|--force-with-lease\b|-f\b)/i,
|
|
1171
|
+
// rewrites remote history
|
|
1172
|
+
/git\s+reset\s+--hard\b/i,
|
|
1173
|
+
// discards local work
|
|
1174
|
+
/\brm\s+-[a-z]*r[a-z]*f\b/i,
|
|
1175
|
+
// rm -rf / -fr / -Rf
|
|
1176
|
+
/\brm\s+-[a-z]*f[a-z]*r\b/i,
|
|
1177
|
+
/\bRemove-Item\b[^\n]*-Recurse\b[^\n]*-Force\b/i
|
|
1178
|
+
// PowerShell recursive force-delete
|
|
1179
|
+
];
|
|
1180
|
+
return hits.some((re) => re.test(command)) ? "This command is irreversible \u2014 a force-push rewrites remote history, and rm -rf / reset --hard / Remove-Item -Recurse -Force cannot be undone. Confirm you have a backup and you are aiming at the right target before you run it." : null;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
];
|
|
1184
|
+
function evaluate(input) {
|
|
1185
|
+
return runRules(input, RULES);
|
|
1186
|
+
}
|
|
1088
1187
|
|
|
1089
1188
|
// mcp-server/tools/setup_wallet.js
|
|
1090
1189
|
init_config_customer();
|
|
@@ -5903,6 +6002,116 @@ async function recallContext({ from_date = null, to_date = null, query = null, d
|
|
|
5903
6002
|
};
|
|
5904
6003
|
}
|
|
5905
6004
|
|
|
6005
|
+
// mcp-server/tools/report_bug.js
|
|
6006
|
+
import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
|
|
6007
|
+
import { join as join13, dirname as dirname4 } from "path";
|
|
6008
|
+
import { homedir as homedir12 } from "os";
|
|
6009
|
+
import { fileURLToPath } from "url";
|
|
6010
|
+
import os from "os";
|
|
6011
|
+
var INTAKE_URL = process.env.INDELIBLE_BUG_INTAKE || "https://indelible.one/api/bug-report";
|
|
6012
|
+
var INDELIBLE_DIR = join13(homedir12(), ".indelible");
|
|
6013
|
+
function getMcpVersion(injected) {
|
|
6014
|
+
if (injected) return injected;
|
|
6015
|
+
const candidates = [];
|
|
6016
|
+
try {
|
|
6017
|
+
candidates.push(join13(dirname4(fileURLToPath(import.meta.url)), "..", "..", "package.json"));
|
|
6018
|
+
} catch {
|
|
6019
|
+
}
|
|
6020
|
+
try {
|
|
6021
|
+
candidates.push(join13(dirname4(fileURLToPath(import.meta.url)), "..", "package.json"));
|
|
6022
|
+
} catch {
|
|
6023
|
+
}
|
|
6024
|
+
candidates.push(join13(homedir12(), "AppData", "Roaming", "npm", "node_modules", "indelible-mcp", "package.json"));
|
|
6025
|
+
candidates.push("/usr/local/lib/node_modules/indelible-mcp/package.json");
|
|
6026
|
+
candidates.push("/usr/lib/node_modules/indelible-mcp/package.json");
|
|
6027
|
+
for (const p of candidates) {
|
|
6028
|
+
try {
|
|
6029
|
+
if (!existsSync14(p)) continue;
|
|
6030
|
+
const pkg = JSON.parse(readFileSync10(p, "utf8"));
|
|
6031
|
+
if (pkg && pkg.name === "indelible-mcp" && pkg.version) return pkg.version;
|
|
6032
|
+
} catch {
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
return "unknown";
|
|
6036
|
+
}
|
|
6037
|
+
function readJson(path) {
|
|
6038
|
+
try {
|
|
6039
|
+
return existsSync14(path) ? JSON.parse(readFileSync10(path, "utf8")) : null;
|
|
6040
|
+
} catch {
|
|
6041
|
+
return null;
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
function getWalletAddress(config) {
|
|
6045
|
+
return config?.address || config?.wallet_address || config?.wallet?.address || null;
|
|
6046
|
+
}
|
|
6047
|
+
function getRecentReceipts(n = 5) {
|
|
6048
|
+
try {
|
|
6049
|
+
const p = join13(INDELIBLE_DIR, "save-log.jsonl");
|
|
6050
|
+
if (!existsSync14(p)) return [];
|
|
6051
|
+
const lines = readFileSync10(p, "utf8").trim().split("\n").filter(Boolean).slice(-n);
|
|
6052
|
+
return lines.map((l) => {
|
|
6053
|
+
try {
|
|
6054
|
+
const e = JSON.parse(l);
|
|
6055
|
+
return {
|
|
6056
|
+
txid: e.txid || e.txId || null,
|
|
6057
|
+
source: e.source || null,
|
|
6058
|
+
bridge: e.bridge || null,
|
|
6059
|
+
confirmed: e.confirmed ?? null,
|
|
6060
|
+
error: e.error || null,
|
|
6061
|
+
ts: e.ts || e.timestamp || null
|
|
6062
|
+
};
|
|
6063
|
+
} catch {
|
|
6064
|
+
return null;
|
|
6065
|
+
}
|
|
6066
|
+
}).filter(Boolean);
|
|
6067
|
+
} catch {
|
|
6068
|
+
return [];
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
function redactSecrets2(s) {
|
|
6072
|
+
if (s == null) return s;
|
|
6073
|
+
return String(s).replace(/\b[KL5][1-9A-HJ-NP-Za-km-z]{50,51}\b/g, "[REDACTED-WIF]");
|
|
6074
|
+
}
|
|
6075
|
+
async function reportBug(args2 = {}, mcpVersion) {
|
|
6076
|
+
const { summary, description, severity = "medium", tool_name = null, last_error = null } = args2;
|
|
6077
|
+
if (!summary || !description) {
|
|
6078
|
+
return { success: false, error: "summary and description are required (you supply the narrative; the tool supplies the facts)." };
|
|
6079
|
+
}
|
|
6080
|
+
const config = readJson(join13(INDELIBLE_DIR, "config.json")) || {};
|
|
6081
|
+
const payload = {
|
|
6082
|
+
// AI-supplied narrative (defensively redacted + length-capped)
|
|
6083
|
+
summary: redactSecrets2(summary).slice(0, 200),
|
|
6084
|
+
description: redactSecrets2(description).slice(0, 5e3),
|
|
6085
|
+
severity: ["low", "medium", "high"].includes(severity) ? severity : "medium",
|
|
6086
|
+
tool_name: tool_name ? String(tool_name).slice(0, 80) : null,
|
|
6087
|
+
last_error: redactSecrets2(last_error),
|
|
6088
|
+
// Machine-pulled facts (the AI cannot get these wrong)
|
|
6089
|
+
mcp_version: getMcpVersion(mcpVersion),
|
|
6090
|
+
wallet_address: getWalletAddress(config),
|
|
6091
|
+
recent_receipts: getRecentReceipts(5),
|
|
6092
|
+
environment: { os: `${os.platform()} ${os.release()}`, arch: os.arch(), node: process.version },
|
|
6093
|
+
reported_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6094
|
+
client: "indelible-mcp"
|
|
6095
|
+
};
|
|
6096
|
+
try {
|
|
6097
|
+
const resp = await fetch(INTAKE_URL, {
|
|
6098
|
+
method: "POST",
|
|
6099
|
+
headers: { "Content-Type": "application/json" },
|
|
6100
|
+
body: JSON.stringify(payload)
|
|
6101
|
+
});
|
|
6102
|
+
if (!resp.ok) {
|
|
6103
|
+
return { success: false, error: `intake responded ${resp.status}`, sent: { summary: payload.summary, mcp_version: payload.mcp_version } };
|
|
6104
|
+
}
|
|
6105
|
+
const data = await resp.json().catch(() => ({}));
|
|
6106
|
+
if (data.known_issue) {
|
|
6107
|
+
return { success: true, status: "known_issue", issue_id: data.issue_id || null, message: data.message || "Matched a known, already-fixed issue." };
|
|
6108
|
+
}
|
|
6109
|
+
return { success: true, status: "filed", ticket_id: data.ticket_id || null, message: data.message || "Bug report sent to the Indelible team." };
|
|
6110
|
+
} catch (err) {
|
|
6111
|
+
return { success: false, error: `could not reach intake (${err.message}). Fallback: save_file the report on-chain and share the txid.` };
|
|
6112
|
+
}
|
|
6113
|
+
}
|
|
6114
|
+
|
|
5906
6115
|
// mcp-server/tools/share_session.js
|
|
5907
6116
|
init_config_customer();
|
|
5908
6117
|
init_spv();
|
|
@@ -6086,16 +6295,16 @@ init_wall_stub();
|
|
|
6086
6295
|
init_wall_stub();
|
|
6087
6296
|
init_wall_stub();
|
|
6088
6297
|
init_wall_stub();
|
|
6089
|
-
var PROJECT_ROOT_FOR_CLONE_CHECK = resolve(
|
|
6090
|
-
var CONTEXT_FILE2 =
|
|
6298
|
+
var PROJECT_ROOT_FOR_CLONE_CHECK = resolve(dirname5(fileURLToPath2(import.meta.url)), "../..");
|
|
6299
|
+
var CONTEXT_FILE2 = join14(homedir13(), ".indelible", "indelible-context.jsonl");
|
|
6091
6300
|
function installHooks() {
|
|
6092
|
-
const claudeDir =
|
|
6093
|
-
const settingsPath =
|
|
6094
|
-
if (!
|
|
6301
|
+
const claudeDir = join14(homedir13(), ".claude");
|
|
6302
|
+
const settingsPath = join14(claudeDir, "settings.local.json");
|
|
6303
|
+
if (!existsSync15(claudeDir)) mkdirSync6(claudeDir, { recursive: true });
|
|
6095
6304
|
let settings = {};
|
|
6096
|
-
if (
|
|
6305
|
+
if (existsSync15(settingsPath)) {
|
|
6097
6306
|
try {
|
|
6098
|
-
settings = JSON.parse(
|
|
6307
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
6099
6308
|
} catch {
|
|
6100
6309
|
settings = {};
|
|
6101
6310
|
}
|
|
@@ -6110,6 +6319,9 @@ function installHooks() {
|
|
|
6110
6319
|
const hasSessionStart = settings.hooks.SessionStart?.some(
|
|
6111
6320
|
(h) => h.hooks?.some((hh) => hh.command?.includes("indelible-mcp hook session-start")) || h.command?.includes("indelible-mcp hook session-start")
|
|
6112
6321
|
);
|
|
6322
|
+
const hasPreToolUse = settings.hooks.PreToolUse?.some(
|
|
6323
|
+
(h) => h.hooks?.some((hh) => hh.command?.includes("indelible-mcp hook pre-tool-use")) || h.command?.includes("indelible-mcp hook pre-tool-use")
|
|
6324
|
+
);
|
|
6113
6325
|
const installed = [];
|
|
6114
6326
|
if (!hasPreCompact) {
|
|
6115
6327
|
if (!settings.hooks.PreCompact) settings.hooks.PreCompact = [];
|
|
@@ -6135,9 +6347,38 @@ function installHooks() {
|
|
|
6135
6347
|
});
|
|
6136
6348
|
installed.push("SessionStart:style");
|
|
6137
6349
|
}
|
|
6350
|
+
if (!hasPreToolUse) {
|
|
6351
|
+
if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
|
|
6352
|
+
settings.hooks.PreToolUse.push({
|
|
6353
|
+
matcher: "Bash",
|
|
6354
|
+
hooks: [{ type: "command", command: "indelible-mcp hook pre-tool-use" }]
|
|
6355
|
+
});
|
|
6356
|
+
installed.push("PreToolUse:guardrails");
|
|
6357
|
+
}
|
|
6138
6358
|
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2));
|
|
6139
6359
|
return { settingsPath, installed, alreadyInstalled: installed.length === 0 };
|
|
6140
6360
|
}
|
|
6361
|
+
function runPreToolUseGuard() {
|
|
6362
|
+
let code = 0;
|
|
6363
|
+
try {
|
|
6364
|
+
const raw = readFileSync11(0, "utf8").trim();
|
|
6365
|
+
if (!raw) process.exit(0);
|
|
6366
|
+
const hit = evaluate(JSON.parse(raw));
|
|
6367
|
+
if (hit) {
|
|
6368
|
+
if (hit.severity === "BLOCK") {
|
|
6369
|
+
process.stderr.write(`[indelible:${hit.id}] BLOCKED: ${hit.reason}
|
|
6370
|
+
`);
|
|
6371
|
+
code = 2;
|
|
6372
|
+
} else {
|
|
6373
|
+
process.stdout.write(`[indelible:${hit.id}] advisory: ${hit.reason} (not blocked)
|
|
6374
|
+
`);
|
|
6375
|
+
}
|
|
6376
|
+
}
|
|
6377
|
+
} catch {
|
|
6378
|
+
code = 0;
|
|
6379
|
+
}
|
|
6380
|
+
process.exit(code);
|
|
6381
|
+
}
|
|
6141
6382
|
async function runCli(args2) {
|
|
6142
6383
|
const command = args2[0];
|
|
6143
6384
|
switch (command) {
|
|
@@ -6325,7 +6566,7 @@ Commands:
|
|
|
6325
6566
|
}
|
|
6326
6567
|
function printHelp() {
|
|
6327
6568
|
console.log(`
|
|
6328
|
-
Indelible MCP \u2014 Blockchain memory for Claude Code (v4.
|
|
6569
|
+
Indelible MCP \u2014 Blockchain memory for Claude Code (v4.6.0)
|
|
6329
6570
|
|
|
6330
6571
|
Setup:
|
|
6331
6572
|
indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
|
|
@@ -6365,6 +6606,7 @@ Payments:
|
|
|
6365
6606
|
Hooks (auto-called by Claude Code):
|
|
6366
6607
|
indelible-mcp hook pre-compact Auto-save before compaction
|
|
6367
6608
|
indelible-mcp hook post-compact Auto-restore after compaction
|
|
6609
|
+
indelible-mcp hook pre-tool-use Guardrails: block a key leak / flag an irreversible command
|
|
6368
6610
|
|
|
6369
6611
|
Get your key: Sign in at indelible.one \u2192 Settings \u2192 Private Key
|
|
6370
6612
|
Learn more: https://indelible.one
|
|
@@ -6399,20 +6641,20 @@ async function runPreCompactSave() {
|
|
|
6399
6641
|
process.exit(0);
|
|
6400
6642
|
}
|
|
6401
6643
|
function findMemoryDir() {
|
|
6402
|
-
const projectsDir =
|
|
6403
|
-
if (!
|
|
6644
|
+
const projectsDir = join14(homedir13(), ".claude", "projects");
|
|
6645
|
+
if (!existsSync15(projectsDir)) return null;
|
|
6404
6646
|
let newestTime = 0;
|
|
6405
6647
|
let newestProject = null;
|
|
6406
6648
|
const projects = readdirSync(projectsDir);
|
|
6407
6649
|
for (const project of projects) {
|
|
6408
|
-
const projectPath =
|
|
6650
|
+
const projectPath = join14(projectsDir, project);
|
|
6409
6651
|
try {
|
|
6410
6652
|
const pStat = statSync(projectPath);
|
|
6411
6653
|
if (!pStat.isDirectory()) continue;
|
|
6412
6654
|
const files = readdirSync(projectPath);
|
|
6413
6655
|
for (const file of files) {
|
|
6414
6656
|
if (!file.endsWith(".jsonl")) continue;
|
|
6415
|
-
const fStat = statSync(
|
|
6657
|
+
const fStat = statSync(join14(projectPath, file));
|
|
6416
6658
|
if (fStat.mtimeMs > newestTime) {
|
|
6417
6659
|
newestTime = fStat.mtimeMs;
|
|
6418
6660
|
newestProject = projectPath;
|
|
@@ -6421,7 +6663,7 @@ function findMemoryDir() {
|
|
|
6421
6663
|
} catch {
|
|
6422
6664
|
}
|
|
6423
6665
|
}
|
|
6424
|
-
return newestProject ?
|
|
6666
|
+
return newestProject ? join14(newestProject, "memory") : null;
|
|
6425
6667
|
}
|
|
6426
6668
|
async function runPostCompactRestore() {
|
|
6427
6669
|
const config = await loadConfig();
|
|
@@ -6470,10 +6712,10 @@ async function runPostCompactRestore() {
|
|
|
6470
6712
|
try {
|
|
6471
6713
|
const memoryDir = findMemoryDir();
|
|
6472
6714
|
if (memoryDir && (config.memory_file_txid || config.session_history_txid)) {
|
|
6473
|
-
if (!
|
|
6715
|
+
if (!existsSync15(memoryDir)) mkdirSync6(memoryDir, { recursive: true });
|
|
6474
6716
|
if (config.memory_file_txid) {
|
|
6475
|
-
const memPath =
|
|
6476
|
-
if (!
|
|
6717
|
+
const memPath = join14(memoryDir, "MEMORY.md");
|
|
6718
|
+
if (!existsSync15(memPath)) {
|
|
6477
6719
|
const memResult = await loadFile(config.memory_file_txid, { outputPath: memPath });
|
|
6478
6720
|
if (memResult.success) {
|
|
6479
6721
|
process.stderr.write(`Indelible: MEMORY.md restored from chain (file was missing)
|
|
@@ -6482,8 +6724,8 @@ async function runPostCompactRestore() {
|
|
|
6482
6724
|
}
|
|
6483
6725
|
}
|
|
6484
6726
|
if (config.session_history_txid) {
|
|
6485
|
-
const histPath =
|
|
6486
|
-
if (!
|
|
6727
|
+
const histPath = join14(memoryDir, "session-history.md");
|
|
6728
|
+
if (!existsSync15(histPath)) {
|
|
6487
6729
|
const histResult = await loadFile(config.session_history_txid, { outputPath: histPath });
|
|
6488
6730
|
if (histResult.success) {
|
|
6489
6731
|
process.stderr.write(`Indelible: session-history.md restored from chain (file was missing)
|
|
@@ -6499,9 +6741,9 @@ async function runPostCompactRestore() {
|
|
|
6499
6741
|
try {
|
|
6500
6742
|
const histDir = findMemoryDir();
|
|
6501
6743
|
if (histDir) {
|
|
6502
|
-
const histPath =
|
|
6503
|
-
if (
|
|
6504
|
-
const histContent =
|
|
6744
|
+
const histPath = join14(histDir, "session-history.md");
|
|
6745
|
+
if (existsSync15(histPath)) {
|
|
6746
|
+
const histContent = readFileSync11(histPath, "utf-8");
|
|
6505
6747
|
const lines = histContent.split("\n");
|
|
6506
6748
|
const entryStarts = [];
|
|
6507
6749
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -6543,7 +6785,7 @@ function readStdin() {
|
|
|
6543
6785
|
}
|
|
6544
6786
|
var SERVER_INFO = {
|
|
6545
6787
|
name: "indelible",
|
|
6546
|
-
version: "4.
|
|
6788
|
+
version: "4.6.0",
|
|
6547
6789
|
description: "Blockchain-backed memory and code storage for Claude Code"
|
|
6548
6790
|
};
|
|
6549
6791
|
var TOOLS = [
|
|
@@ -6827,6 +7069,21 @@ var TOOLS = [
|
|
|
6827
7069
|
required: []
|
|
6828
7070
|
}
|
|
6829
7071
|
},
|
|
7072
|
+
{
|
|
7073
|
+
name: "report_bug",
|
|
7074
|
+
description: `File an ACCURATE bug report to the Indelible team. You supply ONLY the narrative (summary, description, severity, optional tool_name/last_error). The tool auto-attaches the real MCP version, wallet address, recent save receipts, and environment \u2014 so DO NOT invent versions, txids, URLs, or destinations; leave the facts to the tool. Before filing: (1) triage transients \u2014 retry "No UTXOs"/"gateway unreachable" blips (usually an unconfirmed change UTXO) before reporting; (2) do not file known/already-fixed issues (the server auto-matches these); (3) get the user's OK first. Returns status:"filed" (ticket_id) or status:"known_issue" (instant triage).`,
|
|
7075
|
+
inputSchema: {
|
|
7076
|
+
type: "object",
|
|
7077
|
+
properties: {
|
|
7078
|
+
summary: { type: "string", description: "One-line summary of the bug." },
|
|
7079
|
+
description: { type: "string", description: "What you were doing, what went wrong, and exact repro steps." },
|
|
7080
|
+
severity: { type: "string", enum: ["low", "medium", "high"], description: "User-facing impact." },
|
|
7081
|
+
tool_name: { type: "string", description: "Which Indelible tool/command failed (optional)." },
|
|
7082
|
+
last_error: { type: "string", description: "The exact error string you saw (optional)." }
|
|
7083
|
+
},
|
|
7084
|
+
required: ["summary", "description"]
|
|
7085
|
+
}
|
|
7086
|
+
},
|
|
6830
7087
|
{
|
|
6831
7088
|
name: "share_session",
|
|
6832
7089
|
description: "Issue a viewing key for a saved session, granting selective access to a recipient (g-170). Recipient gets an ECIES-wrapped content key bound to their wallet.",
|
|
@@ -6947,6 +7204,15 @@ async function handleMcpRequest(request) {
|
|
|
6947
7204
|
max_index: args2?.max_index || 150
|
|
6948
7205
|
});
|
|
6949
7206
|
break;
|
|
7207
|
+
case "report_bug":
|
|
7208
|
+
result = await reportBug({
|
|
7209
|
+
summary: args2?.summary,
|
|
7210
|
+
description: args2?.description,
|
|
7211
|
+
severity: args2?.severity || "medium",
|
|
7212
|
+
tool_name: args2?.tool_name || null,
|
|
7213
|
+
last_error: args2?.last_error || null
|
|
7214
|
+
});
|
|
7215
|
+
break;
|
|
6950
7216
|
case "share_session":
|
|
6951
7217
|
result = await shareSession({
|
|
6952
7218
|
session_txid: args2?.session_txid,
|
|
@@ -7022,11 +7288,11 @@ async function runWizard() {
|
|
|
7022
7288
|
}
|
|
7023
7289
|
let mcpOk = false;
|
|
7024
7290
|
for (const cfgPath of [
|
|
7025
|
-
|
|
7026
|
-
|
|
7291
|
+
join14(homedir13(), ".claude.json"),
|
|
7292
|
+
join14(homedir13(), ".claude", "settings.json")
|
|
7027
7293
|
]) {
|
|
7028
7294
|
try {
|
|
7029
|
-
const s = JSON.parse(
|
|
7295
|
+
const s = JSON.parse(readFileSync11(cfgPath, "utf8"));
|
|
7030
7296
|
if (s?.mcpServers?.indelible) {
|
|
7031
7297
|
mcpOk = true;
|
|
7032
7298
|
break;
|
|
@@ -7036,10 +7302,10 @@ async function runWizard() {
|
|
|
7036
7302
|
}
|
|
7037
7303
|
console.log(mcpOk ? " \u2713 MCP registered with Claude Code" : " \u2717 MCP not registered");
|
|
7038
7304
|
if (!mcpOk) allGood = false;
|
|
7039
|
-
const hooksPath =
|
|
7305
|
+
const hooksPath = join14(homedir13(), ".claude", "settings.local.json");
|
|
7040
7306
|
let hooksOk = false;
|
|
7041
7307
|
try {
|
|
7042
|
-
const s = JSON.parse(
|
|
7308
|
+
const s = JSON.parse(readFileSync11(hooksPath, "utf8"));
|
|
7043
7309
|
hooksOk = s?.hooks?.PreCompact?.some(
|
|
7044
7310
|
(h) => h.hooks?.some((hh) => hh.command?.includes("indelible-mcp")) || h.command?.includes("indelible-mcp")
|
|
7045
7311
|
) && s?.hooks?.SessionStart?.some(
|
|
@@ -7273,6 +7539,8 @@ if (args[0] === "hook") {
|
|
|
7273
7539
|
}
|
|
7274
7540
|
process.exit(0);
|
|
7275
7541
|
})();
|
|
7542
|
+
} else if (args[1] === "pre-tool-use") {
|
|
7543
|
+
runPreToolUseGuard();
|
|
7276
7544
|
} else {
|
|
7277
7545
|
console.error("Unknown hook:", args[1]);
|
|
7278
7546
|
process.exit(1);
|