bmad-module-skill-forge 0.7.1 → 0.7.2

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 (26) hide show
  1. package/package.json +1 -1
  2. package/src/knowledge/qmd-registry.md +5 -0
  3. package/src/workflows/brief-skill/data/skill-brief-schema.md +15 -0
  4. package/src/workflows/brief-skill/steps-c/step-01-gather-intent.md +16 -0
  5. package/src/workflows/brief-skill/steps-c/step-02-analyze-target.md +34 -6
  6. package/src/workflows/brief-skill/steps-c/step-03-scope-definition.md +14 -0
  7. package/src/workflows/brief-skill/steps-c/step-04-confirm-brief.md +20 -0
  8. package/src/workflows/brief-skill/steps-c/step-05-write-brief.md +34 -9
  9. package/src/workflows/brief-skill/workflow.md +1 -1
  10. package/src/workflows/create-skill/data/extraction-patterns.md +9 -2
  11. package/src/workflows/create-skill/data/source-resolution-protocols.md +15 -1
  12. package/src/workflows/create-skill/steps-c/step-01-load-brief.md +4 -4
  13. package/src/workflows/create-skill/steps-c/step-02b-ccc-discover.md +1 -1
  14. package/src/workflows/create-skill/steps-c/step-03-extract.md +1 -1
  15. package/src/workflows/create-skill/steps-c/step-03b-fetch-temporal.md +16 -1
  16. package/src/workflows/create-skill/steps-c/step-03c-fetch-docs.md +9 -3
  17. package/src/workflows/create-skill/steps-c/step-05-compile.md +6 -1
  18. package/src/workflows/create-skill/steps-c/step-07-generate-artifacts.md +1 -1
  19. package/src/workflows/quick-skill/steps-c/step-04-compile.md +1 -1
  20. package/src/workflows/test-skill/data/output-section-formats.md +15 -0
  21. package/src/workflows/test-skill/data/scoring-rules.md +12 -0
  22. package/src/workflows/test-skill/steps-c/step-03-coverage-check.md +39 -5
  23. package/src/workflows/test-skill/steps-c/step-04-coherence-check.md +48 -4
  24. package/src/workflows/test-skill/steps-c/step-04b-external-validators.md +9 -1
  25. package/src/workflows/test-skill/steps-c/step-05-score.md +2 -0
  26. package/tools/cli/lib/installer.js +4 -0
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.1",
4
+ "version": "0.7.2",
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",
@@ -40,8 +40,13 @@ qmd_collections:
40
40
  source_workflow: "brief-skill"
41
41
  skill_name: "my-lib"
42
42
  created_at: "2026-03-14"
