@phuc1403/musketeer 0.1.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/INSTALLATION.md +22 -0
- package/LICENSE +21 -0
- package/README.md +38 -0
- package/bin/musketeer.js +72 -0
- package/manifest.json +238 -0
- package/package.json +46 -0
- package/src/copier.js +119 -0
- package/src/lockfile.js +61 -0
- package/src/manifest-loader.js +46 -0
- package/src/muster.js +48 -0
- package/src/provisioner/detect.js +93 -0
- package/src/provisioner/guide.js +32 -0
- package/src/provisioner/index.js +113 -0
- package/src/provisioner/mcp.js +26 -0
- package/src/provisioner/os-package-manager.js +71 -0
- package/src/provisioner/venv.js +44 -0
- package/src/reconcile.js +94 -0
- package/src/resolver.js +130 -0
- package/src/schema.js +152 -0
- package/src/settings-merger.js +0 -0
- package/template/.claude/agents/code-reviewer.md +166 -0
- package/template/.claude/agents/hallmark-auditor.md +78 -0
- package/template/.claude/agents/hallmark-explorer.md +81 -0
- package/template/.claude/agents/researcher.md +33 -0
- package/template/.claude/hooks/block-migration-edits.cjs +31 -0
- package/template/.claude/hooks/format-json-hook.js +91 -0
- package/template/.claude/hooks/inject-design-docs.cjs +86 -0
- package/template/.claude/hooks/lib/colors.cjs +122 -0
- package/template/.claude/hooks/lib/git-info-cache.cjs +191 -0
- package/template/.claude/hooks/lib/transcript-parser.cjs +277 -0
- package/template/.claude/hooks/usage-context-awareness.cjs +166 -0
- package/template/.claude/hooks/validate-cml-hook.js +145 -0
- package/template/.claude/skills/adr-writer/SKILL.md +78 -0
- package/template/.claude/skills/adr-writer/references/adr-example.md +38 -0
- package/template/.claude/skills/adr-writer/references/adr-template.md +46 -0
- package/template/.claude/skills/architecture-characteristic-writer/SKILL.md +117 -0
- package/template/.claude/skills/architecture-characteristic-writer/assets/worksheet-template.md +40 -0
- package/template/.claude/skills/architecture-characteristic-writer/references/characteristics-catalog.md +106 -0
- package/template/.claude/skills/code-review/SKILL.md +54 -0
- package/template/.claude/skills/code-review/references/adversarial-review.md +223 -0
- package/template/.claude/skills/code-review/references/checklists/api.md +52 -0
- package/template/.claude/skills/code-review/references/checklists/base.md +100 -0
- package/template/.claude/skills/code-review/references/checklists/web-app.md +54 -0
- package/template/.claude/skills/context-map/SKILL.md +80 -0
- package/template/.claude/skills/context-map/example.cml +106 -0
- package/template/.claude/skills/context-map/reference/Aggregate/Aggregate.md +1 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/Bounded Context.md +41 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/businessModel.md +6 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/domainVisionStatement.md +3 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/evolution.md +6 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/implementationTechnology.md +1 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/implements.md +1 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/knowledgeLevel.md +5 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/realizes.md +9 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/refines.md +10 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/responsibilities.md +26 -0
- package/template/.claude/skills/context-map/reference/Bounded Context/type.md +23 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Anticorruption Layer.md +5 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Bounded Context Relationship.md +13 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Conformist.md +5 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Customer-Supplier (C-S).md +23 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Open Host Service.md +5 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Partnership (P).md +14 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Published Language.md +5 -0
- package/template/.claude/skills/context-map/reference/Bounded Context Relationship/Shared Kernel (SK).md +13 -0
- package/template/.claude/skills/context-map/reference/Context Map.md +63 -0
- package/template/.claude/skills/context-map/reference/Domain/Domain.md +30 -0
- package/template/.claude/skills/context-map/reference/Domain/supports.md +34 -0
- package/template/.claude/skills/context-map/reference/Domain/type.md +3 -0
- package/template/.claude/skills/context-map/reference/Semantic Rules.md +33 -0
- package/template/.claude/skills/hallmark/SKILL.md +552 -0
- package/template/.claude/skills/hallmark/references/anti-patterns.md +412 -0
- package/template/.claude/skills/hallmark/references/assets.md +406 -0
- package/template/.claude/skills/hallmark/references/color.md +95 -0
- package/template/.claude/skills/hallmark/references/component-cookbook.md +256 -0
- package/template/.claude/skills/hallmark/references/components/c1-outlined-chip.md +12 -0
- package/template/.claude/skills/hallmark/references/components/c2-inline-form-as-cta.md +16 -0
- package/template/.claude/skills/hallmark/references/components/c3-typographic-link.md +8 -0
- package/template/.claude/skills/hallmark/references/components/c4-sticky-bottom-bar.md +16 -0
- package/template/.claude/skills/hallmark/references/components/f1-bento-grid.md +20 -0
- package/template/.claude/skills/hallmark/references/components/f2-sticky-scroll-stack.md +20 -0
- package/template/.claude/skills/hallmark/references/components/f3-tabular-spec-sheet.md +11 -0
- package/template/.claude/skills/hallmark/references/components/f4-step-sequence.md +11 -0
- package/template/.claude/skills/hallmark/references/components/f5-annotated-screenshot.md +11 -0
- package/template/.claude/skills/hallmark/references/components/f6-product-card-grid.md +41 -0
- package/template/.claude/skills/hallmark/references/components/ft1-mast-headed.md +13 -0
- package/template/.claude/skills/hallmark/references/components/ft2-inline-rule-single-line.md +10 -0
- package/template/.claude/skills/hallmark/references/components/ft3-index-style-category-list.md +12 -0
- package/template/.claude/skills/hallmark/references/components/ft4-dense-typographic.md +10 -0
- package/template/.claude/skills/hallmark/references/components/ft5-statement.md +21 -0
- package/template/.claude/skills/hallmark/references/components/ft6-letter-close.md +19 -0
- package/template/.claude/skills/hallmark/references/components/ft7-newsletter-first.md +27 -0
- package/template/.claude/skills/hallmark/references/components/ft8-marquee-scroll.md +25 -0
- package/template/.claude/skills/hallmark/references/components/h1-marquee.md +15 -0
- package/template/.claude/skills/hallmark/references/components/h2-split-diptych.md +15 -0
- package/template/.claude/skills/hallmark/references/components/h3-quote-led.md +11 -0
- package/template/.claude/skills/hallmark/references/components/h4-stat-led.md +14 -0
- package/template/.claude/skills/hallmark/references/components/h5-letter-hero.md +11 -0
- package/template/.claude/skills/hallmark/references/components/h6-photographic-fold.md +16 -0
- package/template/.claude/skills/hallmark/references/components/h7-demo-video-clipped-by-viewport-edge.md +27 -0
- package/template/.claude/skills/hallmark/references/components/h8-mockup-split-browser-framed.md +23 -0
- package/template/.claude/skills/hallmark/references/components/h9-custom-illustration-centerpiece.md +27 -0
- package/template/.claude/skills/hallmark/references/components/n1-wordmark-2-links.md +12 -0
- package/template/.claude/skills/hallmark/references/components/n10-floating-on-scroll-morph.md +19 -0
- package/template/.claude/skills/hallmark/references/components/n2-floating-chip.md +14 -0
- package/template/.claude/skills/hallmark/references/components/n3-side-rail.md +14 -0
- package/template/.claude/skills/hallmark/references/components/n4-hidden-behind-k.md +9 -0
- package/template/.claude/skills/hallmark/references/components/n5-floating-pill.md +28 -0
- package/template/.claude/skills/hallmark/references/components/n6-newspaper-masthead.md +24 -0
- package/template/.claude/skills/hallmark/references/components/n7-brutal-slab.md +22 -0
- package/template/.claude/skills/hallmark/references/components/n8-terminal-command.md +21 -0
- package/template/.claude/skills/hallmark/references/components/n9-edge-aligned-minimal.md +17 -0
- package/template/.claude/skills/hallmark/references/components/s1-left-margin-numbered.md +15 -0
- package/template/.claude/skills/hallmark/references/components/s2-hanging.md +13 -0
- package/template/.claude/skills/hallmark/references/components/s3-sticky-pinned.md +19 -0
- package/template/.claude/skills/hallmark/references/components/s4-inline-no-break.md +11 -0
- package/template/.claude/skills/hallmark/references/components/s5-bottom-anchored.md +13 -0
- package/template/.claude/skills/hallmark/references/components/t1-pull-quote-with-marginalia.md +12 -0
- package/template/.claude/skills/hallmark/references/components/t2-logo-wall-hairline.md +19 -0
- package/template/.claude/skills/hallmark/references/components/t3-single-huge-quote.md +11 -0
- package/template/.claude/skills/hallmark/references/components/t4-numbered-stat-strip.md +14 -0
- package/template/.claude/skills/hallmark/references/contract.md +24 -0
- package/template/.claude/skills/hallmark/references/copy.md +182 -0
- package/template/.claude/skills/hallmark/references/custom-craft.md +626 -0
- package/template/.claude/skills/hallmark/references/custom-theme.md +329 -0
- package/template/.claude/skills/hallmark/references/design-md.md +116 -0
- package/template/.claude/skills/hallmark/references/export-formats.md +328 -0
- package/template/.claude/skills/hallmark/references/floating-nav.md +89 -0
- package/template/.claude/skills/hallmark/references/genres/atmospheric.md +65 -0
- package/template/.claude/skills/hallmark/references/genres/editorial.md +70 -0
- package/template/.claude/skills/hallmark/references/genres/modern-minimal.md +67 -0
- package/template/.claude/skills/hallmark/references/genres/playful.md +65 -0
- package/template/.claude/skills/hallmark/references/hero-enrichment.md +474 -0
- package/template/.claude/skills/hallmark/references/imagery-kit.md +170 -0
- package/template/.claude/skills/hallmark/references/interaction-and-states.md +207 -0
- package/template/.claude/skills/hallmark/references/layout-and-space.md +111 -0
- package/template/.claude/skills/hallmark/references/macrostructures/01-bento-grid.md +35 -0
- package/template/.claude/skills/hallmark/references/macrostructures/02-long-document.md +34 -0
- package/template/.claude/skills/hallmark/references/macrostructures/03-marquee-hero.md +31 -0
- package/template/.claude/skills/hallmark/references/macrostructures/04-stat-led.md +32 -0
- package/template/.claude/skills/hallmark/references/macrostructures/05-workbench.md +32 -0
- package/template/.claude/skills/hallmark/references/macrostructures/06-conversational-faq.md +33 -0
- package/template/.claude/skills/hallmark/references/macrostructures/07-manifesto.md +32 -0
- package/template/.claude/skills/hallmark/references/macrostructures/08-photographic.md +34 -0
- package/template/.claude/skills/hallmark/references/macrostructures/09-quote-led.md +32 -0
- package/template/.claude/skills/hallmark/references/macrostructures/10-specimen.md +32 -0
- package/template/.claude/skills/hallmark/references/macrostructures/11-catalogue.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/12-letter.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/13-index-first.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/14-narrative-workflow.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/15-split-studio.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/16-feature-stack.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/17-type-specimen.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/18-portfolio-grid.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/19-map-diagram.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/20-ecosystem-index.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures/21-component-playground.md +23 -0
- package/template/.claude/skills/hallmark/references/macrostructures.md +89 -0
- package/template/.claude/skills/hallmark/references/microinteractions.md +260 -0
- package/template/.claude/skills/hallmark/references/motion.md +109 -0
- package/template/.claude/skills/hallmark/references/preview-examples.md +49 -0
- package/template/.claude/skills/hallmark/references/responsive.md +138 -0
- package/template/.claude/skills/hallmark/references/slop-test.md +205 -0
- package/template/.claude/skills/hallmark/references/structure.md +164 -0
- package/template/.claude/skills/hallmark/references/study.md +511 -0
- package/template/.claude/skills/hallmark/references/typography.md +243 -0
- package/template/.claude/skills/hallmark/references/verbs/audit.md +25 -0
- package/template/.claude/skills/hallmark/references/verbs/redesign.md +269 -0
- package/template/.claude/skills/hallmark-explore/SKILL.md +137 -0
- package/template/.claude/skills/hallmark-explore/references/direction-matrix.md +124 -0
- package/template/.claude/skills/hallmark-explore/references/explorer-call.md +68 -0
- package/template/.claude/skills/hallmark-explore/scripts/build_compare.py +174 -0
- package/template/.claude/skills/hallmark-explore/scripts/scaffold_exploration.py +122 -0
- package/template/.claude/skills/hallmark-loop/SKILL.md +105 -0
- package/template/.claude/skills/hallmark-loop/references/auditor-call.md +60 -0
- package/template/.claude/skills/hallmark-loop/references/capture.md +78 -0
- package/template/.claude/skills/hallmark-loop/references/loop-control.md +79 -0
- package/template/.claude/skills/handoff/SKILL.md +15 -0
- package/template/.claude/skills/install.ps1 +42 -0
- package/template/.claude/skills/install.sh +54 -0
- package/template/.claude/skills/knowledge-crunching/SKILL.md +130 -0
- package/template/.claude/skills/knowledge-crunching/assets/context.template.md +59 -0
- package/template/.claude/skills/knowledge-crunching/references/crunching-dialogue.md +113 -0
- package/template/.claude/skills/research/SKILL.md +69 -0
- package/template/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.claude/skills/skill-creator/SKILL.md +149 -0
- package/template/.claude/skills/skill-creator/agents/analyzer.md +274 -0
- package/template/.claude/skills/skill-creator/agents/comparator.md +202 -0
- package/template/.claude/skills/skill-creator/agents/grader.md +223 -0
- package/template/.claude/skills/skill-creator/assets/eval_review.html +146 -0
- package/template/.claude/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/template/.claude/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/template/.claude/skills/skill-creator/references/benchmark-optimization-guide.md +86 -0
- package/template/.claude/skills/skill-creator/references/distribution-guide.md +79 -0
- package/template/.claude/skills/skill-creator/references/eval-infrastructure-guide.md +129 -0
- package/template/.claude/skills/skill-creator/references/eval-schemas.md +121 -0
- package/template/.claude/skills/skill-creator/references/mcp-skills-integration.md +71 -0
- package/template/.claude/skills/skill-creator/references/metadata-quality-criteria.md +94 -0
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-hosting.md +104 -0
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-overview.md +89 -0
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-schema.md +93 -0
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-sources.md +103 -0
- package/template/.claude/skills/skill-creator/references/plugin-marketplace-troubleshooting.md +76 -0
- package/template/.claude/skills/skill-creator/references/script-quality-criteria.md +106 -0
- package/template/.claude/skills/skill-creator/references/skill-anatomy-and-requirements.md +77 -0
- package/template/.claude/skills/skill-creator/references/skill-creation-workflow.md +151 -0
- package/template/.claude/skills/skill-creator/references/skill-design-patterns.md +75 -0
- package/template/.claude/skills/skill-creator/references/skillmark-benchmark-criteria.md +102 -0
- package/template/.claude/skills/skill-creator/references/structure-organization-criteria.md +114 -0
- package/template/.claude/skills/skill-creator/references/testing-and-iteration.md +78 -0
- package/template/.claude/skills/skill-creator/references/token-efficiency-criteria.md +74 -0
- package/template/.claude/skills/skill-creator/references/troubleshooting-guide.md +81 -0
- package/template/.claude/skills/skill-creator/references/validation-checklist.md +83 -0
- package/template/.claude/skills/skill-creator/references/writing-effective-instructions.md +88 -0
- package/template/.claude/skills/skill-creator/references/yaml-frontmatter-reference.md +92 -0
- package/template/.claude/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/template/.claude/skills/skill-creator/scripts/encoding_utils.py +36 -0
- package/template/.claude/skills/skill-creator/scripts/generate_report.py +326 -0
- package/template/.claude/skills/skill-creator/scripts/improve_description.py +248 -0
- package/template/.claude/skills/skill-creator/scripts/init_skill.py +360 -0
- package/template/.claude/skills/skill-creator/scripts/package_skill.py +143 -0
- package/template/.claude/skills/skill-creator/scripts/quick_validate.py +110 -0
- package/template/.claude/skills/skill-creator/scripts/run_eval.py +310 -0
- package/template/.claude/skills/skill-creator/scripts/run_loop.py +332 -0
- package/template/.claude/skills/skill-creator/scripts/utils.py +47 -0
- package/template/.claude/skills/tdd/SKILL.md +142 -0
- package/template/.claude/skills/tdd/assets/Directory.Build.props +26 -0
- package/template/.claude/skills/tdd/deep-modules.md +15 -0
- package/template/.claude/skills/tdd/dotnet-build-config.md +21 -0
- package/template/.claude/skills/tdd/interface-design.md +31 -0
- package/template/.claude/skills/tdd/mocking.md +59 -0
- package/template/.claude/skills/tdd/refactoring.md +10 -0
- package/template/.claude/skills/tdd/test-per-layer.md +69 -0
- package/template/.claude/skills/tdd/tests.md +61 -0
- package/template/.claude/statusline.cjs +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# YAML Frontmatter Reference
|
|
2
|
+
|
|
3
|
+
## Required Fields
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
---
|
|
7
|
+
name: skill-name-in-kebab-case
|
|
8
|
+
description: What it does and when to use it. Include specific trigger phrases.
|
|
9
|
+
---
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## All Optional Fields
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
---
|
|
16
|
+
name: skill-name
|
|
17
|
+
description: [required - under 200 chars]
|
|
18
|
+
license: MIT # Open-source license
|
|
19
|
+
compatibility: Requires Python 3.10+, network access # 1-500 chars, environment needs
|
|
20
|
+
allowed-tools: "Bash(python:*) Bash(npm:*) WebFetch" # Restrict tool access
|
|
21
|
+
metadata: # Custom key-value pairs
|
|
22
|
+
author: Company Name
|
|
23
|
+
version: 1.0.0
|
|
24
|
+
mcp-server: server-name
|
|
25
|
+
category: productivity
|
|
26
|
+
tags: [project-management, automation]
|
|
27
|
+
documentation: https://example.com/docs
|
|
28
|
+
support: support@example.com
|
|
29
|
+
---
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Field Details
|
|
33
|
+
|
|
34
|
+
### name (required)
|
|
35
|
+
- Supports either `skill-name` or `namespace:skill-name` (for example `ck:plan`)
|
|
36
|
+
- If namespaced, namespace and skill id both use kebab-case only (no spaces, no capitals)
|
|
37
|
+
- Folder name must match the skill id segment (after `:`)
|
|
38
|
+
- Cannot contain "claude" or "anthropic" (reserved)
|
|
39
|
+
|
|
40
|
+
### description (required)
|
|
41
|
+
- Under 200 characters (1024 max per spec, but 200 for this project)
|
|
42
|
+
- Structure: `[What it does] + [When to use it] + [Key capabilities]`
|
|
43
|
+
- Include trigger phrases users would actually say
|
|
44
|
+
- Mention relevant file types if applicable
|
|
45
|
+
- Use third-person: "This skill should be used when..."
|
|
46
|
+
|
|
47
|
+
### license (optional)
|
|
48
|
+
- Common: MIT, Apache-2.0
|
|
49
|
+
- Reference full terms in LICENSE.txt if needed
|
|
50
|
+
|
|
51
|
+
### compatibility (optional)
|
|
52
|
+
- 1-500 characters
|
|
53
|
+
- Environment requirements: intended product, system packages, network access
|
|
54
|
+
|
|
55
|
+
### allowed-tools (optional)
|
|
56
|
+
- Restricts which tools the skill can use
|
|
57
|
+
- Space-separated tool patterns
|
|
58
|
+
|
|
59
|
+
### metadata (optional)
|
|
60
|
+
- Any custom key-value pairs
|
|
61
|
+
- Suggested: author, version, mcp-server, category, tags
|
|
62
|
+
|
|
63
|
+
## Security Restrictions
|
|
64
|
+
|
|
65
|
+
**Forbidden in frontmatter:**
|
|
66
|
+
- XML angle brackets (`< >`) — frontmatter appears in system prompt, could inject instructions
|
|
67
|
+
- Skills named with "claude" or "anthropic" prefix (reserved)
|
|
68
|
+
|
|
69
|
+
**Allowed:**
|
|
70
|
+
- Standard YAML types (strings, numbers, booleans, lists, objects)
|
|
71
|
+
- Custom metadata fields
|
|
72
|
+
- Long descriptions up to 1024 characters (project standard: 200)
|
|
73
|
+
|
|
74
|
+
## Description Examples
|
|
75
|
+
|
|
76
|
+
**Good — specific with triggers:**
|
|
77
|
+
```yaml
|
|
78
|
+
description: Analyzes Figma design files and generates developer handoff docs.
|
|
79
|
+
Use when user uploads .fig files or asks for "design specs" or "design-to-code".
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
description: Manages Linear project workflows including sprint planning and
|
|
84
|
+
task creation. Use when user mentions "sprint", "Linear tasks", or "create tickets".
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Bad — vague or missing triggers:**
|
|
88
|
+
```yaml
|
|
89
|
+
description: Helps with projects. # Too vague
|
|
90
|
+
description: Creates sophisticated documentation systems. # No triggers
|
|
91
|
+
description: Implements the Project entity model. # Too technical
|
|
92
|
+
```
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Aggregate individual run results into benchmark summary statistics.
|
|
4
|
+
|
|
5
|
+
Reads grading.json files from run directories and produces:
|
|
6
|
+
- run_summary with mean, stddev, min, max for each metric
|
|
7
|
+
- delta between with_skill and without_skill configurations
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python aggregate_benchmark.py <benchmark_dir>
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
python aggregate_benchmark.py benchmarks/2026-01-15T10-30-00/
|
|
14
|
+
|
|
15
|
+
The script supports two directory layouts:
|
|
16
|
+
|
|
17
|
+
Workspace layout (from skill-creator iterations):
|
|
18
|
+
<benchmark_dir>/
|
|
19
|
+
└── eval-N/
|
|
20
|
+
├── with_skill/
|
|
21
|
+
│ ├── run-1/grading.json
|
|
22
|
+
│ └── run-2/grading.json
|
|
23
|
+
└── without_skill/
|
|
24
|
+
├── run-1/grading.json
|
|
25
|
+
└── run-2/grading.json
|
|
26
|
+
|
|
27
|
+
Legacy layout (with runs/ subdirectory):
|
|
28
|
+
<benchmark_dir>/
|
|
29
|
+
└── runs/
|
|
30
|
+
└── eval-N/
|
|
31
|
+
├── with_skill/
|
|
32
|
+
│ └── run-1/grading.json
|
|
33
|
+
└── without_skill/
|
|
34
|
+
└── run-1/grading.json
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
import argparse
|
|
38
|
+
import json
|
|
39
|
+
import math
|
|
40
|
+
import sys
|
|
41
|
+
from datetime import datetime, timezone
|
|
42
|
+
from pathlib import Path
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def calculate_stats(values: list[float]) -> dict:
|
|
46
|
+
"""Calculate mean, stddev, min, max for a list of values."""
|
|
47
|
+
if not values:
|
|
48
|
+
return {"mean": 0.0, "stddev": 0.0, "min": 0.0, "max": 0.0}
|
|
49
|
+
|
|
50
|
+
n = len(values)
|
|
51
|
+
mean = sum(values) / n
|
|
52
|
+
|
|
53
|
+
if n > 1:
|
|
54
|
+
variance = sum((x - mean) ** 2 for x in values) / (n - 1)
|
|
55
|
+
stddev = math.sqrt(variance)
|
|
56
|
+
else:
|
|
57
|
+
stddev = 0.0
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"mean": round(mean, 4),
|
|
61
|
+
"stddev": round(stddev, 4),
|
|
62
|
+
"min": round(min(values), 4),
|
|
63
|
+
"max": round(max(values), 4)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def load_run_results(benchmark_dir: Path) -> dict:
|
|
68
|
+
"""
|
|
69
|
+
Load all run results from a benchmark directory.
|
|
70
|
+
|
|
71
|
+
Returns dict keyed by config name (e.g. "with_skill"/"without_skill",
|
|
72
|
+
or "new_skill"/"old_skill"), each containing a list of run results.
|
|
73
|
+
"""
|
|
74
|
+
# Support both layouts: eval dirs directly under benchmark_dir, or under runs/
|
|
75
|
+
runs_dir = benchmark_dir / "runs"
|
|
76
|
+
if runs_dir.exists():
|
|
77
|
+
search_dir = runs_dir
|
|
78
|
+
elif list(benchmark_dir.glob("eval-*")):
|
|
79
|
+
search_dir = benchmark_dir
|
|
80
|
+
else:
|
|
81
|
+
print(f"No eval directories found in {benchmark_dir} or {benchmark_dir / 'runs'}")
|
|
82
|
+
return {}
|
|
83
|
+
|
|
84
|
+
results: dict[str, list] = {}
|
|
85
|
+
|
|
86
|
+
for eval_idx, eval_dir in enumerate(sorted(search_dir.glob("eval-*"))):
|
|
87
|
+
metadata_path = eval_dir / "eval_metadata.json"
|
|
88
|
+
if metadata_path.exists():
|
|
89
|
+
try:
|
|
90
|
+
with open(metadata_path) as mf:
|
|
91
|
+
eval_id = json.load(mf).get("eval_id", eval_idx)
|
|
92
|
+
except (json.JSONDecodeError, OSError):
|
|
93
|
+
eval_id = eval_idx
|
|
94
|
+
else:
|
|
95
|
+
try:
|
|
96
|
+
eval_id = int(eval_dir.name.split("-")[1])
|
|
97
|
+
except ValueError:
|
|
98
|
+
eval_id = eval_idx
|
|
99
|
+
|
|
100
|
+
# Discover config directories dynamically rather than hardcoding names
|
|
101
|
+
for config_dir in sorted(eval_dir.iterdir()):
|
|
102
|
+
if not config_dir.is_dir():
|
|
103
|
+
continue
|
|
104
|
+
# Skip non-config directories (inputs, outputs, etc.)
|
|
105
|
+
if not list(config_dir.glob("run-*")):
|
|
106
|
+
continue
|
|
107
|
+
config = config_dir.name
|
|
108
|
+
if config not in results:
|
|
109
|
+
results[config] = []
|
|
110
|
+
|
|
111
|
+
for run_dir in sorted(config_dir.glob("run-*")):
|
|
112
|
+
run_number = int(run_dir.name.split("-")[1])
|
|
113
|
+
grading_file = run_dir / "grading.json"
|
|
114
|
+
|
|
115
|
+
if not grading_file.exists():
|
|
116
|
+
print(f"Warning: grading.json not found in {run_dir}")
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
with open(grading_file) as f:
|
|
121
|
+
grading = json.load(f)
|
|
122
|
+
except json.JSONDecodeError as e:
|
|
123
|
+
print(f"Warning: Invalid JSON in {grading_file}: {e}")
|
|
124
|
+
continue
|
|
125
|
+
|
|
126
|
+
# Extract metrics
|
|
127
|
+
result = {
|
|
128
|
+
"eval_id": eval_id,
|
|
129
|
+
"run_number": run_number,
|
|
130
|
+
"pass_rate": grading.get("summary", {}).get("pass_rate", 0.0),
|
|
131
|
+
"passed": grading.get("summary", {}).get("passed", 0),
|
|
132
|
+
"failed": grading.get("summary", {}).get("failed", 0),
|
|
133
|
+
"total": grading.get("summary", {}).get("total", 0),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Extract timing — check grading.json first, then sibling timing.json
|
|
137
|
+
timing = grading.get("timing", {})
|
|
138
|
+
result["time_seconds"] = timing.get("total_duration_seconds", 0.0)
|
|
139
|
+
timing_file = run_dir / "timing.json"
|
|
140
|
+
if result["time_seconds"] == 0.0 and timing_file.exists():
|
|
141
|
+
try:
|
|
142
|
+
with open(timing_file) as tf:
|
|
143
|
+
timing_data = json.load(tf)
|
|
144
|
+
result["time_seconds"] = timing_data.get("total_duration_seconds", 0.0)
|
|
145
|
+
result["tokens"] = timing_data.get("total_tokens", 0)
|
|
146
|
+
except json.JSONDecodeError:
|
|
147
|
+
pass
|
|
148
|
+
|
|
149
|
+
# Extract metrics if available
|
|
150
|
+
metrics = grading.get("execution_metrics", {})
|
|
151
|
+
result["tool_calls"] = metrics.get("total_tool_calls", 0)
|
|
152
|
+
if not result.get("tokens"):
|
|
153
|
+
result["tokens"] = metrics.get("output_chars", 0)
|
|
154
|
+
result["errors"] = metrics.get("errors_encountered", 0)
|
|
155
|
+
|
|
156
|
+
# Extract expectations — viewer requires fields: text, passed, evidence
|
|
157
|
+
raw_expectations = grading.get("expectations", [])
|
|
158
|
+
for exp in raw_expectations:
|
|
159
|
+
if "text" not in exp or "passed" not in exp:
|
|
160
|
+
print(f"Warning: expectation in {grading_file} missing required fields (text, passed, evidence): {exp}")
|
|
161
|
+
result["expectations"] = raw_expectations
|
|
162
|
+
|
|
163
|
+
# Extract notes from user_notes_summary
|
|
164
|
+
notes_summary = grading.get("user_notes_summary", {})
|
|
165
|
+
notes = []
|
|
166
|
+
notes.extend(notes_summary.get("uncertainties", []))
|
|
167
|
+
notes.extend(notes_summary.get("needs_review", []))
|
|
168
|
+
notes.extend(notes_summary.get("workarounds", []))
|
|
169
|
+
result["notes"] = notes
|
|
170
|
+
|
|
171
|
+
results[config].append(result)
|
|
172
|
+
|
|
173
|
+
return results
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def aggregate_results(results: dict) -> dict:
|
|
177
|
+
"""
|
|
178
|
+
Aggregate run results into summary statistics.
|
|
179
|
+
|
|
180
|
+
Returns run_summary with stats for each configuration and delta.
|
|
181
|
+
"""
|
|
182
|
+
run_summary = {}
|
|
183
|
+
configs = list(results.keys())
|
|
184
|
+
|
|
185
|
+
for config in configs:
|
|
186
|
+
runs = results.get(config, [])
|
|
187
|
+
|
|
188
|
+
if not runs:
|
|
189
|
+
run_summary[config] = {
|
|
190
|
+
"pass_rate": {"mean": 0.0, "stddev": 0.0, "min": 0.0, "max": 0.0},
|
|
191
|
+
"time_seconds": {"mean": 0.0, "stddev": 0.0, "min": 0.0, "max": 0.0},
|
|
192
|
+
"tokens": {"mean": 0, "stddev": 0, "min": 0, "max": 0}
|
|
193
|
+
}
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
pass_rates = [r["pass_rate"] for r in runs]
|
|
197
|
+
times = [r["time_seconds"] for r in runs]
|
|
198
|
+
tokens = [r.get("tokens", 0) for r in runs]
|
|
199
|
+
|
|
200
|
+
run_summary[config] = {
|
|
201
|
+
"pass_rate": calculate_stats(pass_rates),
|
|
202
|
+
"time_seconds": calculate_stats(times),
|
|
203
|
+
"tokens": calculate_stats(tokens)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Calculate delta between the first two configs (if two exist)
|
|
207
|
+
if len(configs) >= 2:
|
|
208
|
+
primary = run_summary.get(configs[0], {})
|
|
209
|
+
baseline = run_summary.get(configs[1], {})
|
|
210
|
+
else:
|
|
211
|
+
primary = run_summary.get(configs[0], {}) if configs else {}
|
|
212
|
+
baseline = {}
|
|
213
|
+
|
|
214
|
+
delta_pass_rate = primary.get("pass_rate", {}).get("mean", 0) - baseline.get("pass_rate", {}).get("mean", 0)
|
|
215
|
+
delta_time = primary.get("time_seconds", {}).get("mean", 0) - baseline.get("time_seconds", {}).get("mean", 0)
|
|
216
|
+
delta_tokens = primary.get("tokens", {}).get("mean", 0) - baseline.get("tokens", {}).get("mean", 0)
|
|
217
|
+
|
|
218
|
+
run_summary["delta"] = {
|
|
219
|
+
"pass_rate": f"{delta_pass_rate:+.2f}",
|
|
220
|
+
"time_seconds": f"{delta_time:+.1f}",
|
|
221
|
+
"tokens": f"{delta_tokens:+.0f}"
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return run_summary
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def generate_benchmark(benchmark_dir: Path, skill_name: str = "", skill_path: str = "") -> dict:
|
|
228
|
+
"""
|
|
229
|
+
Generate complete benchmark.json from run results.
|
|
230
|
+
"""
|
|
231
|
+
results = load_run_results(benchmark_dir)
|
|
232
|
+
run_summary = aggregate_results(results)
|
|
233
|
+
|
|
234
|
+
# Build runs array for benchmark.json
|
|
235
|
+
runs = []
|
|
236
|
+
for config in results:
|
|
237
|
+
for result in results[config]:
|
|
238
|
+
runs.append({
|
|
239
|
+
"eval_id": result["eval_id"],
|
|
240
|
+
"configuration": config,
|
|
241
|
+
"run_number": result["run_number"],
|
|
242
|
+
"result": {
|
|
243
|
+
"pass_rate": result["pass_rate"],
|
|
244
|
+
"passed": result["passed"],
|
|
245
|
+
"failed": result["failed"],
|
|
246
|
+
"total": result["total"],
|
|
247
|
+
"time_seconds": result["time_seconds"],
|
|
248
|
+
"tokens": result.get("tokens", 0),
|
|
249
|
+
"tool_calls": result.get("tool_calls", 0),
|
|
250
|
+
"errors": result.get("errors", 0)
|
|
251
|
+
},
|
|
252
|
+
"expectations": result["expectations"],
|
|
253
|
+
"notes": result["notes"]
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
# Determine eval IDs from results
|
|
257
|
+
eval_ids = sorted(set(
|
|
258
|
+
r["eval_id"]
|
|
259
|
+
for config in results.values()
|
|
260
|
+
for r in config
|
|
261
|
+
))
|
|
262
|
+
|
|
263
|
+
benchmark = {
|
|
264
|
+
"metadata": {
|
|
265
|
+
"skill_name": skill_name or "<skill-name>",
|
|
266
|
+
"skill_path": skill_path or "<path/to/skill>",
|
|
267
|
+
"executor_model": "<model-name>",
|
|
268
|
+
"analyzer_model": "<model-name>",
|
|
269
|
+
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
270
|
+
"evals_run": eval_ids,
|
|
271
|
+
"runs_per_configuration": 3
|
|
272
|
+
},
|
|
273
|
+
"runs": runs,
|
|
274
|
+
"run_summary": run_summary,
|
|
275
|
+
"notes": [] # To be filled by analyzer
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return benchmark
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def generate_markdown(benchmark: dict) -> str:
|
|
282
|
+
"""Generate human-readable benchmark.md from benchmark data."""
|
|
283
|
+
metadata = benchmark["metadata"]
|
|
284
|
+
run_summary = benchmark["run_summary"]
|
|
285
|
+
|
|
286
|
+
# Determine config names (excluding "delta")
|
|
287
|
+
configs = [k for k in run_summary if k != "delta"]
|
|
288
|
+
config_a = configs[0] if len(configs) >= 1 else "config_a"
|
|
289
|
+
config_b = configs[1] if len(configs) >= 2 else "config_b"
|
|
290
|
+
label_a = config_a.replace("_", " ").title()
|
|
291
|
+
label_b = config_b.replace("_", " ").title()
|
|
292
|
+
|
|
293
|
+
lines = [
|
|
294
|
+
f"# Skill Benchmark: {metadata['skill_name']}",
|
|
295
|
+
"",
|
|
296
|
+
f"**Model**: {metadata['executor_model']}",
|
|
297
|
+
f"**Date**: {metadata['timestamp']}",
|
|
298
|
+
f"**Evals**: {', '.join(map(str, metadata['evals_run']))} ({metadata['runs_per_configuration']} runs each per configuration)",
|
|
299
|
+
"",
|
|
300
|
+
"## Summary",
|
|
301
|
+
"",
|
|
302
|
+
f"| Metric | {label_a} | {label_b} | Delta |",
|
|
303
|
+
"|--------|------------|---------------|-------|",
|
|
304
|
+
]
|
|
305
|
+
|
|
306
|
+
a_summary = run_summary.get(config_a, {})
|
|
307
|
+
b_summary = run_summary.get(config_b, {})
|
|
308
|
+
delta = run_summary.get("delta", {})
|
|
309
|
+
|
|
310
|
+
# Format pass rate
|
|
311
|
+
a_pr = a_summary.get("pass_rate", {})
|
|
312
|
+
b_pr = b_summary.get("pass_rate", {})
|
|
313
|
+
lines.append(f"| Pass Rate | {a_pr.get('mean', 0)*100:.0f}% ± {a_pr.get('stddev', 0)*100:.0f}% | {b_pr.get('mean', 0)*100:.0f}% ± {b_pr.get('stddev', 0)*100:.0f}% | {delta.get('pass_rate', '—')} |")
|
|
314
|
+
|
|
315
|
+
# Format time
|
|
316
|
+
a_time = a_summary.get("time_seconds", {})
|
|
317
|
+
b_time = b_summary.get("time_seconds", {})
|
|
318
|
+
lines.append(f"| Time | {a_time.get('mean', 0):.1f}s ± {a_time.get('stddev', 0):.1f}s | {b_time.get('mean', 0):.1f}s ± {b_time.get('stddev', 0):.1f}s | {delta.get('time_seconds', '—')}s |")
|
|
319
|
+
|
|
320
|
+
# Format tokens
|
|
321
|
+
a_tokens = a_summary.get("tokens", {})
|
|
322
|
+
b_tokens = b_summary.get("tokens", {})
|
|
323
|
+
lines.append(f"| Tokens | {a_tokens.get('mean', 0):.0f} ± {a_tokens.get('stddev', 0):.0f} | {b_tokens.get('mean', 0):.0f} ± {b_tokens.get('stddev', 0):.0f} | {delta.get('tokens', '—')} |")
|
|
324
|
+
|
|
325
|
+
# Notes section
|
|
326
|
+
if benchmark.get("notes"):
|
|
327
|
+
lines.extend([
|
|
328
|
+
"",
|
|
329
|
+
"## Notes",
|
|
330
|
+
""
|
|
331
|
+
])
|
|
332
|
+
for note in benchmark["notes"]:
|
|
333
|
+
lines.append(f"- {note}")
|
|
334
|
+
|
|
335
|
+
return "\n".join(lines)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def main():
|
|
339
|
+
parser = argparse.ArgumentParser(
|
|
340
|
+
description="Aggregate benchmark run results into summary statistics"
|
|
341
|
+
)
|
|
342
|
+
parser.add_argument(
|
|
343
|
+
"benchmark_dir",
|
|
344
|
+
type=Path,
|
|
345
|
+
help="Path to the benchmark directory"
|
|
346
|
+
)
|
|
347
|
+
parser.add_argument(
|
|
348
|
+
"--skill-name",
|
|
349
|
+
default="",
|
|
350
|
+
help="Name of the skill being benchmarked"
|
|
351
|
+
)
|
|
352
|
+
parser.add_argument(
|
|
353
|
+
"--skill-path",
|
|
354
|
+
default="",
|
|
355
|
+
help="Path to the skill being benchmarked"
|
|
356
|
+
)
|
|
357
|
+
parser.add_argument(
|
|
358
|
+
"--output", "-o",
|
|
359
|
+
type=Path,
|
|
360
|
+
help="Output path for benchmark.json (default: <benchmark_dir>/benchmark.json)"
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
args = parser.parse_args()
|
|
364
|
+
|
|
365
|
+
if not args.benchmark_dir.exists():
|
|
366
|
+
print(f"Directory not found: {args.benchmark_dir}")
|
|
367
|
+
sys.exit(1)
|
|
368
|
+
|
|
369
|
+
# Generate benchmark
|
|
370
|
+
benchmark = generate_benchmark(args.benchmark_dir, args.skill_name, args.skill_path)
|
|
371
|
+
|
|
372
|
+
# Determine output paths
|
|
373
|
+
output_json = args.output or (args.benchmark_dir / "benchmark.json")
|
|
374
|
+
output_md = output_json.with_suffix(".md")
|
|
375
|
+
|
|
376
|
+
# Write benchmark.json
|
|
377
|
+
with open(output_json, "w") as f:
|
|
378
|
+
json.dump(benchmark, f, indent=2)
|
|
379
|
+
print(f"Generated: {output_json}")
|
|
380
|
+
|
|
381
|
+
# Write benchmark.md
|
|
382
|
+
markdown = generate_markdown(benchmark)
|
|
383
|
+
with open(output_md, "w") as f:
|
|
384
|
+
f.write(markdown)
|
|
385
|
+
print(f"Generated: {output_md}")
|
|
386
|
+
|
|
387
|
+
# Print summary
|
|
388
|
+
run_summary = benchmark["run_summary"]
|
|
389
|
+
configs = [k for k in run_summary if k != "delta"]
|
|
390
|
+
delta = run_summary.get("delta", {})
|
|
391
|
+
|
|
392
|
+
print(f"\nSummary:")
|
|
393
|
+
for config in configs:
|
|
394
|
+
pr = run_summary[config]["pass_rate"]["mean"]
|
|
395
|
+
label = config.replace("_", " ").title()
|
|
396
|
+
print(f" {label}: {pr*100:.1f}% pass rate")
|
|
397
|
+
print(f" Delta: {delta.get('pass_rate', '—')}")
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
if __name__ == "__main__":
|
|
401
|
+
main()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Cross-platform encoding utilities for Windows compatibility.
|
|
4
|
+
|
|
5
|
+
Fixes UnicodeEncodeError on Windows by reconfiguring stdout/stderr to UTF-8
|
|
6
|
+
and providing encoding-aware file I/O helpers.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def configure_utf8_console():
|
|
14
|
+
"""
|
|
15
|
+
Reconfigure stdout/stderr for UTF-8 on Windows.
|
|
16
|
+
|
|
17
|
+
Windows uses cp1252 by default which cannot encode Unicode emojis.
|
|
18
|
+
This function switches to UTF-8 with 'replace' error handling to
|
|
19
|
+
prevent crashes on truly incompatible terminals.
|
|
20
|
+
"""
|
|
21
|
+
if sys.platform == 'win32':
|
|
22
|
+
try:
|
|
23
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
24
|
+
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
|
25
|
+
except AttributeError:
|
|
26
|
+
pass # Python < 3.7
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def read_text_utf8(path: Path) -> str:
|
|
30
|
+
"""Read file with explicit UTF-8 encoding."""
|
|
31
|
+
return path.read_text(encoding='utf-8')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def write_text_utf8(path: Path, content: str) -> None:
|
|
35
|
+
"""Write file with explicit UTF-8 encoding."""
|
|
36
|
+
path.write_text(content, encoding='utf-8')
|