@wbern/claude-instructions 1.8.1 → 1.9.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.
Files changed (41) hide show
  1. package/README.md +14 -10
  2. package/bin/cli.js +612 -31
  3. package/downloads/with-beads/ask.md +1 -4
  4. package/downloads/with-beads/beepboop.md +0 -1
  5. package/downloads/with-beads/busycommit.md +0 -1
  6. package/downloads/with-beads/commands-metadata.json +43 -15
  7. package/downloads/with-beads/commit.md +0 -1
  8. package/downloads/with-beads/cycle.md +0 -1
  9. package/downloads/with-beads/green.md +0 -1
  10. package/downloads/with-beads/issue.md +0 -1
  11. package/downloads/with-beads/plan.md +0 -1
  12. package/downloads/with-beads/pr.md +82 -0
  13. package/downloads/with-beads/red.md +0 -1
  14. package/downloads/with-beads/refactor.md +0 -1
  15. package/downloads/with-beads/ship.md +1 -4
  16. package/downloads/with-beads/show.md +1 -4
  17. package/downloads/with-beads/spike.md +0 -1
  18. package/downloads/with-beads/summarize.md +0 -1
  19. package/downloads/with-beads/tdd.md +0 -1
  20. package/downloads/with-beads/worktree-add.md +0 -1
  21. package/downloads/with-beads/worktree-cleanup.md +0 -1
  22. package/downloads/without-beads/ask.md +1 -4
  23. package/downloads/without-beads/beepboop.md +0 -1
  24. package/downloads/without-beads/busycommit.md +0 -1
  25. package/downloads/without-beads/commands-metadata.json +43 -15
  26. package/downloads/without-beads/commit.md +0 -1
  27. package/downloads/without-beads/cycle.md +0 -1
  28. package/downloads/without-beads/green.md +0 -1
  29. package/downloads/without-beads/issue.md +0 -1
  30. package/downloads/without-beads/plan.md +0 -1
  31. package/downloads/without-beads/pr.md +70 -0
  32. package/downloads/without-beads/red.md +0 -1
  33. package/downloads/without-beads/refactor.md +0 -1
  34. package/downloads/without-beads/ship.md +1 -4
  35. package/downloads/without-beads/show.md +1 -4
  36. package/downloads/without-beads/spike.md +0 -1
  37. package/downloads/without-beads/summarize.md +0 -1
  38. package/downloads/without-beads/tdd.md +0 -1
  39. package/downloads/without-beads/worktree-add.md +0 -1
  40. package/downloads/without-beads/worktree-cleanup.md +0 -1
  41. package/package.json +4 -1
