@wbern/claude-instructions 2.1.0 → 2.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/README.md CHANGED
@@ -42,7 +42,7 @@ pnpm dlx @wbern/claude-instructions
42
42
 
43
43
  The interactive installer lets you choose:
44
44
 
45
- - **Variant**: With or without [Beads MCP](https://github.com/steveyegge/beads) integration
45
+ - **Feature flags**: Enable optional integrations like [Beads MCP](https://github.com/steveyegge/beads)
46
46
  - **Scope**: User-level (global) or project-level installation
47
47
 
48
48
  After installation, restart Claude Code if it's currently running.
@@ -60,7 +60,7 @@ Then add a postinstall script to your `package.json`:
60
60
  ```json
61
61
  {
62
62
  "scripts": {
63
- "postinstall": "npx @wbern/claude-instructions --variant=without-beads --scope=project --prefix="
63
+ "postinstall": "npx @wbern/claude-instructions --scope=project --overwrite"
64
64
  },
65
65
  "devDependencies": {
66
66
  "@wbern/claude-instructions": "^1.0.0"
package/bin/cli.js CHANGED
@@ -405,7 +405,7 @@ import path2 from "path";
405
405
  init_esm_shims();
406
406
  import fs from "fs";
407
407
  function getMarkdownFiles(dir) {
408
- return fs.readdirSync(dir).filter((f) => f.endsWith(".md"));
408
+ return fs.readdirSync(dir).filter((f) => f.endsWith(".md") && !f.startsWith("_"));
409
409
  }
410
410
  function getErrorMessage(err) {
411
411
  return err instanceof Error ? err.message : String(err);
@@ -522,6 +522,13 @@ var CLI_OPTIONS = [
522
522
  type: "array",
523
523
  description: "Enable feature flags (beads, github, gitlab, etc.)",
524
524
  example: "--flags=beads,github"
525
+ },
526
+ {
527
+ flag: "--include-contrib-commands",
528
+ key: "includeContribCommands",
529
+ type: "boolean",
530
+ description: "Include underscore-prefixed contributor commands",
531
+ internal: true
525
532
  }
526
533
  ];
527
534
  function generateHelpText() {
@@ -648,6 +655,18 @@ var DIRECTORIES = {
648
655
  var TEMPLATE_SOURCE_FILES = ["CLAUDE.md", "AGENTS.md"];
649
656
  var REQUESTED_TOOLS_KEY = "_requested-tools";
650
657
  var ELLIPSIS = "...";
658
+ function isSourceFile(filename, includeContribCommands) {
659
+ return filename.endsWith(".md") && (includeContribCommands || !filename.startsWith("_"));
660
+ }
661
+ function stripContribPrefix(filename) {
662
+ return filename.startsWith("_") ? filename.slice(1) : filename;
663
+ }
664
+ async function getSourceFiles(includeContribCommands) {
665
+ const sourcePath = path4.join(__dirname3, "..", DIRECTORIES.SOURCES);
666
+ return (await fs4.readdir(sourcePath)).filter(
667
+ (f) => isSourceFile(f, includeContribCommands)
668
+ );
669
+ }
651
670
  function truncatePathFromLeft(pathStr, maxLength) {
652
671
  if (pathStr.length <= maxLength) {
653
672
  return pathStr;
@@ -701,7 +720,7 @@ async function checkExistingFiles(outputPath, scope, options) {
701
720
  const sourcePath = path4.join(__dirname3, "..", DIRECTORIES.SOURCES);
702
721
  const destinationPath = getDestinationPath(outputPath, scope);
703
722
  const flags = options?.flags ?? [];
704
- const allFiles = await fs4.readdir(sourcePath);
723
+ const allFiles = await getSourceFiles(options?.includeContribCommands);
705
724
  const files = options?.commands ? allFiles.filter((f) => options.commands.includes(f)) : allFiles;
706
725
  const existingFiles = [];
707
726
  const prefix = options?.commandPrefix || "";
@@ -713,7 +732,8 @@ async function checkExistingFiles(outputPath, scope, options) {
713
732
  }
714
733
  const baseDir = path4.join(__dirname3, "..");
715
734
  for (const file of files) {
716
- const destFileName = prefix + file;
735
+ const outputFileName = stripContribPrefix(file);
736
+ const destFileName = prefix + outputFileName;
717
737
  const destFilePath = path4.join(destinationPath, destFileName);
718
738
  const sourceFilePath = path4.join(sourcePath, file);
719
739
  if (await fs4.pathExists(destFilePath)) {
@@ -886,9 +906,7 @@ async function generateToDirectory(outputPath, scope, options) {
886
906
  const destinationPath = getDestinationPath(outputPath, scope);
887
907
  const sourcePath = path4.join(__dirname3, "..", DIRECTORIES.SOURCES);
888
908
  const flags = options?.flags ?? [];
889
- const allFiles = (await fs4.readdir(sourcePath)).filter(
890
- (f) => f.endsWith(".md")
891
- );
909
+ const allFiles = await getSourceFiles(options?.includeContribCommands);
892
910
  let files = options?.commands ? allFiles.filter((f) => options.commands.includes(f)) : allFiles;
893
911
  if (options?.skipFiles) {
894
912
  const prefix2 = options?.commandPrefix || "";
@@ -907,8 +925,9 @@ async function generateToDirectory(outputPath, scope, options) {
907
925
  const cleanedContent = applyMarkdownFixes(
908
926
  stripInternalMetadata(expandedContent)
909
927
  );
928
+ const outputFileName = stripContribPrefix(file);
910
929
  await fs4.writeFile(
911
- path4.join(destinationPath, prefix + file),
930
+ path4.join(destinationPath, prefix + outputFileName),
912
931
  cleanedContent
913
932
  );
914
933
  }
@@ -922,7 +941,8 @@ async function generateToDirectory(outputPath, scope, options) {
922
941
  (tool) => allowedToolsSet.has(tool)
923
942
  );
924
943
  if (toolsForCommand.length > 0) {
925
- const filePath = path4.join(destinationPath, prefix + file);
944
+ const outputFileName = stripContribPrefix(file);
945
+ const filePath = path4.join(destinationPath, prefix + outputFileName);
926
946
  const content = await fs4.readFile(filePath, "utf-8");
927
947
  const allowedToolsYaml = `allowed-tools: ${toolsForCommand.join(", ")}`;
928
948
  const modifiedContent = content.replace(
@@ -950,8 +970,9 @@ ${allowedToolsYaml}
950
970
  const templates = extractTemplateBlocks(sourceContent);
951
971
  if (templates.length > 0) {
952
972
  for (const file of files) {
953
- const commandName = path4.basename(file, ".md");
954
- const actualFileName = options?.commandPrefix ? options.commandPrefix + file : file;
973
+ const outputFileName = stripContribPrefix(file);
974
+ const commandName = path4.basename(outputFileName, ".md");
975
+ const actualFileName = options?.commandPrefix ? options.commandPrefix + outputFileName : outputFileName;
955
976
  const filePath = path4.join(destinationPath, actualFileName);
956
977
  let content = await fs4.readFile(filePath, "utf-8");
957
978
  let modified = false;
@@ -1122,7 +1143,8 @@ async function main(args) {
1122
1143
  if (args.updateExisting) {
1123
1144
  cachedExistingFiles = await checkExistingFiles(void 0, scope, {
1124
1145
  commandPrefix: commandPrefix || "",
1125
- flags: selectedFlags
1146
+ flags: selectedFlags,
1147
+ includeContribCommands: args.includeContribCommands
1126
1148
  });
1127
1149
  selectedCommands = cachedExistingFiles.map((f) => f.filename);
1128
1150
  if (selectedCommands.length === 0) {
@@ -1175,7 +1197,8 @@ async function main(args) {
1175
1197
  scope,
1176
1198
  {
1177
1199
  commandPrefix: commandPrefix || "",
1178
- flags: selectedFlags
1200
+ flags: selectedFlags,
1201
+ includeContribCommands: args.includeContribCommands
1179
1202
  }
1180
1203
  );
1181
1204
  const existingFilenames = new Set(
@@ -1223,7 +1246,8 @@ async function main(args) {
1223
1246
  commandPrefix,
1224
1247
  commands: selectedCommands,
1225
1248
  allowedTools: selectedAllowedTools,
1226
- flags: selectedFlags
1249
+ flags: selectedFlags,
1250
+ includeContribCommands: args?.includeContribCommands
1227
1251
  });
1228
1252
  const skipFiles = [];
1229
1253
  const conflictingFiles = existingFiles.filter((f) => !f.isIdentical);
@@ -1303,7 +1327,8 @@ async function main(args) {
1303
1327
  commands: selectedCommands,
1304
1328
  skipFiles,
1305
1329
  allowedTools: selectedAllowedTools,
1306
- flags: selectedFlags
1330
+ flags: selectedFlags,
1331
+ includeContribCommands: args?.includeContribCommands
1307
1332
  });
1308
1333
  const fullPath = scope === "project" ? `${process.cwd()}/.claude/commands` : `${os2.homedir()}/.claude/commands`;
1309
1334
  outro(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wbern/claude-instructions",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "TDD workflow commands for Claude Code CLI",
5
5
  "type": "module",
6
6
  "bin": "./bin/cli.js",
@@ -29,7 +29,7 @@
29
29
  "scripts": {
30
30
  "build": "pnpm build:readme && pnpm build:commands && pnpm exec markdownlint --fix .claude/commands/*.md",
31
31
  "build:readme": "tsx scripts/build.ts",
32
- "build:commands": "pnpm build:cli && node bin/cli.js --scope=project --flags=beads,no-plan-files --overwrite",
32
+ "build:commands": "pnpm build:cli && node bin/cli.js --scope=project --flags=beads,no-plan-files --include-contrib-commands --overwrite",
33
33
  "build:cli": "tsup",
34
34
  "test:manual": "pnpm build:cli && TMPDIR=$(mktemp -d) && pnpm pack --pack-destination $TMPDIR && cd $TMPDIR && tar -xzf *.tgz && cd package && pnpm i && node bin/cli.js",
35
35
  "test:quick-manual": "pnpm build:cli && node bin/cli.js",
package/src/README.md CHANGED
@@ -43,7 +43,7 @@ pnpm dlx @wbern/claude-instructions
43
43
 
44
44
  The interactive installer lets you choose:
45
45
 
46
- - **Variant**: With or without [Beads MCP](https://github.com/steveyegge/beads) integration
46
+ - **Feature flags**: Enable optional integrations like [Beads MCP](https://github.com/steveyegge/beads)
47
47
  - **Scope**: User-level (global) or project-level installation
48
48
 
49
49
  After installation, restart Claude Code if it's currently running.
@@ -61,7 +61,7 @@ Then add a postinstall script to your `package.json`:
61
61
  ```json
62
62
  {
63
63
  "scripts": {
64
- "postinstall": "npx @wbern/claude-instructions --variant=without-beads --scope=project --prefix="
64
+ "postinstall": "npx @wbern/claude-instructions --scope=project --overwrite"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@wbern/claude-instructions": "^1.0.0"
@@ -0,0 +1,137 @@
1
+ ---
2
+ description: Create a new slash command for this repository
3
+ argument-hint: <command-name> <command-info>
4
+ _hint: Create command
5
+ _category: Utilities
6
+ _order: 99
7
+ ---
8
+
9
+ <!-- docs INCLUDE path='src/fragments/universal-guidelines.md' -->
10
+ <!-- /docs -->
11
+
12
+ Create a new custom command in `src/sources/` following the patterns below. Assess the structure carefully using the below info but also researching the repo.
13
+
14
+ Command to create: $ARGUMENTS
15
+
16
+ ## File Structure
17
+
18
+ Create `src/sources/<command-name>.md` with:
19
+ 1. Frontmatter (required fields below)
20
+ 2. INCLUDE directives for shared content
21
+ - We always include the `docs INCLUDE path='src/fragments/universal-guidelines.md'` fragment
22
+ 3. Command-specific content
23
+ 4. Exactly ONE `[DOLLAR]ARGUMENTS` placeholder
24
+
25
+ After creating, run `pnpm build` and `pnpm vitest run -u` to update snapshots.
26
+
27
+ ## Frontmatter Template
28
+
29
+ ```yaml
30
+ ---
31
+ description: Brief description for /help
32
+ argument-hint: [optional-arg] or <required-arg> or (no arguments - interactive)
33
+ _hint: Short 2-3 word hint
34
+ _category: Test-Driven Development | Planning | Workflow | Ship / Show / Ask | Utilities | [Something else]
35
+ _order: 1-99
36
+ ---
37
+ ```
38
+
39
+ Optional: `_requested-tools` (array), `_selectedByDefault: false`
40
+
41
+ ## Category Patterns
42
+
43
+ ### Test-Driven Development (spike, red, green, refactor, cycle)
44
+
45
+ ```markdown
46
+ [PHASE] PHASE! Apply the below to the info given by user input here:
47
+
48
+ [DOLLAR]ARGUMENTS
49
+
50
+ < !-- docs INCLUDE path='src/fragments/universal-guidelines.md' -->
51
+ < !-- /docs -->
52
+
53
+ < !-- docs INCLUDE path='src/fragments/beads-awareness.md' featureFlag='beads' -->
54
+ < !-- /docs -->
55
+
56
+ < !-- docs INCLUDE path='src/fragments/fallback-arguments-beads.md' featureFlag='beads' elsePath='src/fragments/fallback-arguments.md' -->
57
+ < !-- /docs -->
58
+
59
+ < !-- docs INCLUDE path='src/fragments/tdd-fundamentals.md' -->
60
+ < !-- /docs -->
61
+ ```
62
+
63
+ Add for refactor: `peeping-tom-warning.md`, `consistency-check.md`
64
+ Add for red: `aaa-pattern.md`
65
+
66
+ ### Planning (issue, plan)
67
+
68
+ ```markdown
69
+ # [Title]
70
+
71
+ < !-- docs INCLUDE path='src/fragments/universal-guidelines.md' -->
72
+ < !-- /docs -->
73
+
74
+ < !-- docs INCLUDE path='src/fragments/beads-awareness.md' featureFlag='beads' -->
75
+ < !-- /docs -->
76
+
77
+ [Description and [DOLLAR] embedded in flow]
78
+
79
+ < !-- docs INCLUDE path='src/fragments/discovery-phase.md' -->
80
+ < !-- /docs -->
81
+
82
+ < !-- docs INCLUDE path='src/fragments/beads-integration.md' featureFlag='beads' -->
83
+ < !-- /docs -->
84
+ ```
85
+
86
+ ### Workflow (commit, pr, gap, code-review)
87
+
88
+ ```markdown
89
+ < !-- docs INCLUDE path='src/fragments/universal-guidelines.md' -->
90
+ < !-- /docs -->
91
+
92
+ < !-- docs INCLUDE path='src/fragments/beads-awareness.md' featureFlag='beads' -->
93
+ < !-- /docs -->
94
+
95
+ [Workflow description]
96
+
97
+ [DOLLAR]
98
+
99
+ [Process steps]
100
+ ```
101
+
102
+ commit: add `commit-process.md`, `no-plan-files.md` (with flag)
103
+ pr/gap: add `beads-integration.md` at end
104
+ code-review: add `_requested-tools` for git commands
105
+
106
+ ### Ship / Show / Ask (ship, show, ask)
107
+
108
+ Use `_selectedByDefault: false`. Include prerequisites, safety checks, and reference other S/S/A commands.
109
+
110
+ ### Utilities (add-command, kata, tdd-review)
111
+
112
+ Flexible structure. Interactive commands use `(no arguments - interactive)` hint.
113
+
114
+ ## Available Fragments
115
+
116
+ | Fragment | Use For |
117
+ |----------|---------|
118
+ | `universal-guidelines.md` | Always first |
119
+ | `beads-awareness.md` | Always second (featureFlag='beads') |
120
+ | `tdd-fundamentals.md` | TDD commands |
121
+ | `fallback-arguments-beads.md` | TDD fallback (featureFlag='beads', elsePath to fallback-arguments.md) |
122
+ | `aaa-pattern.md` | Red phase |
123
+ | `peeping-tom-warning.md` | Refactor phase |
124
+ | `consistency-check.md` | Refactor, gap |
125
+ | `discovery-phase.md` | Planning |
126
+ | `beads-integration.md` | PR, planning (featureFlag='beads') |
127
+ | `commit-process.md` | Commit |
128
+ | `no-plan-files.md` | Commit (featureFlag='no-plan-files') |
129
+ | `github-issue-fetch.md` | Issue fetching |
130
+ | `test-quality-criteria.md` | Code review |
131
+
132
+ ## Rules
133
+
134
+ 1. `[DOLLAR]` - exactly once per source, never in fragments
135
+ 2. Fragments must not include other fragments
136
+ 3. Remove space after `<` in real INCLUDE directives (shown escaped above)
137
+ 4. Underscore-prefixed metadata stripped from output