dig-burrow 1.2.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/install.cjs ADDED
@@ -0,0 +1,430 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const readline = require('node:readline');
7
+
8
+ const {
9
+ CLAUDE_MD_SNIPPET,
10
+ detect,
11
+ performInstall,
12
+ performUpgrade,
13
+ performRepair,
14
+ writeSentinelBlock,
15
+ removeSentinelBlock,
16
+ } = require('./.claude/burrow/lib/installer.cjs');
17
+
18
+ // ── Output helpers ────────────────────────────────────────────────────────────
19
+
20
+ function ok(msg) { console.log(` \u2713 ${msg}`); }
21
+ function skip(msg) { console.log(` \u00b7 ${msg}`); }
22
+ function fail(msg) { console.error(` \u2717 ${msg}`); }
23
+ function warn(msg) { console.log(` ! ${msg}`); }
24
+
25
+ // ── readline helpers ──────────────────────────────────────────────────────────
26
+
27
+ let rl = null;
28
+
29
+ function createInterface() {
30
+ rl = readline.createInterface({ input: process.stdin, output: process.stdout });
31
+ }
32
+
33
+ function closeInterface() {
34
+ if (rl) { rl.close(); rl = null; }
35
+ }
36
+
37
+ /**
38
+ * Prompt the user and return their answer (resolves to defaultAnswer on empty input).
39
+ */
40
+ function ask(question, defaultAnswer = '') {
41
+ return new Promise((resolve) => {
42
+ rl.question(question, (answer) => {
43
+ const trimmed = answer.trim();
44
+ resolve(trimmed === '' ? defaultAnswer : trimmed);
45
+ });
46
+ });
47
+ }
48
+
49
+ // ── Argument parsing ──────────────────────────────────────────────────────────
50
+
51
+ function parseArgs(argv) {
52
+ const args = argv.slice(2);
53
+ const flags = { yes: false, uninstall: false, help: false };
54
+ let positional = null;
55
+
56
+ for (const arg of args) {
57
+ if (arg === '--yes' || arg === '-y') {
58
+ flags.yes = true;
59
+ } else if (arg === '--uninstall') {
60
+ flags.uninstall = true;
61
+ } else if (arg === '--help' || arg === '-h') {
62
+ flags.help = true;
63
+ } else if (!arg.startsWith('-')) {
64
+ positional = arg;
65
+ }
66
+ }
67
+
68
+ return { flags, positional };
69
+ }
70
+
71
+ // ── Usage ─────────────────────────────────────────────────────────────────────
72
+
73
+ function printUsage() {
74
+ console.log(`
75
+ Usage: npx create-burrow [target-dir] [options]
76
+
77
+ Installs (or manages) Burrow in the target project directory.
78
+
79
+ Arguments:
80
+ target-dir Path to the target project root (default: current directory)
81
+
82
+ Options:
83
+ --yes, -y Non-interactive mode. Accept all defaults, skip prompts.
84
+ --uninstall Remove all Burrow files from the target project.
85
+ --help, -h Print this help message and exit.
86
+
87
+ Modes (auto-detected):
88
+ fresh install No Burrow files found — installs everything fresh.
89
+ upgrade All Burrow files present — replaces source files, preserves data.
90
+ repair Some Burrow files missing — restores only missing files.
91
+ uninstall Removes .claude/burrow/, .claude/commands/burrow*, .planning/burrow/, and the CLAUDE.md sentinel block.
92
+
93
+ Examples:
94
+ npx create-burrow Interactive install in current directory
95
+ npx create-burrow /path/to/project Interactive install in specified directory
96
+ npx create-burrow --yes Non-interactive install with all defaults
97
+ npx create-burrow --uninstall Interactive uninstall (requires confirmation)
98
+ npx create-burrow --uninstall --yes Non-interactive uninstall
99
+ `);
100
+ }
101
+
102
+ // ── Checklist output helpers ──────────────────────────────────────────────────
103
+
104
+ function printInstallResults(results) {
105
+ const statusMap = {
106
+ copied: (k) => ok(`${k}`),
107
+ replaced: (k) => ok(`${k} (replaced)`),
108
+ preserved: (k) => skip(`${k} (preserved)`),
109
+ created: (k) => ok(`${k} (created)`),
110
+ updated: (k) => ok(`${k} (updated)`),
111
+ unchanged: (k) => skip(`${k} (unchanged)`),
112
+ 'copied-dir': (k) => ok(`${k}`),
113
+ 'source-missing': (k) => fail(`${k} (source missing)`),
114
+ };
115
+
116
+ const labelMap = {
117
+ burrowDir: '.claude/burrow/',
118
+ commandFile: '.claude/commands/burrow.md',
119
+ commandDir: '.claude/commands/burrow/',
120
+ cardsJson: '.planning/burrow/cards.json',
121
+ gitignore: '.gitignore',
122
+ };
123
+
124
+ for (const [key, status] of Object.entries(results)) {
125
+ const label = labelMap[key] || key;
126
+ const printer = statusMap[status];
127
+ if (printer) {
128
+ printer(label);
129
+ } else {
130
+ ok(`${label} (${status})`);
131
+ }
132
+ }
133
+ }
134
+
135
+ // ── Post-install message ──────────────────────────────────────────────────────
136
+
137
+ function printGettingStarted() {
138
+ console.log(`
139
+ ── Getting Started ──────────────────────────────────────────────────────
140
+
141
+ Burrow is now installed. Here are a few ways to get started:
142
+
143
+ 1. In your Claude session, type /burrow to open the card manager.
144
+
145
+ 2. Add a card from the command line:
146
+ node .claude/burrow/burrow-tools.cjs add --title "My first card" \\
147
+ --body "A note about my project"
148
+
149
+ 3. List all cards:
150
+ node .claude/burrow/burrow-tools.cjs list
151
+
152
+ 4. CLAUDE.md has been updated with Burrow agent instructions so Claude
153
+ will automatically load your cards on every session start.
154
+
155
+ ─────────────────────────────────────────────────────────────────────────
156
+ `);
157
+ }
158
+
159
+ // ── Uninstall helpers ─────────────────────────────────────────────────────────
160
+
161
+ /**
162
+ * Remove a directory recursively, silently skipping if it doesn't exist.
163
+ */
164
+ function removeDir(dir) {
165
+ if (fs.existsSync(dir)) {
166
+ fs.rmSync(dir, { recursive: true, force: true });
167
+ return true;
168
+ }
169
+ return false;
170
+ }
171
+
172
+ /**
173
+ * Remove a file, silently skipping if it doesn't exist.
174
+ */
175
+ function removeFile(filePath) {
176
+ if (fs.existsSync(filePath)) {
177
+ fs.rmSync(filePath);
178
+ return true;
179
+ }
180
+ return false;
181
+ }
182
+
183
+ /**
184
+ * Remove a directory if it exists and is empty.
185
+ */
186
+ function removeIfEmpty(dir) {
187
+ if (!fs.existsSync(dir)) return false;
188
+ try {
189
+ const entries = fs.readdirSync(dir);
190
+ if (entries.length === 0) {
191
+ fs.rmdirSync(dir);
192
+ return true;
193
+ }
194
+ } catch (_) {
195
+ // ignore
196
+ }
197
+ return false;
198
+ }
199
+
200
+ // ── Install flow ──────────────────────────────────────────────────────────────
201
+
202
+ async function runInstall({ sourceDir, targetDir, yes, detection }) {
203
+ const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
204
+
205
+ // Ask for Claude.md opt-in
206
+ let addClaudeMd = true;
207
+ if (!yes) {
208
+ const claudeAnswer = await ask(
209
+ ' Add Burrow instructions to CLAUDE.md? [Y/n] ',
210
+ 'y'
211
+ );
212
+ addClaudeMd = claudeAnswer.toLowerCase() !== 'n';
213
+ }
214
+
215
+ const results = performInstall(sourceDir, targetDir);
216
+ printInstallResults(results);
217
+
218
+ if (addClaudeMd) {
219
+ writeSentinelBlock(claudeMdPath, CLAUDE_MD_SNIPPET);
220
+ ok('CLAUDE.md (burrow block added)');
221
+ } else {
222
+ skip('CLAUDE.md (skipped)');
223
+ }
224
+
225
+ printGettingStarted();
226
+ }
227
+
228
+ // ── Upgrade flow ──────────────────────────────────────────────────────────────
229
+
230
+ async function runUpgrade({ sourceDir, targetDir, yes, detection }) {
231
+ const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
232
+ const { version } = detection;
233
+
234
+ console.log('\n Detected existing Burrow installation.');
235
+
236
+ // Read our own version for the "old -> new" display
237
+ const sourceVersionFile = path.join(sourceDir, '.claude', 'burrow', 'VERSION');
238
+ const sourcePkgFile = path.join(sourceDir, '.claude', 'burrow', 'package.json');
239
+ let newVersion = null;
240
+ if (fs.existsSync(sourceVersionFile)) {
241
+ newVersion = fs.readFileSync(sourceVersionFile, 'utf-8').trim();
242
+ } else if (fs.existsSync(sourcePkgFile)) {
243
+ try {
244
+ const pkg = JSON.parse(fs.readFileSync(sourcePkgFile, 'utf-8'));
245
+ newVersion = pkg.version || null;
246
+ } catch (_) {
247
+ // ignore
248
+ }
249
+ }
250
+
251
+ if (version || newVersion) {
252
+ const fromStr = version || '(unknown)';
253
+ const toStr = newVersion || '(unknown)';
254
+ console.log(` Upgrading Burrow: v${fromStr} -> v${toStr}`);
255
+ }
256
+
257
+ if (!yes) {
258
+ const answer = await ask(' Proceed with upgrade? [Y/n] ', 'y');
259
+ if (answer.toLowerCase() === 'n') {
260
+ console.log('\n Upgrade cancelled.\n');
261
+ return;
262
+ }
263
+ }
264
+
265
+ const results = performUpgrade(sourceDir, targetDir);
266
+ printInstallResults(results);
267
+
268
+ // Handle CLAUDE.md sentinel
269
+ if (detection.hasSentinel) {
270
+ writeSentinelBlock(claudeMdPath, CLAUDE_MD_SNIPPET);
271
+ ok('CLAUDE.md (sentinel block updated)');
272
+ } else if (detection.hasLegacyClaude) {
273
+ // Wrap legacy ## Burrow section in sentinels
274
+ writeSentinelBlock(claudeMdPath, CLAUDE_MD_SNIPPET);
275
+ warn('CLAUDE.md had legacy Burrow section — replaced with sentinel block');
276
+ }
277
+ // else: no action (user opted out previously)
278
+
279
+ console.log('\n Upgrade complete.\n');
280
+ }
281
+
282
+ // ── Repair flow ───────────────────────────────────────────────────────────────
283
+
284
+ async function runRepair({ sourceDir, targetDir, yes, detection }) {
285
+ const { missing } = detection;
286
+
287
+ console.log(`\n Detected partial Burrow installation, ${missing.length} file(s) missing:`);
288
+ for (const f of missing) {
289
+ console.log(` - ${f}`);
290
+ }
291
+
292
+ if (!yes) {
293
+ const answer = await ask(' Proceed with repair? [Y/n] ', 'y');
294
+ if (answer.toLowerCase() === 'n') {
295
+ console.log('\n Repair cancelled.\n');
296
+ return;
297
+ }
298
+ }
299
+
300
+ const results = performRepair(sourceDir, targetDir, missing);
301
+ printInstallResults(results);
302
+
303
+ console.log('\n Repair complete.\n');
304
+ }
305
+
306
+ // ── Uninstall flow ────────────────────────────────────────────────────────────
307
+
308
+ async function runUninstall({ targetDir, yes }) {
309
+ const burrowDir = path.join(targetDir, '.claude', 'burrow');
310
+ const commandFile = path.join(targetDir, '.claude', 'commands', 'burrow.md');
311
+ const commandDir = path.join(targetDir, '.claude', 'commands', 'burrow');
312
+ const dataDir = path.join(targetDir, '.planning', 'burrow');
313
+ const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
314
+ const claudeDir = path.join(targetDir, '.claude');
315
+ const commandsDir = path.join(targetDir, '.claude', 'commands');
316
+ const planningDir = path.join(targetDir, '.planning');
317
+
318
+ console.log('\n The following will be removed:');
319
+ if (fs.existsSync(burrowDir)) console.log(` - .claude/burrow/`);
320
+ if (fs.existsSync(commandFile)) console.log(` - .claude/commands/burrow.md`);
321
+ if (fs.existsSync(commandDir)) console.log(` - .claude/commands/burrow/`);
322
+ if (fs.existsSync(dataDir)) console.log(` - .planning/burrow/`);
323
+ if (fs.existsSync(claudeMdPath)) {
324
+ const content = fs.readFileSync(claudeMdPath, 'utf-8');
325
+ if (content.includes('<!-- burrow:start -->')) {
326
+ console.log(` - CLAUDE.md sentinel block`);
327
+ }
328
+ }
329
+ console.log('');
330
+
331
+ if (!yes) {
332
+ const answer = await ask(' Proceed with uninstall? [y/N] ', 'n');
333
+ if (answer.toLowerCase() !== 'y') {
334
+ console.log('\n Uninstall cancelled.\n');
335
+ return;
336
+ }
337
+ }
338
+
339
+ // Remove files/dirs
340
+ if (removeDir(burrowDir)) ok('.claude/burrow/ removed');
341
+ else skip('.claude/burrow/ (not found)');
342
+
343
+ if (removeFile(commandFile)) ok('.claude/commands/burrow.md removed');
344
+ else skip('.claude/commands/burrow.md (not found)');
345
+
346
+ if (removeDir(commandDir)) ok('.claude/commands/burrow/ removed');
347
+ else skip('.claude/commands/burrow/ (not found)');
348
+
349
+ if (removeDir(dataDir)) ok('.planning/burrow/ removed');
350
+ else skip('.planning/burrow/ (not found)');
351
+
352
+ // Remove sentinel block from CLAUDE.md
353
+ if (fs.existsSync(claudeMdPath)) {
354
+ const before = fs.readFileSync(claudeMdPath, 'utf-8');
355
+ if (before.includes('<!-- burrow:start -->')) {
356
+ removeSentinelBlock(claudeMdPath);
357
+ ok('CLAUDE.md sentinel block removed');
358
+ }
359
+ }
360
+
361
+ // Clean up empty parent dirs
362
+ if (removeIfEmpty(commandsDir)) ok('.claude/commands/ removed (was empty)');
363
+ if (removeIfEmpty(claudeDir)) ok('.claude/ removed (was empty)');
364
+ if (removeIfEmpty(planningDir)) ok('.planning/ removed (was empty)');
365
+
366
+ console.log('\n Uninstall complete.\n');
367
+ }
368
+
369
+ // ── Main ──────────────────────────────────────────────────────────────────────
370
+
371
+ async function main() {
372
+ const { flags, positional } = parseArgs(process.argv);
373
+
374
+ if (flags.help) {
375
+ printUsage();
376
+ process.exit(0);
377
+ }
378
+
379
+ const sourceDir = __dirname;
380
+ const targetDir = positional ? path.resolve(positional) : process.cwd();
381
+
382
+ // Verify source is valid
383
+ const burrowToolsPath = path.join(sourceDir, '.claude', 'burrow', 'burrow-tools.cjs');
384
+ if (!fs.existsSync(burrowToolsPath)) {
385
+ console.error(` \u2717 Cannot find .claude/burrow/burrow-tools.cjs in ${sourceDir}`);
386
+ console.error(' Package may be incomplete — try: npm cache clean --force && npx create-burrow');
387
+ process.exit(1);
388
+ }
389
+
390
+ if (flags.uninstall) {
391
+ console.log('\n── Burrow Uninstall ─────────────────────────────────────────────────────\n');
392
+ if (!flags.yes) createInterface();
393
+ await runUninstall({ targetDir, yes: flags.yes });
394
+ closeInterface();
395
+ return;
396
+ }
397
+
398
+ // Detect mode
399
+ const detection = detect(targetDir);
400
+
401
+ console.log('\n── Burrow Install ───────────────────────────────────────────────────────\n');
402
+
403
+ if (!flags.yes) createInterface();
404
+
405
+ if (detection.mode === 'fresh') {
406
+ // Optionally ask for target path
407
+ let resolvedTarget = targetDir;
408
+ if (!flags.yes) {
409
+ const pathAnswer = await ask(
410
+ ` Install directory [${targetDir}]: `,
411
+ targetDir
412
+ );
413
+ resolvedTarget = path.resolve(pathAnswer);
414
+ }
415
+
416
+ await runInstall({ sourceDir, targetDir: resolvedTarget, yes: flags.yes, detection });
417
+ } else if (detection.mode === 'upgrade') {
418
+ await runUpgrade({ sourceDir, targetDir, yes: flags.yes, detection });
419
+ } else if (detection.mode === 'repair') {
420
+ await runRepair({ sourceDir, targetDir, yes: flags.yes, detection });
421
+ }
422
+
423
+ closeInterface();
424
+ }
425
+
426
+ main().catch((err) => {
427
+ console.error(` \u2717 Fatal error: ${err.message}`);
428
+ closeInterface();
429
+ process.exit(1);
430
+ });
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "dig-burrow",
3
+ "version": "1.2.0",
4
+ "description": "Infinitely nestable Trello for AI agents. Persistent card-based memory for Claude Code.",
5
+ "license": "MIT",
6
+ "author": "mrmatos",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/mrmatos6837/burrow.git"
10
+ },
11
+ "bin": {
12
+ "create-burrow": "install.cjs"
13
+ },
14
+ "files": [
15
+ "install.cjs",
16
+ ".claude/burrow/",
17
+ ".claude/commands/burrow/",
18
+ ".claude/commands/burrow.md"
19
+ ],
20
+ "keywords": [
21
+ "claude",
22
+ "claude-code",
23
+ "ai-agent",
24
+ "memory",
25
+ "trello",
26
+ "cards"
27
+ ],
28
+ "engines": {
29
+ "node": ">=18"
30
+ }
31
+ }