javi-forge 1.4.0 → 1.6.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.
Files changed (69) hide show
  1. package/README.md +191 -3
  2. package/ci-local/hooks/pre-push +17 -13
  3. package/dist/commands/ci.d.ts +4 -0
  4. package/dist/commands/ci.d.ts.map +1 -1
  5. package/dist/commands/ci.js +97 -0
  6. package/dist/commands/ci.js.map +1 -1
  7. package/dist/commands/init.d.ts.map +1 -1
  8. package/dist/commands/init.js +56 -2
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/plugin.d.ts +16 -0
  11. package/dist/commands/plugin.d.ts.map +1 -1
  12. package/dist/commands/plugin.js +70 -1
  13. package/dist/commands/plugin.js.map +1 -1
  14. package/dist/commands/security.d.ts +25 -0
  15. package/dist/commands/security.d.ts.map +1 -0
  16. package/dist/commands/security.js +305 -0
  17. package/dist/commands/security.js.map +1 -0
  18. package/dist/commands/skills.d.ts +82 -0
  19. package/dist/commands/skills.d.ts.map +1 -0
  20. package/dist/commands/skills.js +510 -0
  21. package/dist/commands/skills.js.map +1 -0
  22. package/dist/commands/tdd.d.ts +21 -0
  23. package/dist/commands/tdd.d.ts.map +1 -0
  24. package/dist/commands/tdd.js +117 -0
  25. package/dist/commands/tdd.js.map +1 -0
  26. package/dist/constants.d.ts +7 -0
  27. package/dist/constants.d.ts.map +1 -1
  28. package/dist/constants.js +132 -0
  29. package/dist/constants.js.map +1 -1
  30. package/dist/index.js +156 -1
  31. package/dist/index.js.map +1 -1
  32. package/dist/lib/agent-skills.d.ts +38 -0
  33. package/dist/lib/agent-skills.d.ts.map +1 -0
  34. package/dist/lib/agent-skills.js +111 -0
  35. package/dist/lib/agent-skills.js.map +1 -0
  36. package/dist/lib/claudemd.d.ts +8 -0
  37. package/dist/lib/claudemd.d.ts.map +1 -0
  38. package/dist/lib/claudemd.js +72 -0
  39. package/dist/lib/claudemd.js.map +1 -0
  40. package/dist/lib/codex-export.d.ts +16 -0
  41. package/dist/lib/codex-export.d.ts.map +1 -0
  42. package/dist/lib/codex-export.js +111 -0
  43. package/dist/lib/codex-export.js.map +1 -0
  44. package/dist/lib/context.d.ts +12 -0
  45. package/dist/lib/context.d.ts.map +1 -0
  46. package/dist/lib/context.js +75 -0
  47. package/dist/lib/context.js.map +1 -0
  48. package/dist/lib/plugin.d.ts +13 -1
  49. package/dist/lib/plugin.d.ts.map +1 -1
  50. package/dist/lib/plugin.js +68 -0
  51. package/dist/lib/plugin.js.map +1 -1
  52. package/dist/types/index.d.ts +119 -0
  53. package/dist/types/index.d.ts.map +1 -1
  54. package/dist/ui/App.d.ts.map +1 -1
  55. package/dist/ui/App.js +6 -0
  56. package/dist/ui/App.js.map +1 -1
  57. package/dist/ui/OptionSelector.d.ts +2 -0
  58. package/dist/ui/OptionSelector.d.ts.map +1 -1
  59. package/dist/ui/OptionSelector.js +6 -0
  60. package/dist/ui/OptionSelector.js.map +1 -1
  61. package/dist/ui/Plugin.d.ts +1 -1
  62. package/dist/ui/Plugin.d.ts.map +1 -1
  63. package/dist/ui/Plugin.js +18 -1
  64. package/dist/ui/Plugin.js.map +1 -1
  65. package/dist/ui/Skills.d.ts +11 -0
  66. package/dist/ui/Skills.d.ts.map +1 -0
  67. package/dist/ui/Skills.js +119 -0
  68. package/dist/ui/Skills.js.map +1 -0
  69. package/package.json +1 -1