43
+ # status: "pending" # Optional — see below
43
44
  ```
44
45
 
46
+ **Optional field: `status`**
47
+
48
+ The `status` field is only present when QMD embed verification fails during collection creation. When `status: "pending"` is set, the collection exists in QMD but vector embeddings may be incomplete — only BM25 keyword `search` is reliable. `vector_search` and `deep_search` may return no results until re-embedded. Collections without a `status` field are fully operational. The setup-forge janitor should flag `"pending"` collections for re-embedding.
49
+
45
50
  ### Collection Types
46
51
 
47
52
  | Type | Producer | Content | Primary Consumers |
@@ -106,6 +106,21 @@ Scope: {scope.type}
106
106
  Exclude: {scope.exclude patterns, one per line}
107
107
  Notes: {scope.notes}
108
108
 
109
+ {If source_type is "docs-only":}
110
+ Source Type: docs-only
111
+ Doc URLs:
112
+ {doc_urls, one per line with labels}
113
+
114
+ {If source_type is "source" AND supplemental doc_urls collected:}
115
+ Supplemental Docs:
116
+ {doc_urls, one per line with labels}
117
+
118
+ {If scripts_intent or assets_intent was explicitly set (not default "detect"):}
119
+ Scripts: {scripts_intent}
120
+ Assets: {assets_intent}
121
+
122
+ Source Authority: {source_authority}
123
+
109
124
  Version: {version}
110
125
  Created: {created}
111
126
  Created by: {created_by}
@@ -82,6 +82,9 @@ We'll work through this together:
82
82
  3. **Then:** Define scope boundaries
83
83
  4. **Finally:** Confirm and write the brief
84
84
 
85
+ {If tier override was applied:}
86
+ **Your forge tier:** {override tier} (overridden from {original tier}) — this determines what compilation capabilities are available.
87
+ {Else:}
85
88
  **Your forge tier:** {detected tier} — this determines what compilation capabilities are available.
86
89
 
87
90
  Let's get started."
@@ -110,6 +113,14 @@ Wait for user response.
110
113
  - Optionally ask: "Are there any documentation URLs you'd like to include for supplemental context? (These will be fetched as T3 external references.)"
111
114
  - If yes: collect doc URLs into `doc_urls`
112
115
 
116
+ **Source authority (for all source-type skills):**
117
+ "**Are you the maintainer of this library, or creating a community skill?**"
118
+ - If maintainer: set `source_authority: "official"`
119
+ - If community user: set `source_authority: "community"` (default)
120
+ - If internal/proprietary: set `source_authority: "internal"`
121
+
122
+ Default to `"community"` if user does not specify or skips. For `docs-only` skills, `source_authority` is always forced to `"community"`.
123
+
113
124
  Confirm the target.
114
125
 
115
126
  ### 4. Gather User Intent
@@ -154,6 +165,10 @@ Wait for confirmation or alternative.
154
165
  - **Intent:** {user's intent summary}
155
166
  - **Scope hints:** {any hints, or "None — we'll define scope after analysis"}
156
167
  - **Skill name:** {confirmed name}
168
+ - **Source type:** {source or docs-only}
169
+ - **Source authority:** {official/community/internal}
170
+ {If doc_urls collected:}
171
+ - **Doc URLs:** {count} supplemental documentation URLs
157
172
  - **Forge tier:** {tier}
158
173
 
159
174
  Ready to analyze the target repository?"
@@ -187,6 +202,7 @@ ONLY WHEN C is selected and target repository is confirmed will you load and rea
187
202
  - User intent captured with enough context for scoping
188
203
  - Skill name derived and confirmed
189
204
  - Scope hints noted (if provided)
205
+ - Source type and source authority captured
190
206
  - User ready to proceed to analysis
191
207
 
192
208
  ### ❌ SYSTEM FAILURE:
@@ -135,7 +135,12 @@ Based on detected language, identify public API surface:
135
135
 
136
136
  If `tools.ccc` is true in forge-tier.yaml, supplement the module listing with a semantic discovery pass:
137
137
 
138
- Run `ccc_bridge.search("{repo_name} public API exports modules", source_path, top_k=10)` — **Tool resolution:** `/ccc` skill search (Claude Code), ccc MCP server (Cursor), or `ccc search` CLI. See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md).
138
+ **CCC Semantic Discovery:**
139
+ - **Claude Code:** Use `/ccc search "{repo_name} public API exports modules" {source_path}`
140
+ - **Cursor:** Use `ccc` MCP server `search` tool with query `"{repo_name} public API exports modules"` and path `{source_path}`
141
+ - **CLI fallback:** `ccc search "{repo_name} public API exports modules" --path {source_path} --limit 10`
142
+
143
+ See [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md) for full bridge-to-tool mapping.
139
144
 
140
145
  If results are returned, display:
141
146
 
@@ -146,6 +151,22 @@ This supplements — never replaces — the explicit module list above. CCC may
146
151
 
147
152
  If CCC is unavailable or returns no results: skip this subsection silently.
148
153
 
154
+ ### 4b. Detect Source Version
155
+
156
+ Attempt to auto-detect the source version using the rules from the skill-brief-schema.md Version Detection section:
157
+
158
+ **For Python:** Check `pyproject.toml` `[project] version` (static) → if `dynamic = ["version"]`, check `__init__.py` for `__version__` → `_version.py` if exists → `setup.py` `version=` → `git describe --tags --abbrev=0`
159
+ **For JavaScript/TypeScript:** Check `package.json` `"version"` field
160
+ **For Rust:** Check `Cargo.toml` `[package] version` (static) → if `version = { workspace = true }`, resolve from workspace root `Cargo.toml` → `git describe --tags --abbrev=0`
161
+ **For Go:** Check `go.mod` or `git describe --tags --abbrev=0`
162
+
163
+ **For GitHub repos:** Use `gh api repos/{owner}/{repo}/contents/{file}` to read version files (decode base64 content).
164
+ **For local repos:** Read the file directly.
165
+
166
+ Display: "**Detected version:** {version or 'Not detected — will default to 1.0.0'}"
167
+
168
+ If detection fails or returns a non-semver value: note that version will default to `"1.0.0"` and the user can override in step 04.
169
+
149
170
  ### 5. Report Analysis Summary
150
171
 
151
172
  Present the complete analysis:
@@ -169,6 +190,7 @@ Present the complete analysis:
169
190
  - Tests: {found/not found — location}
170
191
  - Docs: {found/not found — location}
171
192
  - Config: {list of config files found}
193
+ - Version: {detected version or "Not detected — defaulting to 1.0.0"}
172
194
 
173
195
  ---
174
196
 
@@ -179,16 +201,21 @@ Moving to scope definition where you'll choose what to include and exclude."
179
201
 
180
202
  ### 6. Auto-Proceed to Scope Definition
181
203
 
182
- Display: "**Proceeding to scope definition...**"
204
+ Display: "**Proceeding to scope definition...**
205
+
206
+ Review the analysis above. If anything looks wrong, let me know now — otherwise I'll proceed to scope definition."
207
+
208
+ Pause briefly for user input. If the user provides corrections or asks questions, address them and re-present any updated analysis findings. Then proceed.
183
209
 
184
210
  #### Menu Handling Logic:
185
211
 
186
- - After analysis report is presented to user, immediately load, read entire file, then execute {nextStepFile}
212
+ - After analysis report is presented to user and any corrections addressed, load, read entire file, then execute {nextStepFile}
187
213
 
188
214
  #### EXECUTION RULES:
189
215
 
190
- - This is an auto-proceed step — analysis is factual reporting with no user choices
191
- - Proceed directly to step 03 after presenting the analysis summary
216
+ - This is a soft auto-proceed step — present the pause prompt, wait briefly for user input
217
+ - If user provides corrections: address them, then proceed
218
+ - If no user input after a brief pause: proceed directly to step 03
192
219
 
193
220
  ## CRITICAL STEP COMPLETION NOTE
194
221
 
@@ -204,8 +231,9 @@ ONLY WHEN the analysis is complete and the summary has been presented to the use
204
231
  - Repository structure listed clearly
205
232
  - Primary language detected with confidence level
206
233
  - Top-level modules and exports identified
234
+ - Source version detected (or default noted)
207
235
  - Analysis summary presented factually
208
- - Auto-proceeded to scope definition
236
+ - Auto-proceeded to scope definition (with pause for corrections)
209
237
 
210
238
  ### ❌ SYSTEM FAILURE:
211
239
 
@@ -90,6 +90,20 @@ Wait for confirmation. Then skip to section 5 (Summarize Scope Decisions) with:
90
90
 
91
91
  **If `source_type: "source"` (default):** Continue to scope templates below.
92
92
 
93
+ ### 2c. Confirm Supplemental Documentation (if doc_urls collected)
94
+
95
+ **If `source_type: "source"` AND supplemental `doc_urls` were collected in step 01:**
96
+
97
+ "**Supplemental documentation URLs:**
98
+ {numbered list of collected doc_urls with labels}
99
+
100
+ These will be included as T3 external references in the skill brief.
101
+ Add, remove, or confirm these URLs."
102
+
103
+ Wait for confirmation. Record any changes to `doc_urls`.
104
+
105
+ **If no supplemental doc_urls were collected:** Skip this subsection.
106
+
93
107
  ### 2b. Offer Scope Templates
94
108
 
95
109
  Load `{scopeTemplatesFile}` for the scope type options ([F], [M], [P]) and their descriptions.
@@ -78,6 +78,11 @@ Compile all gathered data from steps 01-03 into the complete brief:
78
78
  - **scope.include:** {include patterns from step 03}
79
79
  - **scope.exclude:** {exclude patterns from step 03}
80
80
  - **scope.notes:** {any scope notes from step 03}
81
+ - **source_type:** {source or docs-only, from step 01}
82
+ - **doc_urls:** {collected documentation URLs with labels, from steps 01/03 — include if source_type is "docs-only" or supplemental URLs were collected}
83
+ - **scripts_intent:** {detect/none/description from step 03, or "detect" if not explicitly set}
84
+ - **assets_intent:** {detect/none/description from step 03, or "detect" if not explicitly set}
85
+ - **source_authority:** {official/community/internal from step 01 — default "community"}
81
86
 
82
87
  ### 3. Present Brief for Review
83
88
 
@@ -101,6 +106,21 @@ Scope: {scope.type}
101
106
  Exclude: {scope.exclude patterns, one per line}
102
107
  Notes: {scope.notes}
103
108
 
109
+ {If source_type is "docs-only":}
110
+ Source Type: docs-only
111
+ Doc URLs:
112
+ {doc_urls, one per line with labels}
113
+
114
+ {If source_type is "source" AND supplemental doc_urls collected:}
115
+ Supplemental Docs:
116
+ {doc_urls, one per line with labels}
117
+
118
+ {If scripts_intent or assets_intent was explicitly set (not default "detect"):}
119
+ Scripts: {scripts_intent}
120
+ Assets: {assets_intent}
121
+
122
+ Source Authority: {source_authority}
123
+
104
124
  Version: {version}
105
125
  Created: {created}
106
126
  Created by: {created_by}
@@ -87,19 +87,36 @@ scope:
87
87
  exclude:
88
88
  - "{approved exclude patterns}"
89
89
  notes: "{approved scope notes or empty string}"
90
- # Include doc_urls when source_type is "docs-only" or when supplemental doc URLs were provided
91
- # doc_urls:
92
- # - url: "{documentation URL}"
93
- # label: "{page label}"
94
- # scripts_intent: detect # Optional: detect | none | description of expected scripts
95
- # assets_intent: detect # Optional: detect | none | description of expected assets
96
- # source_authority: community # Optional: official | community | internal
97
90
  ---
98
91
  ```
