@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 +2 -2
- package/bin/cli.js +39 -14
- package/package.json +2 -2
- package/src/README.md +2 -2
- package/src/sources/_contribute-a-command.md +137 -0
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
|
-
- **
|
|
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 --
|
|
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
|
|
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
|
|
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 =
|
|
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 +
|
|
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
|
|
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
|
|
954
|
-
const
|
|
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.
|
|
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
|
-
- **
|
|
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 --
|
|
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
|