@@ -0,0 +1,510 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { parseFrontmatter } from '../lib/frontmatter.js';
4
+ // ── Constants ────────────────────────────────────────────────────────────────
5
+ const DEFAULT_SKILLS_DIR = path.join(process.env['HOME'] ?? '~', '.claude', 'skills');
6
+ const DEFAULT_BUDGET = 8000;
7
+ /** Approximate tokens per character (GPT/Claude rough average) */
8
+ const CHARS_PER_TOKEN = 4;
9
+ // ── Contradiction keywords (pairs that signal opposite intent) ───────────────
10
+ const CONTRADICTION_PAIRS = [
11
+ [/\buse semicolons\b/i, /\bno semicolons\b/i],
12
+ [/\bsemicolons required\b/i, /\bno semicolons\b/i],
13
+ [/\bsingle quotes\b/i, /\bdouble quotes\b/i],
14
+ [/\btabs\b/i, /\bspaces\b/i],
15
+ [/\b2[- ]?spaces?\b/i, /\b4[- ]?spaces?\b/i],
16
+ [/\bclass[- ]?based\b/i, /\bfunctional\b/i],
17
+ [/\bOOP\b/i, /\bfunctional\b/i],
18
+ [/\bmutable\b/i, /\bimmutable\b/i],
19
+ [/\bany\b.*\ballowed\b/i, /\bno any\b/i],
20
+ [/\bdefault export\b/i, /\bnamed export\b/i],
21
+ [/\bnever use\b/i, /\balways use\b/i],
22
+ [/\bavoid\b/i, /\bprefer\b/i],
23
+ [/\bdo not\b/i, /\bmust\b/i],
24
+ ];
25
+ // ── Helpers ──────────────────────────────────────────────────────────────────
26
+ /** Estimate token count from a string */
27
+ export function estimateTokens(text) {
28
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
29
+ }
30
+ /** Read a SKILL.md and extract its name + critical rules section */
31
+ export async function parseSkillFile(skillPath) {
32
+ if (!await fs.pathExists(skillPath))
33
+ return null;
34
+ const raw = await fs.readFile(skillPath, 'utf-8');
35
+ const fm = parseFrontmatter(raw);
36
+ const name = fm?.data?.['name'] ?? path.basename(path.dirname(skillPath));
37
+ // Extract critical rules — look for "Critical Rules" or numbered list after it
38
+ const rules = extractCriticalRules(fm?.content ?? raw);
39
+ // Extract trigger keywords from description
40
+ const description = fm?.data?.['description'] ?? '';
41
+ const triggers = extractTriggers(description);
42
+ return { name, rules, rawContent: raw, triggers };
43
+ }
44
+ /** Extract critical rules from markdown content */
45
+ export function extractCriticalRules(content) {
46
+ const rules = [];
47
+ // Strategy 1: Find "Critical Rules" or "## Critical Rules" section
48
+ const block1 = extractSection(content, /Critical Rules?/i);
49
+ if (block1) {
50
+ extractListItems(block1, rules);
51
+ }
52
+ // Strategy 2: If no critical rules section, look for rules/conventions in any section
53
+ if (rules.length === 0) {
54
+ const block2 = extractSection(content, /Rules?/i);
55
+ if (block2) {
56
+ extractListItems(block2, rules);
57
+ }
58
+ }
59
+ return rules;
60
+ }
61
+ /** Extract a markdown section body by heading pattern */
62
+ function extractSection(content, headingPattern) {
63
+ const lines = content.split('\n');
64
+ let capturing = false;
65
+ const blockLines = [];
66
+ for (const line of lines) {
67
+ if (capturing) {
68
+ // Stop at next heading
69
+ if (/^#+\s/.test(line) || /^---/.test(line))
70
+ break;
71
+ blockLines.push(line);
72
+ }
73
+ else if (/^#+\s/.test(line) && headingPattern.test(line)) {
74
+ capturing = true;
75
+ }
76
+ }
77
+ return blockLines.length > 0 ? blockLines.join('\n') : null;
78
+ }
79
+ /** Extract numbered or bulleted list items from a markdown block */
80
+ function extractListItems(block, out) {
81
+ const lines = block.split('\n');
82
+ for (const line of lines) {
83
+ const match = line.match(/^\s*(?:\d+[\.\)]\s+|-\s+|\*\s+)(.+)/);
84
+ if (match) {
85
+ const cleaned = match[1].trim();
86
+ if (cleaned.length > 5)
87
+ out.push(cleaned);
88
+ }
89
+ }
90
+ }
91
+ /** Extract trigger keywords from a skill description */
92
+ export function extractTriggers(description) {
93
+ const triggerMatch = description.match(/Trigger:\s*(.+)/i);
94
+ if (!triggerMatch)
95
+ return [];
96
+ const triggerText = triggerMatch[1];
97
+ // Split on commas, "or", "and", common delimiters
98
+ const keywords = triggerText
99
+ .split(/[,;]|\bor\b/i)
100
+ .map(k => k.trim().toLowerCase().replace(/^when\s+/i, ''))
101
+ .filter(k => k.length > 2);
102
+ return keywords;
103
+ }
104
+ // ── Core: Scan installed skills ─────────────────────────────────────────────
105
+ /** Discover all SKILL.md files in a skills directory */
106
+ export async function discoverSkills(skillsDir) {
107
+ if (!await fs.pathExists(skillsDir))
108
+ return [];
109
+ const entries = await fs.readdir(skillsDir);
110
+ const skillFiles = [];
111
+ for (const entry of entries) {
112
+ if (entry.startsWith('.') || entry.startsWith('_'))
113
+ continue;
114
+ const skillPath = path.join(skillsDir, entry, 'SKILL.md');
115
+ if (await fs.pathExists(skillPath)) {
116
+ skillFiles.push(skillPath);
117
+ }
118
+ }
119
+ return skillFiles.sort();
120
+ }
121
+ // ── Conflict Detection ──────────────────────────────────────────────────────
122
+ /** Check if two rules contradict each other */
123
+ export function detectRuleConflict(ruleA, ruleB) {
124
+ const normA = ruleA.toLowerCase().trim();
125
+ const normB = ruleB.toLowerCase().trim();
126
+ for (const [patternA, patternB] of CONTRADICTION_PAIRS) {
127
+ if ((patternA.test(normA) && patternB.test(normB)) ||
128
+ (patternB.test(normA) && patternA.test(normB))) {
129
+ return `"${ruleA.slice(0, 60)}" vs "${ruleB.slice(0, 60)}"`;
130
+ }
131
+ }
132
+ return null;
133
+ }
134
+ /** Scan all skills for conflicting critical rules */
135
+ export async function findConflicts(skillsDir) {
136
+ const skillPaths = await discoverSkills(skillsDir);
137
+ const allRules = [];
138
+ for (const sp of skillPaths) {
139
+ const parsed = await parseSkillFile(sp);
140
+ if (!parsed)
141
+ continue;
142
+ for (const rule of parsed.rules) {
143
+ allRules.push({
144
+ skillName: parsed.name,
145
+ skillPath: sp,
146
+ rule,
147
+ normalized: rule.toLowerCase().trim(),
148
+ });
149
+ }
150
+ }
151
+ const conflicts = [];
152
+ // Compare every pair (O(n^2) but skill count is small ~20-50)
153
+ for (let i = 0; i < allRules.length; i++) {
154
+ for (let j = i + 1; j < allRules.length; j++) {
155
+ const a = allRules[i];
156
+ const b = allRules[j];
157
+ // Skip rules from the same skill
158
+ if (a.skillName === b.skillName)
159
+ continue;
160
+ const reason = detectRuleConflict(a.rule, b.rule);
161
+ if (reason) {
162
+ conflicts.push({ ruleA: a, ruleB: b, reason });
163
+ }
164
+ }
165
+ }
166
+ return conflicts;
167
+ }
168
+ // ── Context Budget ──────────────────────────────────────────────────────────
169
+ /** Calculate token budget for all installed skills */
170
+ export async function calculateBudget(skillsDir, budget = DEFAULT_BUDGET) {
171
+ const skillPaths = await discoverSkills(skillsDir);
172
+ const entries = [];
173
+ for (const sp of skillPaths) {
174
+ const parsed = await parseSkillFile(sp);
175
+ if (!parsed)
176
+ continue;
177
+ entries.push({
178
+ skillName: parsed.name,
179
+ skillPath: sp,
180
+ tokens: estimateTokens(parsed.rawContent),
181
+ });
182
+ }
183
+ // Sort by token count descending (biggest consumers first)
184
+ entries.sort((a, b) => b.tokens - a.tokens);
185
+ const totalTokens = entries.reduce((sum, e) => sum + e.tokens, 0);
186
+ const overBudget = totalTokens > budget;
187
+ const suggestions = [];
188
+ if (overBudget) {
189
+ const excess = totalTokens - budget;
190
+ suggestions.push(`Over budget by ~${excess} tokens`);
191
+ // Suggest disabling the largest skills until under budget
192
+ let saved = 0;
193
+ for (const entry of entries) {
194
+ if (saved >= excess)
195
+ break;
196
+ suggestions.push(`Consider disabling "${entry.skillName}" (~${entry.tokens} tokens)`);
197
+ saved += entry.tokens;
198
+ }
199
+ }
200
+ return { entries, totalTokens, budget, overBudget, suggestions };
201
+ }
202
+ // ── Duplicate Detection ─────────────────────────────────────────────────────
203
+ /** Find skills that overlap in scope/triggers */
204
+ export async function findDuplicates(skillsDir) {
205
+ const skillPaths = await discoverSkills(skillsDir);
206
+ const skillData = [];
207
+ for (const sp of skillPaths) {
208
+ const parsed = await parseSkillFile(sp);
209
+ if (!parsed || parsed.triggers.length === 0)
210
+ continue;
211
+ skillData.push({ name: parsed.name, triggers: parsed.triggers });
212
+ }
213
+ const duplicates = [];
214
+ for (let i = 0; i < skillData.length; i++) {
215
+ for (let j = i + 1; j < skillData.length; j++) {
216
+ const a = skillData[i];
217
+ const b = skillData[j];
218
+ const sharedTriggers = a.triggers.filter(t => b.triggers.some(bt => bt.includes(t) || t.includes(bt)));
219
+ if (sharedTriggers.length === 0)
220
+ continue;
221
+ const maxTriggers = Math.max(a.triggers.length, b.triggers.length);
222
+ const similarity = maxTriggers > 0
223
+ ? Math.round((sharedTriggers.length / maxTriggers) * 100)
224
+ : 0;
225
+ if (similarity >= 30) {
226
+ duplicates.push({
227
+ skillA: a.name,
228
+ skillB: b.name,
229
+ sharedTriggers,
230
+ similarity,
231
+ });
232
+ }
233
+ }
234
+ }
235
+ // Sort by similarity descending
236
+ duplicates.sort((a, b) => b.similarity - a.similarity);
237
+ return duplicates;
238
+ }
239
+ /**
240
+ * Run the skills doctor analysis.
241
+ * - `doctor --deep`: full conflict + budget + duplicate analysis
242
+ * - `budget -b N`: budget-only analysis with custom token limit
243
+ */
244
+ export async function runSkillsDoctor(options) {
245
+ const skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
246
+ const budget = options.budget ?? DEFAULT_BUDGET;
247
+ if (options.mode === 'budget') {
248
+ const budgetResult = await calculateBudget(skillsDir, budget);
249
+ return { conflicts: [], budget: budgetResult, duplicates: [] };
250
+ }
251
+ // Deep doctor mode
252
+ const [conflicts, budgetResult, duplicates] = await Promise.all([
253
+ options.deep ? findConflicts(skillsDir) : Promise.resolve([]),
254
+ calculateBudget(skillsDir, budget),
255
+ options.deep ? findDuplicates(skillsDir) : Promise.resolve([]),
256
+ ]);
257
+ return { conflicts, budget: budgetResult, duplicates };
258
+ }
259
+ // ── Quality Scoring ────────────────────────────────────────────────────────
260
+ const DEFAULT_THRESHOLD = 50;
261
+ /** Vague terms that reduce clarity score */
262
+ const VAGUE_TERMS = [
263
+ /\bstuff\b/i, /\bthings?\b/i, /\betc\.?\b/i, /\bmisc\b/i,
264
+ /\bvarious\b/i, /\bsome\b/i, /\bmaybe\b/i, /\bprobably\b/i,
265
+ ];
266
+ /** Action verbs that indicate actionable rules */
267
+ const ACTION_VERBS = [
268
+ /\buse\b/i, /\bavoid\b/i, /\bprefer\b/i, /\bnever\b/i,
269
+ /\balways\b/i, /\bmust\b/i, /\bshould\b/i, /\bshall\b/i,
270
+ /\bensure\b/i, /\bwrite\b/i, /\bcreate\b/i, /\bfollow\b/i,
271
+ /\bdo not\b/i, /\bapply\b/i, /\bimplement\b/i, /\brun\b/i,
272
+ ];
273
+ /**
274
+ * Score completeness (0-100): frontmatter fields, critical rules, structure.
275
+ */
276
+ export function scoreCompleteness(parsed) {
277
+ let score = 0;
278
+ const max = 100;
279
+ // Has a name (10 pts)
280
+ if (parsed.name && parsed.name.length > 0)
281
+ score += 10;
282
+ // Has triggers / description with "Trigger:" (15 pts)
283
+ if (parsed.triggers.length > 0)
284
+ score += 15;
285
+ // Has critical rules section (20 pts)
286
+ if (parsed.rules.length > 0)
287
+ score += 20;
288
+ // Number of rules: 1-2 = 10, 3-5 = 20, 6+ = 25
289
+ if (parsed.rules.length >= 6)
290
+ score += 25;
291
+ else if (parsed.rules.length >= 3)
292
+ score += 20;
293
+ else if (parsed.rules.length >= 1)
294
+ score += 10;
295
+ // Has substantial content (>= 200 chars = 10, >= 500 = 20, >= 1000 = 30)
296
+ const len = parsed.rawContent.length;
297
+ if (len >= 1000)
298
+ score += 30;
299
+ else if (len >= 500)
300
+ score += 20;
301
+ else if (len >= 200)
302
+ score += 10;
303
+ return Math.min(score, max);
304
+ }
305
+ /**
306
+ * Score clarity (0-100): description quality, rule actionability, no vague terms.
307
+ */
308
+ export function scoreClarity(parsed) {
309
+ let score = 0;
310
+ const max = 100;
311
+ // Trigger description exists and is meaningful (>= 50 chars in raw = 20 pts)
312
+ if (parsed.rawContent.length >= 50)
313
+ score += 20;
314
+ // Rules contain action verbs (up to 40 pts)
315
+ if (parsed.rules.length > 0) {
316
+ const actionableCount = parsed.rules.filter(rule => ACTION_VERBS.some(verb => verb.test(rule))).length;
317
+ const ratio = actionableCount / parsed.rules.length;
318
+ score += Math.round(ratio * 40);
319
+ }
320
+ // Penalty for vague terms in rules (-5 each, max -20)
321
+ let penalty = 0;
322
+ for (const rule of parsed.rules) {
323
+ for (const vague of VAGUE_TERMS) {
324
+ if (vague.test(rule)) {
325
+ penalty += 5;
326
+ break;
327
+ }
328
+ }
329
+ }
330
+ score -= Math.min(penalty, 20);
331
+ // Name is descriptive (not single char) (10 pts)
332
+ if (parsed.name.length >= 3)
333
+ score += 10;
334
+ // Has multiple triggers (10 pts for >= 2, 20 for >= 3)
335
+ if (parsed.triggers.length >= 3)
336
+ score += 20;
337
+ else if (parsed.triggers.length >= 2)
338
+ score += 10;
339
+ // Base content score for having structured sections (10 pts)
340
+ if (/^#+\s/m.test(parsed.rawContent))
341
+ score += 10;
342
+ return Math.max(0, Math.min(score, max));
343
+ }
344
+ /**
345
+ * Score testability (0-100): Given/When/Then scenarios, specific rules.
346
+ */
347
+ export function scoreTestability(parsed) {
348
+ let score = 0;
349
+ const max = 100;
350
+ // Has Given/When/Then scenarios (40 pts)
351
+ const gwtMatches = parsed.rawContent.match(/\bGIVEN\b.*\bWHEN\b.*\bTHEN\b/gis);
352
+ const gwtCount = gwtMatches?.length ?? 0;
353
+ if (gwtCount >= 3)
354
+ score += 40;
355
+ else if (gwtCount >= 1)
356
+ score += 25;
357
+ // Rules are specific enough (contain file paths, code refs, or patterns)
358
+ const specificRules = parsed.rules.filter(rule => /[`'"]/.test(rule) || /\.\w+/.test(rule) || /\bfile\b/i.test(rule) || /\bpath\b/i.test(rule)).length;
359
+ if (parsed.rules.length > 0) {
360
+ const specificity = specificRules / parsed.rules.length;
361
+ score += Math.round(specificity * 30);
362
+ }
363
+ // Has examples or code blocks (20 pts)
364
+ const codeBlocks = (parsed.rawContent.match(/```/g) ?? []).length / 2;
365
+ if (codeBlocks >= 2)
366
+ score += 20;
367
+ else if (codeBlocks >= 1)
368
+ score += 10;
369
+ // Has a "Testing" or "Test" section (10 pts)
370
+ if (/^#+\s.*test/im.test(parsed.rawContent))
371
+ score += 10;
372
+ return Math.min(score, max);
373
+ }
374
+ /**
375
+ * Score token efficiency (0-100): information density (rules per 1000 tokens).
376
+ */
377
+ export function scoreTokenEfficiency(parsed) {
378
+ const tokens = estimateTokens(parsed.rawContent);
379
+ if (tokens === 0)
380
+ return 0;
381
+ // Rules per 1000 tokens — higher is more efficient
382
+ const rulesPerKToken = (parsed.rules.length / tokens) * 1000;
383
+ // Ideal: 3-8 rules per 1000 tokens
384
+ // < 1 = bloated, > 10 = maybe too terse
385
+ let score;
386
+ if (rulesPerKToken >= 3 && rulesPerKToken <= 8) {
387
+ score = 100;
388
+ }
389
+ else if (rulesPerKToken >= 2) {
390
+ score = 80;
391
+ }
392
+ else if (rulesPerKToken >= 1) {
393
+ score = 60;
394
+ }
395
+ else if (rulesPerKToken > 0) {
396
+ score = 40;
397
+ }
398
+ else {
399
+ score = 10;
400
+ }
401
+ // Bonus for small total size (under 2000 tokens = +0, under 1000 = already great)
402
+ // Penalty for huge skills (> 5000 tokens = -20)
403
+ if (tokens > 5000)
404
+ score -= 20;
405
+ else if (tokens > 3000)
406
+ score -= 10;
407
+ return Math.max(0, Math.min(score, 100));
408
+ }
409
+ /**
410
+ * Score a skill on all 4 dimensions and compute overall.
411
+ */
412
+ export async function scoreSkill(skillPath, threshold = DEFAULT_THRESHOLD) {
413
+ const parsed = await parseSkillFile(skillPath);
414
+ if (!parsed)
415
+ return null;
416
+ const completeness = scoreCompleteness(parsed);
417
+ const clarity = scoreClarity(parsed);
418
+ const testability = scoreTestability(parsed);
419
+ const tokenEfficiency = scoreTokenEfficiency(parsed);
420
+ // Weighted average: completeness 30%, clarity 25%, testability 25%, token-efficiency 20%
421
+ const overall = Math.round(completeness * 0.30 +
422
+ clarity * 0.25 +
423
+ testability * 0.25 +
424
+ tokenEfficiency * 0.20);
425
+ return {
426
+ skillName: parsed.name,
427
+ completeness,
428
+ clarity,
429
+ testability,
430
+ tokenEfficiency,
431
+ overall,
432
+ threshold,
433
+ passing: overall >= threshold,
434
+ };
435
+ }
436
+ // ── Benchmarking ───────────────────────────────────────────────────────────
437
+ /**
438
+ * Run structural quality benchmark checks against a skill.
439
+ */
440
+ export async function benchmarkSkill(skillPath) {
441
+ const parsed = await parseSkillFile(skillPath);
442
+ if (!parsed)
443
+ return null;
444
+ const checks = [];
445
+ // Check 1: Has YAML frontmatter with name
446
+ checks.push({
447
+ name: 'has-frontmatter-name',
448
+ passed: parsed.name.length > 0 && parsed.name !== path.basename(path.dirname(skillPath)),
449
+ detail: parsed.name.length > 0 ? `name: ${parsed.name}` : 'No explicit name in frontmatter',
450
+ });
451
+ // Check 2: Has trigger keywords
452
+ checks.push({
453
+ name: 'has-triggers',
454
+ passed: parsed.triggers.length > 0,
455
+ detail: parsed.triggers.length > 0
456
+ ? `${parsed.triggers.length} trigger(s) found`
457
+ : 'No "Trigger:" in description',
458
+ });
459
+ // Check 3: Has critical rules (>= 3)
460
+ checks.push({
461
+ name: 'has-critical-rules',
462
+ passed: parsed.rules.length >= 3,
463
+ detail: `${parsed.rules.length} rule(s) found`,
464
+ });
465
+ // Check 4: Rules are actionable (contain verbs)
466
+ const actionableRules = parsed.rules.filter(rule => ACTION_VERBS.some(verb => verb.test(rule)));
467
+ checks.push({
468
+ name: 'rules-actionable',
469
+ passed: parsed.rules.length > 0 && actionableRules.length / parsed.rules.length >= 0.5,
470
+ detail: `${actionableRules.length}/${parsed.rules.length} rules have action verbs`,
471
+ });
472
+ // Check 5: Has code examples
473
+ const codeBlocks = (parsed.rawContent.match(/```/g) ?? []).length / 2;
474
+ checks.push({
475
+ name: 'has-code-examples',
476
+ passed: codeBlocks >= 1,
477
+ detail: `${Math.floor(codeBlocks)} code block(s)`,
478
+ });
479
+ // Check 6: Has structured sections (headings)
480
+ const headings = (parsed.rawContent.match(/^#+\s/gm) ?? []).length;
481
+ checks.push({
482
+ name: 'has-sections',
483
+ passed: headings >= 3,
484
+ detail: `${headings} section heading(s)`,
485
+ });
486
+ // Check 7: Token budget reasonable (< 3000 tokens)
487
+ const tokens = estimateTokens(parsed.rawContent);
488
+ checks.push({
489
+ name: 'token-budget-ok',
490
+ passed: tokens <= 3000,
491
+ detail: `~${tokens} tokens`,
492
+ });
493
+ // Check 8: No vague terms in rules
494
+ const vagueRules = parsed.rules.filter(rule => VAGUE_TERMS.some(vague => vague.test(rule)));
495
+ checks.push({
496
+ name: 'no-vague-rules',
497
+ passed: vagueRules.length === 0,
498
+ detail: vagueRules.length > 0
499
+ ? `${vagueRules.length} rule(s) contain vague terms`
500
+ : 'All rules are specific',
501
+ });
502
+ const passedCount = checks.filter(c => c.passed).length;
503
+ const passRate = Math.round((passedCount / checks.length) * 100);
504
+ return {
505
+ skillName: parsed.name,
506
+ checks,
507
+ passRate,
508
+ };
509
+ }
510
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { Stack } from '../types/index.js';
2
+ export interface TddHookResult {
3
+ installed: string[];
4
+ errors: string[];
5
+ }
6
+ /**
7
+ * Resolve the correct test command for TDD hook based on stack and build tool.
8
+ * Returns null if no test command can be determined.
9
+ */
10
+ export declare function getTddTestCommand(stack: Stack, buildTool: string, projectDir: string): Promise<string | null>;
11
+ /**
12
+ * Generate a TDD-enforcing pre-commit hook script.
13
+ * If testCmd is null, generates a warning-only hook.
14
+ */
15
+ export declare function generateTddHook(testCmd: string | null, stack: Stack): string;
16
+ /**
17
+ * Install TDD pre-commit hook into .git/hooks/.
18
+ * Detects the project stack automatically and generates the appropriate hook.
19
+ */
20
+ export declare function installTddHooks(projectDir: string): Promise<TddHookResult>;
21
+ //# sourceMappingURL=tdd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tdd.d.ts","sourceRoot":"","sources":["../../src/commands/tdd.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAO9C,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAMD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAmBxB;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAyC5E;AAMD;;;GAGG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAgChF"}