99
92
 
100
- **If `source_type: "docs-only"`:** Include `doc_urls` array (required). `source_repo` may be set to the doc site URL for reference or omitted. `source_authority` must be `community`.
93
+ **Conditional optional field inclusion:**
101
94
 
102
- **If `doc_urls` were collected during scope definition (supplemental mode):** Include the `doc_urls` array even when `source_type: "source"`.
95
+ **If `source_type: "docs-only"` OR supplemental `doc_urls` were collected:**
96
+ Include the `doc_urls` array (uncommented) in the generated YAML:
97
+ ```yaml
98
+ doc_urls:
99
+ - url: "{documentation URL}"
100
+ label: "{page label}"
101
+ ```
102
+ When `source_type: "docs-only"`: `doc_urls` is required (at least one entry), `source_repo` may be set to the doc site URL for reference or omitted.
103
+
104
+ **If `scripts_intent` was collected and is not the default `"detect"`:**
105
+ Include the `scripts_intent` field (uncommented):
106
+ ```yaml
107
+ scripts_intent: "{none or description}"
108
+ ```
109
+
110
+ **If `assets_intent` was collected and is not the default `"detect"`:**
111
+ Include the `assets_intent` field (uncommented):
112
+ ```yaml
113
+ assets_intent: "{none or description}"
114
+ ```
115
+
116
+ **Always include** `source_authority` (default: `"community"`, forced to `"community"` when `source_type: "docs-only"`):
117
+ ```yaml
118
+ source_authority: "{official|community|internal}"
119
+ ```
103
120
 
104
121
  ### 4. Write the File
105
122
 
@@ -128,6 +145,13 @@ qmd collection add {forge_data_folder}/{skill-name} --name {skill-name}-brief --
128
145
  qmd embed
129
146
  ```
130
147
 
148
+ **Embed verification:**
149
+
150
+ After `qmd embed` completes, verify the collection was embedded:
151
+ - Run `qmd status` or `qmd collection list` and confirm `{skill-name}-brief` shows document count > 0
152
+ - If verification succeeds: proceed to registry update normally
153
+ - If verification fails: log warning "QMD embed verification failed for {skill-name}-brief — collection may not be searchable yet", still proceed to registry update but add `status: "pending"` field to the registry entry
154
+
131
155
  **Registry update:**
132
156
 
133
157
  Read `{sidecar_path}/forge-tier.yaml` and update the `qmd_collections` array.
@@ -140,6 +164,7 @@ If an entry with `name: "{skill-name}-brief"` already exists, replace it. Otherw
140
164
  source_workflow: "brief-skill"
141
165
  skill_name: "{skill-name}"
142
166
  created_at: "{current ISO date}"
167
+ # status: "pending" # Added only when embed verification fails
143
168
  ```
