@spardutti/claude-skills 1.24.1 → 1.26.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 +1 -0
- package/lib/setup-claude-md.mjs +100 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,6 +79,7 @@ Portable slash commands for common git workflows. Installed to `.claude/commands
|
|
|
79
79
|
| `/release` | Release flow — dev→main PR with semver, changelog, tag, and GitHub release |
|
|
80
80
|
| `/refactor` | Detect size/complexity/duplication/coupling issues via 4 parallel Haiku subagents, then refactor |
|
|
81
81
|
| `/deep-review` | Multi-agent deep code review — 5 parallel Sonnet subagents catch guard bypasses, lost async state, wrong-table queries, dead references, protocol violations |
|
|
82
|
+
| `/plan-feature` | Integration-first feature planning — 3 parallel Haiku subagents scan for reusable code, established patterns, and touch points before producing a short integration plan |
|
|
82
83
|
|
|
83
84
|
## Quick Start
|
|
84
85
|
|
package/lib/setup-claude-md.mjs
CHANGED
|
@@ -1,65 +1,121 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
## File Size Enforcement
|
|
4
|
+
const SKILL_BODY = `## Skills
|
|
5
|
+
|
|
6
|
+
BEFORE writing ANY code, you MUST:
|
|
7
|
+
|
|
8
|
+
1. List EVERY skill available: check \`.claude/skills/\` (project) and \`~/.claude/skills/\` (global). The system-reminder's available-skills section is a hint, not the source of truth — if it's missing or empty, still check the directories.
|
|
9
|
+
2. For each skill, write: [skill-name] → ACTIVATE / SKIP — [one-line reason]
|
|
10
|
+
3. Call Skill(name) for every skill marked ACTIVATE
|
|
11
|
+
4. Only THEN proceed to implementation
|
|
12
|
+
|
|
13
|
+
If you skip this evaluation, your response is INCOMPLETE and WRONG.`;
|
|
14
|
+
|
|
15
|
+
const FILE_SIZE_BODY = `## File Size Enforcement
|
|
17
16
|
|
|
18
17
|
- **Never write a file longer than 200 lines of code.** If a file would exceed 200 lines, split it into smaller modules before writing.
|
|
19
18
|
- This rule applies during skill evaluation: if the code you're about to write would exceed 200 lines in any single file, refactor into multiple files first.
|
|
20
19
|
- Skill evaluation must check this limit as part of every ACTIVATE decision.`;
|
|
21
20
|
|
|
22
|
-
const
|
|
23
|
-
|
|
21
|
+
const BLOCKS = [
|
|
22
|
+
{
|
|
23
|
+
id: "skill-evaluation",
|
|
24
|
+
body: SKILL_BODY,
|
|
25
|
+
legacyHeadings: ["## Skills"],
|
|
26
|
+
legacyYamlMarker: "skill_evaluation:",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "file-size",
|
|
30
|
+
body: FILE_SIZE_BODY,
|
|
31
|
+
legacyHeadings: ["## File Size Enforcement"],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
function wrap(id, body) {
|
|
36
|
+
return `<!-- claude-skills:${id}:start -->\n${body}\n<!-- claude-skills:${id}:end -->`;
|
|
37
|
+
}
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
function spliceSentinels(content, id, replacement) {
|
|
40
|
+
const start = `<!-- claude-skills:${id}:start -->`;
|
|
41
|
+
const end = `<!-- claude-skills:${id}:end -->`;
|
|
42
|
+
const startIdx = content.indexOf(start);
|
|
43
|
+
if (startIdx === -1) return null;
|
|
44
|
+
const endIdx = content.indexOf(end, startIdx);
|
|
45
|
+
if (endIdx === -1) return null;
|
|
46
|
+
return content.slice(0, startIdx) + replacement + content.slice(endIdx + end.length);
|
|
47
|
+
}
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
const
|
|
49
|
+
function spliceLegacyHeading(content, heading, replacement) {
|
|
50
|
+
const lines = content.split("\n");
|
|
51
|
+
const startIdx = lines.findIndex((l) => l.trim() === heading);
|
|
52
|
+
if (startIdx === -1) return null;
|
|
53
|
+
let endIdx = lines.length;
|
|
54
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
55
|
+
if (lines[i].startsWith("## ")) {
|
|
56
|
+
endIdx = i;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return joinSplice(lines, startIdx, endIdx, replacement);
|
|
61
|
+
}
|
|
38
62
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
63
|
+
function spliceLegacyYaml(content, marker, replacement) {
|
|
64
|
+
const lines = content.split("\n");
|
|
65
|
+
const startIdx = lines.findIndex((l) => l.startsWith(marker));
|
|
66
|
+
if (startIdx === -1) return null;
|
|
67
|
+
let endIdx = lines.length;
|
|
68
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
69
|
+
const line = lines[i];
|
|
70
|
+
if (line.length > 0 && !/^\s/.test(line)) {
|
|
71
|
+
endIdx = i;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
42
74
|
}
|
|
75
|
+
return joinSplice(lines, startIdx, endIdx, replacement);
|
|
76
|
+
}
|
|
43
77
|
|
|
44
|
-
|
|
78
|
+
function joinSplice(lines, startIdx, endIdx, replacement) {
|
|
79
|
+
const before = lines.slice(0, startIdx).join("\n").replace(/\s+$/, "");
|
|
80
|
+
const after = lines.slice(endIdx).join("\n").replace(/^\s+/, "");
|
|
81
|
+
const parts = [before, replacement, after].filter((s) => s.length > 0);
|
|
82
|
+
return parts.join("\n\n");
|
|
83
|
+
}
|
|
45
84
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
85
|
+
function applyBlock(content, block) {
|
|
86
|
+
const wrapped = wrap(block.id, block.body);
|
|
87
|
+
let next = spliceSentinels(content, block.id, wrapped);
|
|
88
|
+
if (next !== null) return { content: next, action: `replaced ${block.id}` };
|
|
89
|
+
for (const heading of block.legacyHeadings ?? []) {
|
|
90
|
+
next = spliceLegacyHeading(content, heading, wrapped);
|
|
91
|
+
if (next !== null) return { content: next, action: `migrated ${block.id}` };
|
|
50
92
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
93
|
+
if (block.legacyYamlMarker) {
|
|
94
|
+
next = spliceLegacyYaml(content, block.legacyYamlMarker, wrapped);
|
|
95
|
+
if (next !== null) return { content: next, action: `migrated ${block.id}` };
|
|
54
96
|
}
|
|
97
|
+
const base = content.replace(/\s+$/, "");
|
|
98
|
+
const merged = base.length > 0 ? base + "\n\n" + wrapped : wrapped;
|
|
99
|
+
return { content: merged, action: `added ${block.id}` };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function setupClaudeMd(targetDir = process.cwd()) {
|
|
103
|
+
const claudeMdPath = join(resolve(targetDir), "CLAUDE.md");
|
|
55
104
|
|
|
56
|
-
|
|
105
|
+
let content = "";
|
|
106
|
+
try {
|
|
107
|
+
content = await readFile(claudeMdPath, "utf-8");
|
|
108
|
+
} catch {
|
|
109
|
+
// File doesn't exist — will create
|
|
110
|
+
}
|
|
57
111
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.log(" CLAUDE.md updated with file size enforcement rule.");
|
|
112
|
+
const actions = [];
|
|
113
|
+
for (const block of BLOCKS) {
|
|
114
|
+
const result = applyBlock(content, block);
|
|
115
|
+
content = result.content;
|
|
116
|
+
actions.push(result.action);
|
|
64
117
|
}
|
|
118
|
+
|
|
119
|
+
await writeFile(claudeMdPath, content.replace(/\s+$/, "") + "\n", { mode: 0o644 });
|
|
120
|
+
console.log(` CLAUDE.md: ${actions.join(", ")}.`);
|
|
65
121
|
}
|