package/bin/cli.js CHANGED
@@ -1,16 +1,400 @@
1
1
  #!/usr/bin/env node
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __commonJS = (cb, mod) => function __require() {
12
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+
31
+ // node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
32
+ import path from "path";
33
+ import { fileURLToPath } from "url";
34
+ var init_esm_shims = __esm({
35
+ "node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.6_tsx@4.21.0_typescript@5.9.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
36
+ "use strict";
37
+ }
38
+ });
39
+
40
+ // node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js
41
+ var require_picocolors = __commonJS({
42
+ "node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports, module) {
43
+ "use strict";
44
+ init_esm_shims();
45
+ var p = process || {};
46
+ var argv = p.argv || [];
47
+ var env = p.env || {};
48
+ var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
49
+ var formatter = (open, close, replace = open) => (input) => {
50
+ let string = "" + input, index = string.indexOf(close, open.length);
51
+ return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
52
+ };
53
+ var replaceClose = (string, close, replace, index) => {
54
+ let result = "", cursor = 0;
55
+ do {
56
+ result += string.substring(cursor, index) + replace;
57
+ cursor = index + close.length;
58
+ index = string.indexOf(close, cursor);
59
+ } while (~index);
60
+ return result + string.substring(cursor);
61
+ };
62
+ var createColors = (enabled = isColorSupported) => {
63
+ let f = enabled ? formatter : () => String;
64
+ return {
65
+ isColorSupported: enabled,
66
+ reset: f("\x1B[0m", "\x1B[0m"),
67
+ bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
68
+ dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
69
+ italic: f("\x1B[3m", "\x1B[23m"),
70
+ underline: f("\x1B[4m", "\x1B[24m"),
71
+ inverse: f("\x1B[7m", "\x1B[27m"),
72
+ hidden: f("\x1B[8m", "\x1B[28m"),
73
+ strikethrough: f("\x1B[9m", "\x1B[29m"),
74
+ black: f("\x1B[30m", "\x1B[39m"),
75
+ red: f("\x1B[31m", "\x1B[39m"),
76
+ green: f("\x1B[32m", "\x1B[39m"),
77
+ yellow: f("\x1B[33m", "\x1B[39m"),
78
+ blue: f("\x1B[34m", "\x1B[39m"),
79
+ magenta: f("\x1B[35m", "\x1B[39m"),
80
+ cyan: f("\x1B[36m", "\x1B[39m"),
81
+ white: f("\x1B[37m", "\x1B[39m"),
82
+ gray: f("\x1B[90m", "\x1B[39m"),
83
+ bgBlack: f("\x1B[40m", "\x1B[49m"),
84
+ bgRed: f("\x1B[41m", "\x1B[49m"),
85
+ bgGreen: f("\x1B[42m", "\x1B[49m"),
86
+ bgYellow: f("\x1B[43m", "\x1B[49m"),
87
+ bgBlue: f("\x1B[44m", "\x1B[49m"),
88
+ bgMagenta: f("\x1B[45m", "\x1B[49m"),
89
+ bgCyan: f("\x1B[46m", "\x1B[49m"),
90
+ bgWhite: f("\x1B[47m", "\x1B[49m"),
91
+ blackBright: f("\x1B[90m", "\x1B[39m"),
92
+ redBright: f("\x1B[91m", "\x1B[39m"),
93
+ greenBright: f("\x1B[92m", "\x1B[39m"),
94
+ yellowBright: f("\x1B[93m", "\x1B[39m"),
95
+ blueBright: f("\x1B[94m", "\x1B[39m"),
96
+ magentaBright: f("\x1B[95m", "\x1B[39m"),
97
+ cyanBright: f("\x1B[96m", "\x1B[39m"),
98
+ whiteBright: f("\x1B[97m", "\x1B[39m"),
99
+ bgBlackBright: f("\x1B[100m", "\x1B[49m"),
100
+ bgRedBright: f("\x1B[101m", "\x1B[49m"),
101
+ bgGreenBright: f("\x1B[102m", "\x1B[49m"),
102
+ bgYellowBright: f("\x1B[103m", "\x1B[49m"),
103
+ bgBlueBright: f("\x1B[104m", "\x1B[49m"),
104
+ bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
105
+ bgCyanBright: f("\x1B[106m", "\x1B[49m"),
106
+ bgWhiteBright: f("\x1B[107m", "\x1B[49m")
107
+ };
108
+ };
109
+ module.exports = createColors();
110
+ module.exports.createColors = createColors;
111
+ }
112
+ });
113
+
114
+ // scripts/bin.ts
115
+ init_esm_shims();
2
116
 
3
117
  // scripts/cli.ts
4
- import { select, text, groupMultiselect, isCancel, intro, outro } from "@clack/prompts";
118
+ init_esm_shims();
119
+ import {
120
+ select,
121
+ text,
122
+ groupMultiselect,
123
+ isCancel,
124
+ intro,
125
+ outro,
126
+ confirm,
127
+ note,
128
+ log
129
+ } from "@clack/prompts";
5
130
  import os2 from "os";
6
131
 
