bmad-module-skill-forge 0.7.3 → 0.8.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/package.json +1 -1
- package/src/agents/forger.agent.yaml +1 -1
- package/src/knowledge/ccc-bridge.md +34 -2
- package/src/knowledge/tool-resolution.md +3 -3
- package/src/module.yaml +5 -3
- package/src/workflows/analyze-source/data/skill-brief-schema.md +11 -2
- package/src/workflows/analyze-source/data/unit-detection-heuristics.md +9 -0
- package/src/workflows/analyze-source/steps-c/step-05-recommend.md +1 -1
- package/src/workflows/analyze-source/steps-c/step-06-generate-briefs.md +2 -1
- package/src/workflows/brief-skill/data/scope-templates.md +41 -0
- package/src/workflows/brief-skill/data/skill-brief-schema.md +11 -3
- package/src/workflows/brief-skill/steps-c/step-02-analyze-target.md +1 -1
- package/src/workflows/brief-skill/steps-c/step-03-scope-definition.md +6 -6
- package/src/workflows/create-skill/data/compile-assembly-rules.md +108 -0
- package/src/workflows/create-skill/data/extraction-patterns.md +94 -3
- package/src/workflows/create-skill/data/skill-sections.md +76 -0
- package/src/workflows/create-skill/data/source-resolution-protocols.md +21 -9
- package/src/workflows/create-skill/steps-c/step-02b-ccc-discover.md +6 -4
- package/src/workflows/create-skill/steps-c/step-03-extract.md +48 -12
- package/src/workflows/create-skill/steps-c/step-03d-component-extraction.md +292 -0
- package/src/workflows/create-skill/steps-c/step-05-compile.md +9 -0
- package/src/workflows/setup-forge/steps-c/step-01b-ccc-index.md +65 -13
- package/src/workflows/setup-forge/steps-c/step-02-write-config.md +5 -3
- package/src/workflows/update-skill/steps-c/step-03-re-extract.md +2 -0
|
@@ -59,6 +59,82 @@ SKILL.md uses a two-tier structure to ensure actionable content survives `split-
|
|
|
59
59
|
- **Section 4b (Migration & Deprecation Warnings)** is conditional: only emitted for Deep tier when T2-future annotations exist. Quick/Forge/Forge+ tiers and Deep tiers without T2-future annotations omit it entirely (no empty section). Parsers and validators must treat this section as optional.
|
|
60
60
|
- **Section 7b (Scripts & Assets)** is conditional: only emitted when `scripts_inventory` or `assets_inventory` is non-empty. Omitted entirely when no scripts or assets are detected. Parsers and validators must treat this section as optional.
|
|
61
61
|
|
|
62
|
+
### Component Library Section Overrides
|
|
63
|
+
|
|
64
|
+
When `scope.type: "component-library"`, these section formats replace their standard counterparts. All other sections (Overview, Common Workflows, Architecture, CLI, Manual Sections, etc.) use the standard format.
|
|
65
|
+
|
|
66
|
+
#### Section 4 — Component Catalog (replaces Key API Summary)
|
|
67
|
+
|
|
68
|
+
```markdown
|
|
69
|
+
## Component Catalog
|
|
70
|
+
|
|
71
|
+
| Category | Count | Key Components |
|
|
72
|
+
|----------|-------|---------------|
|
|
73
|
+
| {category} | {count} | {ComponentA}, {ComponentB}, {ComponentC}, ... |
|
|
74
|
+
|
|
75
|
+
**Design system variants:** {primary} (primary), {variant2}, {variant3}
|
|
76
|
+
**Total components:** {unique_count} | **With {variant1}:** {count1} | **With {variant2}:** {count2}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- Source: `component_catalog[]` from step-03d extraction
|
|
80
|
+
- Group by `category` field from registry
|
|
81
|
+
- Show top 3-5 component names per category
|
|
82
|
+
- Cite registry file: `[SRC:{registry_file}:L1]`
|
|
83
|
+
- Budget: ~20 lines (same as standard Section 4)
|
|
84
|
+
|
|
85
|
+
#### Section 5 — Key Props (replaces Key Types)
|
|
86
|
+
|
|
87
|
+
```markdown
|
|
88
|
+
## Key Types
|
|
89
|
+
|
|
90
|
+
### {ComponentName}Props
|
|
91
|
+
|
|
92
|
+
| Prop | Type | Default | Required |
|
|
93
|
+
|------|------|---------|----------|
|
|
94
|
+
| {propName} | {type} | {default or —} | {yes/no} |
|
|
95
|
+
|
|
96
|
+
{Repeat for top 5 Props interfaces}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- Show only the 5 most-used Props interfaces (by component count or documentation prominence)
|
|
100
|
+
- Full Props details for all components go in Tier 2 reference
|
|
101
|
+
- Budget: ~20 lines (same as standard Section 5)
|
|
102
|
+
|
|
103
|
+
#### Tier 2 — Props Reference (replaces Full API Reference)
|
|
104
|
+
|
|
105
|
+
```markdown
|
|
106
|
+
## Full API Reference
|
|
107
|
+
|
|
108
|
+
### {ComponentName}
|
|
109
|
+
|
|
110
|
+
**Install:** `npx {cli} add {component-id}`
|
|
111
|
+
**Available in:** {variant list}
|
|
112
|
+
**Props:** `{ComponentName}Props`
|
|
113
|
+
|
|
114
|
+
| Prop | Type | Default | Description |
|
|
115
|
+
|------|------|---------|-------------|
|
|
116
|
+
| {prop} | {type} | {default} | {description from JSDoc or —} |
|
|
117
|
+
|
|
118
|
+
**Provenance:** `[AST:{file}:L{line}]`
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- Organize by component, not by function
|
|
122
|
+
- Each component shows: install command, variant availability, full props table
|
|
123
|
+
- Include JSDoc descriptions for props when available
|
|
124
|
+
- Standard provenance citations apply
|
|
125
|
+
|
|
126
|
+
#### context-snippet.md — Component Library Format
|
|
127
|
+
|
|
128
|
+
```markdown
|
|
129
|
+
[{name} v{version}]|root: skills/{name}/
|
|
130
|
+
|IMPORTANT: {name} v{version} — read SKILL.md before writing {name} code. Do NOT rely on training data.
|
|
131
|
+
|install: npx {cli} add <component-id>
|
|
132
|
+
|catalog:{SKILL.md#component-catalog} — {N} components: {category(count), ...}
|
|
133
|
+
|variants: {variant list} — {provider wrapping note if applicable}
|
|
134
|
+
|key-props:{SKILL.md#key-types} — {top props interfaces with key fields}
|
|
135
|
+
|gotchas: {detected gotchas}
|
|
136
|
+
```
|
|
137
|
+
|
|
62
138
|
### Provenance Citation Format
|
|
63
139
|
|
|
64
140
|
| Tier | Format | Example |
|
|
@@ -68,8 +68,8 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
|
|
|
68
68
|
|
|
69
69
|
When `exclude_patterns` exist, use `--no-cone` mode to pass both include and exclude patterns directly as gitignore-style rules:
|
|
70
70
|
|
|
71
|
-
1. Convert `include_patterns` to gitignore-style patterns.
|
|
72
|
-
2. Convert `exclude_patterns` to negation patterns by prepending `!`. Apply the same
|
|
71
|
+
1. Convert `include_patterns` to gitignore-style patterns. For patterns without glob characters (`*`, `?`, `[`) that do not end with `/`, apply the **file-detection heuristic**: if the last path segment contains a `.` (file extension), it is an **individual file** — prepend `/` and keep as-is (e.g., `packages/registry/registry.json` → `/packages/registry/registry.json`). If the last path segment has no extension, it is a **bare directory name** — append `/**` (e.g., `cognee` → `cognee/**`). Patterns that already contain glob characters are kept as-is (e.g., `cognee/**` → kept as-is).
|
|
72
|
+
2. Convert `exclude_patterns` to negation patterns by prepending `!`. Apply the same file-detection heuristic: individual files (last segment has `.` extension) get `!/` prefix only (e.g., `src/internal/config.json` → `!/src/internal/config.json`); bare directory names get `/**` appended (e.g., `cognee/tests` → `!cognee/tests/**`). Patterns with globs are kept as-is with `!` prefix (e.g., `cognee/tests/**` → `!cognee/tests/**`; `**/test_*` → `!**/test_*`).
|
|
73
73
|
3. **CRITICAL:** List all include patterns BEFORE negated exclude patterns — git processes patterns in order and a negation can only suppress a prior inclusion.
|
|
74
74
|
4. Pass to sparse-checkout — include patterns first, then negated exclude patterns:
|
|
75
75
|
|
|
@@ -79,15 +79,17 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
|
|
|
79
79
|
|
|
80
80
|
Example transformation:
|
|
81
81
|
```
|
|
82
|
-
Brief include_patterns:
|
|
83
|
-
cognee/**
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
Brief include_patterns: Brief exclude_patterns:
|
|
83
|
+
cognee/** cognee/tests/**
|
|
84
|
+
packages/registry/registry.json cognee/alembic/**
|
|
85
|
+
**/test_*
|
|
86
86
|
|
|
87
87
|
sparse-checkout args (--no-cone):
|
|
88
|
-
'cognee/**' '!cognee/tests/**' '!cognee/alembic/**' '!**/test_*'
|
|
88
|
+
'cognee/**' '/packages/registry/registry.json' '!cognee/tests/**' '!cognee/alembic/**' '!**/test_*'
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
Note: `registry.json` is an individual file (has `.json` extension), so it gets `/` prefix instead of `/**` suffix.
|
|
92
|
+
|
|
91
93
|
**Note:** `--no-cone` mode is slower than cone mode for very large repositories but eliminates downloading excluded blobs entirely.
|
|
92
94
|
|
|
93
95
|
**Always-included root files:**
|
|
@@ -112,7 +114,13 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
|
|
|
112
114
|
|
|
113
115
|
Proceed with Quick tier extraction strategy below. Note the degradation reason in context for the evidence report.
|
|
114
116
|
|
|
115
|
-
**Ephemeral clone cleanup:** After extraction is complete for all files in scope (whether successful or partially failed), before presenting the Gate 2 summary (Section 6), if `ephemeral_clone_active
|
|
117
|
+
**Ephemeral clone cleanup:** After extraction is complete for all files in scope (whether successful or partially failed), before presenting the Gate 2 summary (Section 6), if `ephemeral_clone_active`:
|
|
118
|
+
|
|
119
|
+
1. **Reset working directory first:** Before deleting the clone, ensure the shell working directory is not inside the temp path. Run `cd {project-root}` using the **absolute path** captured at workflow start (not a relative path). This prevents `getcwd` errors when the temp directory is deleted while it is the shell's cwd — which happens if `cd {temp_path}` was used during CCC init or extraction operations.
|
|
120
|
+
2. **Delete the clone:** `rm -rf {temp_path}`
|
|
121
|
+
3. **Log:** "Ephemeral source clone cleaned up."
|
|
122
|
+
|
|
123
|
+
This ensures cleanup runs even if some extractions failed, as long as the step itself is still executing. **If any error halts the extraction step before Gate 2 is reached**, cleanup must still occur: reset cwd to `{project-root}` and attempt to delete `{temp_path}` before halting. Log the cleanup attempt regardless of success.
|
|
116
124
|
|
|
117
125
|
---
|
|
118
126
|
|
|
@@ -137,7 +145,11 @@ Store the result as `source_commit` in context. If capture fails (not a git repo
|
|
|
137
145
|
After the source path is accessible (local path from step-01, or ephemeral clone from above), check whether the source contains a version identifier and reconcile it with `brief.version`. Look for the first matching version file in the resolved source path:
|
|
138
146
|
|
|
139
147
|
- Python: `pyproject.toml` (`[project] version`), `setup.py` (`version=`), `__version__` in `__init__.py`
|
|
140
|
-
- JavaScript/TypeScript: `package.json` (`"version"`)
|
|
148
|
+
- JavaScript/TypeScript: `package.json` (`"version"`). **Monorepo resolution:** When multiple `package.json` files exist (workspace root + packages), resolve version using this priority:
|
|
149
|
+
1. Package whose `name` field matches `brief.name` (e.g., the skill's target library name)
|
|
150
|
+
2. Package with a `bin` field (CLI entry point — represents the published version)
|
|
151
|
+
3. Root workspace `package.json` version (if present)
|
|
152
|
+
4. Fall back to `brief.version` if no version found. For monorepos using workspace protocols (pnpm, yarn, npm workspaces), the root `package.json` often has no `version` field — this is expected, not an error.
|
|
141
153
|
- Rust: `Cargo.toml` (`[package] version`)
|
|
142
154
|
- Go: `go.mod` (module version if tagged)
|
|
143
155
|
|
|
@@ -65,9 +65,11 @@ Set `{ccc_discovery: []}` in context. Auto-proceed silently. Display no message.
|
|
|
65
65
|
|
|
66
66
|
Check `tools.ccc` from forge-tier.yaml. If `tools.ccc` is false, set `{ccc_discovery: []}` in context and auto-proceed to section 5.
|
|
67
67
|
|
|
68
|
-
If `tools.ccc` is true,
|
|
68
|
+
If `tools.ccc` is true, check the remote source guard **before** proceeding to section 2:
|
|
69
69
|
|
|
70
|
-
**Remote source guard:** If `source_root` is a remote URL (GitHub repository not yet cloned locally — ephemeral clone happens in step-03), CCC cannot operate. Set `{ccc_discovery: []}` and display: "CCC discovery
|
|
70
|
+
**Remote source guard:** If `source_root` is a remote URL (GitHub repository not yet cloned locally — ephemeral clone happens in step-03), CCC cannot operate yet. Set `{ccc_discovery: []}` and display: "CCC discovery deferred — remote source will be indexed after ephemeral clone in step-03." Auto-proceed to section 5 (step completion). Step-03 will detect the deferred scenario and run CCC discovery on the ephemeral clone before AST extraction begins.
|
|
71
|
+
|
|
72
|
+
If `source_root` is a local path, continue to section 2.
|
|
71
73
|
|
|
72
74
|
### 2. Check CCC Index State
|
|
73
75
|
|
|
@@ -77,7 +79,7 @@ Read `ccc_index` from forge-tier.yaml:
|
|
|
77
79
|
- If `ccc_index.status` is `"stale"`: display brief note — "CCC index is stale — discovery results may miss recent changes." Continue to section 3.
|
|
78
80
|
- If `ccc_index.status` is `"none"` or `"failed"`: attempt lazy indexing via `ccc_bridge.ensure_index(source_root)`. If indexing succeeds, continue to section 3. If indexing fails, set `{ccc_discovery: []}` and auto-proceed to section 5.
|
|
79
81
|
|
|
80
|
-
**Tool resolution for ccc_bridge.ensure_index:** Use `/ccc` skill indexing (Claude Code), ccc MCP server (Cursor), or `ccc init` + `ccc index
|
|
82
|
+
**Tool resolution for ccc_bridge.ensure_index:** Use `/ccc` skill indexing (Claude Code), ccc MCP server (Cursor), or `cd {source_root} && ccc init` + `ccc index` (CLI). Note: `ccc init` takes no positional arguments — it initializes the index for the current working directory. See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
|
|
81
83
|
|
|
82
84
|
### 3. Construct Semantic Query
|
|
83
85
|
|
|
@@ -95,7 +97,7 @@ Where:
|
|
|
95
97
|
|
|
96
98
|
Run `ccc_bridge.search(query, source_root, top_k=20)`:
|
|
97
99
|
|
|
98
|
-
**Tool resolution for ccc_bridge.search:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `ccc search "{query}"
|
|
100
|
+
**Tool resolution for ccc_bridge.search:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `cd {source_root} && ccc search --limit 20 "{query}"` (CLI). Note: `ccc search` operates on the index in the current working directory — there is no flag to specify a project directory. See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
|
|
99
101
|
|
|
100
102
|
**If search succeeds:**
|
|
101
103
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: 'step-03-extract'
|
|
3
3
|
description: 'Tier-dependent source code extraction — AST or source reading for exports, signatures, and types'
|
|
4
4
|
nextStepFile: './step-03b-fetch-temporal.md'
|
|
5
|
+
componentExtractionStepFile: './step-03d-component-extraction.md'
|
|
5
6
|
extractionPatternsData: '../data/extraction-patterns.md'
|
|
6
7
|
extractionPatternsTracingData: '../data/extraction-patterns-tracing.md'
|
|
7
8
|
tierDegradationRulesData: '../data/tier-degradation-rules.md'
|
|
@@ -49,7 +50,7 @@ To extract all public exports, function signatures, type definitions, and co-imp
|
|
|
49
50
|
- Available: brief_data, tier, source_root, file_tree from step-01; ecosystem check outcome from step-02 (proceed/halt decision — no named variable stored)
|
|
50
51
|
- Focus: Source code extraction and inventory building
|
|
51
52
|
- Limits: Do NOT compile, assemble, or write any output
|
|
52
|
-
- Dependencies: Source code must be accessible (resolved in step-01)
|
|
53
|
+
- Dependencies: Source code must be accessible (local repos resolved in step-01; remote repos cloned in section 2a)
|
|
53
54
|
|
|
54
55
|
## MANDATORY SEQUENCE
|
|
55
56
|
|
|
@@ -69,6 +70,50 @@ From the brief, apply scope and pattern filters:
|
|
|
69
70
|
|
|
70
71
|
Build the filtered file list from the source tree resolved in step-01. Record the result: "**Filtered file count: {N} files in scope**" — this count is the input to the AST Extraction Protocol decision tree in the extraction patterns data file.
|
|
71
72
|
|
|
73
|
+
### 2a. Resolve Source Access
|
|
74
|
+
|
|
75
|
+
Load `{sourceResolutionData}` completely. Follow the **Remote Source Resolution** protocol for Forge/Deep tiers (ephemeral clone, sparse-checkout, cleanup), the **Source Commit Capture** protocol for all tiers, and the **Version Reconciliation** protocol for all tiers. This ensures source code is accessible regardless of which extraction path is taken below (standard, component-library, or docs-only).
|
|
76
|
+
|
|
77
|
+
**Deferred CCC Discovery (Forge+ and Deep — remote sources only):**
|
|
78
|
+
|
|
79
|
+
If ALL of these conditions are true:
|
|
80
|
+
- `tools.ccc` is true in forge-tier.yaml
|
|
81
|
+
- `{ccc_discovery}` is empty (step-02b deferred because source was remote)
|
|
82
|
+
- `ephemeral_clone_active` is true (clone succeeded in source resolution above)
|
|
83
|
+
- Tier is Forge+ or Deep
|
|
84
|
+
|
|
85
|
+
Then run CCC indexing and discovery on the ephemeral clone:
|
|
86
|
+
|
|
87
|
+
1. **Index the clone:** Run `cd {temp_path} && ccc init` then `ccc index` with an extended timeout or in background mode — `ccc init` takes no positional arguments and initializes the index for the current working directory. Indexing can take several minutes on large codebases (1000+ files). Use `ccc status` to verify completion — check that `Chunks` and `Files` counts are non-zero. If init fails or indexing fails, set `{ccc_discovery: []}` and continue — this is not an error.
|
|
88
|
+
|
|
89
|
+
2. **Construct semantic query:** Build from brief data: `"{brief.name} {brief.scope}"`. Truncate to 80 characters — keep the full skill name and trim `brief.scope` from the end. If `brief.scope` is very short (< 10 chars), append terms from `brief.description` to fill the remaining space.
|
|
90
|
+
|
|
91
|
+
3. **Execute search:** Run `ccc_bridge.search(query, temp_path, top_k=20)`:
|
|
92
|
+
- **Tool resolution:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `cd {temp_path} && ccc search --limit 20 "{query}"` (CLI). Note: `ccc search` operates on the index in the current working directory. See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
|
|
93
|
+
|
|
94
|
+
4. **Store results:** If search succeeds, store as `{ccc_discovery: [{file, score, snippet}]}`. Display: "**CCC semantic discovery (post-clone): {N} relevant regions identified across {M} unique files.**"
|
|
95
|
+
|
|
96
|
+
5. **On failure:** Set `{ccc_discovery: []}`. Display: "CCC post-clone discovery unavailable — proceeding with standard extraction." Do NOT halt.
|
|
97
|
+
|
|
98
|
+
**CCC Discovery Integration (Forge+ and Deep with ccc only):**
|
|
99
|
+
|
|
100
|
+
If `{ccc_discovery}` is in context and non-empty (populated by step-02b or deferred discovery above):
|
|
101
|
+
- Sort the filtered file list by CCC relevance score: files appearing in `{ccc_discovery}` results move to the front of the extraction queue, sorted by their relevance score descending
|
|
102
|
+
- Files NOT in CCC results remain in the queue after ranked files — they are not excluded, only deprioritized
|
|
103
|
+
- Display: "**CCC discovery: {N} files pre-ranked by semantic relevance** — extraction will prioritize these first."
|
|
104
|
+
|
|
105
|
+
If `{ccc_discovery}` is empty or not in context: proceed with existing file ordering (no change to current behavior).
|
|
106
|
+
|
|
107
|
+
### 2b. Component Library Delegation
|
|
108
|
+
|
|
109
|
+
**If `scope.type: "component-library"` in the brief:**
|
|
110
|
+
|
|
111
|
+
"**Component library detected.** Delegating to specialized extraction strategy for registry-first, props-focused extraction."
|
|
112
|
+
|
|
113
|
+
Load and execute `{componentExtractionStepFile}` completely. When that step completes, it returns control here. Resume at section 5 (Build Extraction Inventory) with the enriched extraction data and `component_catalog[]` from the component extraction step.
|
|
114
|
+
|
|
115
|
+
**Otherwise:** Continue with standard extraction below.
|
|
116
|
+
|
|
72
117
|
### 3. Check for Docs-Only Mode
|
|
73
118
|
|
|
74
119
|
**If `source_type: "docs-only"` in the brief data:**
|
|
@@ -81,7 +126,7 @@ Build an empty extraction inventory with zero exports. Set `extraction_mode: "do
|
|
|
81
126
|
|
|
82
127
|
### 4. Execute Tier-Dependent Extraction
|
|
83
128
|
|
|
84
|
-
|
|
129
|
+
Source resolution, version reconciliation, and CCC discovery were completed in section 2a. Proceed with the tier-specific extraction strategy below.
|
|
85
130
|
|
|
86
131
|
**Quick Tier (No AST tools):**
|
|
87
132
|
|
|
@@ -96,16 +141,7 @@ Load `{sourceResolutionData}` completely. Follow the **Remote Source Resolution*
|
|
|
96
141
|
|
|
97
142
|
**Forge/Forge+/Deep Tier (AST available):**
|
|
98
143
|
|
|
99
|
-
**
|
|
100
|
-
|
|
101
|
-
If `{ccc_discovery}` is in context and non-empty (populated by step-02b):
|
|
102
|
-
- Sort the filtered file list by CCC relevance score: files appearing in `{ccc_discovery}` results move to the front of the extraction queue, sorted by their relevance score descending
|
|
103
|
-
- Files NOT in CCC results remain in the queue after ranked files — they are not excluded, only deprioritized
|
|
104
|
-
- Display: "**CCC discovery: {N} files pre-ranked by semantic relevance** — extraction will prioritize these first."
|
|
105
|
-
|
|
106
|
-
If `{ccc_discovery}` is empty or not in context: proceed with existing file ordering (no change to current behavior).
|
|
107
|
-
|
|
108
|
-
⚠️ **CRITICAL:** Before executing AST extraction, load the **AST Extraction Protocol** section from `{extractionPatternsData}`. Follow the decision tree based on the file count from step-01's file tree. This determines whether to use the MCP tool, scoped YAML rules, or CLI streaming. Never use `ast-grep --json` (without `=stream`) — it loads the entire result set into memory and will fail on large codebases.
|
|
144
|
+
⚠️ **CRITICAL:** Before executing AST extraction, load the **AST Extraction Protocol** section from `{extractionPatternsData}`. Follow the decision tree based on the file count from step-01's file tree. This determines whether to use the MCP tool, scoped YAML rules, or CLI streaming. Never use `ast-grep --json` (without `=stream`) — it loads the entire result set into memory and will fail on large codebases. Always use the explicit `run` subcommand with streaming: `ast-grep run -p '{pattern}' --json=stream`.
|
|
109
145
|
|
|
110
146
|
1. Detect language from brief or file extensions
|
|
111
147
|
2. Follow the AST Extraction Protocol decision tree from `{extractionPatternsData}`:
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 'step-03d-component-extraction'
|
|
3
|
+
description: 'Component-library extraction strategy — registry detection, props-first extraction, variant consolidation, demo exclusion'
|
|
4
|
+
returnToStep: './step-03-extract.md'
|
|
5
|
+
extractionPatternsData: '../data/extraction-patterns.md'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Step 3d: Component Library Extraction
|
|
9
|
+
|
|
10
|
+
## STEP GOAL:
|
|
11
|
+
|
|
12
|
+
When `scope.type: "component-library"`, perform specialized extraction that treats the component registry as the primary API surface and props interfaces as API contracts. This step replaces the standard AST extraction flow (step-03 sections 4-4c) and returns control to step-03 section 5 (Build Extraction Inventory).
|
|
13
|
+
|
|
14
|
+
## MANDATORY EXECUTION RULES (READ FIRST):
|
|
15
|
+
|
|
16
|
+
### Universal Rules:
|
|
17
|
+
|
|
18
|
+
- 📖 CRITICAL: Read the complete step file before taking any action
|
|
19
|
+
- 🎯 ALWAYS follow the exact instructions in the step file
|
|
20
|
+
- ⚙️ TOOL/SUBPROCESS FALLBACK: If any instruction references a tool you do not have access to, you MUST still achieve the outcome in your main context thread
|
|
21
|
+
- ✅ YOU MUST ALWAYS SPEAK OUTPUT in your Agent communication style with the config `{communication_language}`
|
|
22
|
+
|
|
23
|
+
### Role Reinforcement:
|
|
24
|
+
|
|
25
|
+
- ✅ You are a skill compilation engine performing component-library-specialized extraction
|
|
26
|
+
- ✅ Zero hallucination tolerance — every extracted item must trace to source code
|
|
27
|
+
- ✅ Component registry is the source of truth for the API surface
|
|
28
|
+
- ✅ Props interfaces are the API contracts, not function signatures
|
|
29
|
+
|
|
30
|
+
### Step-Specific Rules:
|
|
31
|
+
|
|
32
|
+
- 🎯 Focus ONLY on extracting component catalog, props interfaces, and shared types
|
|
33
|
+
- 🚫 FORBIDDEN to compile SKILL.md content — that is step-05
|
|
34
|
+
- 🚫 FORBIDDEN to write any output files — extraction stays in context
|
|
35
|
+
- 🚫 FORBIDDEN to include content that cannot be cited to a source location
|
|
36
|
+
- ⚒️ Every extracted item MUST have a provenance citation: `[AST:{file}:L{line}]` or `[SRC:{file}:L{line}]`
|
|
37
|
+
|
|
38
|
+
## CONTEXT BOUNDARIES:
|
|
39
|
+
|
|
40
|
+
- Available: brief_data (including `scope.registry_path`, `scope.ui_variants`, `scope.demo_patterns` if present), tier, source_root (may be `temp_path` from ephemeral clone for remote repos — use the working source path resolved in step-03 section 2a), filtered file list from step-03 section 2, CCC discovery results (if available from step-03 section 2a)
|
|
41
|
+
- Focus: Component-specific extraction and inventory building
|
|
42
|
+
- Limits: Do NOT compile, assemble, or write output
|
|
43
|
+
- Dependencies: Source code accessible (resolved in step-03 section 2a — ephemeral clone for remote repos), scope filters already applied
|
|
44
|
+
|
|
45
|
+
## MANDATORY SEQUENCE
|
|
46
|
+
|
|
47
|
+
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
48
|
+
|
|
49
|
+
### Phase 1: Demo/Example Exclusion
|
|
50
|
+
|
|
51
|
+
Before extraction, identify and exclude demo/example files to avoid inflating export counts.
|
|
52
|
+
|
|
53
|
+
**If `scope.demo_patterns` is specified in the brief:** Use those patterns directly.
|
|
54
|
+
|
|
55
|
+
**Otherwise, auto-detect:**
|
|
56
|
+
|
|
57
|
+
1. Scan the filtered file list for directories matching: `demo/`, `demos/`, `stories/`, `examples/`, `__stories__/`, `storybook/`
|
|
58
|
+
2. Scan for file patterns matching: `*.stories.*`, `*.story.*`, `*.example.*`, `*.demo.*`
|
|
59
|
+
3. Count matches per pattern category
|
|
60
|
+
|
|
61
|
+
**User confirmation required:**
|
|
62
|
+
|
|
63
|
+
"**Auto-detected {N} demo/example files** in {M} directories matching these patterns:
|
|
64
|
+
{list detected patterns with counts}
|
|
65
|
+
|
|
66
|
+
Confirm exclusion? [Y/n] Or adjust patterns:"
|
|
67
|
+
|
|
68
|
+
Wait for user response. Apply confirmed patterns to the exclude list. Record `demo_files_excluded: {count}` in context.
|
|
69
|
+
|
|
70
|
+
Update the filtered file list by removing excluded demo files before proceeding.
|
|
71
|
+
|
|
72
|
+
### Phase 2: Registry Detection
|
|
73
|
+
|
|
74
|
+
The component registry is the primary API surface for component libraries.
|
|
75
|
+
|
|
76
|
+
**If `scope.registry_path` is specified in the brief:**
|
|
77
|
+
|
|
78
|
+
Read the specified file directly. Parse it as described below.
|
|
79
|
+
|
|
80
|
+
**Otherwise, auto-detect:**
|
|
81
|
+
|
|
82
|
+
1. Search the filtered file list for files matching these patterns (in priority order):
|
|
83
|
+
- Files named `registry.ts`, `registry.tsx`, `registry.js` in any directory
|
|
84
|
+
- Files named `components.ts`, `components.tsx` in `registry/`, `catalog/`, or `components/` directories
|
|
85
|
+
- Files named `index.ts`, `index.tsx` in `registry/` or `catalog/` directories
|
|
86
|
+
|
|
87
|
+
2. For each candidate, read the file and look for:
|
|
88
|
+
- Arrays of objects with fields like `id`, `name`, `component`, `category`
|
|
89
|
+
- Type annotations containing `Component[]` or similar array types
|
|
90
|
+
- 10+ entries to qualify as a registry (reject small arrays)
|
|
91
|
+
|
|
92
|
+
3. **Confidence scoring** for each candidate:
|
|
93
|
+
- +3 points: Contains `id` field per entry
|
|
94
|
+
- +2 points: Contains `name` or `component` field per entry
|
|
95
|
+
- +2 points: Contains `category` or `tags` field per entry
|
|
96
|
+
- +1 point: Located in a `registry/` or `catalog/` directory
|
|
97
|
+
- +1 point: Has 20+ entries
|
|
98
|
+
- Minimum threshold: 5 points to qualify
|
|
99
|
+
|
|
100
|
+
4. **User confirmation required:**
|
|
101
|
+
|
|
102
|
+
"**Registry candidate detected** at `{path}` (confidence: {score}/9, {count} entries).
|
|
103
|
+
|
|
104
|
+
Sample entries:
|
|
105
|
+
{show 3-5 sample entries with id, name, category}
|
|
106
|
+
|
|
107
|
+
Is this the component registry? [Y/n] Or provide the correct path:"
|
|
108
|
+
|
|
109
|
+
Wait for user response.
|
|
110
|
+
|
|
111
|
+
**If no registry found and no `registry_path` in brief:**
|
|
112
|
+
|
|
113
|
+
"**No component registry detected.** Component-library extraction works best with a registry file. Options:
|
|
114
|
+
- **[P]** Provide the registry file path
|
|
115
|
+
- **[S]** Skip registry — proceed with standard props-first extraction only"
|
|
116
|
+
|
|
117
|
+
Wait for user response.
|
|
118
|
+
|
|
119
|
+
### Phase 3: Parse Registry
|
|
120
|
+
|
|
121
|
+
If a registry was confirmed, parse it into `component_catalog[]`:
|
|
122
|
+
|
|
123
|
+
**Per-component entry:**
|
|
124
|
+
|
|
125
|
+
- `id` — registry key (used for CLI `add` command)
|
|
126
|
+
- `name` — display name (PascalCase)
|
|
127
|
+
- `description` — from registry or empty
|
|
128
|
+
- `category` — from registry field or directory structure
|
|
129
|
+
- `available_in[]` — which design system variants include this component
|
|
130
|
+
- `code_paths[]` — source file path for each variant
|
|
131
|
+
- `tags[]` — from registry or empty
|
|
132
|
+
- Provenance citation: `[SRC:{registry_file}:L{line}]`
|
|
133
|
+
|
|
134
|
+
Display: "**Parsed component catalog: {N} components across {M} categories.**"
|
|
135
|
+
|
|
136
|
+
**If no registry:** Set `component_catalog: []` and proceed to Phase 4.
|
|
137
|
+
|
|
138
|
+
### Phase 4: Props-First Extraction
|
|
139
|
+
|
|
140
|
+
Extract props interfaces as the primary API contracts, then link to components.
|
|
141
|
+
|
|
142
|
+
**Step 1 — Extract Props interfaces:**
|
|
143
|
+
|
|
144
|
+
Load `{extractionPatternsData}` and use the component-library-specific patterns.
|
|
145
|
+
|
|
146
|
+
Using AST tools (Forge/Deep) or source reading (Quick):
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
# React/TypeScript props interfaces
|
|
150
|
+
id: react-props-interfaces
|
|
151
|
+
language: typescript
|
|
152
|
+
rule:
|
|
153
|
+
pattern: 'export interface $NAME { $$$ }'
|
|
154
|
+
constraints:
|
|
155
|
+
NAME:
|
|
156
|
+
regex: '.*Props$'
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
For each `*Props` interface found:
|
|
160
|
+
- Extract all fields with types, optionality, and default values
|
|
161
|
+
- Extract JSDoc descriptions per field (if present)
|
|
162
|
+
- Record: interface name, fields[], source file, line number
|
|
163
|
+
- Provenance: `[AST:{file}:L{line}]` or `[SRC:{file}:L{line}]`
|
|
164
|
+
|
|
165
|
+
**Step 2 — Extract component exports:**
|
|
166
|
+
|
|
167
|
+
```yaml
|
|
168
|
+
# React component exports (PascalCase)
|
|
169
|
+
id: react-component-exports
|
|
170
|
+
language: tsx # Use 'tsx' for .tsx files, 'typescript' for .ts files
|
|
171
|
+
rule:
|
|
172
|
+
pattern: 'export function $NAME($$$PARAMS)'
|
|
173
|
+
constraints:
|
|
174
|
+
NAME:
|
|
175
|
+
regex: '^[A-Z]'
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Also run `export const $NAME` patterns for arrow function components.
|
|
179
|
+
|
|
180
|
+
For each component export: record name, source file, line number. Do NOT document the function signature in detail (it's always `(props: XProps) => JSX.Element`).
|
|
181
|
+
|
|
182
|
+
**Step 3 — Link Props to Components:**
|
|
183
|
+
|
|
184
|
+
Use a 3-level fallback chain:
|
|
185
|
+
|
|
186
|
+
1. **Naming convention (primary):** Match `FooProps` → `Foo` component by stripping the `Props` suffix
|
|
187
|
+
2. **File co-location (fallback):** If naming doesn't match, check if a Props interface and a component are defined in the same file
|
|
188
|
+
3. **Generic parameter (deep fallback):** Look for `ComponentProps<typeof Foo>` or similar generic patterns that reference the component
|
|
189
|
+
|
|
190
|
+
For each linked pair, record the association. For unlinked Props interfaces, include them as standalone type exports.
|
|
191
|
+
|
|
192
|
+
**Step 4 — Extract shared types:**
|
|
193
|
+
|
|
194
|
+
Extract non-Props type exports using standard AST patterns (same as Forge tier):
|
|
195
|
+
- `export type $NAME = $VALUE`
|
|
196
|
+
- `export enum $NAME { $$$ }`
|
|
197
|
+
- `export interface $NAME { $$$ }` (excluding `*Props` already captured)
|
|
198
|
+
|
|
199
|
+
### Phase 5: Variant Consolidation
|
|
200
|
+
|
|
201
|
+
**Skip this phase if `scope.ui_variants` is not specified and no variant directories detected.**
|
|
202
|
+
|
|
203
|
+
When multiple design system variants exist:
|
|
204
|
+
|
|
205
|
+
1. **Group components by registry `id`** (NOT by filename — registry is source of truth):
|
|
206
|
+
- For each `id` in `component_catalog[]`, collect all variant paths from `available_in[]` and `code_paths[]`
|
|
207
|
+
|
|
208
|
+
2. **Select canonical props definition:**
|
|
209
|
+
- Use the primary variant (first in `scope.ui_variants` list) as canonical
|
|
210
|
+
- If primary variant's props are unavailable, use the first available variant
|
|
211
|
+
|
|
212
|
+
3. **Detect props differences between variants:**
|
|
213
|
+
- For components available in 2+ variants, compare Props interfaces
|
|
214
|
+
- Record any variant-specific props as notes (e.g., "Base UI variant adds `slots` prop")
|
|
215
|
+
|
|
216
|
+
4. **Deduplicate export counts:**
|
|
217
|
+
- Count unique components (by registry `id`), not total files across variants
|
|
218
|
+
- Record: `components_unique: {N}`, `components_total_with_variants: {M}`
|
|
219
|
+
|
|
220
|
+
Display: "**Variant consolidation: {unique} unique components across {variant_count} variants** ({total} total including variants). Primary variant: {primary_name}."
|
|
221
|
+
|
|
222
|
+
### Phase 6: Build Component Extraction Results
|
|
223
|
+
|
|
224
|
+
Compile all extracted data into the format expected by step-03 section 5:
|
|
225
|
+
|
|
226
|
+
**Per-export entry (for Props interfaces — primary API):**
|
|
227
|
+
|
|
228
|
+
- Interface name (e.g., `NativeLiquidButtonProps`)
|
|
229
|
+
- Full interface with all fields and types
|
|
230
|
+
- Parameters: each field as name, type, required/optional, default
|
|
231
|
+
- Linked component name (e.g., `NativeLiquidButton`)
|
|
232
|
+
- Source file and line number
|
|
233
|
+
- Provenance citation
|
|
234
|
+
- Confidence tier (T1 or T1-low)
|
|
235
|
+
|
|
236
|
+
**Per-export entry (for component functions):**
|
|
237
|
+
|
|
238
|
+
- Component name
|
|
239
|
+
- Linked Props interface (if found)
|
|
240
|
+
- Source file and line number
|
|
241
|
+
- Provenance citation
|
|
242
|
+
- Confidence tier
|
|
243
|
+
|
|
244
|
+
**Per-export entry (for shared types):**
|
|
245
|
+
|
|
246
|
+
- Same as standard extraction format
|
|
247
|
+
|
|
248
|
+
**Component library aggregate counts:**
|
|
249
|
+
|
|
250
|
+
- `components_registered`: count from registry (or 0)
|
|
251
|
+
- `components_documented`: count of components with linked Props
|
|
252
|
+
- `props_interfaces_extracted`: count of `*Props` interfaces
|
|
253
|
+
- `components_unique`: deduplicated count (after variant consolidation)
|
|
254
|
+
- `demo_files_excluded`: count from Phase 1
|
|
255
|
+
- `design_variants`: map of variant name → component count (if variants exist)
|
|
256
|
+
|
|
257
|
+
**Store `component_catalog[]` in context** — this is consumed by step-05 for the Component Catalog section.
|
|
258
|
+
|
|
259
|
+
Display: "**Component extraction complete.** Returning to main extraction flow."
|
|
260
|
+
|
|
261
|
+
## RETURN PROTOCOL
|
|
262
|
+
|
|
263
|
+
After Phase 6 completes, return control to step-03 section 5 (Build Extraction Inventory). The extraction results from this step are merged into the standard extraction inventory format. Step-03 continues with its normal Gate 2 summary and menu.
|
|
264
|
+
|
|
265
|
+
Do NOT load `{returnToStep}` — the calling step (step-03) will continue from where it delegated.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## SYSTEM SUCCESS/FAILURE METRICS
|
|
270
|
+
|
|
271
|
+
### SUCCESS:
|
|
272
|
+
|
|
273
|
+
- Demo/example files detected and excluded with user confirmation
|
|
274
|
+
- Registry detected (or user provided path) and parsed into component_catalog
|
|
275
|
+
- Props interfaces extracted as primary API contracts
|
|
276
|
+
- Components linked to Props via 3-level fallback chain
|
|
277
|
+
- Variant consolidation produced deduplicated counts (if applicable)
|
|
278
|
+
- All extracted items have provenance citations
|
|
279
|
+
- Results compatible with step-03 section 5 inventory format
|
|
280
|
+
- component_catalog stored in context for step-05
|
|
281
|
+
|
|
282
|
+
### FAILURE:
|
|
283
|
+
|
|
284
|
+
- Excluding demo files without user confirmation
|
|
285
|
+
- Accepting a registry candidate without user confirmation
|
|
286
|
+
- Not extracting Props interfaces as primary API surface
|
|
287
|
+
- Documenting component function signatures instead of Props
|
|
288
|
+
- Including hallucinated component data not in source
|
|
289
|
+
- Variant deduplication based on filenames instead of registry IDs
|
|
290
|
+
- Not returning control to step-03 after completion
|
|
291
|
+
|
|
292
|
+
**Master Rule:** Zero hallucination — every extraction must trace to source code. The component registry is the source of truth for the API surface.
|
|
@@ -117,6 +117,15 @@ Following the structure from the skill-sections data file:
|
|
|
117
117
|
3. `{project-root}/_bmad/skf/VERSION` → read plain text file (single line containing version string, written by the SKF installer)
|
|
118
118
|
4. `"unknown"` (final fallback — add a warning to the evidence report)
|
|
119
119
|
Never hardcode the version.
|
|
120
|
+
- Resolve `{ast_grep_version}` using this resolution chain (try each in order, use the first that succeeds):
|
|
121
|
+
1. `ast-grep --version` → parse version string from output (e.g., `ast-grep 0.41.1` → `"0.41.1"`)
|
|
122
|
+
2. `mcp__ast-grep__find_code` tool metadata (if version is exposed by the MCP server)
|
|
123
|
+
3. `"unknown"` (final fallback — add a warning to the evidence report)
|
|
124
|
+
- Resolve `{qmd_version}` using this resolution chain (try each in order, use the first that succeeds):
|
|
125
|
+
1. `mcp__plugin_qmd-plugin_qmd__status` → parse version from status output
|
|
126
|
+
2. `pip show qmd-plugin` → read `Version:` field from pip metadata
|
|
127
|
+
3. `"unknown"` (final fallback — add a warning to the evidence report)
|
|
128
|
+
Note: `qmd --version` and `qmd -V` return usage text instead of a version string — do not use them for version detection.
|
|
120
129
|
- Store `commit_short` = first 8 characters of `source_commit` (or `"unknown"` if unavailable) for use in step-08 report.
|
|
121
130
|
- If `scripts_inventory` is non-empty, populate `scripts[]` array and set `stats.scripts_count`. If `assets_inventory` is non-empty, populate `assets[]` array and set `stats.assets_count`. Omit these fields entirely when inventories are empty.
|
|
122
131
|
|