144
169
 
145
170
  Write the updated forge-tier.yaml.
@@ -51,7 +51,7 @@ installed_path: '{project-root}/_bmad/skf/workflows/skillforge/brief-skill'
51
51
 
52
52
  Load and read full config from {project-root}/_bmad/skf/config.yaml and resolve:
53
53
 
54
- - `project_name`, `output_folder`, `user_name`, `communication_language`, `document_output_language`, `forge_data_folder`, `sidecar_path`
54
+ - `project_name`, `output_folder`, `user_name`, `communication_language`, `forge_data_folder`, `sidecar_path`
55
55
 
56
56
  ### 2. First Step Execution
57
57
 
@@ -168,12 +168,19 @@ find_code_by_rule(
168
168
 
169
169
  When MCP tools are unavailable or the repo exceeds 500 files in scope, use `--json=stream` (NEVER `--json` or `--json=pretty`) with line-by-line Python processing:
170
170
 
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
+ - **Default (Quick/Forge, any scope):** `N = 200`
173
+ - **Forge+/Deep with `scope.type: "full-library"`:** `N = 500`
174
+
175
+ 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
+
171
177
  ```bash
172
178
  # Note: use $$$ for variadic params in ast-grep patterns (e.g., 'def $NAME($$$PARAMS)')
173
179
  # {exclude_patterns} = Python list from brief's scope.exclude, e.g. ['tests/**', '**/test_*']
174
180
  # If scope.exclude is absent or empty in the brief, inject [] as the default.
175
181
  # Patterns are matched against the full file path as emitted by ast-grep.
176
182
  # Ensure paths are relative to the same root as the patterns (strip ./ prefix if needed).
183
+ # {HEAD_CAP} = 200 (default) or 500 (Forge+/Deep full-library) — see head cap selection above.
177
184
  ast-grep -p '{pattern}' -l {language} --json=stream {path} | python3 -c "
178
185
  import sys, json, fnmatch, signal
179
186
  signal.signal(signal.SIGPIPE, signal.SIG_DFL)
@@ -193,7 +200,7 @@ for line in sys.stdin:
193
200
  sig = m.get('text','').split(chr(10))[0].strip()
194
201
  print(f'[AST:{f}:L{ln}] {sig}')
195
202
  except: pass
196
- " | head -200
203
+ " | head -{HEAD_CAP}
197
204
  ```
198
205
 
199
206
  **Critical constraints:**
@@ -201,7 +208,7 @@ for line in sys.stdin:
201
208
  - ALWAYS use `--json=stream` — never `--json` (loads entire array into memory)
202
209
  - ALWAYS process line-by-line (`for line in sys.stdin`) — never `json.load(sys.stdin)`
203
210
  - ALWAYS cap output with `| head -N` as a safety valve
204
- - For repos > 500 files, process in directory batches of 20-50 files each
211
+ - For repos > 500 files, process in directory batches of 20-50 files each: split by top-level source directory, run the CLI streaming template per batch with the same head cap, then merge results and deduplicate by export name (keep the first occurrence if duplicates exist across batches)
205
212
 
206
213
  ### YAML Rule Recipes by Language
207
214
 
@@ -102,7 +102,7 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
102
102
 
103
103
  After checkout, apply the original glob `include_patterns` as file-level filters when building the extraction file list — sparse-checkout gets the right directories, glob filtering narrows to the exact files. When `--no-cone` mode was used, most exclude filtering is already done at the git level, but apply `exclude_patterns` as a final pass to catch any edge cases where gitignore pattern matching diverges from the brief's glob semantics. Always-included root files (see above) are exempt from post-checkout filtering.
104
104
 
105
- 3. **If clone succeeds:** Update the working source path to `{temp_path}` for all subsequent AST operations in this step. Proceed with the **Forge/Deep Tier** extraction strategy below. Mark `ephemeral_clone_active = true` for cleanup.
105
+ 3. **If clone succeeds:** Update the working source path to `{temp_path}` for all subsequent AST operations in this step. Capture the source commit: `git -C {temp_path} rev-parse HEAD` — store as `source_commit` in context. Proceed with the **Forge/Deep Tier** extraction strategy below. Mark `ephemeral_clone_active = true` for cleanup.
106
106
 
107
107
  4. **If clone fails (network error, auth failure, timeout):**
108
108
 
@@ -116,6 +116,20 @@ If `source_repo` is a remote URL (GitHub URL or owner/repo format) AND tier is F
116
116
 
117
117
  ---
118
118
 
119
+ ## Source Commit Capture (all tiers, source mode only)
120
+
121
+ **If `source_type: "docs-only"`:** skip — set `source_commit: null`.
122
+
123
+ After the source path is accessible, capture the current commit hash for provenance tracking:
124
+
125
+ - **Local path:** `git -C {source_root} rev-parse HEAD` — if the path is a git repo
126
+ - **Ephemeral clone (Forge/Deep):** already captured during clone (step 3 above)
127
+ - **Quick tier (remote, no clone):** `gh api repos/{owner}/{repo}/commits/{branch} --jq '.sha'`
128
+
129
+ Store the result as `source_commit` in context. If capture fails (not a git repo, API unavailable), set `source_commit: null` — this is not an error.
130
+
131
+ ---
132
+
119
133
  ## Version Reconciliation (all tiers, source mode only)
120
134
 
121
135
  **If `source_type: "docs-only"`:** skip this section — no source files exist to reconcile.
@@ -111,16 +111,16 @@ Halt with specific error: "Brief validation failed: missing required field `{fie
111
111
 
112
112
  ### 4. Resolve Source Code Location
113
113
 
114
- **If `source_type: "docs-only"`:** Skip source resolution. Set `source_location: null` in context. Proceed directly to section 5 (Report Initialization) — docs-only skills have no source to resolve.
114
+ **If `source_type: "docs-only"`:** Skip source resolution. Set `source_root: null` in context. Proceed directly to section 5 (Report Initialization) — docs-only skills have no source to resolve.
115
115
 
116
116
  **If source_repo is a GitHub URL or owner/repo format:**
117
117
  - Verify repository exists via `gh_bridge.list_tree(owner, repo, branch)` — **Tool resolution:** `gh api repos/{owner}/{repo}/git/trees/{branch}?recursive=1` or direct file listing if local; see [knowledge/tool-resolution.md](../../../knowledge/tool-resolution.md)
118
118
  - If branch not specified, detect default branch
119
- - Store resolved: owner, repo, branch, file tree
119
+ - Store resolved: owner, repo, branch, file tree — note: `source_root` for remote repos is set to the ephemeral clone path during extraction (step-03)
120
120
 
121
121
  **If source_repo is a local path:**
122
122
  - Verify path exists and contains source files
123
- - Store resolved: local path, file listing
123
+ - Store resolved: local path as `source_root`, file listing
124
124
 
125
125
  **If source cannot be resolved:**
126
126
  Halt with: "Source not found: `{source_repo}`. Verify the repository exists and is accessible."
@@ -170,7 +170,7 @@ ONLY WHEN forge-tier.yaml is loaded, skill-brief.yaml is validated, and source c
170
170
 
171
171
  - Forge tier loaded from sidecar with tool availability
172
172
  - Skill brief loaded and all required fields validated
173
- - Source code location resolved and accessible (or `source_location: null` confirmed for docs-only skills)
173
+ - Source code location resolved and accessible (or `source_root: null` confirmed for docs-only skills)
174
174
  - Initialization summary displayed with tier and capabilities
175
175
  - Auto-proceeded to step-02
176
176
 
@@ -71,7 +71,7 @@ If `tools.ccc` is true, continue to section 2.
71
71
 
72
72
  Read `ccc_index` from forge-tier.yaml:
73
73
 
74
- - If `ccc_index.status` is `"fresh"`: continue to section 3.
74
+ - If `ccc_index.status` is `"fresh"` or `"created"`: continue to section 3.
75
75
  - If `ccc_index.status` is `"stale"`: display brief note — "CCC index is stale — discovery results may miss recent changes." Continue to section 3.
76
76
  - 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.
77
77
 
@@ -46,7 +46,7 @@ To extract all public exports, function signatures, type definitions, and co-imp
46
46
 
47
47
  ## CONTEXT BOUNDARIES:
48
48
 
49
- - Available: brief_data, tier, source_location, file_tree from step-01; ecosystem check outcome from step-02 (proceed/halt decision — no named variable stored)
49
+ - 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
50
  - Focus: Source code extraction and inventory building
51
51
  - Limits: Do NOT compile, assemble, or write any output
52
52
  - Dependencies: Source code must be accessible (resolved in step-01)
@@ -57,11 +57,26 @@ To fetch temporal context (issues, PRs, changelogs, release notes) from the sour
57
57
  Evaluate the following conditions sequentially. **If ANY condition fails, skip silently to section 5 (auto-proceed) with no output:**
58
58
 
59
59
  1. **Tier is Deep:** If tier is Quick, Forge, or Forge+, skip silently.
60
- 2. **Source is GitHub:** Verify `source_repo` is a GitHub URL (`https://github.com/...`) or `owner/repo` format. If the source is a local path, a non-GitHub URL, or any other format, skip silently.
60
+ 2. **Source is GitHub:** Verify `source_repo` is a GitHub URL (`https://github.com/...`) or `owner/repo` format. If the source is a local path, a non-GitHub URL, or any other format, attempt GitHub remote detection (section 1b) before skipping.
61
61
  3. **`gh` CLI is available:** Run `gh auth status` to verify the CLI is installed and authenticated. If it fails, skip silently.
62
62
 
63
63
  All three conditions must pass to proceed to section 2.
64
64
 
65
+ ### 1b. GitHub Remote Detection for Local Sources
66
+
67
+ **Only runs when condition 2 above fails because `source_repo` is a local path.**
68
+
69
+ Local repositories that are clones of GitHub repos contain temporal context (issues, PRs, releases) accessible via `gh`. Detect this automatically:
70
+
71
+ 1. Check if the local path is a git repository: `git -C {source_repo} rev-parse --is-inside-work-tree`
72
+ 2. If not a git repo: skip silently to section 5 (current behavior).
73
+ 3. Extract the origin remote: `git -C {source_repo} remote get-url origin`
74
+ 4. If the remote URL contains `github.com`:
75
+ - Extract `owner/repo` from the remote URL (strip `.git` suffix, handle both HTTPS and SSH formats)
76
+ - Log: "**Local source with GitHub remote detected:** {owner}/{repo} — fetching temporal context."
77
+ - Use the extracted `owner/repo` for all `gh` API calls in sections 3-4. Continue to condition 3 (gh CLI check).
78
+ 5. If no remote, or remote is not GitHub: skip silently to section 5 (current behavior).
79
+
65
80
  ### 2. Check Cache (Skip If Fresh)
66
81
 
67
82
  Read `forge-tier.yaml` from the sidecar path.
@@ -85,14 +85,20 @@ Content fetched from external URLs is classified as **T3** (external, untrusted)
85
85
 
86
86
  After fetching a URL, apply the following heuristic to detect documentation root pages that contain no useful API content. This is common with modern documentation sites (Mintlify, Docusaurus, ReadTheDocs, GitBook) that render API content on subpages.
87
87
 
88
- **Root page detection heuristic — apply only when the URL path ends in `/`, `/index`, `/index.html`, has no path component (bare domain), or has 1 path segment (e.g., `/docs`). For deeper URL paths (2+ segments like `/api/reference`), skip this heuristic and keep the content as-is.**
88
+ **Root page detection — apply only when the URL path ends in `/`, `/index`, `/index.html`, has no path component (bare domain), or has 1 path segment (e.g., `/docs`). For deeper URL paths (2+ segments like `/api/reference`), skip this heuristic and keep the content as-is.**
89
89
 
90
- **A page is classified as a root if BOTH conditions are true:**
90
+ Subpage discovery is triggered if **either** of the following independent triggers fires:
91
+
92
+ **Trigger 1 — Content-based (both conditions must be true):**
91
93
 
92
94
  1. **Zero API content indicators:** The fetched markdown contains none of: fenced code blocks (`` ``` ``), parameter tables (`|---|`), or function signature patterns (`def `, `function `, `fn `, `func `, `export `).
93
95
  2. **High link density:** More than 70% of non-empty lines are markdown links (matching `[text](url)` with no other substantive content on the line).
94
96
 
95
- If only one condition is true, treat the page as having partial content keep it as-is and do NOT trigger subpage discovery.
97
+ **Trigger 2URL-based (independent of content analysis):**
98
+
99
+ The URL matches the path criteria above (ends in `/`, bare domain, or 1 segment) AND the fetched content is under **2000 words**. Short content on root-like URLs almost certainly indicates a navigation hub or landing page, even if it contains introductory code examples that would prevent Trigger 1 from firing. This handles modern doc sites (Mintlify, Docusaurus, GitBook) that include hero sections with code snippets on their root pages.
100
+
101
+ If neither trigger fires, keep the page content as-is and do NOT trigger subpage discovery.
96
102
 
97
103
  **If a root URL with minimal content is detected:**
98
104
 
@@ -100,7 +100,12 @@ Following the structure from the skill-sections data file:
100
100
  - Set `description` from the SKILL.md frontmatter `description` field (already assembled in section 2)
101
101
  - Set `language` from source analysis (e.g., `"typescript"`, `"python"`) — use the primary language of the entry point file
102
102
  - Set `ast_node_count` from extraction stats if ast-grep was used (Forge/Deep tier), otherwise omit
103
- - Set `tool_versions` based on tier and available tools. Resolve `{skf_version}` from the installed module's `package.json` at `{project-root}/_bmad/skf/package.json`. If unresolvable there, fall back to `node -p "require('./node_modules/bmad-module-skill-forge/package.json').version"`. If still unresolvable, use `"unknown"` and add a warning to the evidence report. Never hardcode the version.
103
+ - Set `tool_versions` based on tier and available tools. Resolve `{skf_version}` using this resolution chain (try each in order, use the first that succeeds):
104
+ 1. `{project-root}/_bmad/skf/package.json` → read `.version` field
105
+ 2. `node -p "require('./node_modules/bmad-module-skill-forge/package.json').version"`
106
+ 3. `{project-root}/_bmad/skf/VERSION` → read plain text file (single line containing version string, written by the SKF installer)
107
+ 4. `"unknown"` (final fallback — add a warning to the evidence report)
108
+ Never hardcode the version.
104
109
  - Store `commit_short` = first 8 characters of `source_commit` (or `"unknown"` if unavailable) for use in step-08 report.
105
110
  - 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.
106
111
 
@@ -43,7 +43,7 @@ To write all compiled content to disk — 4 deliverable files to `{skills_output
43
43
 
44
44
  ## CONTEXT BOUNDARIES:
45
45
 
46
- - Available: All compiled content from step-05, validation results from step-06
46
+ - Available: All compiled content from step-05, validation results from step-06, source_root from step-01 (needed for section 5b CCC registration)
47
47
  - Focus: File system operations — create directories, write files
48
48
  - Limits: Do NOT modify content during writing
49
49
  - Dependencies: All content must be compiled and validated in context
@@ -145,7 +145,7 @@ Generate metadata.json following the exact structure defined in {skillTemplateDa
145
145
  "tool_versions": {
146
146
  "ast_grep": null,
147
147
  "qmd": null,
148
- "skf": "{skf_version}"
148
+ "skf": "{skf_version}" // Resolution chain: _bmad/skf/package.json → npm require → _bmad/skf/VERSION → "unknown"
149
149
  },
150
150
  "stats": {
151
151
  "exports_documented": "{number of exports found}",
@@ -17,6 +17,21 @@
17
17
  **Structural Issues:** {count}
18
18
  ```
19
19
 
20
+ ## Coherence Analysis — Naive Mode: Reference Consistency (split-body)
21
+
22
+ Only rendered when `references/` directory exists alongside SKILL.md.
23
+
24
+ ```markdown
25
+ ### Reference Consistency (split-body)
26
+
27
+ | # | Reference File | Export | Issue | SKILL.md Line | Reference Line |
28
+ |---|---------------|--------|-------|---------------|---------------|
29
+ | {per-mismatch rows} |
30
+
31
+ **Exports Cross-Checked:** {count}
32
+ **Mismatches Found:** {count}
33
+ ```
34
+
20
35
  ## Coherence Analysis — Contextual Mode
21
36
 
22
37
  ```markdown
@@ -42,6 +42,18 @@ tessl evaluates SKILL.md body content only — it does not read `references/*.md
42
42
  - Score based on: structural completeness only
43
43
  - Weight redistribution: skipped categories' weights (Signature Accuracy 22% + Type Coverage 14%) redistributed proportionally to remaining active categories
44
44
 
45
+ ### Docs-Only Mode (Quick tier, all [EXT:...] citations)
46
+
47
+ When `docs_only_mode: true` is set by step-03 (indicating a Quick tier skill where all SKILL.md citations are `[EXT:...]` format with no local source code):
48
+
49
+ - **Signature Accuracy:** Not scored (no source to compare against)
50
+ - **Type Coverage:** Not scored (no source to compare against)
51
+ - **Weight redistribution:** Same as Quick tier — Signature Accuracy (22%) and Type Coverage (14%) weights redistributed proportionally to remaining active categories
52
+ - **Export Coverage basis:** Documentation completeness rather than source coverage. Score = (documented_items_with_complete_descriptions / total_documented_items) * 100. A "complete" item has: description, parameters (if function/method), and return type (if function/method).
53
+ - **Coherence:** Standard rules for the detected mode (naive or contextual) apply unchanged
54
+
55
+ This is functionally identical to Quick tier weight redistribution but with a different coverage denominator (self-consistency instead of source comparison).
56
+
45
57
  ### Forge Tier (ast-grep)
46
58
  - Export Coverage: AST-backed export comparison
47
59
  - Signature Accuracy: AST-verified signature matching
@@ -87,6 +87,38 @@ Build the **documented inventory** — a list of everything the SKILL.md claims
87
87
 
88
88
  **Split-body traversal:** If a `references/` directory exists alongside SKILL.md and SKILL.md's `## Full` headings are absent or stubs (not a stack skill's structural references), extend the documented inventory scan to include all `references/*.md` files. After split-body, Tier 2 content (Full API Reference, Full Type Definitions) lives in reference files — the inventory must reflect the full skill content regardless of where it resides.
89
89
 
90
+ ### 1b. Cross-Check Split-Body Consistency
91
+
92
+ **Only execute if a `references/` directory exists alongside SKILL.md** (detected during split-body traversal in Section 1). Skip silently otherwise.
93
+
94
+ For each function, class, type, or interface that appears in BOTH the SKILL.md body AND any `references/*.md` file, compare the documented signatures:
95
+
96
+ - **Parameters:** name, type, order, optionality
97
+ - **Return types:** exact type match
98
+ - **Description:** no contradictions (brief vs detailed is acceptable; conflicting semantics is not)
99
+
100
+ **SKILL.md body is authoritative.** When a mismatch is found, the reference file is the one that needs updating.
101
+
102
+ Build a split-body consistency findings list:
103
+
104
+ ```json
105
+ {
106
+ "cross_check_mismatches": [
107
+ {
108
+ "export": "formatDate",
109
+ "skill_md_line": 42,
110
+ "reference_file": "references/api-reference.md",
111
+ "reference_line": 18,
112
+ "issue": "SKILL.md shows (date: Date) => string, reference shows (date: Date, format?: string) => string"
113
+ }
114
+ ],
115
+ "exports_cross_checked": 12,
116
+ "mismatches_found": 1
117
+ }
118
+ ```
119
+
120
+ Flag each mismatch as **High severity** — signature inconsistency between SKILL.md body and reference files undermines agent trust. These findings feed into the gap report (step-06).
121
+
90
122
  ### 2. Analyze Source Code (Tier-Dependent)
91
123
 
92
124
  Start from the package entry point (see 0b) and identify the public API surface. Then analyze those exports at the appropriate tier depth.
@@ -181,11 +213,13 @@ Append the **Coverage Analysis** section to `{outputFile}`:
181
213
 
182
214
  ### Category Scores
183
215
 
184
- | Category | Score | Weight | Weighted |
185
- |----------|-------|--------|----------|
186
- | Export Coverage | {N}% | {weight}% | {weighted}% |
187
- | Signature Accuracy | {N}% | {weight}% | {weighted}% |
188
- | Type Coverage | {N}% | {weight}% | {weighted}% |
216
+ | Category | Score |
217
+ |----------|-------|
218
+ | Export Coverage | {N}% |
219
+ | Signature Accuracy | {N}% or N/A |
220
+ | Type Coverage | {N}% or N/A |
221
+
222
+ Note: Weight application is deferred to step-05 where all category weights are calculated after external validation availability is known.
189
223
  ```
190
224
 
191
225
  ### 6. Report Coverage Results
@@ -91,7 +91,27 @@ Build a simple structural findings list:
91
91
  }
92
92
  ```
93
93
 
94
- **After naive coherence → Skip to Section 6 (Append Results)**
94
+ **After naive coherence → Execute Section 2b if gate conditions met, then skip to Section 6 (Append Results)**
95
+
96
+ ### 2b. Migration/Deprecation Verification (Mode-Independent)
97
+
98
+ **Gate check:** Execute this section ONLY IF both conditions are met:
99
+ 1. Forge tier is **Deep** (tool-gated)
100
+ 2. `{forge_data_folder}/{skill_name}/evidence-report.md` exists (data-gated)
101
+
102
+ If either condition fails, skip silently and proceed to Section 6.
103
+
104
+ **This check runs regardless of naive/contextual mode.** T2-future annotations are a property of the source code and enrichment data, not the skill type.
105
+
106
+ Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Section 4b). Then check the skill's `evidence-report.md` (at `{forge_data_folder}/{skill_name}/evidence-report.md`) for T2-future annotation counts.
107
+
108
+ - **If T2-future annotations > 0 AND Section 4b is absent:** Flag as Medium severity gap: "Migration section missing — T2-future annotations exist but Section 4b is not present in SKILL.md Tier 1."
109
+ - **If T2-future annotations = 0 AND Section 4b is present:** Flag as Medium severity gap: "Migration section unexpected — Section 4b is present but no T2-future annotations were produced."
110
+ - **If evidence-report.md is unavailable:** Skip this check silently. Note: "Section 4b verification skipped — evidence-report.md not found."
111
+
112
+ Add findings to the coherence analysis results.
113
+
114
+ **After Section 2b (naive path) → Skip to Section 6 (Append Results)**
95
115
 
96
116
  ### 3. Contextual Mode: Extract References
97
117
 
@@ -121,6 +141,8 @@ DO NOT BE LAZY — For EACH reference found, launch a subprocess that:
121
141
 
122
142
  If subprocess unavailable, validate each reference in main thread.
123
143
 
144
+ 4. **Scripts/assets directory check:** If a `scripts/` or `assets/` directory exists alongside SKILL.md, verify that a "Scripts & Assets" section (Section 7b) is present in SKILL.md. This directory-level check applies in both modes (naive mode performs it in Section 2; contextual mode performs it here alongside per-reference validation). Flag absence as Medium severity gap per `{scoringRulesFile}`.
145
+
124
146
  ### 5. Contextual Mode: Check Integration Pattern Completeness
125
147
 
126
148
  For stack skills, verify integration patterns are complete:
@@ -146,11 +168,19 @@ Build integration completeness findings:
146
168
  }
147
169
  ```
148
170
 
149
- ### 5b. Contextual Mode + Deep Tier: Section 4b Verification
171
+ **Zero integration patterns:** If no integration patterns are documented in SKILL.md (e.g., a contextual-mode skill that uses shared types but has no middleware chains, plugin hooks, or event flows): record `patterns_documented: 0`, `patterns_complete: 0`. The coherence score will use reference validity alone — see `{scoringRulesFile}` Coherence Score Aggregation: "If no integration patterns exist, combined coherence equals reference validity."
150
172
 
151
- **Only execute if forge tier is Deep.** Skip silently for Quick/Forge tiers.
173
+ ### 5b. Migration/Deprecation Verification (Contextual Path)
152
174
 
153
- Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Section 4b). Then check the skill's `evidence-report.md` (at `{forge_data_folder}/{skill_name}/evidence-report.md`) for T2-future annotation counts.
175
+ **This section shares logic with Section 2b.** If you are on the contextual mode path (Sections 3-5), execute the migration check here using the same rules as Section 2b:
176
+
177
+ **Gate check:** Execute ONLY IF both conditions are met:
178
+ 1. Forge tier is **Deep** (tool-gated)
179
+ 2. `{forge_data_folder}/{skill_name}/evidence-report.md` exists (data-gated)
180
+
181
+ If either condition fails, skip silently.
182
+
183
+ Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Section 4b). Then check the skill's `evidence-report.md` for T2-future annotation counts.
154
184
 
155
185
  - **If T2-future annotations > 0 AND Section 4b is absent:** Flag as Medium severity gap: "Migration section missing — T2-future annotations exist but Section 4b is not present in SKILL.md Tier 1."
156
186
  - **If T2-future annotations = 0 AND Section 4b is present:** Flag as Medium severity gap: "Migration section unexpected — Section 4b is present but no T2-future annotations were produced."
@@ -158,6 +188,20 @@ Check whether SKILL.md contains a "Migration & Deprecation Warnings" section (Se
158
188
 
159
189
  Add findings to the coherence analysis results.
160
190
 
191
+ ### 5c. Calculate Coherence Scores
192
+
193
+ **Contextual mode only.** Calculate coherence percentages using the formulas defined in `{scoringRulesFile}` — Coherence Score Aggregation section:
194
+
195
+ ```
196
+ reference_validity = (valid_references / total_references) * 100
197
+ integration_completeness = (complete_patterns / total_patterns) * 100
198
+ combined_coherence = (reference_validity * 0.6) + (integration_completeness * 0.4)
199
+ ```
200
+
201
+ **Edge case:** If no integration patterns are documented (patterns_documented = 0), combined coherence equals reference validity alone. Do not divide by zero.
202
+
203
+ These values fill the `{percentage}%` placeholders in the output template loaded in Section 6.
204
+
161
205
  ### 6. Append Coherence Analysis to Output
162
206
 
163
207
  Load `{outputFormatsFile}` and use the appropriate Coherence Analysis section format (naive or contextual) to append findings to `{outputFile}`.
@@ -60,7 +60,15 @@ 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:** Compare the evidence report's generation date against SKILL.md's last-modified timestamp (or `metadata.json` `generation_date`). 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.
63
+ **Staleness check:** Determine whether SKILL.md has changed since the evidence report was generated.
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.
66
+
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.
68
+
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."
70
+
71
+ 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.
64
72
 
65
73
  If recent, non-stale results exist (from a create-skill run that just completed), auto-reuse them — skip re-running validators and use the existing scores. Record: "External validation: reused from create-skill evidence report." Skip to section 5 (append results).
66
74
 
@@ -64,6 +64,8 @@ Load `{scoringRulesFile}` to get:
64
64
  - Tier-dependent scoring adjustments
65
65
  - Any custom threshold override from workflow input
66
66
 
67
+ **Docs-only mode check:** If the Coverage Analysis section in `{outputFile}` notes docs-only mode (set by step-03 for skills with all `[EXT:...]` citations and no local source), apply Quick-tier weight redistribution: Signature Accuracy and Type Coverage are not scored, their weights (22% + 14%) are redistributed proportionally to remaining active categories. Coverage score is based on documentation completeness rather than source coverage (as calculated by step-03).
68
+
67
69
  ### 2. Read Category Scores from Output
68
70
 
69
71
  Read `{outputFile}` and extract the category scores calculated in previous steps:
@@ -185,6 +185,10 @@ class Installer {
185
185
  await fs.copy(src, path.join(skfDir, file));
186
186
  }
187
187
  }
188
+
189
+ // Write VERSION file for SKF version resolution in installed projects
190
+ const packageJson = require('../../../package.json');
191
+ await fs.writeFile(path.join(skfDir, 'VERSION'), packageJson.version, 'utf8');
188
192
  }
189
193
 
190
194
  /**