132
+ // node_modules/.pnpm/diff@8.0.2/node_modules/diff/libesm/index.js
133
+ init_esm_shims();
134
+
135
+ // node_modules/.pnpm/diff@8.0.2/node_modules/diff/libesm/diff/base.js
136
+ init_esm_shims();
137
+ var Diff = class {
138
+ diff(oldStr, newStr, options = {}) {
139
+ let callback;
140
+ if (typeof options === "function") {
141
+ callback = options;
142
+ options = {};
143
+ } else if ("callback" in options) {
144
+ callback = options.callback;
145
+ }
146
+ const oldString = this.castInput(oldStr, options);
147
+ const newString = this.castInput(newStr, options);
148
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
149
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
150
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
151
+ }
152
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
153
+ var _a;
154
+ const done = (value) => {
155
+ value = this.postProcess(value, options);
156
+ if (callback) {
157
+ setTimeout(function() {
158
+ callback(value);
159
+ }, 0);
160
+ return void 0;
161
+ } else {
162
+ return value;
163
+ }
164
+ };
165
+ const newLen = newTokens.length, oldLen = oldTokens.length;
166
+ let editLength = 1;
167
+ let maxEditLength = newLen + oldLen;
168
+ if (options.maxEditLength != null) {
169
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
170
+ }
171
+ const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
172
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
173
+ const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
174
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
175
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
176
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
177
+ }
178
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
179
+ const execEditLength = () => {
180
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
181
+ let basePath;
182
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
183
+ if (removePath) {
184
+ bestPath[diagonalPath - 1] = void 0;
185
+ }
186
+ let canAdd = false;
187
+ if (addPath) {
188
+ const addPathNewPos = addPath.oldPos - diagonalPath;
189
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
190
+ }
191
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
192
+ if (!canAdd && !canRemove) {
193
+ bestPath[diagonalPath] = void 0;
194
+ continue;
195
+ }
196
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
197
+ basePath = this.addToPath(addPath, true, false, 0, options);
198
+ } else {
199
+ basePath = this.addToPath(removePath, false, true, 1, options);
200
+ }
201
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
202
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
203
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
204
+ } else {
205
+ bestPath[diagonalPath] = basePath;
206
+ if (basePath.oldPos + 1 >= oldLen) {
207
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
208
+ }
209
+ if (newPos + 1 >= newLen) {
210
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
211
+ }
212
+ }
213
+ }
214
+ editLength++;
215
+ };
216
+ if (callback) {
217
+ (function exec() {
218
+ setTimeout(function() {
219
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
220
+ return callback(void 0);
221
+ }
222
+ if (!execEditLength()) {
223
+ exec();
224
+ }
225
+ }, 0);
226
+ })();
227
+ } else {
228
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
229
+ const ret = execEditLength();
230
+ if (ret) {
231
+ return ret;
232
+ }
233
+ }
234
+ }
235
+ }
236
+ addToPath(path3, added, removed, oldPosInc, options) {
237
+ const last = path3.lastComponent;
238
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
239
+ return {
240
+ oldPos: path3.oldPos + oldPosInc,
241
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
242
+ };
243
+ } else {
244
+ return {
245
+ oldPos: path3.oldPos + oldPosInc,
246
+ lastComponent: { count: 1, added, removed, previousComponent: last }
247
+ };
248
+ }
249
+ }
250
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
251
+ const newLen = newTokens.length, oldLen = oldTokens.length;
252
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
253
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
254
+ newPos++;
255
+ oldPos++;
256
+ commonCount++;
257
+ if (options.oneChangePerToken) {
258
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
259
+ }
260
+ }
261
+ if (commonCount && !options.oneChangePerToken) {
262
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
263
+ }
264
+ basePath.oldPos = oldPos;
265
+ return newPos;
266
+ }
267
+ equals(left, right, options) {
268
+ if (options.comparator) {
269
+ return options.comparator(left, right);
270
+ } else {
271
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
272
+ }
273
+ }
274
+ removeEmpty(array) {
275
+ const ret = [];
276
+ for (let i = 0; i < array.length; i++) {
277
+ if (array[i]) {
278
+ ret.push(array[i]);
279
+ }
280
+ }
281
+ return ret;
282
+ }
283
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
284
+ castInput(value, options) {
285
+ return value;
286
+ }
287
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
288
+ tokenize(value, options) {
289
+ return Array.from(value);
290
+ }
291
+ join(chars) {
292
+ return chars.join("");
293
+ }
294
+ postProcess(changeObjects, options) {
295
+ return changeObjects;
296
+ }
297
+ get useLongestToken() {
298
+ return false;
299
+ }
300
+ buildValues(lastComponent, newTokens, oldTokens) {
301
+ const components = [];
302
+ let nextComponent;
303
+ while (lastComponent) {
304
+ components.push(lastComponent);
305
+ nextComponent = lastComponent.previousComponent;
306
+ delete lastComponent.previousComponent;
307
+ lastComponent = nextComponent;
308
+ }
309
+ components.reverse();
310
+ const componentLen = components.length;
311
+ let componentPos = 0, newPos = 0, oldPos = 0;
312
+ for (; componentPos < componentLen; componentPos++) {
313
+ const component = components[componentPos];
314
+ if (!component.removed) {
315
+ if (!component.added && this.useLongestToken) {
316
+ let value = newTokens.slice(newPos, newPos + component.count);
317
+ value = value.map(function(value2, i) {
318
+ const oldValue = oldTokens[oldPos + i];
319
+ return oldValue.length > value2.length ? oldValue : value2;
320
+ });
321
+ component.value = this.join(value);
322
+ } else {
323
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
324
+ }
325
+ newPos += component.count;
326
+ if (!component.added) {
327
+ oldPos += component.count;
328
+ }
329
+ } else {
330
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
331
+ oldPos += component.count;
332
+ }
333
+ }
334
+ return components;
335
+ }
336
+ };
337
+
338
+ // node_modules/.pnpm/diff@8.0.2/node_modules/diff/libesm/diff/line.js
339
+ init_esm_shims();
340
+ var LineDiff = class extends Diff {
341
+ constructor() {
342
+ super(...arguments);
343
+ this.tokenize = tokenize;
344
+ }
345
+ equals(left, right, options) {
346
+ if (options.ignoreWhitespace) {
347
+ if (!options.newlineIsToken || !left.includes("\n")) {
348
+ left = left.trim();
349
+ }
350
+ if (!options.newlineIsToken || !right.includes("\n")) {
351
+ right = right.trim();
352
+ }
353
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
354
+ if (left.endsWith("\n")) {
355
+ left = left.slice(0, -1);
356
+ }
357
+ if (right.endsWith("\n")) {
358
+ right = right.slice(0, -1);
359
+ }
360
+ }
361
+ return super.equals(left, right, options);
362
+ }
363
+ };
364
+ var lineDiff = new LineDiff();
365
+ function diffLines(oldStr, newStr, options) {
366
+ return lineDiff.diff(oldStr, newStr, options);
367
+ }
368
+ function tokenize(value, options) {
369
+ if (options.stripTrailingCr) {
370
+ value = value.replace(/\r\n/g, "\n");
371
+ }
372
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
373
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
374
+ linesAndNewlines.pop();
375
+ }
376
+ for (let i = 0; i < linesAndNewlines.length; i++) {
377
+ const line = linesAndNewlines[i];
378
+ if (i % 2 && !options.newlineIsToken) {
379
+ retLines[retLines.length - 1] += line;
380
+ } else {
381
+ retLines.push(line);
382
+ }
383
+ }
384
+ return retLines;
385
+ }
386
+
387
+ // scripts/cli.ts
388
+ var import_picocolors = __toESM(require_picocolors(), 1);
389
+
7
390
  // scripts/cli-generator.ts
