bmad-module-skill-forge 0.7.4 → 0.8.1

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 (23) hide show
  1. package/package.json +1 -1
  2. package/src/agents/forger.agent.yaml +1 -1
  3. package/src/knowledge/ccc-bridge.md +3 -3
  4. package/src/knowledge/tool-resolution.md +4 -3
  5. package/src/module.yaml +5 -3
  6. package/src/workflows/analyze-source/data/skill-brief-schema.md +11 -2
  7. package/src/workflows/analyze-source/data/unit-detection-heuristics.md +9 -0
  8. package/src/workflows/analyze-source/steps-c/step-05-recommend.md +1 -1
  9. package/src/workflows/analyze-source/steps-c/step-06-generate-briefs.md +2 -1
  10. package/src/workflows/brief-skill/data/scope-templates.md +41 -0
  11. package/src/workflows/brief-skill/data/skill-brief-schema.md +11 -3
  12. package/src/workflows/brief-skill/steps-c/step-03-scope-definition.md +6 -6
  13. package/src/workflows/create-skill/data/compile-assembly-rules.md +108 -0
  14. package/src/workflows/create-skill/data/extraction-patterns.md +94 -3
  15. package/src/workflows/create-skill/data/skill-sections.md +76 -0
  16. package/src/workflows/create-skill/data/source-resolution-protocols.md +21 -9
  17. package/src/workflows/create-skill/steps-c/step-02b-ccc-discover.md +2 -2
  18. package/src/workflows/create-skill/steps-c/step-03-extract.md +44 -29
  19. package/src/workflows/create-skill/steps-c/step-03d-component-extraction.md +292 -0
  20. package/src/workflows/create-skill/steps-c/step-05-compile.md +9 -0
  21. package/src/workflows/test-skill/data/scoring-rules.md +12 -0
  22. package/src/workflows/test-skill/steps-c/step-04b-external-validators.md +11 -4
  23. package/src/workflows/update-skill/steps-c/step-03-re-extract.md +18 -2
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "bmad-module-skill-forge",
4
- "version": "0.7.4",
4
+ "version": "0.8.1",
5
5
  "description": "BMAD module — Turn code and docs into instructions AI agents can actually follow. Progressive capability tiers (Quick/Forge/Forge+/Deep).",
