@wbern/claude-instructions 1.21.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/bin/cli.js +329 -192
- package/package.json +2 -3
- package/src/README.md +279 -0
- package/src/fragments/aaa-pattern.md +7 -0
- package/src/fragments/beads-awareness.md +1 -0
- package/src/fragments/beads-integration.md +8 -0
- package/{downloads/without-beads/commit.md → src/fragments/commit-process.md} +0 -17
- package/src/fragments/consistency-check.md +1 -0
- package/src/fragments/discovery-phase.md +22 -0
- package/src/fragments/fallback-arguments-beads.md +3 -0
- package/src/fragments/fallback-arguments.md +1 -0
- package/src/fragments/fullwidth-dollar-note.md +1 -0
- package/src/fragments/gap-beads.md +1 -0
- package/src/fragments/git-host-detection.md +19 -0
- package/src/fragments/github-issue-fetch.md +10 -0
- package/src/fragments/peeping-tom-warning.md +9 -0
- package/src/fragments/plan-beads-context-hint.md +1 -0
- package/src/fragments/plan-beads-details.md +49 -0
- package/src/fragments/plan-beads-integration.md +2 -0
- package/src/fragments/summarize-beads.md +8 -0
- package/{downloads/without-beads/summarize.md → src/fragments/summarize-structure.md} +0 -20
- package/{downloads/without-beads/tdd.md → src/fragments/tdd-fundamentals.md} +0 -21
- package/src/fragments/test-quality-criteria.md +24 -0
- package/src/fragments/universal-guidelines.md +6 -0
- package/{downloads/without-beads → src/sources}/add-command.md +11 -25
- package/{downloads/without-beads → src/sources}/ask.md +11 -6
- package/{downloads/without-beads → src/sources}/beepboop.md +7 -6
- package/{downloads/without-beads → src/sources}/busycommit.md +9 -36
- package/{downloads/without-beads → src/sources}/code-review.md +16 -30
- package/src/sources/commit.md +20 -0
- package/src/sources/cycle.md +23 -0
- package/{downloads/without-beads → src/sources}/gap.md +11 -8
- package/src/sources/green.md +23 -0
- package/src/sources/issue.md +42 -0
- package/{downloads/without-beads → src/sources}/kata.md +10 -9
- package/{downloads/without-beads → src/sources}/plan.md +18 -39
- package/{downloads/without-beads → src/sources}/pr.md +10 -6
- package/src/sources/red.md +26 -0
- package/src/sources/refactor.md +27 -0
- package/{downloads/without-beads → src/sources}/ship.md +11 -6
- package/{downloads/without-beads → src/sources}/show.md +11 -6
- package/src/sources/spike.md +23 -0
- package/src/sources/summarize.md +23 -0
- package/{downloads/without-beads → src/sources}/tdd-review.md +11 -31
- package/src/sources/tdd.md +24 -0
- package/{downloads/without-beads → src/sources}/worktree-add.md +13 -31
- package/{downloads/without-beads → src/sources}/worktree-cleanup.md +9 -27
- package/downloads/with-beads/add-command.md +0 -159
- package/downloads/with-beads/ask.md +0 -144
- package/downloads/with-beads/beepboop.md +0 -47
- package/downloads/with-beads/busycommit.md +0 -78
- package/downloads/with-beads/code-review.md +0 -263
- package/downloads/with-beads/commands-metadata.json +0 -155
- package/downloads/with-beads/commit.md +0 -49
- package/downloads/with-beads/cycle.md +0 -95
- package/downloads/with-beads/gap.md +0 -38
- package/downloads/with-beads/green.md +0 -95
- package/downloads/with-beads/issue.md +0 -152
- package/downloads/with-beads/kata.md +0 -444
- package/downloads/with-beads/plan.md +0 -186
- package/downloads/with-beads/pr.md +0 -82
- package/downloads/with-beads/red.md +0 -103
- package/downloads/with-beads/refactor.md +0 -105
- package/downloads/with-beads/ship.md +0 -98
- package/downloads/with-beads/show.md +0 -114
- package/downloads/with-beads/spike.md +0 -95
- package/downloads/with-beads/summarize.md +0 -54
- package/downloads/with-beads/tdd-review.md +0 -102
- package/downloads/with-beads/tdd.md +0 -94
- package/downloads/with-beads/worktree-add.md +0 -357
- package/downloads/with-beads/worktree-cleanup.md +0 -250
- package/downloads/without-beads/commands-metadata.json +0 -155
- package/downloads/without-beads/cycle.md +0 -90
- package/downloads/without-beads/green.md +0 -90
- package/downloads/without-beads/issue.md +0 -140
- package/downloads/without-beads/red.md +0 -98
- package/downloads/without-beads/refactor.md +0 -100
- package/downloads/without-beads/spike.md +0 -90
package/bin/cli.js
CHANGED
|
@@ -234,16 +234,16 @@ var Diff = class {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
-
addToPath(
|
|
238
|
-
const last =
|
|
237
|
+
addToPath(path5, added, removed, oldPosInc, options) {
|
|
238
|
+
const last = path5.lastComponent;
|
|
239
239
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
240
240
|
return {
|
|
241
|
-
oldPos:
|
|
241
|
+
oldPos: path5.oldPos + oldPosInc,
|
|
242
242
|
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
243
243
|
};
|
|
244
244
|
} else {
|
|
245
245
|
return {
|
|
246
|
-
oldPos:
|
|
246
|
+
oldPos: path5.oldPos + oldPosInc,
|
|
247
247
|
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
248
248
|
};
|
|
249
249
|
}
|
|
@@ -390,16 +390,232 @@ var import_picocolors = __toESM(require_picocolors(), 1);
|
|
|
390
390
|
|
|
391
391
|
// scripts/cli-generator.ts
|
|
392
392
|
init_esm_shims();
|
|
393
|
+
import fs4 from "fs-extra";
|
|
394
|
+
import path4 from "path";
|
|
395
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
396
|
+
import os from "os";
|
|
397
|
+
|
|
398
|
+
// scripts/fragment-expander.ts
|
|
399
|
+
init_esm_shims();
|
|
393
400
|
import fs from "fs-extra";
|
|
394
401
|
import path2 from "path";
|
|
402
|
+
var TRANSFORM_BLOCK_REGEX = /<!--\s*docs\s+(\w+)([^>]*)-->([\s\S]*?)<!--\s*\/docs\s*-->/g;
|
|
403
|
+
function parseOptions(attrString) {
|
|
404
|
+
const options = {};
|
|
405
|
+
const attrRegex = /(\w+)=['"]([^'"]*)['"]/g;
|
|
406
|
+
let match;
|
|
407
|
+
while ((match = attrRegex.exec(attrString)) !== null) {
|
|
408
|
+
options[match[1]] = match[2];
|
|
409
|
+
}
|
|
410
|
+
return options;
|
|
411
|
+
}
|
|
412
|
+
function expandContent(content, options) {
|
|
413
|
+
const { baseDir, flags } = options;
|
|
414
|
+
return content.replace(
|
|
415
|
+
TRANSFORM_BLOCK_REGEX,
|
|
416
|
+
(_match, transformName, attrString) => {
|
|
417
|
+
if (transformName !== "INCLUDE") {
|
|
418
|
+
return "";
|
|
419
|
+
}
|
|
420
|
+
const attrs = parseOptions(attrString);
|
|
421
|
+
const { path: includePath, featureFlag, elsePath } = attrs;
|
|
422
|
+
if (featureFlag && !flags.includes(featureFlag)) {
|
|
423
|
+
if (elsePath) {
|
|
424
|
+
const fullElsePath = path2.join(baseDir, elsePath);
|
|
425
|
+
try {
|
|
426
|
+
return fs.readFileSync(fullElsePath, "utf8");
|
|
427
|
+
} catch (err) {
|
|
428
|
+
throw new Error(
|
|
429
|
+
`Failed to read elsePath '${elsePath}': ${err instanceof Error ? err.message : String(err)}`
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return "";
|
|
434
|
+
}
|
|
435
|
+
if (!includePath) {
|
|
436
|
+
return "";
|
|
437
|
+
}
|
|
438
|
+
const fullPath = path2.join(baseDir, includePath);
|
|
439
|
+
try {
|
|
440
|
+
return fs.readFileSync(fullPath, "utf8");
|
|
441
|
+
} catch (err) {
|
|
442
|
+
throw new Error(
|
|
443
|
+
`Failed to read '${includePath}': ${err instanceof Error ? err.message : String(err)}`
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// scripts/generate-readme.ts
|
|
451
|
+
init_esm_shims();
|
|
452
|
+
import fs3 from "fs";
|
|
453
|
+
import path3 from "path";
|
|
395
454
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
396
|
-
|
|
455
|
+
|
|
456
|
+
// scripts/cli-options.ts
|
|
457
|
+
init_esm_shims();
|
|
458
|
+
var CLI_OPTIONS = [
|
|
459
|
+
{
|
|
460
|
+
flag: "--scope",
|
|
461
|
+
key: "scope",
|
|
462
|
+
type: "string",
|
|
463
|
+
description: "Installation scope (project, user)",
|
|
464
|
+
example: "--scope=project",
|
|
465
|
+
requiredForNonInteractive: true
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
flag: "--prefix",
|
|
469
|
+
key: "prefix",
|
|
470
|
+
type: "string",
|
|
471
|
+
description: "Add prefix to command names",
|
|
472
|
+
example: "--prefix=my-"
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
flag: "--commands",
|
|
476
|
+
key: "commands",
|
|
477
|
+
type: "array",
|
|
478
|
+
description: "Install only specific commands",
|
|
479
|
+
example: "--commands=commit,red,green"
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
flag: "--skip-template-injection",
|
|
483
|
+
key: "skipTemplateInjection",
|
|
484
|
+
type: "boolean",
|
|
485
|
+
description: "Skip injecting project CLAUDE.md customizations"
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
flag: "--update-existing",
|
|
489
|
+
key: "updateExisting",
|
|
490
|
+
type: "boolean",
|
|
491
|
+
description: "Only update already-installed commands"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
flag: "--overwrite",
|
|
495
|
+
key: "overwrite",
|
|
496
|
+
type: "boolean",
|
|
497
|
+
description: "Overwrite conflicting files without prompting"
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
flag: "--skip-on-conflict",
|
|
501
|
+
key: "skipOnConflict",
|
|
502
|
+
type: "boolean",
|
|
503
|
+
description: "Skip conflicting files without prompting"
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
flag: "--flags",
|
|
507
|
+
key: "flags",
|
|
508
|
+
type: "array",
|
|
509
|
+
description: "Enable feature flags (beads, github, gitlab, etc.)",
|
|
510
|
+
example: "--flags=beads,github"
|
|
511
|
+
}
|
|
512
|
+
];
|
|
513
|
+
function generateHelpText() {
|
|
514
|
+
const lines = [
|
|
515
|
+
"Usage: npx @wbern/claude-instructions [options]",
|
|
516
|
+
"",
|
|
517
|
+
"Options:"
|
|
518
|
+
];
|
|
519
|
+
for (const opt of CLI_OPTIONS) {
|
|
520
|
+
const suffix = opt.type === "string" ? "=<value>" : opt.type === "array" ? "=<list>" : "";
|
|
521
|
+
const padding = 28 - (opt.flag.length + suffix.length);
|
|
522
|
+
lines.push(
|
|
523
|
+
` ${opt.flag}${suffix}${" ".repeat(Math.max(1, padding))}${opt.description}`
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
lines.push(" --help, -h Show this help message");
|
|
527
|
+
lines.push(" --version, -v Show version number");
|
|
528
|
+
return lines.join("\n");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// scripts/utils.ts
|
|
532
|
+
init_esm_shims();
|
|
533
|
+
import fs2 from "fs";
|
|
534
|
+
function getMarkdownFiles(dir) {
|
|
535
|
+
return fs2.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// scripts/generate-readme.ts
|
|
397
539
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
398
|
-
var __dirname2 =
|
|
399
|
-
var
|
|
400
|
-
|
|
401
|
-
|
|
540
|
+
var __dirname2 = path3.dirname(__filename2);
|
|
541
|
+
var PROJECT_ROOT = path3.resolve(__dirname2, "..");
|
|
542
|
+
var SOURCES_DIR = "src/sources";
|
|
543
|
+
var FRONTMATTER_REGEX = /^---\s*\n([\s\S]*?)\n---/;
|
|
544
|
+
var CATEGORIES = {
|
|
545
|
+
PLANNING: "Planning",
|
|
546
|
+
TDD: "Test-Driven Development",
|
|
547
|
+
WORKFLOW: "Workflow",
|
|
548
|
+
SHIP_SHOW_ASK: "Ship / Show / Ask",
|
|
549
|
+
WORKTREE: "Worktree Management",
|
|
550
|
+
UTILITIES: "Utilities"
|
|
402
551
|
};
|
|
552
|
+
function parseFrontmatter(content) {
|
|
553
|
+
const match = content.match(FRONTMATTER_REGEX);
|
|
554
|
+
if (!match) return {};
|
|
555
|
+
const frontmatter = {};
|
|
556
|
+
const lines = match[1].split("\n");
|
|
557
|
+
let currentKey = null;
|
|
558
|
+
let currentArray = null;
|
|
559
|
+
for (const line of lines) {
|
|
560
|
+
if (line.match(/^\s+-\s+/) && currentKey && currentArray) {
|
|
561
|
+
const value2 = line.replace(/^\s+-\s+/, "").trim();
|
|
562
|
+
currentArray.push(value2);
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
if (currentKey && currentArray) {
|
|
566
|
+
frontmatter[currentKey] = currentArray;
|
|
567
|
+
currentKey = null;
|
|
568
|
+
currentArray = null;
|
|
569
|
+
}
|
|
570
|
+
const colonIndex = line.indexOf(":");
|
|
571
|
+
if (colonIndex === -1) continue;
|
|
572
|
+
const key = line.slice(0, colonIndex).trim();
|
|
573
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
574
|
+
if (value === "") {
|
|
575
|
+
currentKey = key;
|
|
576
|
+
currentArray = [];
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
if (key === "_order" && !isNaN(Number(value))) {
|
|
580
|
+
value = parseInt(value, 10);
|
|
581
|
+
}
|
|
582
|
+
if (key === "_selectedByDefault") {
|
|
583
|
+
frontmatter[key] = value === "true";
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
frontmatter[key] = value;
|
|
587
|
+
}
|
|
588
|
+
if (currentKey && currentArray) {
|
|
589
|
+
frontmatter[currentKey] = currentArray;
|
|
590
|
+
}
|
|
591
|
+
return frontmatter;
|
|
592
|
+
}
|
|
593
|
+
function getCategory(frontmatter) {
|
|
594
|
+
return frontmatter._category || CATEGORIES.UTILITIES;
|
|
595
|
+
}
|
|
596
|
+
function generateCommandsMetadata() {
|
|
597
|
+
const sourcesDir = path3.join(PROJECT_ROOT, SOURCES_DIR);
|
|
598
|
+
const files = getMarkdownFiles(sourcesDir);
|
|
599
|
+
const metadata = {};
|
|
600
|
+
for (const file of files) {
|
|
601
|
+
const content = fs3.readFileSync(path3.join(sourcesDir, file), "utf8");
|
|
602
|
+
const frontmatter = parseFrontmatter(content);
|
|
603
|
+
const requestedTools = frontmatter[REQUESTED_TOOLS_KEY];
|
|
604
|
+
metadata[file] = {
|
|
605
|
+
description: frontmatter.description || "No description",
|
|
606
|
+
hint: frontmatter._hint,
|
|
607
|
+
category: getCategory(frontmatter),
|
|
608
|
+
order: typeof frontmatter._order === "number" ? frontmatter._order : 999,
|
|
609
|
+
...frontmatter._selectedByDefault === false ? { selectedByDefault: false } : {},
|
|
610
|
+
...requestedTools ? { [REQUESTED_TOOLS_KEY]: requestedTools } : {}
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
return metadata;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// scripts/cli-generator.ts
|
|
617
|
+
var __filename3 = fileURLToPath3(import.meta.url);
|
|
618
|
+
var __dirname3 = path4.dirname(__filename3);
|
|
403
619
|
var SCOPES = {
|
|
404
620
|
PROJECT: "project",
|
|
405
621
|
USER: "user"
|
|
@@ -407,7 +623,7 @@ var SCOPES = {
|
|
|
407
623
|
var DIRECTORIES = {
|
|
408
624
|
CLAUDE: ".claude",
|
|
409
625
|
COMMANDS: "commands",
|
|
410
|
-
|
|
626
|
+
SOURCES: "src/sources"
|
|
411
627
|
};
|
|
412
628
|
var TEMPLATE_SOURCE_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
413
629
|
var REQUESTED_TOOLS_KEY = "_requested-tools";
|
|
@@ -423,25 +639,32 @@ function truncatePathFromLeft(pathStr, maxLength) {
|
|
|
423
639
|
}
|
|
424
640
|
return ELLIPSIS + truncated;
|
|
425
641
|
}
|
|
426
|
-
var
|
|
427
|
-
{
|
|
428
|
-
value: VARIANTS.WITH_BEADS,
|
|
429
|
-
label: "With Beads",
|
|
430
|
-
hint: "Includes Beads task tracking"
|
|
431
|
-
},
|
|
642
|
+
var FLAG_OPTIONS = [
|
|
432
643
|
{
|
|
433
|
-
value:
|
|
434
|
-
label: "
|
|
435
|
-
hint: "
|
|
644
|
+
value: "beads",
|
|
645
|
+
label: "Beads MCP",
|
|
646
|
+
hint: "Local issue tracking",
|
|
647
|
+
category: "Feature Flags"
|
|
436
648
|
}
|
|
437
649
|
];
|
|
650
|
+
function getFlagsGroupedByCategory() {
|
|
651
|
+
const grouped = {};
|
|
652
|
+
for (const flag of FLAG_OPTIONS) {
|
|
653
|
+
const { category, ...option } = flag;
|
|
654
|
+
if (!grouped[category]) {
|
|
655
|
+
grouped[category] = [];
|
|
656
|
+
}
|
|
657
|
+
grouped[category].push(option);
|
|
658
|
+
}
|
|
659
|
+
return grouped;
|
|
660
|
+
}
|
|
438
661
|
function getScopeOptions(terminalWidth = 80) {
|
|
439
|
-
const projectPath =
|
|
662
|
+
const projectPath = path4.join(
|
|
440
663
|
process.cwd(),
|
|
441
664
|
DIRECTORIES.CLAUDE,
|
|
442
665
|
DIRECTORIES.COMMANDS
|
|
443
666
|
);
|
|
444
|
-
const userPath =
|
|
667
|
+
const userPath = path4.join(
|
|
445
668
|
os.homedir(),
|
|
446
669
|
DIRECTORIES.CLAUDE,
|
|
447
670
|
DIRECTORIES.COMMANDS
|
|
@@ -459,31 +682,32 @@ function getScopeOptions(terminalWidth = 80) {
|
|
|
459
682
|
}
|
|
460
683
|
];
|
|
461
684
|
}
|
|
462
|
-
async function checkExistingFiles(outputPath,
|
|
463
|
-
const sourcePath =
|
|
464
|
-
__dirname2,
|
|
465
|
-
"..",
|
|
466
|
-
DIRECTORIES.DOWNLOADS,
|
|
467
|
-
variant || VARIANTS.WITH_BEADS
|
|
468
|
-
);
|
|
685
|
+
async function checkExistingFiles(outputPath, scope, options) {
|
|
686
|
+
const sourcePath = path4.join(__dirname3, "..", DIRECTORIES.SOURCES);
|
|
469
687
|
const destinationPath = outputPath || getDestinationPath(outputPath, scope);
|
|
470
|
-
const
|
|
688
|
+
const flags = options?.flags ?? [];
|
|
689
|
+
const allFiles = await fs4.readdir(sourcePath);
|
|
471
690
|
const files = options?.commands ? allFiles.filter((f) => options.commands.includes(f)) : allFiles;
|
|
472
691
|
const existingFiles = [];
|
|
473
692
|
const prefix = options?.commandPrefix || "";
|
|
474
693
|
let metadata = null;
|
|
475
694
|
let allowedToolsSet = null;
|
|
476
695
|
if (options?.allowedTools && options.allowedTools.length > 0) {
|
|
477
|
-
metadata = await loadCommandsMetadata(
|
|
696
|
+
metadata = await loadCommandsMetadata();
|
|
478
697
|
allowedToolsSet = new Set(options.allowedTools);
|
|
479
698
|
}
|
|
699
|
+
const baseDir = path4.join(__dirname3, "..");
|
|
480
700
|
for (const file of files) {
|
|
481
701
|
const destFileName = prefix + file;
|
|
482
|
-
const destFilePath =
|
|
483
|
-
const sourceFilePath =
|
|
484
|
-
if (await
|
|
485
|
-
const existingContent = await
|
|
486
|
-
|
|
702
|
+
const destFilePath = path4.join(destinationPath, destFileName);
|
|
703
|
+
const sourceFilePath = path4.join(sourcePath, file);
|
|
704
|
+
if (await fs4.pathExists(destFilePath)) {
|
|
705
|
+
const existingContent = await fs4.readFile(destFilePath, "utf-8");
|
|
706
|
+
const sourceContent = await fs4.readFile(sourceFilePath, "utf-8");
|
|
707
|
+
let newContent = expandContent(sourceContent, {
|
|
708
|
+
flags,
|
|
709
|
+
baseDir
|
|
710
|
+
});
|
|
487
711
|
if (metadata && allowedToolsSet) {
|
|
488
712
|
const commandMetadata = metadata[file];
|
|
489
713
|
const requestedTools = commandMetadata?.[REQUESTED_TOOLS_KEY] || [];
|
|
@@ -518,19 +742,11 @@ var CATEGORY_ORDER = [
|
|
|
518
742
|
"Utilities",
|
|
519
743
|
"Ship / Show / Ask"
|
|
520
744
|
];
|
|
521
|
-
async function loadCommandsMetadata(
|
|
522
|
-
|
|
523
|
-
__dirname2,
|
|
524
|
-
"..",
|
|
525
|
-
DIRECTORIES.DOWNLOADS,
|
|
526
|
-
variant || VARIANTS.WITH_BEADS
|
|
527
|
-
);
|
|
528
|
-
const metadataPath = path2.join(sourcePath, "commands-metadata.json");
|
|
529
|
-
const metadataContent = await fs.readFile(metadataPath, "utf-8");
|
|
530
|
-
return JSON.parse(metadataContent);
|
|
745
|
+
async function loadCommandsMetadata() {
|
|
746
|
+
return generateCommandsMetadata();
|
|
531
747
|
}
|
|
532
|
-
async function getCommandsGroupedByCategory(
|
|
533
|
-
const metadata = await loadCommandsMetadata(
|
|
748
|
+
async function getCommandsGroupedByCategory() {
|
|
749
|
+
const metadata = await loadCommandsMetadata();
|
|
534
750
|
const grouped = {};
|
|
535
751
|
for (const [filename, data] of Object.entries(metadata)) {
|
|
536
752
|
const category = data.category;
|
|
@@ -577,8 +793,8 @@ function formatCommandsHint(commands) {
|
|
|
577
793
|
const remaining = commands.length - 2;
|
|
578
794
|
return `${first.join(", ")}, and ${remaining} ${remaining === 1 ? "other" : "others"}`;
|
|
579
795
|
}
|
|
580
|
-
async function getRequestedToolsOptions(
|
|
581
|
-
const metadata = await loadCommandsMetadata(
|
|
796
|
+
async function getRequestedToolsOptions() {
|
|
797
|
+
const metadata = await loadCommandsMetadata();
|
|
582
798
|
const toolToCommands = /* @__PURE__ */ new Map();
|
|
583
799
|
for (const [filename, data] of Object.entries(metadata)) {
|
|
584
800
|
if (data[REQUESTED_TOOLS_KEY]) {
|
|
@@ -601,10 +817,10 @@ function getDestinationPath(outputPath, scope) {
|
|
|
601
817
|
return outputPath;
|
|
602
818
|
}
|
|
603
819
|
if (scope === SCOPES.PROJECT) {
|
|
604
|
-
return
|
|
820
|
+
return path4.join(process.cwd(), DIRECTORIES.CLAUDE, DIRECTORIES.COMMANDS);
|
|
605
821
|
}
|
|
606
822
|
if (scope === SCOPES.USER) {
|
|
607
|
-
return
|
|
823
|
+
return path4.join(os.homedir(), DIRECTORIES.CLAUDE, DIRECTORIES.COMMANDS);
|
|
608
824
|
}
|
|
609
825
|
throw new Error("Either outputPath or scope must be provided");
|
|
610
826
|
}
|
|
@@ -625,34 +841,35 @@ function extractTemplateBlocks(content) {
|
|
|
625
841
|
}
|
|
626
842
|
return blocks;
|
|
627
843
|
}
|
|
628
|
-
async function generateToDirectory(outputPath,
|
|
629
|
-
const sourcePath = path2.join(
|
|
630
|
-
__dirname2,
|
|
631
|
-
"..",
|
|
632
|
-
DIRECTORIES.DOWNLOADS,
|
|
633
|
-
variant || VARIANTS.WITH_BEADS
|
|
634
|
-
);
|
|
844
|
+
async function generateToDirectory(outputPath, scope, options) {
|
|
635
845
|
const destinationPath = getDestinationPath(outputPath, scope);
|
|
636
|
-
const
|
|
846
|
+
const sourcePath = path4.join(__dirname3, "..", DIRECTORIES.SOURCES);
|
|
847
|
+
const flags = options?.flags ?? [];
|
|
848
|
+
const allFiles = (await fs4.readdir(sourcePath)).filter(
|
|
849
|
+
(f) => f.endsWith(".md")
|
|
850
|
+
);
|
|
637
851
|
let files = options?.commands ? allFiles.filter((f) => options.commands.includes(f)) : allFiles;
|
|
638
852
|
if (options?.skipFiles) {
|
|
639
853
|
const prefix2 = options?.commandPrefix || "";
|
|
640
854
|
files = files.filter((f) => !options.skipFiles.includes(prefix2 + f));
|
|
641
855
|
}
|
|
642
856
|
const prefix = options?.commandPrefix || "";
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
await
|
|
857
|
+
await fs4.ensureDir(destinationPath);
|
|
858
|
+
const baseDir = path4.join(__dirname3, "..");
|
|
859
|
+
for (const file of files) {
|
|
860
|
+
const sourceFilePath = path4.join(sourcePath, file);
|
|
861
|
+
const sourceContent = await fs4.readFile(sourceFilePath, "utf-8");
|
|
862
|
+
const expandedContent = expandContent(sourceContent, {
|
|
863
|
+
flags,
|
|
864
|
+
baseDir
|
|
865
|
+
});
|
|
866
|
+
await fs4.writeFile(
|
|
867
|
+
path4.join(destinationPath, prefix + file),
|
|
868
|
+
expandedContent
|
|
869
|
+
);
|
|
653
870
|
}
|
|
654
871
|
if (options?.allowedTools && options.allowedTools.length > 0) {
|
|
655
|
-
const metadata = await loadCommandsMetadata(
|
|
872
|
+
const metadata = await loadCommandsMetadata();
|
|
656
873
|
const allowedToolsSet = new Set(options.allowedTools);
|
|
657
874
|
for (const file of files) {
|
|
658
875
|
const commandMetadata = metadata[file];
|
|
@@ -661,8 +878,8 @@ async function generateToDirectory(outputPath, variant, scope, options) {
|
|
|
661
878
|
(tool) => allowedToolsSet.has(tool)
|
|
662
879
|
);
|
|
663
880
|
if (toolsForCommand.length > 0) {
|
|
664
|
-
const filePath =
|
|
665
|
-
const content = await
|
|
881
|
+
const filePath = path4.join(destinationPath, prefix + file);
|
|
882
|
+
const content = await fs4.readFile(filePath, "utf-8");
|
|
666
883
|
const allowedToolsYaml = `allowed-tools: ${toolsForCommand.join(", ")}`;
|
|
667
884
|
const modifiedContent = content.replace(
|
|
668
885
|
/^---\n/,
|
|
@@ -670,7 +887,7 @@ async function generateToDirectory(outputPath, variant, scope, options) {
|
|
|
670
887
|
${allowedToolsYaml}
|
|
671
888
|
`
|
|
672
889
|
);
|
|
673
|
-
await
|
|
890
|
+
await fs4.writeFile(filePath, modifiedContent);
|
|
674
891
|
}
|
|
675
892
|
}
|
|
676
893
|
}
|
|
@@ -678,21 +895,21 @@ ${allowedToolsYaml}
|
|
|
678
895
|
if (!options?.skipTemplateInjection) {
|
|
679
896
|
let templateSourcePath = null;
|
|
680
897
|
for (const filename of TEMPLATE_SOURCE_FILES) {
|
|
681
|
-
const candidatePath =
|
|
682
|
-
if (await
|
|
898
|
+
const candidatePath = path4.join(process.cwd(), filename);
|
|
899
|
+
if (await fs4.pathExists(candidatePath)) {
|
|
683
900
|
templateSourcePath = candidatePath;
|
|
684
901
|
break;
|
|
685
902
|
}
|
|
686
903
|
}
|
|
687
904
|
if (templateSourcePath) {
|
|
688
|
-
const sourceContent = await
|
|
905
|
+
const sourceContent = await fs4.readFile(templateSourcePath, "utf-8");
|
|
689
906
|
const templates = extractTemplateBlocks(sourceContent);
|
|
690
907
|
if (templates.length > 0) {
|
|
691
908
|
for (const file of files) {
|
|
692
|
-
const commandName =
|
|
909
|
+
const commandName = path4.basename(file, ".md");
|
|
693
910
|
const actualFileName = options?.commandPrefix ? options.commandPrefix + file : file;
|
|
694
|
-
const filePath =
|
|
695
|
-
let content = await
|
|
911
|
+
const filePath = path4.join(destinationPath, actualFileName);
|
|
912
|
+
let content = await fs4.readFile(filePath, "utf-8");
|
|
696
913
|
let modified = false;
|
|
697
914
|
for (const template of templates) {
|
|
698
915
|
if (template.commands && !template.commands.includes(commandName)) {
|
|
@@ -702,7 +919,7 @@ ${allowedToolsYaml}
|
|
|
702
919
|
modified = true;
|
|
703
920
|
}
|
|
704
921
|
if (modified) {
|
|
705
|
-
await
|
|
922
|
+
await fs4.writeFile(filePath, content);
|
|
706
923
|
}
|
|
707
924
|
}
|
|
708
925
|
templateInjected = true;
|
|
@@ -712,88 +929,12 @@ ${allowedToolsYaml}
|
|
|
712
929
|
return {
|
|
713
930
|
success: true,
|
|
714
931
|
filesGenerated: files.length,
|
|
715
|
-
variant,
|
|
716
932
|
templateInjectionSkipped: options?.skipTemplateInjection,
|
|
717
|
-
templateInjected
|
|
933
|
+
templateInjected,
|
|
934
|
+
flags: options?.flags
|
|
718
935
|
};
|
|
719
936
|
}
|
|
720
937
|
|
|
721
|
-
// scripts/cli-options.ts
|
|
722
|
-
init_esm_shims();
|
|
723
|
-
var CLI_OPTIONS = [
|
|
724
|
-
{
|
|
725
|
-
flag: "--variant",
|
|
726
|
-
key: "variant",
|
|
727
|
-
type: "string",
|
|
728
|
-
description: "Command variant (with-beads, without-beads)",
|
|
729
|
-
example: "--variant=with-beads",
|
|
730
|
-
requiredForNonInteractive: true
|
|
731
|
-
},
|
|
732
|
-
{
|
|
733
|
-
flag: "--scope",
|
|
734
|
-
key: "scope",
|
|
735
|
-
type: "string",
|
|
736
|
-
description: "Installation scope (project, user)",
|
|
737
|
-
example: "--scope=project",
|
|
738
|
-
requiredForNonInteractive: true
|
|
739
|
-
},
|
|
740
|
-
{
|
|
741
|
-
flag: "--prefix",
|
|
742
|
-
key: "prefix",
|
|
743
|
-
type: "string",
|
|
744
|
-
description: "Add prefix to command names",
|
|
745
|
-
example: "--prefix=my-"
|
|
746
|
-
},
|
|
747
|
-
{
|
|
748
|
-
flag: "--commands",
|
|
749
|
-
key: "commands",
|
|
750
|
-
type: "array",
|
|
751
|
-
description: "Install only specific commands",
|
|
752
|
-
example: "--commands=commit,red,green"
|
|
753
|
-
},
|
|
754
|
-
{
|
|
755
|
-
flag: "--skip-template-injection",
|
|
756
|
-
key: "skipTemplateInjection",
|
|
757
|
-
type: "boolean",
|
|
758
|
-
description: "Skip injecting project CLAUDE.md customizations"
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
flag: "--update-existing",
|
|
762
|
-
key: "updateExisting",
|
|
763
|
-
type: "boolean",
|
|
764
|
-
description: "Only update already-installed commands"
|
|
765
|
-
},
|
|
766
|
-
{
|
|
767
|
-
flag: "--overwrite",
|
|
768
|
-
key: "overwrite",
|
|
769
|
-
type: "boolean",
|
|
770
|
-
description: "Overwrite conflicting files without prompting"
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
flag: "--skip-on-conflict",
|
|
774
|
-
key: "skipOnConflict",
|
|
775
|
-
type: "boolean",
|
|
776
|
-
description: "Skip conflicting files without prompting"
|
|
777
|
-
}
|
|
778
|
-
];
|
|
779
|
-
function generateHelpText() {
|
|
780
|
-
const lines = [
|
|
781
|
-
"Usage: npx @wbern/claude-instructions [options]",
|
|
782
|
-
"",
|
|
783
|
-
"Options:"
|
|
784
|
-
];
|
|
785
|
-
for (const opt of CLI_OPTIONS) {
|
|
786
|
-
const suffix = opt.type === "string" ? "=<value>" : opt.type === "array" ? "=<list>" : "";
|
|
787
|
-
const padding = 28 - (opt.flag.length + suffix.length);
|
|
788
|
-
lines.push(
|
|
789
|
-
` ${opt.flag}${suffix}${" ".repeat(Math.max(1, padding))}${opt.description}`
|
|
790
|
-
);
|
|
791
|
-
}
|
|
792
|
-
lines.push(" --help, -h Show this help message");
|
|
793
|
-
lines.push(" --version, -v Show version number");
|
|
794
|
-
return lines.join("\n");
|
|
795
|
-
}
|
|
796
|
-
|
|
797
938
|
// scripts/tty.ts
|
|
798
939
|
init_esm_shims();
|
|
799
940
|
function isInteractiveTTY() {
|
|
@@ -919,23 +1060,22 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
|
919
1060
|
`;
|
|
920
1061
|
async function main(args) {
|
|
921
1062
|
intro(BATMAN_LOGO);
|
|
922
|
-
let variant;
|
|
923
1063
|
let scope;
|
|
924
1064
|
let commandPrefix;
|
|
925
1065
|
let selectedCommands;
|
|
926
1066
|
let selectedAllowedTools;
|
|
1067
|
+
let selectedFlags;
|
|
927
1068
|
let cachedExistingFiles;
|
|
928
|
-
if (args?.
|
|
929
|
-
variant = args.variant;
|
|
1069
|
+
if (args?.scope) {
|
|
930
1070
|
scope = args.scope;
|
|
931
1071
|
commandPrefix = args.prefix ?? "";
|
|
932
1072
|
selectedCommands = args.commands;
|
|
1073
|
+
selectedFlags = args.flags;
|
|
933
1074
|
if (args.updateExisting) {
|
|
934
1075
|
cachedExistingFiles = await checkExistingFiles(
|
|
935
1076
|
void 0,
|
|
936
|
-
variant,
|
|
937
1077
|
scope,
|
|
938
|
-
{ commandPrefix: commandPrefix || "" }
|
|
1078
|
+
{ commandPrefix: commandPrefix || "", flags: selectedFlags }
|
|
939
1079
|
);
|
|
940
1080
|
selectedCommands = cachedExistingFiles.map((f) => f.filename);
|
|
941
1081
|
if (selectedCommands.length === 0) {
|
|
@@ -951,13 +1091,6 @@ async function main(args) {
|
|
|
951
1091
|
log.warn(`Non-interactive mode requires ${requiredFlags} arguments`);
|
|
952
1092
|
return;
|
|
953
1093
|
}
|
|
954
|
-
variant = await select({
|
|
955
|
-
message: "Select variant",
|
|
956
|
-
options: [...VARIANT_OPTIONS]
|
|
957
|
-
});
|
|
958
|
-
if (isCancel(variant)) {
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
1094
|
const terminalWidth = process.stdout.columns || 80;
|
|
962
1095
|
const uiOverhead = 25;
|
|
963
1096
|
scope = await select({
|
|
@@ -974,15 +1107,24 @@ async function main(args) {
|
|
|
974
1107
|
if (isCancel(commandPrefix)) {
|
|
975
1108
|
return;
|
|
976
1109
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1110
|
+
const flagOptions = getFlagsGroupedByCategory();
|
|
1111
|
+
selectedFlags = await groupMultiselect({
|
|
1112
|
+
message: "Select feature flags (optional)",
|
|
1113
|
+
options: flagOptions,
|
|
1114
|
+
required: false
|
|
1115
|
+
});
|
|
1116
|
+
if (isCancel(selectedFlags)) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
let groupedCommands = await getCommandsGroupedByCategory();
|
|
980
1120
|
if (args?.updateExisting) {
|
|
981
1121
|
cachedExistingFiles = await checkExistingFiles(
|
|
982
1122
|
void 0,
|
|
983
|
-
variant,
|
|
984
1123
|
scope,
|
|
985
|
-
{
|
|
1124
|
+
{
|
|
1125
|
+
commandPrefix: commandPrefix || "",
|
|
1126
|
+
flags: selectedFlags
|
|
1127
|
+
}
|
|
986
1128
|
);
|
|
987
1129
|
const existingFilenames = new Set(
|
|
988
1130
|
cachedExistingFiles.map((f) => f.filename)
|
|
@@ -1011,9 +1153,7 @@ async function main(args) {
|
|
|
1011
1153
|
if (isCancel(selectedCommands)) {
|
|
1012
1154
|
return;
|
|
1013
1155
|
}
|
|
1014
|
-
const requestedToolsOptions = await getRequestedToolsOptions(
|
|
1015
|
-
variant
|
|
1016
|
-
);
|
|
1156
|
+
const requestedToolsOptions = await getRequestedToolsOptions();
|
|
1017
1157
|
if (requestedToolsOptions.length > 0) {
|
|
1018
1158
|
selectedAllowedTools = await groupMultiselect({
|
|
1019
1159
|
message: "Select allowed tools for commands (optional)",
|
|
@@ -1027,10 +1167,11 @@ async function main(args) {
|
|
|
1027
1167
|
}
|
|
1028
1168
|
}
|
|
1029
1169
|
}
|
|
1030
|
-
const existingFiles = cachedExistingFiles ?? await checkExistingFiles(void 0,
|
|
1170
|
+
const existingFiles = cachedExistingFiles ?? await checkExistingFiles(void 0, scope, {
|
|
1031
1171
|
commandPrefix,
|
|
1032
1172
|
commands: selectedCommands,
|
|
1033
|
-
allowedTools: selectedAllowedTools
|
|
1173
|
+
allowedTools: selectedAllowedTools,
|
|
1174
|
+
flags: selectedFlags
|
|
1034
1175
|
});
|
|
1035
1176
|
const skipFiles = [];
|
|
1036
1177
|
const shouldSkipConflicts = args?.skipOnConflict || !isInteractiveTTY();
|
|
@@ -1108,18 +1249,14 @@ async function main(args) {
|
|
|
1108
1249
|
);
|
|
1109
1250
|
}
|
|
1110
1251
|
}
|
|
1111
|
-
const result = await generateToDirectory(
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
skipFiles,
|
|
1120
|
-
allowedTools: selectedAllowedTools
|
|
1121
|
-
}
|
|
1122
|
-
);
|
|
1252
|
+
const result = await generateToDirectory(void 0, scope, {
|
|
1253
|
+
commandPrefix,
|
|
1254
|
+
skipTemplateInjection: args?.skipTemplateInjection,
|
|
1255
|
+
commands: selectedCommands,
|
|
1256
|
+
skipFiles,
|
|
1257
|
+
allowedTools: selectedAllowedTools,
|
|
1258
|
+
flags: selectedFlags
|
|
1259
|
+
});
|
|
1123
1260
|
const fullPath = scope === "project" ? `${process.cwd()}/.claude/commands` : `${os2.homedir()}/.claude/commands`;
|
|
1124
1261
|
outro(
|
|
1125
1262
|
`Installed ${result.filesGenerated} commands to ${fullPath}
|