391
+ init_esm_shims();
8
392
  import fs from "fs-extra";
9
- import path from "path";
10
- import { fileURLToPath } from "url";
393
+ import path2 from "path";
394
+ import { fileURLToPath as fileURLToPath2 } from "url";
11
395
  import os from "os";
12
- var __filename = fileURLToPath(import.meta.url);
13
- var __dirname = path.dirname(__filename);
396
+ var __filename2 = fileURLToPath2(import.meta.url);
397
+ var __dirname2 = path2.dirname(__filename2);
14
398
  var VARIANTS = {
15
399
  WITH_BEADS: "with-beads",
16
400
  WITHOUT_BEADS: "without-beads"
@@ -50,12 +434,12 @@ var VARIANT_OPTIONS = [
50
434
  }
51
435
  ];
52
436
  function getScopeOptions(terminalWidth = 80) {
53
- const projectPath = path.join(
437
+ const projectPath = path2.join(
54
438
  process.cwd(),
55
439
  DIRECTORIES.CLAUDE,
56
440
  DIRECTORIES.COMMANDS
57
441
  );
58
- const userPath = path.join(
442
+ const userPath = path2.join(
59
443
  os.homedir(),
60
444
  DIRECTORIES.CLAUDE,
61
445
  DIRECTORIES.COMMANDS
@@ -73,14 +457,53 @@ function getScopeOptions(terminalWidth = 80) {
73
457
  }
74
458
  ];
75
459
  }
460
+ async function checkExistingFiles(outputPath, variant, scope, options) {
461
+ const sourcePath = path2.join(
462
+ __dirname2,
463
+ "..",
464
+ DIRECTORIES.DOWNLOADS,
465
+ variant || VARIANTS.WITH_BEADS
466
+ );
467
+ const destinationPath = outputPath || getDestinationPath(outputPath, scope);
468
+ if (!destinationPath) {
469
+ return [];
470
+ }
471
+ const files = await fs.readdir(sourcePath);
472
+ const existingFiles = [];
473
+ const prefix = options?.commandPrefix || "";
474
+ for (const file of files) {
475
+ const destFileName = prefix + file;
476
+ const destFilePath = path2.join(destinationPath, destFileName);
477
+ const sourceFilePath = path2.join(sourcePath, file);
478
+ if (await fs.pathExists(destFilePath)) {
479
+ const existingContent = await fs.readFile(destFilePath, "utf-8");
480
+ const newContent = await fs.readFile(sourceFilePath, "utf-8");
481
+ existingFiles.push({
482
+ filename: destFileName,
483
+ existingContent,
484
+ newContent,
485
+ isIdentical: existingContent === newContent
486
+ });
487
+ }
488
+ }
489
+ return existingFiles;
490
+ }
491
+ var CATEGORY_ORDER = [
492
+ "Test-Driven Development",
493
+ "Planning",
494
+ "Workflow",
495
+ "Worktree Management",
496
+ "Utilities",
497
+ "Ship / Show / Ask"
498
+ ];
76
499
  async function getCommandsGroupedByCategory(variant) {
77
- const sourcePath = path.join(
78
- __dirname,
500
+ const sourcePath = path2.join(
501
+ __dirname2,
79
502
  "..",
80
503
  DIRECTORIES.DOWNLOADS,
81
504
  variant || VARIANTS.WITH_BEADS
82
505
  );
83
- const metadataPath = path.join(sourcePath, "commands-metadata.json");
506
+ const metadataPath = path2.join(sourcePath, "commands-metadata.json");
84
507
  const metadataContent = await fs.readFile(metadataPath, "utf-8");
85
508
  const metadata = JSON.parse(metadataContent);
86
509
  const grouped = {};
@@ -91,7 +514,9 @@ async function getCommandsGroupedByCategory(variant) {
91
514
  }
92
515
  grouped[category].push({
93
516
  value: filename,
94
- label: filename
517
+ label: filename,
518
+ hint: data.hint,
519
+ selectedByDefault: data.selectedByDefault !== false
95
520
  });
96
521
  }
97
522
  for (const category of Object.keys(grouped)) {
@@ -101,17 +526,29 @@ async function getCommandsGroupedByCategory(variant) {
101
526
  return orderA - orderB;
102
527
  });
103
528
  }
104
- return grouped;
529
+ const sortedCategories = Object.keys(grouped).sort((a, b) => {
530
+ const indexA = CATEGORY_ORDER.indexOf(a);
531
+ const indexB = CATEGORY_ORDER.indexOf(b);
532
+ if (indexA !== -1 && indexB !== -1) return indexA - indexB;
533
+ if (indexA !== -1) return -1;
534
+ if (indexB !== -1) return 1;
535
+ return a.localeCompare(b);
536
+ });
537
+ const sortedGrouped = {};
538
+ for (const category of sortedCategories) {
539
+ sortedGrouped[category] = grouped[category];
540
+ }
541
+ return sortedGrouped;
105
542
  }
106
543
  function getDestinationPath(outputPath, scope) {
107
544
  if (outputPath) {
108
545
  return outputPath;
109
546
  }
110
547
  if (scope === SCOPES.PROJECT) {
111
- return path.join(process.cwd(), DIRECTORIES.CLAUDE, DIRECTORIES.COMMANDS);
548
+ return path2.join(process.cwd(), DIRECTORIES.CLAUDE, DIRECTORIES.COMMANDS);
112
549
  }
113
550
  if (scope === SCOPES.USER) {
114
- return path.join(os.homedir(), DIRECTORIES.CLAUDE, DIRECTORIES.COMMANDS);
551
+ return path2.join(os.homedir(), DIRECTORIES.CLAUDE, DIRECTORIES.COMMANDS);
115
552
  }
116
553
  return void 0;
117
554
  }
@@ -133,8 +570,8 @@ function extractTemplateBlocks(content) {
133
570
  return blocks;
134
571
  }
135
572
  async function generateToDirectory(outputPath, variant, scope, options) {
136
- const sourcePath = path.join(
137
- __dirname,
573
+ const sourcePath = path2.join(
574
+ __dirname2,
138
575
  "..",
139
576
  DIRECTORIES.DOWNLOADS,
140
577
  variant || VARIANTS.WITH_BEADS
@@ -144,13 +581,16 @@ async function generateToDirectory(outputPath, variant, scope, options) {
144
581
  throw new Error("Either outputPath or scope must be provided");
145
582
  }
146
583
  const allFiles = await fs.readdir(sourcePath);
147
- const files = options?.commands ? allFiles.filter((f) => options.commands.includes(f)) : allFiles;
148
- if (options?.commands) {
584
+ let files = options?.commands ? allFiles.filter((f) => options.commands.includes(f)) : allFiles;
585
+ if (options?.skipFiles) {
586
+ files = files.filter((f) => !options.skipFiles.includes(f));
587
+ }
588
+ if (options?.commands || options?.skipFiles) {
149
589
  await fs.ensureDir(destinationPath);
150
590
  for (const file of files) {
151
591
  await fs.copy(
152
- path.join(sourcePath, file),
153
- path.join(destinationPath, file)
592
+ path2.join(sourcePath, file),
593
+ path2.join(destinationPath, file)
154
594
  );
155
595
  }
156
596
  } else {
@@ -158,8 +598,8 @@ async function generateToDirectory(outputPath, variant, scope, options) {
158
598
  }
159
599
  if (options?.commandPrefix) {
160
600
  for (const file of files) {
161
- const oldPath = path.join(destinationPath, file);
162
- const newPath = path.join(destinationPath, options.commandPrefix + file);
601
+ const oldPath = path2.join(destinationPath, file);
602
+ const newPath = path2.join(destinationPath, options.commandPrefix + file);
163
603
  await fs.rename(oldPath, newPath);
164
604
  }
165
605
  }
@@ -167,7 +607,7 @@ async function generateToDirectory(outputPath, variant, scope, options) {
167
607
  if (!options?.skipTemplateInjection) {
168
608
  let templateSourcePath = null;
169
609
  for (const filename of TEMPLATE_SOURCE_FILES) {
170
- const candidatePath = path.join(process.cwd(), filename);
610
+ const candidatePath = path2.join(process.cwd(), filename);
171
611
  if (await fs.pathExists(candidatePath)) {
172
612
  templateSourcePath = candidatePath;
173
613
  break;
@@ -178,9 +618,9 @@ async function generateToDirectory(outputPath, variant, scope, options) {
178
618
  const templates = extractTemplateBlocks(sourceContent);
179
619
  if (templates.length > 0) {
180
620
  for (const file of files) {
181
- const commandName = path.basename(file, ".md");
621
+ const commandName = path2.basename(file, ".md");
182
622
  const actualFileName = options?.commandPrefix ? options.commandPrefix + file : file;
183
- const filePath = path.join(destinationPath, actualFileName);
623
+ const filePath = path2.join(destinationPath, actualFileName);
184
624
  let content = await fs.readFile(filePath, "utf-8");
185
625
  let modified = false;
186
626
  for (const template of templates) {
@@ -208,6 +648,106 @@ async function generateToDirectory(outputPath, variant, scope, options) {
208
648
  }
209
649
 
210
650
  // scripts/cli.ts
651
+ var pc = process.env.FORCE_COLOR ? import_picocolors.default.createColors(true) : import_picocolors.default;
652
+ function splitChangeIntoLines(value) {
653
+ const lines = value.split("\n");
654
+ if (lines[lines.length - 1] === "") lines.pop();
655
+ return lines;
656
+ }
657
+ function formatCompactDiff(oldContent, newContent, contextLines = 3) {
658
+ const changes = diffLines(oldContent, newContent);
659
+ const lines = [];
660
+ const allLines = [];
661
+ let oldLineNum = 1;
662
+ let newLineNum = 1;
663
+ for (const change of changes) {
664
+ const changeLines = splitChangeIntoLines(change.value);
665
+ for (const text2 of changeLines) {
666
+ if (change.added) {
667
+ allLines.push({
668
+ text: text2,
669
+ type: "added",
670
+ oldLineNum: -1,
671
+ newLineNum: newLineNum++
672
+ });
673
+ } else if (change.removed) {
674
+ allLines.push({
675
+ text: text2,
676
+ type: "removed",
677
+ oldLineNum: oldLineNum++,
678
+ newLineNum: -1
679
+ });
680
+ } else {
681
+ allLines.push({
682
+ text: text2,
683
+ type: "unchanged",
684
+ oldLineNum: oldLineNum++,
685
+ newLineNum: newLineNum++
686
+ });
687
+ }
688
+ }
689
+ }
690
+ let i = 0;
691
+ while (i < allLines.length) {
692
+ if (allLines[i].type === "unchanged") {
693
+ i++;
694
+ continue;
695
+ }
696
+ const hunkStart = Math.max(0, i - contextLines);
697
+ let hunkEnd = i;
698
+ while (hunkEnd < allLines.length) {
699
+ if (allLines[hunkEnd].type !== "unchanged") {
700
+ hunkEnd++;
701
+ } else {
702
+ let nextChange = hunkEnd;
703
+ while (nextChange < allLines.length && nextChange < hunkEnd + contextLines * 2 + 1) {
704
+ if (allLines[nextChange].type !== "unchanged") break;
705
+ nextChange++;
706
+ }
707
+ if (nextChange < allLines.length && nextChange < hunkEnd + contextLines * 2 + 1 && allLines[nextChange].type !== "unchanged") {
708
+ hunkEnd = nextChange + 1;
709
+ } else {
710
+ break;
711
+ }
712
+ }
713
+ }
714
+ hunkEnd = Math.min(allLines.length, hunkEnd + contextLines);
715
+ const hunkLines = allLines.slice(hunkStart, hunkEnd);
716
+ const firstOldLine = hunkLines.find((l) => l.oldLineNum > 0)?.oldLineNum || 1;
717
+ const firstNewLine = hunkLines.find((l) => l.newLineNum > 0)?.newLineNum || 1;
718
+ const oldCount = hunkLines.filter((l) => l.type !== "added").length;
719
+ const newCount = hunkLines.filter((l) => l.type !== "removed").length;
720
+ lines.push(
721
+ pc.cyan(
722
+ `@@ -${firstOldLine},${oldCount} +${firstNewLine},${newCount} @@`
723
+ )
724
+ );
725
+ for (let j = hunkStart; j < hunkEnd; j++) {
726
+ const line = allLines[j];
727
+ if (line.type === "added") {
728
+ lines.push(pc.bgGreen(pc.black(`+ ${line.text}`)));
729
+ } else if (line.type === "removed") {
730
+ lines.push(pc.bgRed(pc.black(`- ${line.text}`)));
731
+ } else {
732
+ lines.push(pc.dim(` ${line.text}`));
733
+ }
734
+ }
735
+ lines.push("");
736
+ i = hunkEnd;
737
+ }
738
+ return lines.join("\n").trimEnd();
739
+ }
740
+ function getDiffStats(oldContent, newContent) {
741
+ const changes = diffLines(oldContent, newContent);
742
+ let added = 0;
743
+ let removed = 0;
744
+ for (const change of changes) {
745
+ const lineCount = splitChangeIntoLines(change.value).length;
746
+ if (change.added) added += lineCount;
747
+ else if (change.removed) removed += lineCount;
748
+ }
749
+ return { added, removed };
750
+ }
211
751
  var BATMAN_LOGO = `
212
752
  _==/ i i \\==_
213
753
  /XX/ |\\___/| \\XX\\
@@ -259,24 +799,65 @@ async function main(args) {
259
799
  if (isCancel(commandPrefix)) {
260
800
  return;
261
801
  }
262
- const groupedCommands = await getCommandsGroupedByCategory(variant);
263
- const allCommandValues = Object.values(groupedCommands).flat().map((cmd) => cmd.value);
802
+ const groupedCommands = await getCommandsGroupedByCategory(
803
+ variant
804
+ );
805
+ const enabledCommandValues = Object.values(groupedCommands).flat().filter((cmd) => cmd.selectedByDefault).map((cmd) => cmd.value);
264
806
  selectedCommands = await groupMultiselect({
265
807
  message: "Select commands to install (Enter to accept all)",
266
808
  options: groupedCommands,
267
- initialValues: allCommandValues
809
+ initialValues: enabledCommandValues
268
810
  });
269
811
  if (isCancel(selectedCommands)) {
270
812
  return;
271
813
  }
272
814
  }
273
- const result = await generateToDirectory(void 0, variant, scope, { commandPrefix, skipTemplateInjection: args?.skipTemplateInjection, commands: selectedCommands });
815
+ const existingFiles = await checkExistingFiles(
816
+ void 0,
817
+ variant,
818
+ scope,
819
+ {
820
+ commandPrefix,
821
+ commands: selectedCommands
822
+ }
823
+ );
824
+ const skipFiles = [];
825
+ for (const file of existingFiles) {
826
+ if (file.isIdentical) {
827
+ log.info(`${file.filename} is identical, skipping`);
828
+ skipFiles.push(file.filename);
829
+ continue;
830
+ }
831
+ const stats = getDiffStats(file.existingContent, file.newContent);
832
+ const diff = formatCompactDiff(file.existingContent, file.newContent);
833
+ note(diff, `Diff: ${file.filename}`);
834
+ log.info(`+${stats.added} -${stats.removed}`);
835
+ const shouldOverwrite = await confirm({
836
+ message: `Overwrite ${file.filename}?`
837
+ });
838
+ if (!shouldOverwrite) {
839
+ skipFiles.push(file.filename);
840
+ }
841
+ }
842
+ const result = await generateToDirectory(
843
+ void 0,
844
+ variant,
845
+ scope,
846
+ {
847
+ commandPrefix,
848
+ skipTemplateInjection: args?.skipTemplateInjection,
849
+ commands: selectedCommands,
850
+ skipFiles
851
+ }
852
+ );
274
853
  const fullPath = scope === "project" ? `${process.cwd()}/.claude/commands` : `${os2.homedir()}/.claude/commands`;
275
- outro(`Installed ${result.filesGenerated} commands to ${fullPath}
854
+ outro(
855
+ `Installed ${result.filesGenerated} commands to ${fullPath}
276
856
 
277
857
  If Claude Code is already running, restart it to pick up the new commands.
278
858
 
279
- Happy TDD'ing!`);
859
+ Happy TDD'ing!`
860
+ );
280
861
  }
281
862
 
282
863
  // scripts/bin.ts