6
6
  "keywords": [
7
7
  "bmad",
@@ -8,7 +8,7 @@ agent:
8
8
  module: skf
9
9
  hasSidecar: true
10
10
  sidecar-folder: forger-sidecar
11
- sidecar-path: "{project-root}/_bmad/_memory/forger-sidecar/"
11
+ sidecar-path: "{project-root}/_bmad/_memory/forger-sidecar"
12
12
 
13
13
  persona:
14
14
  role: |
@@ -33,7 +33,7 @@ When either condition is false, calling steps skip ccc discovery silently and pr
33
33
 
34
34
  ### `ccc_bridge.search(query, path?, top_k?)`
35
35
 
36
- **Resolves to:** `ccc search "{query}" --path {path} --top {top_k}` (CLI) or the `ccc` MCP search tool (preferred)
36
+ **Resolves to:** `cd {path} && ccc search --limit {top_k} "{query}"` (CLI) or the `ccc` MCP search tool (preferred). Note: `ccc search` operates on the index in the current working directory — there is no flag to specify a project directory. The `--path` flag is a file path glob filter within the index, not a project selector.
37
37
 
38
38
  Returns: list of `{file, score, snippet}` entries ranked by semantic relevance to the query. These are **candidates** for ast-grep extraction — not verified exports.
39
39
 
@@ -41,7 +41,7 @@ Returns: list of `{file, score, snippet}` entries ranked by semantic relevance t
41
41
 
42
42
  ### `ccc_bridge.ensure_index(path)`
43
43
 
44
- **Resolves to:** Check `ccc_index.status` in forge-tier.yaml. If `"none"` or the indexed_path does not match, run `ccc init {path}` then `ccc index` and update forge-tier.yaml.
44
+ **Resolves to:** Check `ccc_index.status` in forge-tier.yaml. If `"none"` or the indexed_path does not match, run `cd {path} && ccc init` then `ccc index` and update forge-tier.yaml. Note: `ccc init` takes no positional arguments — it initializes the index for the current working directory.
45
45
 
46
46
  **Usage context:** Called by setup-forge step-01b to ensure the project root is indexed. Called lazily by extraction steps when `ccc_index.status` is `"none"` but ccc is available.
47
47
 
@@ -102,7 +102,7 @@ For remote repository sources (GitHub URLs), CCC cannot operate during step-02b
102
102
 
103
103
  1. **step-02b:** Detects remote source, sets `{ccc_discovery: []}`, displays deferred message
104
104
  2. **step-03:** After ephemeral clone succeeds, detects the deferred scenario (`tools.ccc == true AND {ccc_discovery} is empty AND ephemeral_clone_active == true AND tier is Forge+/Deep`)
105
- 3. **step-03:** Runs `ccc init {temp_path}` + `ccc index` on the ephemeral clone (extended timeout or background mode, verified via `ccc status`)
105
+ 3. **step-03:** Runs `cd {temp_path} && ccc init` + `ccc index` on the ephemeral clone (extended timeout or background mode, verified via `ccc status`)
106
106
  4. **step-03:** Executes CCC search and populates `{ccc_discovery}` before AST extraction begins
107
107
 
108
108
  The ephemeral clone index is not registered in `ccc_index_registry` — the clone is deleted after extraction and the CCC daemon's own GC handles orphaned indexes.
@@ -7,12 +7,13 @@ Bridge names (`ast_bridge`, `ccc_bridge`, `qmd_bridge`, `gh_bridge`) and subproc
7
7
  | Bridge | Operation | Claude Code | Cursor | CLI | Fallback |
8
8
  |--------------|----------------------------------|------------------------------------------------------------------|----------------|----------------------------------------------------|----------------------------------|
9
9
  | `ast_bridge` | `scan_definitions()` | `mcp__ast-grep__find_code` or `mcp__ast-grep__find_code_by_rule` | ast-grep MCP | `sg run` / `ast-grep -p` | Source reading (T1-low) |
10
- | `ast_bridge` | `detect_co_imports()` | `mcp__ast-grep__find_code_by_rule` with co-import YAML rule | ast-grep MCP | `ast-grep -p 'import $$$' --json=stream` | grep-based co-import count |
11
- | `ccc_bridge` | `search(query, root, top_k)` | `/ccc` skill search | ccc MCP server | `ccc search "{query}" --path {root} --top {top_k}` | Skip silently |
12
- | `ccc_bridge` | `ensure_index(root)` | `/ccc` skill indexing | ccc MCP server | `ccc init` + `ccc index {root}` | Skip silently |
10
+ | `ast_bridge` | `detect_co_imports()` | `mcp__ast-grep__find_code_by_rule` with co-import YAML rule | ast-grep MCP | `ast-grep run -p 'import $$$' --json=stream` | grep-based co-import count |
11
+ | `ccc_bridge` | `search(query, root, top_k)` | `/ccc` skill search | ccc MCP server | `cd {root} && ccc search --limit {top_k} "{query}"` | Skip silently |
12
+ | `ccc_bridge` | `ensure_index(root)` | `/ccc` skill indexing | ccc MCP server | `cd {root} && ccc init` + `ccc index` | Skip silently |
13
13
  | `ccc_bridge` | `status()` | `/ccc` skill status | ccc MCP server | `ccc --help` + `ccc doctor` | Unavailable = `tools.ccc: false` |
14
14
  | `qmd_bridge` | `search(query)` | `mcp__plugin_qmd-plugin_qmd__search` | qmd MCP server | `qmd search "{query}"` | Skip enrichment |
15
15
  | `qmd_bridge` | `vector_search(query)` | `mcp__plugin_qmd-plugin_qmd__vector_search` | qmd MCP server | `qmd vector_search "{query}"` | Use BM25 search only |
16
+ | `qmd_bridge` | `version()` | `qmd --version` → parse `"qmd X.Y.Z"` → `"X.Y.Z"` | qmd MCP server | `qmd --version` | `"unknown"` |
16
17
  | `gh_bridge` | `list_tree(owner, repo, branch)` | `gh api repos/{owner}/{repo}/git/trees/{branch}?recursive=1` | gh CLI | `gh api ...` | Direct file listing if local |
17
18
  | `gh_bridge` | `read_file(owner, repo, path)` | `gh api repos/{owner}/{repo}/contents/{path}` | gh CLI | `gh api ...` | Direct file read if local |
18
19
 
package/src/module.yaml CHANGED
@@ -15,11 +15,13 @@ forge_data_folder:
15
15
  default: "forge-data"
16
16
  result: "{project-root}/{value}"
17
17
 
18
+ sidecar_path:
19
+ prompt: false
20
+ default: "_bmad/_memory/forger-sidecar"
21
+ result: "{project-root}/{value}"
22
+
18
23
  # Variables from Core Config inserted:
19
24
  ## user_name
20
25
  ## communication_language
21
26
  ## document_output_language
22
27
  ## output_folder
23
-
24
- # Variables from Agent Sidecar Config:
25
- ## sidecar_path (from forger.agent.yaml metadata.sidecar-path)
@@ -55,12 +55,20 @@ The create-skill workflow (step-03-extract) also performs version reconciliation
55
55
 
56
56
  ```yaml
57
57
  scope:
58
- type: full-library | specific-modules | public-api | docs-only
58
+ type: full-library | specific-modules | public-api | component-library | docs-only
59
59
  include:
60
60
  - 'src/**/*.ts' # At least one required
61
61
  exclude:
62
62
  - 'src/**/*.test.ts' # Optional
63
63
  notes: 'Optional rationale for scope decision'
64
+ # Additional fields when scope.type is "component-library":
65
+ # registry_path: "path/to/registry.ts" # Optional — auto-detected if omitted
66
+ # ui_variants: # Optional — design system variants
67
+ # - name: "shadcnui"
68
+ # package: "packages/components/react-shadcn"
69
+ # demo_patterns: # Optional — auto-detected if omitted
70
+ # - "**/demo/**"
71
+ # - "**/*.stories.*"
64
72
  ```
65
73
 
66
74
  ### Scope Types
@@ -70,6 +78,7 @@ scope:
70
78
  | full-library | Entire codebase of a unit |
71
79
  | specific-modules | Selected components or packages |
72
80
  | public-api | Only exported interfaces |
81
+ | component-library | UI component libraries with registries, props-based APIs, and design system variants |
73
82
  | docs-only | When source_type is docs-only — no source code available, all content from doc_urls |
74
83
 
75
84
  ## YAML Template
@@ -98,7 +107,7 @@ created_by: '{user_name}'
98
107
  1. **Name uniqueness**: No duplicate names within forge_data_folder
99
108
  2. **Source accessibility**: source_repo path must exist or be reachable
100
109
  3. **Language recognized**: Must be a known programming language
101
- 4. **Scope type valid**: Must match one of the four defined types (full-library, specific-modules, public-api, docs-only)
110
+ 4. **Scope type valid**: Must match one of the five defined types (full-library, specific-modules, public-api, component-library, docs-only)
102
111
  5. **Include patterns**: At least one include glob pattern required (exception: `docs-only` scope, where include patterns are optional since no source code is available)
103
112
  6. **Forge tier match**: Must match value in forge-tier.yaml
104
113
  7. **Docs-only mode**: When `source_type: "docs-only"`, `doc_urls` required (>= 1), `source_repo` optional
@@ -59,6 +59,15 @@ Rules for identifying discrete skillable units within a project. A "skillable un
59
59
  - Custom wrappers, configurations, or integration code
60
60
  - Scope type: `public-api`
61
61
 
62
+ ### Component Library Boundary
63
+ - Contains a component registry or catalog file (array of component definitions with IDs, names, categories)
64
+ - Has `components/`, `packages/components/`, or similar multi-component directory structure
65
+ - Multiple design system variant directories (e.g., `react-shadcn/`, `react-baseui/`, `react-carbon/`)
66
+ - Significant demo/story/example file ratio (>30% of total files)
67
+ - CLI-based installation pattern (e.g., `npx <tool> add <component-id>`)
68
+ - Props interfaces outnumber function signatures as primary API surface
69
+ - Scope type: `component-library`
70
+
62
71
  ## Disqualification Rules
63
72
 
64
73
  Do NOT recommend as a skillable unit if:
@@ -90,7 +90,7 @@ For EACH qualifying unit, prepare a recommendation card:
90
90
 
91
91
  **Proposed Brief Fields:**
92
92
  - name: {suggested kebab-case name}
93
- - scope.type: {full-library / specific-modules / public-api}
93
+ - scope.type: {full-library / specific-modules / public-api / component-library}
94
94
  - scope.include: {suggested glob patterns}
95
95
  - description: {suggested 1-3 sentence description}
96
96
  ```
@@ -97,7 +97,7 @@ For each generated brief, check against {schemaFile} validation rules:
97
97
  1. **Name uniqueness** — no duplicate names within the batch or existing skills
98
98
  2. **Source accessible** — project_path exists
99
99
  3. **Language recognized** — valid programming language identifier
100
- 4. **Scope type valid** — matches `full-library`, `specific-modules`, or `public-api`
100
+ 4. **Scope type valid** — matches `full-library`, `specific-modules`, `public-api`, or `component-library`
101
101
  5. **Include patterns** — at least one glob pattern present
102
102
  6. **Forge tier match** — matches forge_tier from config
103
103
 
@@ -152,6 +152,7 @@ For each generated brief, recommend the appropriate next workflow:
152
152
  | Condition | Recommendation |
153
153
  |-----------|---------------|
154
154
  | Brief has `scope.type: full-library` and unit is well-bounded | create-skill — brief is sufficient for direct skill creation |
155
+ | Brief has `scope.type: component-library` and registry defines boundaries | create-skill — component boundaries defined by registry |
155
156
  | Brief has `scope.type: specific-modules` or scope needs refinement | brief-skill — refine scope before creating skill |
156
157
  | Brief has `scope.type: public-api` or complex interface | brief-skill — detailed scoping needed |
157
158
  | Unit flagged as stack skill candidate | create-stack-skill — after individual skills exist |
@@ -16,6 +16,12 @@ Present these options to the user for selection:
16
16
  - Entry points and exported interfaces only
17
17
  - Internal implementation excluded
18
18
 
19
+ **[C] Component Library** — Optimized for UI component libraries with registries, props-based APIs, and design system variants.
20
+ - Component registry as primary API surface (not individual exports)
21
+ - Props interfaces as API contracts (not function signatures)
22
+ - Auto-exclude demo/example/story files (with user confirmation)
23
+ - Variant consolidation across design systems
24
+
19
25
  ## Boundary Definitions by Scope Type
20
26
 
21
27
  ### Full Library Boundaries
@@ -69,6 +75,41 @@ Prompt: "Any additional items you'd like to include or exclude?"
69
75
 
70
76
  Prompt: "Any additional documentation URLs to include? Or URLs to exclude from the ones collected?"
71
77
 
78
+ ### Component Library Boundaries
79
+
80
+ **Phase 1 — Registry Detection:**
81
+
82
+ Auto-detect or accept explicit `registry_path` from user. Scan source tree for files matching common registry patterns:
83
+ - Files named `registry.ts`, `components.ts`, `index.ts` in `registry/`, `catalog/`, or `components/` directories
84
+ - Arrays of objects with `{ id, name, component }` structure and 10+ entries
85
+ - Files with `Component[]` type annotations
86
+
87
+ Present detected registry candidate(s) to user for confirmation.
88
+ Prompt: "I found what looks like a component registry at {path} ({count} entries). Is this correct? Or provide the registry path:"
89
+
90
+ **Phase 2 — Demo/Example Exclusion:**
91
+
92
+ Auto-detect demo directories and file patterns:
93
+ - Directories: `demo/`, `demos/`, `stories/`, `examples/`, `__stories__/`, `storybook/`
94
+ - Files: `*.stories.*`, `*.story.*`, `*.example.*`, `*.demo.*`
95
+
96
+ Present detected patterns to user before applying (do NOT exclude silently).
97
+ Prompt: "**Auto-detected {N} demo/example files** in {M} directories. Confirm exclusion? [Y/n] Or adjust patterns:"
98
+
99
+ **Phase 3 — Variant Selection (if applicable):**
100
+
101
+ If multiple design system variant directories detected (e.g., `react-shadcn/`, `react-baseui/`, `react-carbon/`):
102
+ - Present detected variants with component counts per variant
103
+ - User selects primary variant and which variants to include
104
+ - Record as `ui_variants` in brief
105
+
106
+ Prompt: "I detected {count} design system variants: {list with counts}. Which is the primary variant? Include all? [Y/n]"
107
+
108
+ **Phase 4 — Scope Confirmation:**
109
+
110
+ Summary showing: component count, excluded demo count, variant summary, include/exclude patterns.
111
+ Prompt: "Does this component library scope look right? Adjust before continuing."
112
+
72
113
  ## Scripts & Assets Detection (Optional Refinement)
73
114
 
74
115
  When `scripts_intent` or `assets_intent` is `detect` (default), SKF auto-detects from source directories matching: `scripts/`, `bin/`, `tools/`, `cli/` (for scripts) and `assets/`, `templates/`, `schemas/`, `configs/`, `examples/` (for assets). Detection applies to all scope types except `docs-only`.
@@ -51,13 +51,21 @@ The create-skill workflow (step-03-extract) also performs version reconciliation
51
51
 
52
52
  ```yaml
53
53
  scope:
54
- type: full-library | specific-modules | public-api | docs-only
54
+ type: full-library | specific-modules | public-api | component-library | docs-only
55
55
  include:
56
56
  - "src/**/*.ts" # Glob patterns for included files/directories
57
57
  exclude:
58
58
  - "**/*.test.*" # Glob patterns for excluded files
59
59
  - "**/node_modules/**"
60
60
  notes: "Optional notes about scope decisions"
61
+ # Additional fields when scope.type is "component-library":
62
+ # registry_path: "path/to/registry.ts" # Optional — auto-detected if omitted
63
+ # ui_variants: # Optional — design system variants
64
+ # - name: "shadcnui"
65
+ # package: "packages/components/react-shadcn"
66
+ # demo_patterns: # Optional — auto-detected if omitted
67
+ # - "**/demo/**"
68
+ # - "**/*.stories.*"
61
69
  ```
62
70
 
63
71
  ## YAML Template
@@ -74,7 +82,7 @@ forge_tier: "{Quick|Forge|Forge+|Deep}"
74
82
  created: "{date}"
75
83
  created_by: "{user_name}"
76
84
  scope:
77
- type: "{full-library|specific-modules|public-api|docs-only}"
85
+ type: "{full-library|specific-modules|public-api|component-library|docs-only}"
78
86
  include:
79
87
  - "{pattern}"
80
88
  exclude:
@@ -133,7 +141,7 @@ Created by: {created_by}
133
141
  1. `name` must be unique within {forge_data_folder}
134
142
  2. `source_repo` must be accessible (gh api for GitHub, path exists for local)
135
143
  3. `language` must be a recognized programming language
136
- 4. `scope.type` must be one of the four defined types
144
+ 4. `scope.type` must be one of the five defined types
137
145
  5. `scope.include` must have at least one pattern (exception: `docs-only` scope, where include patterns are optional since no source code is available)
138
146
  6. `forge_tier` must be one of: Quick, Forge, Forge+, Deep (Title Case, must match the tier from forge-tier.yaml, or default to Quick)
139
147
  7. When `source_type: "docs-only"`: `doc_urls` must have >= 1 entry, `source_repo` becomes optional
@@ -106,9 +106,9 @@ Wait for confirmation. Record any changes to `doc_urls`.
106
106
 
107
107
  ### 2c. Offer Scope Templates
108
108
 
109
- Load `{scopeTemplatesFile}` for the scope type options ([F], [M], [P]) and their descriptions.
109
+ Load `{scopeTemplatesFile}` for the scope type options ([F], [M], [P], [C]) and their descriptions.
110
110
 
111
- Present: "**How broadly should this skill cover the library?**" followed by the three scope type options from the loaded reference.
111
+ Present: "**How broadly should this skill cover the library?**" followed by the scope type options from the loaded reference.
112
112
 
113
113
  Ask: "Which scope type fits your needs?"
114
114
 
@@ -116,7 +116,7 @@ Wait for user selection.
116
116
 
117
117
  ### 3. Define Boundaries Based on Selection
118
118
 
119
- Using the boundary definitions from `{scopeTemplatesFile}`, present the appropriate flow for the user's selected scope type ([F], [M], or [P]). Follow each type's prompts and wait for user input at each phase before proceeding.
119
+ Using the boundary definitions from `{scopeTemplatesFile}`, present the appropriate flow for the user's selected scope type ([F], [M], [P], or [C]). Follow each type's prompts and wait for user input at each phase before proceeding.
120
120
 
121
121
  ### 4. Handle Language Override
122
122
 
@@ -132,7 +132,7 @@ Wait for confirmation or override.
132
132
 
133
133
  "**Scope Summary:**
134
134
 
135
- **Type:** {Full Library / Specific Modules / Public API}
135
+ **Type:** {Full Library / Specific Modules / Public API / Component Library}
136
136
 
137
137
  **Include:**
138
138
  {bulleted list of include patterns}
@@ -151,7 +151,7 @@ Wait for confirmation. Make adjustments if requested.
151
151
 
152
152
  ### 5b. Scripts & Assets Intent (Optional)
153
153
 
154
- **Only ask when `scope.type` is `full-library` or `specific-modules` (skip for `public-api` and `docs-only`).**
154
+ **Only ask when `scope.type` is `full-library`, `specific-modules`, or `component-library` (skip for `public-api` and `docs-only`).**
155
155
 
156
156
  "Does this library include executable scripts (CLI tools, validation scripts, setup helpers) or static assets (config templates, JSON schemas, example configs) that should be packaged with the skill?"
157
157
 
@@ -189,7 +189,7 @@ ONLY WHEN C is selected and scope boundaries are confirmed will you load and rea
189
189
 
190
190
  ### ✅ SUCCESS:
191
191
 
192
- - Scope type selected by user (Full Library, Specific Modules, or Public API)
192
+ - Scope type selected by user (Full Library, Specific Modules, Public API, or Component Library)
193
193
  - Include patterns defined and confirmed
194
194
  - Exclude patterns defined and confirmed
195
195
  - Language confirmed (or overridden if detection was low confidence)
@@ -108,6 +108,114 @@ Assemble Sections 9-11 (Full API Reference, Full Type Definitions, Full Integrat
108
108
 
109
109
  Do NOT repeat Tier 1's name/purpose/key-params table format in Tier 2. Tier 2 is a deep reference, not a reformatted summary. This distinction prevents conciseness scorers from flagging the two-tier design as redundancy.
110
110
 
111
+ ### Component Library Assembly Overrides
112
+
113
+ When `scope.type: "component-library"` in the brief AND `component_catalog[]` is available in context, apply these overrides to the standard assembly. All other sections remain unchanged.
114
+
115
+ **Section 2 (Quick Start) — CLI-first override:**
116
+
117
+ Replace the standard function-based Quick Start with CLI installation:
118
+
119
+ ```markdown
120
+ ## Quick Start
121
+
122
+ Install a component:
123
+ `npx {cli-name} add {top-component-id}`
124
+
125
+ {If provider wrapping detected in source:}
126
+ Set up providers:
127
+ \`\`\`tsx
128
+ import { ThemeProvider, UILibraryProvider } from "{primary-package}";
129
+
130
+ <ThemeProvider>
131
+ <UILibraryProvider>
132
+ <YourApp />
133
+ </UILibraryProvider>
134
+ </ThemeProvider>
135
+ \`\`\`
136
+ ```
137
+
138
+ Detect CLI name from: `package.json` `bin` field, README usage examples, or `registry_path` context. If no CLI detected, fall back to standard import-based Quick Start.
139
+
140
+ **Section 4 (Key API Summary) — Component Catalog override:**
141
+
142
+ Replace the function table with a Component Catalog organized by category:
143
+
144
+ ```markdown
145
+ ## Component Catalog
146
+
147
+ | Category | Count | Key Components |
148
+ |----------|-------|---------------|
149
+ | {category} | {count} | {top 3-5 component names, comma-separated} |
150
+
151
+ **Design system variants:** {variant list with primary marked}
152
+ **Total components:** {unique count} | {Per variant: **With {name}:** {count}}
153
+ ```
154
+
155
+ Source: `component_catalog[]` from step-03d. Group by `category` field. Provenance: cite the registry file.
156
+
157
+ **Section 5 (Key Types) — Props-focused override:**
158
+
159
+ Replace generic types with the top 5 most-used Props interfaces (by component count or prominence):
160
+
161
+ ```markdown
162
+ ## Key Types
163
+
164
+ ### {ComponentName}Props
165
+ | Prop | Type | Default | Required |
166
+ |------|------|---------|----------|
167
+ | {prop} | {type} | {default or —} | {yes/no} |
168
+ ```
169
+
170
+ Show only the 5 most important Props interfaces inline. Full Props details go in Tier 2.
171
+
172
+ **Tier 2 (Full API Reference) — Props Reference override:**
173
+
174
+ Organize by component (not by function). Per component:
175
+
176
+ ```markdown
177
+ ### {ComponentName}
178
+
179
+ **Install:** `npx {cli} add {component-id}`
180
+ **Available in:** {variant list}
181
+ **Props:** `{ComponentName}Props`
182
+
183
+ | Prop | Type | Default | Description |
184
+ |------|------|---------|-------------|
185
+ | {prop} | {type} | {default} | {JSDoc description or —} |
186
+ ```
187
+
188
+ **context-snippet.md — Component Library format:**
189
+
190
+ ```markdown
191
+ [{name} v{version}]|root: skills/{name}/
192
+ |IMPORTANT: {name} v{version} — read SKILL.md before writing {name} code. Do NOT rely on training data.
193
+ |install: npx {cli} add <component-id>
194
+ |catalog:{SKILL.md#component-catalog} — {N} components: {category(count), ...}
195
+ |variants: {variant list} — {provider wrapping note if applicable}
196
+ |key-props:{SKILL.md#key-types} — {top props interfaces with key fields}
197
+ |gotchas: {detected gotchas}
198
+ ```
199
+
200
+ **metadata.json — Component Library stats:**
201
+
202
+ When `scope.type: "component-library"`, add these fields to `stats`:
203
+
204
+ ```json
205
+ {
206
+ "stats": {
207
+ "components_registered": 0,
208
+ "components_documented": 0,
209
+ "props_interfaces_extracted": 0,
210
+ "components_unique": 0,
211
+ "demo_files_excluded": 0,
212
+ "design_variants": {}
213
+ }
214
+ }
215
+ ```
216
+
217
+ These are in addition to the standard stats fields (exports_documented, etc.).
218
+
111
219
  ### Assembly Rules
112
220
 
113
221
  1. Assemble all Tier 1 sections first — these form the essential standalone body
@@ -130,7 +130,7 @@ Files in scope 101–500
130
130
  → Parse compact text output into extraction inventory
131
131
 
132
132
  Files in scope > 500
133
- → CLI streaming fallback: ast-grep --json=stream + line-by-line Python processing
133
+ → CLI streaming fallback: ast-grep run --json=stream + line-by-line Python processing
134
134
  → Process in directory batches, cap per-batch output
135
135
  → Merge batch results into extraction inventory
136
136
  ```
@@ -171,6 +171,7 @@ When MCP tools are unavailable or the repo exceeds 500 files in scope, use `--js
171
171
  **Head cap selection:** The `| head -N` cap at the end of the pipeline controls how many exports are captured. Select `N` based on scope and tier:
172
172
  - **Default (Quick/Forge, any scope):** `N = 200`
173
173
  - **Forge+/Deep with `scope.type: "full-library"`:** `N = 500`
174
+ - **Forge+/Deep with `scope.type: "component-library"`:** `N = 300` (components have fewer but richer exports; props interfaces are the primary API surface)
174
175
 
175
176
  For full-library skills at higher tiers, the larger cap prevents silently dropping internal module exports that maintainers need. The cap is applied AFTER exclude-pattern filtering, so useful results are not wasted on excluded files.
176
177
 
@@ -181,7 +182,8 @@ For full-library skills at higher tiers, the larger cap prevents silently droppi
181
182
  # Patterns are matched against the full file path as emitted by ast-grep.
182
183
  # Ensure paths are relative to the same root as the patterns (strip ./ prefix if needed).
183
184
  # {HEAD_CAP} = 200 (default) or 500 (Forge+/Deep full-library) — see head cap selection above.
184
- ast-grep -p '{pattern}' -l {language} --json=stream {path} | python3 -c "
185
+ # IMPORTANT: The explicit 'run' subcommand is required for --json=stream to work.
186
+ ast-grep run -p '{pattern}' -l {language} --json=stream {path} | python3 -c "
185
187
  import sys, json, fnmatch, signal
186
188
  signal.signal(signal.SIGPIPE, signal.SIG_DFL)
187
189
 
@@ -244,9 +246,11 @@ constraints:
244
246
 
245
247
  **JavaScript/TypeScript — exported functions:**
246
248
 
249
+ > **Language selection:** Use `language: typescript` for `.ts` files and `language: tsx` for `.tsx` files. Patterns that work with `typescript` may return zero results with `tsx` and vice versa — they use different tree-sitter parsers. For mixed codebases, run each pattern twice (once per language) and merge results. Note: `export function` patterns may fail with `tsx` on ast-grep 0.41.x (see Known Limitations #5) — use source reading as fallback for those.
250
+
247
251
  ```yaml
248
252
  id: js-exported-functions
249
- language: typescript
253
+ language: typescript # Use 'tsx' for .tsx files — see language selection note above
250
254
  rule:
251
255
  pattern: 'export function $NAME($$$PARAMS)'
252
256
  ```
@@ -311,6 +315,67 @@ constraints:
311
315
  regex: '^[A-Z]'
312
316
  ```
313
317
 
318
+ ### Component Library YAML Rule Recipes
319
+
320
+ These patterns are used by `step-03d-component-extraction.md` when `scope.type: "component-library"`. They prioritize Props interfaces and PascalCase component exports.
321
+
322
+ **React/TypeScript — Props interfaces (primary API contracts):**
323
+
324
+ ```yaml
325
+ id: react-props-interfaces
326
+ language: typescript # Use 'tsx' for .tsx files
327
+ rule:
328
+ pattern: 'export interface $NAME { $$$ }'
329
+ constraints:
330
+ NAME:
331
+ regex: '.*Props$'
332
+ ```
333
+
334
+ **React/TypeScript — Component function exports (PascalCase):**
335
+
336
+ > **Language note:** Use `language: tsx` for `.tsx` files. The `export function` pattern may fail with tsx on ast-grep 0.41.x (see Known Limitations #5). Use `export const` patterns as primary and fall back to source reading for `export function` in tsx files.
337
+
338
+ ```yaml
339
+ id: react-component-functions
340
+ language: tsx
341
+ rule:
342
+ pattern: 'export function $NAME($$$PARAMS)'
343
+ constraints:
344
+ NAME:
345
+ regex: '^[A-Z]'
346
+ ```
347
+
348
+ **React/TypeScript — Component arrow function exports:**
349
+
350
+ ```yaml
351
+ id: react-component-arrow-functions
352
+ language: typescript
353
+ rule:
354
+ pattern: 'export const $NAME = ($$$PARAMS) => $BODY'
355
+ constraints:
356
+ NAME:
357
+ regex: '^[A-Z]'
358
+ ```
359
+
360
+ **Vue — defineProps extraction:**
361
+
362
+ ```yaml
363
+ id: vue-define-props
364
+ language: typescript
365
+ rule:
366
+ pattern: 'defineProps<$TYPE>()'
367
+ ```
368
+
369
+ **Props-to-Component linking strategy:**
370
+
371
+ After extracting Props interfaces and component exports, link them using this 3-level fallback chain:
372
+
373
+ 1. **Naming convention (primary):** Strip `Props` suffix from interface name → match to component export (e.g., `NativeLiquidButtonProps` → `NativeLiquidButton`)
374
+ 2. **File co-location (fallback):** If naming doesn't match, check if a Props interface and a PascalCase export function are defined in the same file — link them
375
+ 3. **Generic parameter (deep fallback):** Search for `ComponentProps<typeof $NAME>` or `React.ComponentProps<typeof $NAME>` patterns that reference the component by name
376
+
377
+ Unlinked Props interfaces are included as standalone type exports. Unlinked component exports are included with a note that no Props interface was found (signature-only, T1-low confidence for API contract).
378
+
314
379
  ### Known ast-grep Limitations
315
380
 
316
381
  When using ast-grep for extraction, be aware of these documented limitations:
@@ -326,6 +391,32 @@ When using ast-grep for extraction, be aware of these documented limitations:
326
391
  - Second: if `find_code()` also fails, fall back to source reading for that pattern category (T1-low confidence)
327
392
  - Never silently accept zero results for a pattern category that the source language commonly uses
328
393
 
394
+ 5. **TSX `export function` pattern failure:** The `export function $NAME($$$PARAMS)` pattern may return zero results in TSX files with ast-grep 0.41.x. This affects both MCP tools and CLI. `export const` and `export type` patterns are unaffected. **Workaround:** For TSX files, use `export const` patterns first (which work), then fall back to source reading (grep/file read) for `export function` declarations. When a TSX codebase shows zero `export function` matches but source files clearly contain them, this is a known ast-grep tree-sitter tsx parser limitation — not an extraction error. Log it in the evidence report and proceed with T1-low confidence for those exports.
395
+
396
+ 6. **CLI `--json=stream` may produce no output:** On ast-grep 0.41.x, `--json=stream` may produce empty output for certain patterns. The `--json=stream` flag requires the explicit `run` subcommand: use `ast-grep run -p '{pattern}' --json=stream` (not `ast-grep -p '{pattern}' --json=stream`). If streaming still produces no output, fall back to the MCP tool or source reading.
397
+
398
+ ### Component Library Demo/Example Auto-Exclusion
399
+
400
+ When `scope.type: "component-library"`, auto-detect and propose demo/example exclusions before extraction begins. **User confirmation is required before applying** — some `examples/` directories contain API-level code.
401
+
402
+ **Auto-detect directory patterns:**
403
+ - `**/demo/**`, `**/demos/**`
404
+ - `**/stories/**`, `**/__stories__/**`, `**/storybook/**`
405
+ - `**/examples/**`, `**/example/**`
406
+
407
+ **Auto-detect file patterns:**
408
+ - `**/*.stories.*`, `**/*.story.*`
409
+ - `**/*.example.*`, `**/*.demo.*`
410
+
411
+ If `demo_patterns` is specified in the brief, use those instead of auto-detection.
412
+
413
+ **Procedure:**
414
+ 1. Scan the scoped file tree for matching directories and files
415
+ 2. Count matches per pattern category
416
+ 3. Present to user: "**Auto-detected {N} demo/example files** in {M} directories matching these patterns: {list}. Confirm exclusion? [Y/n] Or adjust patterns:"
417
+ 4. Apply confirmed patterns to the exclude list before AST extraction
418
+ 5. Record in extraction inventory: `demo_files_excluded: {count}`
419
+
329
420
  ### Re-Export Tracing and Script/Asset Extraction
330
421
 
331
422
  See `extraction-patterns-tracing.md` for:
@@ -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. If a pattern contains no glob characters (`*`, `?`, `[`) and does not end with `/`, append `/**` to match directory contents recursively (e.g., `cognee` → `cognee/**`; `cognee/**` → kept as-is).
72
- 2. Convert `exclude_patterns` to negation patterns by prepending `!`. Apply the same anchoring rule (e.g., `cognee/tests` → `!cognee/tests/**`; `cognee/tests/**` → `!cognee/tests/**`; `**/test_*` → `!**/test_*`).
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: Brief exclude_patterns:
83
- cognee/** cognee/tests/**
84
- cognee/alembic/**
85
- **/test_*
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`, delete the `{temp_path}` directory. Log: "Ephemeral source clone cleaned up." 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: attempt to delete `{temp_path}` before halting. Log the cleanup attempt regardless of success.
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
 
@@ -79,7 +79,7 @@ Read `ccc_index` from forge-tier.yaml:
79
79
  - If `ccc_index.status` is `"stale"`: display brief note — "CCC index is stale — discovery results may miss recent changes." Continue to section 3.
80
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.
81
81
 
82
- **Tool resolution for ccc_bridge.ensure_index:** Use `/ccc` skill indexing (Claude Code), ccc MCP server (Cursor), or `ccc init` + `ccc index {source_root}` (CLI). See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
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).
83
83
 
84
84
  ### 3. Construct Semantic Query
85
85
 
@@ -97,7 +97,7 @@ Where:
97
97
 
98
98
  Run `ccc_bridge.search(query, source_root, top_k=20)`:
99
99
 
100
- **Tool resolution for ccc_bridge.search:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `ccc search "{query}" --path {source_root} --top 20` (CLI). See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
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).
101
101
 
