indelible-mcp 2.2.0 → 2.3.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 CHANGED
@@ -6,41 +6,27 @@ Blockchain-backed memory for Claude Code. Save your AI conversations permanently
6
6
 
7
7
  ```bash
8
8
  npm install -g indelible-mcp
9
- indelible-mcp --show-config
9
+ indelible-mcp setup
10
10
  ```
11
11
 
12
- Copy the config output and paste it into your Claude Code settings, then restart Claude Code.
13
-
14
12
  ## Setup
15
13
 
16
- ### 1. Sign Up at indelible.one
17
-
18
- Sign in with Google at [indelible.one](https://indelible.one). Your BSV private key (WIF) is generated automatically and shown in Settings.
19
-
20
- ### 2. Install
14
+ ### 1. Install & Create Wallet
21
15
 
22
16
  ```bash
23
17
  npm install -g indelible-mcp
18
+ indelible-mcp setup
24
19
  ```
25
20
 
26
- ### 3. Import Your Key
27
-
28
- ```bash
29
- indelible-mcp setup --wif=YOUR_KEY
30
- ```
31
-
32
- Paste the WIF from your indelible.one Settings page. This saves it to `~/.indelible/config.json`.
21
+ This generates a BSV keypair locally (your private key never leaves your machine) and registers with the Indelible server.
33
22
 
34
- **Your key never leaves your machine.**
23
+ ### 2. Fund Your Wallet
35
24
 
36
- ### 4. Configure Claude Code
25
+ Send a small amount of BSV to the address shown after setup. Any BSV wallet works (HandCash, RelayX, etc.).
37
26
 
38
- Run:
39
- ```bash
40
- indelible-mcp --show-config
41
- ```
27
+ ### 3. Add MCP Config to Claude Code
42
28
 
43
- Add the output to your Claude Code settings:
29
+ Run `indelible-mcp --show-config` to get your config, or add this to your Claude Code `settings.json`:
44
30
 
45
31
  ```json
46
32
  {
@@ -52,52 +38,45 @@ Add the output to your Claude Code settings:
52
38
  }
53
39
  ```
54
40
 
55
- ### 5. Restart Claude Code
41
+ ## Usage
56
42
 
57
- Close and reopen VS Code (or your Claude Code client).
43
+ Once configured, just talk to Claude Code naturally:
58
44
 
59
- ### 6. Fund Your Wallet
45
+ - **"Save this session to blockchain"** - saves your conversation
46
+ - **"Load my previous context"** - restores past sessions
47
+ - **"Delta save"** - saves only new messages (cheaper, faster)
60
48
 
61
- Send a small amount of BSV (~$0.01) to your address for transaction fees.
49
+ ### Auto-Save & Auto-Restore
62
50
 
63
- ## Tools
51
+ Indelible automatically saves before Claude Code compacts your context, and restores your sessions after compaction. Nothing is lost.
64
52
 
65
- ### `setup_wallet`
66
- Import your BSV wallet from indelible.one. Only needed once.
53
+ ### CLI Commands
67
54
 
68
55
  ```
69
- "Import my wallet" or indelible-mcp setup --wif=YOUR_KEY
70
- ```
71
-
72
- ### `save_session`
73
- Save the current conversation to the blockchain.
74
-
75
- ```
76
- "Save this session to blockchain"
77
- ```
78
-
79
- ### `load_context`
80
- Load previous sessions from the blockchain.
81
-
82
- ```
83
- "Load my previous context"
56
+ indelible-mcp setup Generate wallet & register
57
+ indelible-mcp setup --migrate Derive API key from existing wallet
58
+ indelible-mcp save Save current session
59
+ indelible-mcp save --summary XYZ Save with custom summary
60
+ indelible-mcp load Load context from blockchain
61
+ indelible-mcp load --sessions=5 Load N sessions
62
+ indelible-mcp status Show account status
63
+ indelible-mcp hook pre-compact Pre-compaction save hook
64
+ indelible-mcp hook post-compact Post-compaction restore hook
84
65
  ```
85
66
 
86
67
  ## How It Works
87
68
 
88
- 1. Your conversations are encrypted with AES-256-GCM using your WIF-derived key
89
- 2. Encrypted data is stored on the BSV blockchain via OP_RETURN
90
- 3. Only you can decrypt your data (using your WIF)
91
- 4. Sessions are chained together for continuity
92
-
93
- ## View Your Data
94
-
95
- Visit [indelible.one](https://indelible.one) and sign in with Google to see all your saved sessions and code.
69
+ 1. Your conversation is encrypted locally with your WIF key
70
+ 2. An OP_RETURN transaction is built and signed locally using `@bsv/sdk`
71
+ 3. The signed transaction is broadcast via Indelible's SPV bridge
72
+ 4. Your private key **never** leaves your machine
96
73
 
97
- ## Cost
74
+ ## Security
98
75
 
99
- ~1 satoshi per byte. A typical session costs less than $0.01.
76
+ - **Zero-knowledge encryption** - your WIF-derived AES-256-GCM key encrypts all data before it touches the network
77
+ - **Self-sovereign keys** - generated locally with `@bsv/sdk`, never transmitted
78
+ - **Immutable storage** - once on BSV, your data cannot be altered or deleted
100
79
 
101
- ## License
80
+ ## Learn More
102
81
 
103
- MIT
82
+ [indelible.one](https://indelible.one)
package/package.json CHANGED
@@ -1,35 +1,39 @@
1
1
  {
2
2
  "name": "indelible-mcp",
3
- "version": "2.2.0",
4
- "description": "MCP server for Indelible AI - blockchain-backed memory for Claude Code",
3
+ "version": "2.3.0",
4
+ "description": "Blockchain-backed memory and code storage for Claude Code. Save AI conversations and source code permanently on BSV.",
5
5
  "type": "module",
6
- "main": "index.js",
6
+ "main": "src/index.js",
7
7
  "bin": {
8
- "indelible-mcp": "index.js"
8
+ "indelible-mcp": "src/index.js"
9
9
  },
10
10
  "files": [
11
- "index.js",
12
- "lib/",
13
- "tools/",
14
- "README.md"
11
+ "src/"
15
12
  ],
16
13
  "scripts": {
17
- "start": "node index.js"
14
+ "start": "node src/index.js",
15
+ "build": "bun build --compile src/index.js --outfile dist/indelible.exe",
16
+ "build:all": "bun build --compile --target=bun-windows-x64 src/index.js --outfile dist/indelible-win-x64.exe && bun build --compile --target=bun-linux-x64 src/index.js --outfile dist/indelible-linux-x64 && bun build --compile --target=bun-darwin-arm64 src/index.js --outfile dist/indelible-darwin-arm64"
18
17
  },
19
18
  "keywords": [
19
+ "claude-code",
20
20
  "mcp",
21
- "claude",
22
- "indelible",
23
21
  "blockchain",
24
22
  "bsv",
25
- "ai-memory"
23
+ "memory",
24
+ "context",
25
+ "indelible",
26
+ "code-vault",
27
+ "encrypted-storage"
26
28
  ],
27
- "author": "",
28
29
  "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/indelibleai/indelible-mcp"
33
+ },
34
+ "homepage": "https://indelible.one",
29
35
  "dependencies": {
30
- "@anthropic-ai/sdk": "^0.52.0",
31
- "@bsv/sdk": "^1.1.23",
32
- "cross-keychain": "^1.1.0",
33
- "node-fetch": "^3.3.2"
36
+ "@bsv/sdk": "1.10.1",
37
+ "cross-keychain": "^1.1.0"
34
38
  }
35
39
  }
package/src/index.js ADDED
@@ -0,0 +1,432 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Indelible CLI — Blockchain memory for Claude Code
4
+ *
5
+ * Modes:
6
+ * indelible-mcp setup --wif=KEY Import private key from indelible.one
7
+ * indelible-mcp save [--summary] Save current session to blockchain
8
+ * indelible-mcp load [--sessions] Load context from blockchain
9
+ * indelible-mcp status Show account status
10
+ * indelible-mcp hook pre-compact Pre-compaction save hook
11
+ * indelible-mcp hook post-compact Post-compaction restore hook
12
+ * (stdin pipe, no TTY) MCP stdio JSON-RPC server mode
13
+ */
14
+
15
+ import { createInterface } from 'node:readline'
16
+ import { homedir } from 'node:os'
17
+ import { join } from 'node:path'
18
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs'
19
+ import { loadConfig } from './lib/config.js'
20
+ import { setupWallet } from './tools/setup_wallet.js'
21
+ import { saveSession } from './tools/save_session.js'
22
+ import { loadContext } from './tools/load_context.js'
23
+ import { saveFile } from './tools/save_file.js'
24
+ import { saveProject } from './tools/save_project.js'
25
+ import { loadFile } from './tools/load_file.js'
26
+ import { loadProject } from './tools/load_project.js'
27
+
28
+ const CONTEXT_FILE = join(homedir(), '.indelible', 'indelible-context.jsonl')
29
+
30
+ // ============ HOOKS INSTALLER ============
31
+
32
+ function installHooks() {
33
+ const claudeDir = join(homedir(), '.claude')
34
+ const settingsPath = join(claudeDir, 'settings.local.json')
35
+
36
+ if (!existsSync(claudeDir)) mkdirSync(claudeDir, { recursive: true })
37
+
38
+ let settings = {}
39
+ if (existsSync(settingsPath)) {
40
+ try { settings = JSON.parse(readFileSync(settingsPath, 'utf8')) } catch { settings = {} }
41
+ }
42
+
43
+ if (!settings.hooks) settings.hooks = {}
44
+
45
+ // Check if already installed
46
+ const hasPreCompact = settings.hooks.PreCompact?.some(h =>
47
+ h.hooks?.some(hh => hh.command?.includes('indelible-mcp hook pre-compact')) ||
48
+ h.command?.includes('indelible-mcp hook pre-compact')
49
+ )
50
+ const hasSessionStart = settings.hooks.SessionStart?.some(h =>
51
+ h.hooks?.some(hh => hh.command?.includes('indelible-mcp hook post-compact')) ||
52
+ h.command?.includes('indelible-mcp hook post-compact')
53
+ )
54
+
55
+ const installed = []
56
+
57
+ if (!hasPreCompact) {
58
+ if (!settings.hooks.PreCompact) settings.hooks.PreCompact = []
59
+ settings.hooks.PreCompact.push({
60
+ matcher: '',
61
+ hooks: [{ type: 'command', command: 'indelible-mcp hook pre-compact' }]
62
+ })
63
+ installed.push('PreCompact')
64
+ }
65
+
66
+ if (!hasSessionStart) {
67
+ if (!settings.hooks.SessionStart) settings.hooks.SessionStart = []
68
+ settings.hooks.SessionStart.push({
69
+ matcher: 'compact',
70
+ hooks: [{ type: 'command', command: 'indelible-mcp hook post-compact' }]
71
+ })
72
+ installed.push('SessionStart')
73
+ }
74
+
75
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2))
76
+ return { settingsPath, installed, alreadyInstalled: installed.length === 0 }
77
+ }
78
+
79
+ // ============ CLI MODE ============
80
+
81
+ async function runCli(args) {
82
+ const command = args[0]
83
+
84
+ switch (command) {
85
+ case 'setup': {
86
+ const apiUrl = args.find(a => a.startsWith('--api='))?.split('=')[1]
87
+ const importWif = args.find(a => a.startsWith('--wif='))?.split('=')[1]
88
+ const pin = args.find(a => a.startsWith('--pin='))?.split('=')[1]
89
+ const result = await setupWallet(apiUrl || undefined, importWif, pin)
90
+ // Auto-install hooks into Claude Code settings
91
+ const hooks = installHooks()
92
+ result.hooks = hooks.alreadyInstalled
93
+ ? 'Auto-save hooks already installed'
94
+ : `Installed hooks: ${hooks.installed.join(', ')} → ${hooks.settingsPath}`
95
+ console.log(JSON.stringify(result, null, 2))
96
+ break
97
+ }
98
+
99
+ case 'save': {
100
+ const summaryIdx = args.indexOf('--summary')
101
+ const summary = summaryIdx !== -1 ? args.slice(summaryIdx + 1).join(' ') : undefined
102
+ const result = await saveSession(CONTEXT_FILE, summary)
103
+ console.log(JSON.stringify(result, null, 2))
104
+ break
105
+ }
106
+
107
+ case 'load': {
108
+ const sessionsArg = args.find(a => a.startsWith('--sessions='))
109
+ const numSessions = sessionsArg ? parseInt(sessionsArg.split('=')[1]) : 5
110
+ const result = await loadContext(numSessions)
111
+ if (result.context) {
112
+ // Output context to stdout (for piping)
113
+ console.log(result.context)
114
+ } else {
115
+ console.log(JSON.stringify(result, null, 2))
116
+ }
117
+ break
118
+ }
119
+
120
+ case 'status': {
121
+ const config = loadConfig()
122
+ if (!config?.wif_encrypted && !config?.wif) {
123
+ console.log(JSON.stringify({ error: 'Wallet not configured. Run: indelible-mcp setup' }))
124
+ break
125
+ }
126
+ console.log(JSON.stringify({
127
+ address: config.address,
128
+ encrypted: !!config.wif_encrypted,
129
+ api_key: config.api_key ? '***' + config.api_key.slice(-8) : 'not set',
130
+ api_url: config.api_url,
131
+ last_session_id: config.last_session_id || null,
132
+ last_saved_message_count: config.last_saved_message_count || 0
133
+ }, null, 2))
134
+ break
135
+ }
136
+
137
+ case 'vault': {
138
+ const subCmd = args[1]
139
+ if (subCmd === 'save-file') {
140
+ const filePath = args[2]
141
+ if (!filePath) { console.error('Usage: indelible-mcp vault save-file <path>'); break }
142
+ const result = await saveFile(filePath)
143
+ console.log(JSON.stringify(result, null, 2))
144
+ } else if (subCmd === 'save-project') {
145
+ const dirPath = args[2]
146
+ if (!dirPath) { console.error('Usage: indelible-mcp vault save-project <dir>'); break }
147
+ const nameArg = args.find(a => a.startsWith('--name='))
148
+ const name = nameArg ? nameArg.split('=')[1] : undefined
149
+ const result = await saveProject(dirPath, { name })
150
+ console.log(JSON.stringify(result, null, 2))
151
+ } else if (subCmd === 'load-file') {
152
+ const txId = args[2]
153
+ if (!txId) { console.error('Usage: indelible-mcp vault load-file <txid> [--output=path]'); break }
154
+ const outArg = args.find(a => a.startsWith('--output='))
155
+ const outputPath = outArg ? outArg.split('=')[1] : undefined
156
+ const result = await loadFile(txId, { outputPath })
157
+ console.log(JSON.stringify(result, null, 2))
158
+ } else if (subCmd === 'load-project') {
159
+ const txId = args[2]
160
+ if (!txId) { console.error('Usage: indelible-mcp vault load-project <txid> [--output-dir=path]'); break }
161
+ const outArg = args.find(a => a.startsWith('--output-dir='))
162
+ const outputDir = outArg ? outArg.split('=')[1] : undefined
163
+ const result = await loadProject(txId, { outputDir })
164
+ console.log(JSON.stringify(result, null, 2))
165
+ } else {
166
+ console.log(`
167
+ Code Vault — Encrypted code storage on BSV blockchain
168
+
169
+ Commands:
170
+ vault save-file <path> Save a file to blockchain
171
+ vault save-project <dir> [--name=NAME] Save a project directory to blockchain
172
+ vault load-file <txid> [--output=path] Load a file from blockchain
173
+ vault load-project <txid> [--output-dir=path] Load a project from blockchain
174
+ `)
175
+ }
176
+ break
177
+ }
178
+
179
+ case 'install-hooks': {
180
+ const hooks = installHooks()
181
+ if (hooks.alreadyInstalled) {
182
+ console.log(JSON.stringify({ success: true, message: 'Auto-save hooks already installed', settingsPath: hooks.settingsPath }))
183
+ } else {
184
+ console.log(JSON.stringify({ success: true, installed: hooks.installed, settingsPath: hooks.settingsPath, message: `Installed ${hooks.installed.join(' + ')} hooks into ${hooks.settingsPath}` }))
185
+ }
186
+ break
187
+ }
188
+
189
+ default:
190
+ printHelp()
191
+ }
192
+ }
193
+
194
+ function printHelp() {
195
+ console.log(`
196
+ Indelible MCP — Blockchain memory for Claude Code
197
+
198
+ Usage:
199
+ indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
200
+ indelible-mcp save Save current session to blockchain
201
+ indelible-mcp save --summary XYZ Save with custom summary
202
+ indelible-mcp load Load context from blockchain
203
+ indelible-mcp load --sessions=5 Load N sessions
204
+ indelible-mcp status Show account status
205
+ indelible-mcp install-hooks Install auto-save/restore hooks into Claude Code
206
+ indelible-mcp hook pre-compact Pre-compaction save hook
207
+ indelible-mcp hook post-compact Post-compaction restore hook
208
+
209
+ Code Vault:
210
+ indelible-mcp vault save-file <path> Save a file to blockchain
211
+ indelible-mcp vault save-project <dir> Save a project to blockchain
212
+ indelible-mcp vault load-file <txid> Load a file from blockchain
213
+ indelible-mcp vault load-project <txid> Load a project from blockchain
214
+
215
+ Get your key: Sign in at indelible.one with Google → Settings → Private Key
216
+ Learn more: https://indelible.one
217
+ `)
218
+ }
219
+
220
+ // ============ HOOK MODE ============
221
+
222
+ async function runPreCompactSave() {
223
+ // Read stdin for hook metadata (Claude Code sends JSON)
224
+ let trigger = 'auto'
225
+ try {
226
+ const stdin = await readStdin()
227
+ if (stdin) {
228
+ const meta = JSON.parse(stdin)
229
+ trigger = meta.trigger || 'auto'
230
+ }
231
+ } catch { /* no stdin or not JSON */ }
232
+
233
+ const config = loadConfig()
234
+ if (!config?.mcp_enabled && config?.mcp_enabled !== undefined) {
235
+ process.stderr.write('Indelible: MCP disabled, skipping save\n')
236
+ process.exit(0)
237
+ }
238
+
239
+ const result = await saveSession(CONTEXT_FILE, `Auto-save before ${trigger} compaction`)
240
+
241
+ if (result.success) {
242
+ process.stderr.write(`Indelible: Saved ${result.newMessages} messages (${result.saveType}) tx:${result.txId?.slice(0, 12)}...\n`)
243
+ } else {
244
+ process.stderr.write(`Indelible: Save failed: ${result.error}\n`)
245
+ }
246
+
247
+ process.exit(0)
248
+ }
249
+
250
+ async function runPostCompactRestore() {
251
+ const config = loadConfig()
252
+ if (!config?.mcp_enabled && config?.mcp_enabled !== undefined) {
253
+ process.stderr.write('Indelible: MCP disabled, skipping restore\n')
254
+ process.exit(0)
255
+ }
256
+
257
+ const result = await loadContext(5)
258
+
259
+ if (result.success && result.context) {
260
+ // Write to stdout — Claude Code injects this into context
261
+ process.stdout.write(result.context)
262
+ process.stderr.write(`Indelible: Restored ${result.sessionCount} session(s)\n`)
263
+ } else if (result.error) {
264
+ process.stderr.write(`Indelible: Restore failed: ${result.error}\n`)
265
+ }
266
+
267
+ process.exit(0)
268
+ }
269
+
270
+ function readStdin() {
271
+ return new Promise((resolve) => {
272
+ let data = ''
273
+ const timeout = setTimeout(() => resolve(data), 1000)
274
+ process.stdin.on('data', chunk => { data += chunk })
275
+ process.stdin.on('end', () => { clearTimeout(timeout); resolve(data) })
276
+ process.stdin.on('error', () => { clearTimeout(timeout); resolve('') })
277
+ })
278
+ }
279
+
280
+ // ============ MCP SERVER MODE ============
281
+
282
+ const SERVER_INFO = {
283
+ name: 'indelible',
284
+ version: '2.0.0',
285
+ description: 'Blockchain-backed memory for Claude Code'
286
+ }
287
+
288
+ const TOOLS = [
289
+ {
290
+ name: 'setup_wallet',
291
+ description: 'Import your BSV private key from indelible.one. Get your key by signing in with Google at indelible.one.',
292
+ inputSchema: {
293
+ type: 'object',
294
+ properties: {
295
+ api_url: { type: 'string', description: 'Indelible API URL (default: https://indelible.one)' },
296
+ import_wif: { type: 'string', description: 'Your private key (WIF) from indelible.one' },
297
+ pin: { type: 'string', description: 'PIN to encrypt your private key (minimum 4 characters)' }
298
+ },
299
+ required: ['import_wif', 'pin']
300
+ }
301
+ },
302
+ {
303
+ name: 'save_session',
304
+ description: 'Save the current Claude Code session to the blockchain. Encrypts locally, broadcasts via SPV bridge.',
305
+ inputSchema: {
306
+ type: 'object',
307
+ properties: {
308
+ transcript_path: { type: 'string', description: 'Path to the JSONL transcript file' },
309
+ summary: { type: 'string', description: 'Brief summary of this session' }
310
+ },
311
+ required: ['transcript_path']
312
+ }
313
+ },
314
+ {
315
+ name: 'load_context',
316
+ description: 'Load previous session context from the blockchain. Returns summary of past sessions.',
317
+ inputSchema: {
318
+ type: 'object',
319
+ properties: {
320
+ num_sessions: { type: 'number', description: 'Number of recent sessions to load (default: 5)' }
321
+ },
322
+ required: []
323
+ }
324
+ }
325
+ ]
326
+
327
+ async function handleMcpRequest(request) {
328
+ const { method, params, id } = request
329
+
330
+ try {
331
+ switch (method) {
332
+ case 'initialize':
333
+ return {
334
+ jsonrpc: '2.0', id,
335
+ result: {
336
+ protocolVersion: '2024-11-05',
337
+ serverInfo: SERVER_INFO,
338
+ capabilities: { tools: {} }
339
+ }
340
+ }
341
+
342
+ case 'tools/list':
343
+ return { jsonrpc: '2.0', id, result: { tools: TOOLS } }
344
+
345
+ case 'tools/call': {
346
+ const { name, arguments: args } = params
347
+ let result
348
+
349
+ switch (name) {
350
+ case 'setup_wallet':
351
+ result = await setupWallet(args?.api_url, args?.import_wif, args?.pin)
352
+ break
353
+ case 'save_session':
354
+ result = await saveSession(args?.transcript_path, args?.summary)
355
+ break
356
+ case 'load_context':
357
+ result = await loadContext(args?.num_sessions)
358
+ break
359
+ default:
360
+ throw new Error(`Unknown tool: ${name}`)
361
+ }
362
+
363
+ return {
364
+ jsonrpc: '2.0', id,
365
+ result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }
366
+ }
367
+ }
368
+
369
+ case 'notifications/initialized':
370
+ return null
371
+
372
+ default:
373
+ return { jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${method}` } }
374
+ }
375
+ } catch (error) {
376
+ return { jsonrpc: '2.0', id, error: { code: -32000, message: error.message } }
377
+ }
378
+ }
379
+
380
+ async function runMcpServer() {
381
+ const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: false })
382
+
383
+ for await (const line of rl) {
384
+ if (!line.trim()) continue
385
+ try {
386
+ const request = JSON.parse(line)
387
+ const response = await handleMcpRequest(request)
388
+ if (response) console.log(JSON.stringify(response))
389
+ } catch {
390
+ console.log(JSON.stringify({
391
+ jsonrpc: '2.0', id: null,
392
+ error: { code: -32700, message: 'Parse error' }
393
+ }))
394
+ }
395
+ }
396
+ }
397
+
398
+ // ============ MAIN ============
399
+
400
+ const args = process.argv.slice(2)
401
+
402
+ if (args[0] === 'hook') {
403
+ if (args[1] === 'pre-compact') {
404
+ runPreCompactSave().catch(e => { process.stderr.write(`Indelible error: ${e.message}\n`); process.exit(0) })
405
+ } else if (args[1] === 'post-compact') {
406
+ runPostCompactRestore().catch(e => { process.stderr.write(`Indelible error: ${e.message}\n`); process.exit(0) })
407
+ } else {
408
+ console.error('Unknown hook:', args[1])
409
+ process.exit(1)
410
+ }
411
+ } else if (args[0] === '--help' || args[0] === '-h') {
412
+ printHelp()
413
+ } else if (args[0] === '--show-config') {
414
+ console.log(`
415
+ Add this to your Claude Code settings.json:
416
+
417
+ {
418
+ "mcpServers": {
419
+ "indelible": {
420
+ "command": "indelible-mcp"
421
+ }
422
+ }
423
+ }
424
+ `)
425
+ } else if (args.length > 0) {
426
+ runCli(args).catch(e => { console.error('Error:', e.message); process.exit(1) })
427
+ } else if (!process.stdin.isTTY) {
428
+ // No args, piped stdin = MCP server mode
429
+ runMcpServer().catch(console.error)
430
+ } else {
431
+ printHelp()
432
+ }