@wbern/claude-instructions 1.9.0 → 1.11.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/LICENSE +21 -0
- package/README.md +21 -15
- package/bin/cli.js +265 -47
- package/downloads/with-beads/busycommit.md +16 -0
- package/downloads/with-beads/code-review.md +248 -0
- package/downloads/with-beads/commands-metadata.json +15 -1
- package/downloads/with-beads/commit.md +16 -0
- package/downloads/with-beads/gap.md +1 -0
- package/downloads/with-beads/plan.md +1 -2
- package/downloads/with-beads/worktree-add.md +68 -44
- package/downloads/with-beads/worktree-cleanup.md +74 -55
- package/downloads/without-beads/busycommit.md +16 -0
- package/downloads/without-beads/code-review.md +246 -0
- package/downloads/without-beads/commands-metadata.json +15 -1
- package/downloads/without-beads/commit.md +16 -0
- package/downloads/without-beads/gap.md +1 -0
- package/downloads/without-beads/worktree-add.md +68 -44
- package/downloads/without-beads/worktree-cleanup.md +74 -55
- package/package.json +4 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 wbern
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -21,6 +21,10 @@ TDD workflow commands for Claude Code CLI.
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
npx @wbern/claude-instructions
|
|
24
|
+
|
|
25
|
+
// or
|
|
26
|
+
|
|
27
|
+
pnpm dlx @wbern/claude-instructions
|
|
24
28
|
```
|
|
25
29
|
|
|
26
30
|
The interactive installer lets you choose:
|
|
@@ -57,13 +61,14 @@ This ensures commands are regenerated whenever anyone runs `npm install`, `pnpm
|
|
|
57
61
|
|
|
58
62
|
| Option | Description |
|
|
59
63
|
|--------|-------------|
|
|
60
|
-
| `--variant=with-beads` |
|
|
61
|
-
| `--
|
|
62
|
-
| `--
|
|
63
|
-
| `--scope=user` | Install to `~/.claude/commands` (global) |
|
|
64
|
-
| `--prefix=my-` | Add prefix to command names (e.g., `my-commit.md`) |
|
|
65
|
-
| `--skip-template-injection` | Don't inject CLAUDE.md template content |
|
|
64
|
+
| `--variant=with-beads` | Command variant (with-beads, without-beads) |
|
|
65
|
+
| `--scope=project` | Installation scope (project, user) |
|
|
66
|
+
| `--prefix=my-` | Add prefix to command names |
|
|
66
67
|
| `--commands=commit,red,green` | Install only specific commands |
|
|
68
|
+
| `--skip-template-injection` | Skip injecting project CLAUDE.md customizations |
|
|
69
|
+
| `--update-existing` | Only update already-installed commands |
|
|
70
|
+
| `--overwrite` | Overwrite conflicting files without prompting |
|
|
71
|
+
| `--skip-on-conflict` | Skip conflicting files without prompting |
|
|
67
72
|
|
|
68
73
|
## Customizing Commands
|
|
69
74
|
|
|
@@ -190,14 +195,6 @@ flowchart TB
|
|
|
190
195
|
- `/issue` - Analyze GitHub issue and create TDD implementation plan
|
|
191
196
|
- `/plan` - Create implementation plan from feature/requirement with PRD-style discovery and TDD acceptance criteria
|
|
192
197
|
|
|
193
|
-
### Workflow
|
|
194
|
-
|
|
195
|
-
- `/commit` - Create a git commit following project standards
|
|
196
|
-
- `/busycommit` - Create multiple atomic git commits, one logical change at a time
|
|
197
|
-
- `/pr` - Creates a pull request using GitHub MCP
|
|
198
|
-
- `/summarize` - Summarize conversation progress and next steps
|
|
199
|
-
- `/gap` - Analyze conversation context for unaddressed items and gaps
|
|
200
|
-
|
|
201
198
|
### Test-Driven Development
|
|
202
199
|
|
|
203
200
|
- `/spike` - Execute TDD Spike Phase - exploratory coding to understand problem space before TDD
|
|
@@ -207,6 +204,15 @@ flowchart TB
|
|
|
207
204
|
- `/refactor` - Execute TDD Refactor Phase - improve code structure while keeping tests green
|
|
208
205
|
- `/cycle` - Execute complete TDD cycle - Red, Green, and Refactor phases in sequence
|
|
209
206
|
|
|
207
|
+
### Workflow
|
|
208
|
+
|
|
209
|
+
- `/commit` - Create a git commit following project standards
|
|
210
|
+
- `/busycommit` - Create multiple atomic git commits, one logical change at a time
|
|
211
|
+
- `/pr` - Creates a pull request using GitHub MCP
|
|
212
|
+
- `/summarize` - Summarize conversation progress and next steps
|
|
213
|
+
- `/gap` - Analyze conversation context for unaddressed items and gaps
|
|
214
|
+
- `/code-review` - Code review using dynamic category detection and domain-specific analysis
|
|
215
|
+
|
|
210
216
|
### Ship / Show / Ask
|
|
211
217
|
|
|
212
218
|
- `/ship` - Ship code directly to main - for small, obvious changes that don't need review
|
|
@@ -215,7 +221,7 @@ flowchart TB
|
|
|
215
221
|
|
|
216
222
|
### Worktree Management
|
|
217
223
|
|
|
218
|
-
- `/worktree-add` - Add a new git worktree from branch name or
|
|
224
|
+
- `/worktree-add` - Add a new git worktree from branch name or issue URL, copy settings, install deps, and open in current IDE
|
|
219
225
|
- `/worktree-cleanup` - Clean up merged worktrees by verifying PR/issue status, consolidating settings, and removing stale worktrees
|
|
220
226
|
|
|
221
227
|
### Utilities
|
package/bin/cli.js
CHANGED
|
@@ -119,6 +119,7 @@ init_esm_shims();
|
|
|
119
119
|
import {
|
|
120
120
|
select,
|
|
121
121
|
text,
|
|
122
|
+
multiselect,
|
|
122
123
|
groupMultiselect,
|
|
123
124
|
isCancel,
|
|
124
125
|
intro,
|
|
@@ -496,7 +497,7 @@ var CATEGORY_ORDER = [
|
|
|
496
497
|
"Utilities",
|
|
497
498
|
"Ship / Show / Ask"
|
|
498
499
|
];
|
|
499
|
-
async function
|
|
500
|
+
async function loadCommandsMetadata(variant) {
|
|
500
501
|
const sourcePath = path2.join(
|
|
501
502
|
__dirname2,
|
|
502
503
|
"..",
|
|
@@ -505,7 +506,10 @@ async function getCommandsGroupedByCategory(variant) {
|
|
|
505
506
|
);
|
|
506
507
|
const metadataPath = path2.join(sourcePath, "commands-metadata.json");
|
|
507
508
|
const metadataContent = await fs.readFile(metadataPath, "utf-8");
|
|
508
|
-
|
|
509
|
+
return JSON.parse(metadataContent);
|
|
510
|
+
}
|
|
511
|
+
async function getCommandsGroupedByCategory(variant) {
|
|
512
|
+
const metadata = await loadCommandsMetadata(variant);
|
|
509
513
|
const grouped = {};
|
|
510
514
|
for (const [filename, data] of Object.entries(metadata)) {
|
|
511
515
|
const category = data.category;
|
|
@@ -519,6 +523,11 @@ async function getCommandsGroupedByCategory(variant) {
|
|
|
519
523
|
selectedByDefault: data.selectedByDefault !== false
|
|
520
524
|
});
|
|
521
525
|
}
|
|
526
|
+
for (const category of Object.keys(grouped)) {
|
|
527
|
+
if (!CATEGORY_ORDER.includes(category)) {
|
|
528
|
+
throw new Error(`Unknown category: ${category}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
522
531
|
for (const category of Object.keys(grouped)) {
|
|
523
532
|
grouped[category].sort((a, b) => {
|
|
524
533
|
const orderA = metadata[a.value].order;
|
|
@@ -527,12 +536,7 @@ async function getCommandsGroupedByCategory(variant) {
|
|
|
527
536
|
});
|
|
528
537
|
}
|
|
529
538
|
const sortedCategories = Object.keys(grouped).sort((a, b) => {
|
|
530
|
-
|
|
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);
|
|
539
|
+
return CATEGORY_ORDER.indexOf(a) - CATEGORY_ORDER.indexOf(b);
|
|
536
540
|
});
|
|
537
541
|
const sortedGrouped = {};
|
|
538
542
|
for (const category of sortedCategories) {
|
|
@@ -540,6 +544,37 @@ async function getCommandsGroupedByCategory(variant) {
|
|
|
540
544
|
}
|
|
541
545
|
return sortedGrouped;
|
|
542
546
|
}
|
|
547
|
+
function extractLabelFromTool(tool) {
|
|
548
|
+
const match = tool.match(/^Bash\(([^:]+):/);
|
|
549
|
+
return match ? match[1] : tool;
|
|
550
|
+
}
|
|
551
|
+
function formatCommandsHint(commands) {
|
|
552
|
+
if (commands.length <= 2) {
|
|
553
|
+
return commands.map((c) => `/${c}`).join(", ");
|
|
554
|
+
}
|
|
555
|
+
const first = commands.slice(0, 2).map((c) => `/${c}`);
|
|
556
|
+
const remaining = commands.length - 2;
|
|
557
|
+
return `${first.join(", ")}, and ${remaining} ${remaining === 1 ? "other" : "others"}`;
|
|
558
|
+
}
|
|
559
|
+
async function getRequestedToolsOptions(variant) {
|
|
560
|
+
const metadata = await loadCommandsMetadata(variant);
|
|
561
|
+
const toolToCommands = /* @__PURE__ */ new Map();
|
|
562
|
+
for (const [filename, data] of Object.entries(metadata)) {
|
|
563
|
+
if (data["_requested-tools"]) {
|
|
564
|
+
const commandName = filename.replace(/\.md$/, "");
|
|
565
|
+
for (const tool of data["_requested-tools"]) {
|
|
566
|
+
const commands = toolToCommands.get(tool) || [];
|
|
567
|
+
commands.push(commandName);
|
|
568
|
+
toolToCommands.set(tool, commands);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return Array.from(toolToCommands.entries()).map(([tool, commands]) => ({
|
|
573
|
+
value: tool,
|
|
574
|
+
label: extractLabelFromTool(tool),
|
|
575
|
+
hint: formatCommandsHint(commands)
|
|
576
|
+
}));
|
|
577
|
+
}
|
|
543
578
|
function getDestinationPath(outputPath, scope) {
|
|
544
579
|
if (outputPath) {
|
|
545
580
|
return outputPath;
|
|
@@ -596,6 +631,20 @@ async function generateToDirectory(outputPath, variant, scope, options) {
|
|
|
596
631
|
} else {
|
|
597
632
|
await fs.copy(sourcePath, destinationPath, {});
|
|
598
633
|
}
|
|
634
|
+
if (options?.allowedTools && options.allowedTools.length > 0) {
|
|
635
|
+
for (const file of files) {
|
|
636
|
+
const filePath = path2.join(destinationPath, file);
|
|
637
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
638
|
+
const allowedToolsYaml = `allowed-tools: ${options.allowedTools.join(", ")}`;
|
|
639
|
+
const modifiedContent = content.replace(
|
|
640
|
+
/^---\n/,
|
|
641
|
+
`---
|
|
642
|
+
${allowedToolsYaml}
|
|
643
|
+
`
|
|
644
|
+
);
|
|
645
|
+
await fs.writeFile(filePath, modifiedContent);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
599
648
|
if (options?.commandPrefix) {
|
|
600
649
|
for (const file of files) {
|
|
601
650
|
const oldPath = path2.join(destinationPath, file);
|
|
@@ -770,11 +819,26 @@ async function main(args) {
|
|
|
770
819
|
let scope;
|
|
771
820
|
let commandPrefix;
|
|
772
821
|
let selectedCommands;
|
|
822
|
+
let selectedAllowedTools;
|
|
823
|
+
let cachedExistingFiles;
|
|
773
824
|
if (args?.variant && args?.scope && args?.prefix !== void 0) {
|
|
774
825
|
variant = args.variant;
|
|
775
826
|
scope = args.scope;
|
|
776
827
|
commandPrefix = args.prefix;
|
|
777
828
|
selectedCommands = args.commands;
|
|
829
|
+
if (args.updateExisting) {
|
|
830
|
+
cachedExistingFiles = await checkExistingFiles(
|
|
831
|
+
void 0,
|
|
832
|
+
variant,
|
|
833
|
+
scope,
|
|
834
|
+
{ commandPrefix: commandPrefix || "" }
|
|
835
|
+
);
|
|
836
|
+
selectedCommands = cachedExistingFiles.map((f) => f.filename);
|
|
837
|
+
if (selectedCommands.length === 0) {
|
|
838
|
+
log.warn("No existing commands found in target directory");
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
778
842
|
} else {
|
|
779
843
|
variant = await select({
|
|
780
844
|
message: "Select variant",
|
|
@@ -799,9 +863,34 @@ async function main(args) {
|
|
|
799
863
|
if (isCancel(commandPrefix)) {
|
|
800
864
|
return;
|
|
801
865
|
}
|
|
802
|
-
|
|
866
|
+
let groupedCommands = await getCommandsGroupedByCategory(
|
|
803
867
|
variant
|
|
804
868
|
);
|
|
869
|
+
if (args?.updateExisting) {
|
|
870
|
+
cachedExistingFiles = await checkExistingFiles(
|
|
871
|
+
void 0,
|
|
872
|
+
variant,
|
|
873
|
+
scope,
|
|
874
|
+
{ commandPrefix: commandPrefix || "" }
|
|
875
|
+
);
|
|
876
|
+
const existingFilenames = new Set(
|
|
877
|
+
cachedExistingFiles.map((f) => f.filename)
|
|
878
|
+
);
|
|
879
|
+
const filteredGrouped = {};
|
|
880
|
+
for (const [category, commands] of Object.entries(groupedCommands)) {
|
|
881
|
+
const filtered = commands.filter(
|
|
882
|
+
(cmd) => existingFilenames.has(cmd.value)
|
|
883
|
+
);
|
|
884
|
+
if (filtered.length > 0) {
|
|
885
|
+
filteredGrouped[category] = filtered;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
groupedCommands = filteredGrouped;
|
|
889
|
+
if (Object.keys(groupedCommands).length === 0) {
|
|
890
|
+
log.warn("No existing commands found in target directory");
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
805
894
|
const enabledCommandValues = Object.values(groupedCommands).flat().filter((cmd) => cmd.selectedByDefault).map((cmd) => cmd.value);
|
|
806
895
|
selectedCommands = await groupMultiselect({
|
|
807
896
|
message: "Select commands to install (Enter to accept all)",
|
|
@@ -811,32 +900,85 @@ async function main(args) {
|
|
|
811
900
|
if (isCancel(selectedCommands)) {
|
|
812
901
|
return;
|
|
813
902
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
903
|
+
const requestedToolsOptions = await getRequestedToolsOptions(
|
|
904
|
+
variant
|
|
905
|
+
);
|
|
906
|
+
if (requestedToolsOptions.length > 0) {
|
|
907
|
+
selectedAllowedTools = await multiselect({
|
|
908
|
+
message: "Select allowed tools for commands (optional)",
|
|
909
|
+
options: requestedToolsOptions,
|
|
910
|
+
required: false
|
|
911
|
+
});
|
|
912
|
+
if (isCancel(selectedAllowedTools)) {
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
822
915
|
}
|
|
823
|
-
|
|
916
|
+
}
|
|
917
|
+
const existingFiles = cachedExistingFiles ?? await checkExistingFiles(void 0, variant, scope, {
|
|
918
|
+
commandPrefix,
|
|
919
|
+
commands: selectedCommands
|
|
920
|
+
});
|
|
824
921
|
const skipFiles = [];
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
922
|
+
if (!args?.overwrite && !args?.skipOnConflict) {
|
|
923
|
+
const conflictingFiles = existingFiles.filter((f) => !f.isIdentical);
|
|
924
|
+
const hasMultipleConflicts = conflictingFiles.length > 1;
|
|
925
|
+
let overwriteAllSelected = false;
|
|
926
|
+
let skipAllSelected = false;
|
|
927
|
+
for (const file of existingFiles) {
|
|
928
|
+
if (file.isIdentical) {
|
|
929
|
+
log.info(`${file.filename} is identical, skipping`);
|
|
930
|
+
skipFiles.push(file.filename);
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
if (overwriteAllSelected) {
|
|
934
|
+
continue;
|
|
935
|
+
}
|
|
936
|
+
if (skipAllSelected) {
|
|
937
|
+
skipFiles.push(file.filename);
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
const stats = getDiffStats(file.existingContent, file.newContent);
|
|
941
|
+
const diff = formatCompactDiff(file.existingContent, file.newContent);
|
|
942
|
+
note(diff, `Diff: ${file.filename}`);
|
|
943
|
+
log.info(`+${stats.added} -${stats.removed}`);
|
|
944
|
+
if (hasMultipleConflicts) {
|
|
945
|
+
const choice = await select({
|
|
946
|
+
message: `Overwrite ${file.filename}?`,
|
|
947
|
+
options: [
|
|
948
|
+
{ value: "yes", label: "Yes" },
|
|
949
|
+
{ value: "no", label: "No" },
|
|
950
|
+
{ value: "overwrite_all", label: "Overwrite all" },
|
|
951
|
+
{ value: "skip_all", label: "Skip all" }
|
|
952
|
+
]
|
|
953
|
+
});
|
|
954
|
+
if (isCancel(choice)) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
if (choice === "no") {
|
|
958
|
+
skipFiles.push(file.filename);
|
|
959
|
+
} else if (choice === "overwrite_all") {
|
|
960
|
+
overwriteAllSelected = true;
|
|
961
|
+
} else if (choice === "skip_all") {
|
|
962
|
+
skipAllSelected = true;
|
|
963
|
+
skipFiles.push(file.filename);
|
|
964
|
+
}
|
|
965
|
+
} else {
|
|
966
|
+
const shouldOverwrite = await confirm({
|
|
967
|
+
message: `Overwrite ${file.filename}?`
|
|
968
|
+
});
|
|
969
|
+
if (isCancel(shouldOverwrite)) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
if (!shouldOverwrite) {
|
|
973
|
+
skipFiles.push(file.filename);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
830
976
|
}
|
|
831
|
-
|
|
832
|
-
const
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
message: `Overwrite ${file.filename}?`
|
|
837
|
-
});
|
|
838
|
-
if (!shouldOverwrite) {
|
|
839
|
-
skipFiles.push(file.filename);
|
|
977
|
+
} else if (args?.skipOnConflict) {
|
|
978
|
+
for (const file of existingFiles) {
|
|
979
|
+
if (!file.isIdentical) {
|
|
980
|
+
skipFiles.push(file.filename);
|
|
981
|
+
}
|
|
840
982
|
}
|
|
841
983
|
}
|
|
842
984
|
const result = await generateToDirectory(
|
|
@@ -847,7 +989,8 @@ async function main(args) {
|
|
|
847
989
|
commandPrefix,
|
|
848
990
|
skipTemplateInjection: args?.skipTemplateInjection,
|
|
849
991
|
commands: selectedCommands,
|
|
850
|
-
skipFiles
|
|
992
|
+
skipFiles,
|
|
993
|
+
allowedTools: selectedAllowedTools
|
|
851
994
|
}
|
|
852
995
|
);
|
|
853
996
|
const fullPath = scope === "project" ? `${process.cwd()}/.claude/commands` : `${os2.homedir()}/.claude/commands`;
|
|
@@ -860,36 +1003,111 @@ Happy TDD'ing!`
|
|
|
860
1003
|
);
|
|
861
1004
|
}
|
|
862
1005
|
|
|
863
|
-
// scripts/
|
|
864
|
-
|
|
865
|
-
var
|
|
866
|
-
|
|
867
|
-
|
|
1006
|
+
// scripts/cli-options.ts
|
|
1007
|
+
init_esm_shims();
|
|
1008
|
+
var CLI_OPTIONS = [
|
|
1009
|
+
{
|
|
1010
|
+
flag: "--variant",
|
|
1011
|
+
key: "variant",
|
|
1012
|
+
type: "string",
|
|
1013
|
+
description: "Command variant (with-beads, without-beads)",
|
|
1014
|
+
example: "--variant=with-beads"
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
flag: "--scope",
|
|
1018
|
+
key: "scope",
|
|
1019
|
+
type: "string",
|
|
1020
|
+
description: "Installation scope (project, user)",
|
|
1021
|
+
example: "--scope=project"
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
flag: "--prefix",
|
|
1025
|
+
key: "prefix",
|
|
1026
|
+
type: "string",
|
|
1027
|
+
description: "Add prefix to command names",
|
|
1028
|
+
example: "--prefix=my-"
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
flag: "--commands",
|
|
1032
|
+
key: "commands",
|
|
1033
|
+
type: "array",
|
|
1034
|
+
description: "Install only specific commands",
|
|
1035
|
+
example: "--commands=commit,red,green"
|
|
1036
|
+
},
|
|
1037
|
+
{
|
|
1038
|
+
flag: "--skip-template-injection",
|
|
1039
|
+
key: "skipTemplateInjection",
|
|
1040
|
+
type: "boolean",
|
|
1041
|
+
description: "Skip injecting project CLAUDE.md customizations"
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
flag: "--update-existing",
|
|
1045
|
+
key: "updateExisting",
|
|
1046
|
+
type: "boolean",
|
|
1047
|
+
description: "Only update already-installed commands"
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
flag: "--overwrite",
|
|
1051
|
+
key: "overwrite",
|
|
1052
|
+
type: "boolean",
|
|
1053
|
+
description: "Overwrite conflicting files without prompting"
|
|
1054
|
+
},
|
|
1055
|
+
{
|
|
1056
|
+
flag: "--skip-on-conflict",
|
|
1057
|
+
key: "skipOnConflict",
|
|
1058
|
+
type: "boolean",
|
|
1059
|
+
description: "Skip conflicting files without prompting"
|
|
1060
|
+
}
|
|
868
1061
|
];
|
|
1062
|
+
function generateHelpText() {
|
|
1063
|
+
const lines = [
|
|
1064
|
+
"Usage: npx @wbern/claude-instructions [options]",
|
|
1065
|
+
"",
|
|
1066
|
+
"Options:"
|
|
1067
|
+
];
|
|
1068
|
+
for (const opt of CLI_OPTIONS) {
|
|
1069
|
+
const suffix = opt.type === "string" ? "=<value>" : opt.type === "array" ? "=<list>" : "";
|
|
1070
|
+
const padding = 28 - (opt.flag.length + suffix.length);
|
|
1071
|
+
lines.push(
|
|
1072
|
+
` ${opt.flag}${suffix}${" ".repeat(Math.max(1, padding))}${opt.description}`
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
lines.push(" --help, -h Show this help message");
|
|
1076
|
+
return lines.join("\n");
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// scripts/bin.ts
|
|
869
1080
|
function parseArgs(argv) {
|
|
870
1081
|
const args = {};
|
|
1082
|
+
const booleanOpts = CLI_OPTIONS.filter((o) => o.type === "boolean");
|
|
1083
|
+
const stringOpts = CLI_OPTIONS.filter((o) => o.type === "string");
|
|
1084
|
+
const arrayOpts = CLI_OPTIONS.filter((o) => o.type === "array");
|
|
871
1085
|
for (const arg of argv) {
|
|
872
|
-
for (const
|
|
873
|
-
if (arg === flag) {
|
|
874
|
-
args[key] = true;
|
|
1086
|
+
for (const opt of booleanOpts) {
|
|
1087
|
+
if (arg === opt.flag) {
|
|
1088
|
+
args[opt.key] = true;
|
|
875
1089
|
}
|
|
876
1090
|
}
|
|
877
|
-
for (const
|
|
878
|
-
const prefix =
|
|
1091
|
+
for (const opt of stringOpts) {
|
|
1092
|
+
const prefix = `${opt.flag}=`;
|
|
879
1093
|
if (arg.startsWith(prefix)) {
|
|
880
|
-
args[key] = arg.slice(prefix.length);
|
|
1094
|
+
args[opt.key] = arg.slice(prefix.length);
|
|
881
1095
|
}
|
|
882
1096
|
}
|
|
883
|
-
for (const
|
|
884
|
-
const prefix =
|
|
1097
|
+
for (const opt of arrayOpts) {
|
|
1098
|
+
const prefix = `${opt.flag}=`;
|
|
885
1099
|
if (arg.startsWith(prefix)) {
|
|
886
|
-
args[key] = arg.slice(prefix.length).split(",");
|
|
1100
|
+
args[opt.key] = arg.slice(prefix.length).split(",");
|
|
887
1101
|
}
|
|
888
1102
|
}
|
|
889
1103
|
}
|
|
890
1104
|
return args;
|
|
891
1105
|
}
|
|
892
1106
|
async function run(argv) {
|
|
1107
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
1108
|
+
console.log(generateHelpText());
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
893
1111
|
const args = parseArgs(argv);
|
|
894
1112
|
await main(args);
|
|
895
1113
|
}
|
|
@@ -17,6 +17,22 @@ Create multiple atomic git commits, committing the smallest possible logical uni
|
|
|
17
17
|
|
|
18
18
|
Include any of the following info if specified: $ARGUMENTS
|
|
19
19
|
|
|
20
|
+
## Commit Message Rules
|
|
21
|
+
|
|
22
|
+
Follows [Conventional Commits](https://www.conventionalcommits.org/) standard.
|
|
23
|
+
|
|
24
|
+
1. **Format**: `type(#issue): description`
|
|
25
|
+
- Use `#123` for local repo issues
|
|
26
|
+
- Use `owner/repo#123` for cross-repo issues
|
|
27
|
+
- Common types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`
|
|
28
|
+
|
|
29
|
+
2. **AI Credits**: **NEVER include AI credits in commit messages**
|
|
30
|
+
- No "Generated with Claude Code"
|
|
31
|
+
- No "Co-Authored-By: Claude" or "Co-Authored-By: Happy"
|
|
32
|
+
- Focus on the actual changes made, not conversation history
|
|
33
|
+
|
|
34
|
+
3. **Content**: Write clear, concise commit messages describing what changed and why
|
|
35
|
+
|
|
20
36
|
## Process
|
|
21
37
|
|
|
22
38
|
1. Run `git status` and `git diff` to review changes
|