102
102
  **If search succeeds:**
103
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,32 +70,9 @@ 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
 
72
- ### 3. Check for Docs-Only Mode
73
-
74
- **If `source_type: "docs-only"` in the brief data:**
75
-
76
- "**Docs-only mode:** No source code to extract. Documentation content will be fetched from `doc_urls` in step-03c."
77
-
78
- Build an empty extraction inventory with zero exports. Set `extraction_mode: "docs-only"` in context. Auto-proceed through Gate 2 (section 6) — display the empty inventory and note that T3 content will be produced by the doc-fetcher step.
79
-
80
- **If `source_type: "source"` (default):** Continue with extraction below.
81
-
82
- ### 4. Execute Tier-Dependent Extraction
83
-
84
- Load `{sourceResolutionData}` completely. Follow the **Remote Source Resolution** protocol for Forge/Deep tiers (ephemeral clone, sparse-checkout, cleanup) and the **Version Reconciliation** protocol for all tiers. Apply the protocols before proceeding with the tier-specific extraction strategy below.
85
-
86
- **Quick Tier (No AST tools):**
73
+ ### 2a. Resolve Source Access
87
74
 
88
- 1. Use `gh_bridge.list_tree(owner, repo, branch)` to map source structure (if remote)
89
- 2. Identify entry points: index files, main exports, public modules
90
- 3. Use `gh_bridge.read_file(owner, repo, path)` to read each entry point
91
- 4. Extract from source text: exported function names, parameter lists, return types
92
- 5. Infer types from JSDoc, docstrings, type annotations
93
- 6. Confidence: All results T1-low — `[SRC:{file}:L{line}]`
94
-
95
- **Tool resolution for gh_bridge:** Use `gh api repos/{owner}/{repo}/git/trees/{branch}?recursive=1` for list_tree, `gh api repos/{owner}/{repo}/contents/{path}` for read_file. If source is local, use direct file listing/reading instead. See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
96
-
97
- **Forge/Forge+/Deep Tier (AST available):**
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).
98
76
 
99
77
  **Deferred CCC Discovery (Forge+ and Deep — remote sources only):**
100
78
 
@@ -106,12 +84,12 @@ If ALL of these conditions are true:
106
84
 
107
85
  Then run CCC indexing and discovery on the ephemeral clone:
108
86
 
109
- 1. **Index the clone:** Run `ccc init {temp_path}` then `ccc index` with an extended timeout or in background mode — 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.
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.
110
88
 
111
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.
112
90
 
113
91
  3. **Execute search:** Run `ccc_bridge.search(query, temp_path, top_k=20)`:
114
- - **Tool resolution:** Use `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `ccc search "{query}" --path {temp_path} --top 20` (CLI). See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
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).
115
93
 
116
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.**"
117
95
 
@@ -126,7 +104,44 @@ If `{ccc_discovery}` is in context and non-empty (populated by step-02b or defer
126
104
 
127
105
  If `{ccc_discovery}` is empty or not in context: proceed with existing file ordering (no change to current behavior).
128
106
 
129
- ⚠️ **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.
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
+
117
+ ### 3. Check for Docs-Only Mode
118
+
119
+ **If `source_type: "docs-only"` in the brief data:**
120
+
121
+ "**Docs-only mode:** No source code to extract. Documentation content will be fetched from `doc_urls` in step-03c."
122
+
123
+ Build an empty extraction inventory with zero exports. Set `extraction_mode: "docs-only"` in context. Auto-proceed through Gate 2 (section 6) — display the empty inventory and note that T3 content will be produced by the doc-fetcher step.
124
+
125
+ **If `source_type: "source"` (default):** Continue with extraction below.
126
+
127
+ ### 4. Execute Tier-Dependent Extraction
128
+
129
+ Source resolution, version reconciliation, and CCC discovery were completed in section 2a. Proceed with the tier-specific extraction strategy below.
130
+
131
+ **Quick Tier (No AST tools):**
132
+
133
+ 1. Use `gh_bridge.list_tree(owner, repo, branch)` to map source structure (if remote)
134
+ 2. Identify entry points: index files, main exports, public modules
135
+ 3. Use `gh_bridge.read_file(owner, repo, path)` to read each entry point
136
+ 4. Extract from source text: exported function names, parameter lists, return types
137
+ 5. Infer types from JSDoc, docstrings, type annotations
138
+ 6. Confidence: All results T1-low — `[SRC:{file}:L{line}]`
139
+
140
+ **Tool resolution for gh_bridge:** Use `gh api repos/{owner}/{repo}/git/trees/{branch}?recursive=1` for list_tree, `gh api repos/{owner}/{repo}/contents/{path}` for read_file. If source is local, use direct file listing/reading instead. See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
141
+
142
+ **Forge/Forge+/Deep Tier (AST available):**
143
+
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`.
130
145
 
131
146
  1. Detect language from brief or file extensions
132
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. `qmd --version` → parse version string from output (e.g., `qmd 2.0.1` → `"2.0.1"`)
126
+ 2. `mcp__plugin_qmd-plugin_qmd__status` → parse version if exposed in status output
127
+ 3. `"unknown"` (final fallback — add a warning to the evidence report)
128
+ Note: QMD is a Bun/Node package (`@tobilu/qmd`). Install via `bun install -g @tobilu/qmd`.
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
 
@@ -54,6 +54,18 @@ When `docs_only_mode: true` is set by step-03 (indicating a Quick tier skill whe
54
54
 
55
55
  This is functionally identical to Quick tier weight redistribution but with a different coverage denominator (self-consistency instead of source comparison).
56
56
 
57
+ ### State 2 Source Access (Any Tier, Provenance-Map Only)
58
+
59
+ When source is not locally available and analysis resolves to State 2 (provenance-map baseline per source-access-protocol.md):
60
+
61
+ - **Signature Accuracy:** N/A — provenance-map stores parameters as flat string arrays; verification is string comparison only, not semantic AST verification. Type aliases (`str` vs `String`, `list` vs `List[Any]`) cannot be resolved without live source.
62
+ - **Type Coverage:** N/A — cannot verify type completeness without local source access for AST re-parsing.
63
+ - **Weight redistribution:** Same as Quick tier — Signature Accuracy (22%) and Type Coverage (14%) weights redistributed proportionally to remaining active categories (Export Coverage, Coherence, External Validation).
64
+ - **Applies regardless of detected tier** (including Forge, Forge+, Deep) whenever `analysis_confidence` is `provenance-map` and local source is unavailable.
65
+ - **Export Coverage denominator:** Uses the union of provenance-map entry names and metadata.json `exports[]` names (per source-access-protocol.md State 2 rules).
66
+
67
+ Note: When provenance-map entries are predominantly T1 (AST-verified at compilation time), the coverage and name-matching data is already at highest confidence. The N/A categories reflect the inability to re-verify at test time, not low-quality extraction data.
68
+
57
69
  ### Forge Tier (ast-grep)
58
70
  - Export Coverage: AST-backed export comparison
59
71
  - Signature Accuracy: AST-verified signature matching
@@ -60,13 +60,20 @@ Read {outputFile} frontmatter to get the skill directory path (`skillDir`).
60
60
 
61
61
  Before running external validators, check if `{forge_data_folder}/{skill_name}/evidence-report.md` contains validation results (a `## Validation Results` section with quality scores).
62
62
 
63
- **Staleness check:** Determine whether SKILL.md has changed since the evidence report was generated.
63
+ **Staleness check:** Determine whether SKILL.md has changed since the evidence report was generated. Walk through these checks in order:
64
64
 
65
- **Primary (git-tracked):** Run `git log -1 --format=%cI -- {skillDir}/SKILL.md` to get the last commit date of SKILL.md. Compare against the evidence report's generation date (from its frontmatter or the `## Validation Results` timestamp). If SKILL.md's last commit is newer, results are stale.
65
+ **Pre-check (untracked or staged-only file):** Run `git ls-files --error-unmatch {skillDir}/SKILL.md 2>/dev/null`.
66
+ - If the command fails (exit code non-zero) or git is not available, the file is either **untracked** (new, never committed) or we're in a **non-git environment**:
67
+ - Check if `{skillDir}/metadata.json` exists and has a `generation_date` field
68
+ - Compare `metadata.json` `generation_date` against the evidence report's generation date (from its frontmatter or `## Validation Results` timestamp)
69
+ - If timestamps match within the same minute (same workflow session): auto-reuse is safe — the evidence report was generated from the same SKILL.md content
70
+ - If timestamps differ or metadata.json is missing: treat as stale, proceed to section 2 for a fresh run
71
+ - Note: "Staleness check: SKILL.md is untracked/non-git — using metadata.json timestamp comparison."
72
+ - If the command succeeds (file is tracked by git), continue to Primary check below.
66
73
 
67
- **Secondary (uncommitted changes):** Run `git diff --name-only -- {skillDir}/SKILL.md`. If output is non-empty, SKILL.md has uncommitted changes treat results as stale regardless of commit dates.
74
+ **Primary (git-tracked):** Run `git log -1 --format=%cI -- {skillDir}/SKILL.md` to get the last commit date of SKILL.md. Compare against the evidence report's generation date (from its frontmatter or the `## Validation Results` timestamp). If SKILL.md's last commit is newer, results are stale.
68
75
 
69
- **Fallback (non-git environments):** If git commands fail (not a git repository), fall back to `metadata.json` `generation_date` comparison with a warning: "Staleness check using metadata.json may be identical to evidence report timestamp. Consider a git-tracked directory for reliable staleness detection."
76
+ **Secondary (uncommitted changes):** Run `git diff --name-only -- {skillDir}/SKILL.md`. If output is non-empty, SKILL.md has uncommitted changes treat results as stale regardless of commit dates. Also check `git diff --cached --name-only -- {skillDir}/SKILL.md` for staged-but-uncommitted changes — if non-empty, SKILL.md has been staged since last commit, treat results as stale.
70
77
 
71
78
  If SKILL.md was modified after the evidence report was generated (e.g., after update-skill), the cached results are stale — skip auto-reuse and proceed to section 2 for a fresh run.
72
79
 
@@ -76,9 +76,25 @@ Perform tier-aware extraction on only the changed files identified in step 02, p
76
76
 
77
77
  **Remote Source Resolution (Forge/Deep only):**
78
78
 
79
- **MCP source access check (before ephemeral clone):** If MCP source-reading tools are available (zread, deepwiki, gh API, or similar) and `source_repo` is set in metadata.json, use MCP tools to fetch only the changed files from the change manifest. This avoids ephemeral clone overhead entirely. MCP provides full source file content equivalent to a local read. If the fetched content is written to a temp file and analyzed with ast-grep, label confidence as T1. If AST is unavailable (the common case for MCP-fetched content), use pattern-based extraction and label confidence as T1-low.
79
+ **MCP source access (ordered fallback):** When `source_repo` is set in metadata.json, try each MCP tool in order to fetch only the changed files from the change manifest. This avoids ephemeral clone overhead entirely. Tools are ordered by data freshness gh API returns live GitHub content and is preferred for update-skill where current file versions are required. zread and deepwiki depend on manual indexing and may return stale data if indexes haven't been refreshed since the changes being extracted.
80
80
 
81
- **If MCP unavailable:** Load and follow `{remoteSourceResolutionData}` for ephemeral clone setup, version reconciliation, and AST tool unavailability handling.
81
+ 1. **gh API** `gh api repos/{owner}/{repo}/contents/{path}` for raw file content
82
+ - If accessible: fetch file content (base64-decoded), always current
83
+ - If rate-limited, 404, or inaccessible: log tool and reason, continue to next tool
84
+ 2. **zread** — `get_repo_structure` + `read_file` for targeted file access
85
+ - If repo found: fetch changed files, proceed with extraction
86
+ - If "repo not found" or error: log tool and reason, continue to next tool
87
+ - Caveat: indexed data — may be stale if index wasn't refreshed after the target changes
88
+ 3. **deepwiki** — `ask_question` for targeted export/signature queries
89
+ - If repo indexed and returns usable source data: extract from response
90
+ - If no results or repo not indexed: log tool and reason, continue to next tool
91
+ - Caveat: returns synthesized content, not raw source — extraction quality varies; index may be stale
92
+
93
+ **Confidence labeling:** MCP-fetched content written to a temp file and analyzed with ast-grep → T1. MCP-fetched content analyzed with pattern matching (AST unavailable) → T1-low.
94
+
95
+ **If all MCP tools fail for this repo:** Fall back to ephemeral clone — load and follow `{remoteSourceResolutionData}` for clone setup, version reconciliation, and AST tool unavailability handling.
96
+
97
+ **If all approaches fail (MCP + ephemeral clone):** Degrade to provenance-map-only analysis (State 2, T1 confidence from compilation-time data). Warn user: "Source access failed for {source_repo}. Analysis limited to provenance-map baseline."
82
98
 
83
99
  **Quick tier (text pattern matching):**
84
100
  - Extract function/class/type names via regex patterns