bmad-module-skill-forge 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,15 @@
1
1
  ---
2
2
  nextStepFile: './step-02-ecosystem-check.md'
3
3
  registryResolutionData: 'references/registry-resolution.md'
4
+ packageResolverProbeOrder:
5
+ - '{project-root}/_bmad/skf/shared/scripts/skf-resolve-package.py'
6
+ - '{project-root}/src/shared/scripts/skf-resolve-package.py'
4
7
  ---
5
8
 
6
9
  # Step 1: Resolve Target
7
10
 
11
+ Communicate with the user in `{communication_language}`.
12
+
8
13
  ## STEP GOAL:
9
14
 
10
15
  To accept a GitHub URL or package name from the user, resolve it to a GitHub repository, detect the primary language, and prepare state for source extraction.
@@ -16,10 +21,12 @@ To accept a GitHub URL or package name from the user, resolve it to a GitHub rep
16
21
 
17
22
  ## MANDATORY SEQUENCE
18
23
 
19
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
20
-
21
24
  ### 1. Accept User Input
22
25
 
26
+ **Batch mode:** if `--batch` is active (see SKILL.md "Batch Mode"), the current target was already resolved by On Activation step 4 from the next batch line and placed into the workflow context as `target`, with optional `language_hint` and `scope_hint` per-line modifiers. Skip the prompt below — emit `{"batch":<n>,"target":"<target>","status":"start"}` to stderr and proceed directly to §1b with the batch-supplied values.
27
+
28
+ **Single-target mode** (default):
29
+
23
30
  "**Quick Skill — fastest path to a skill.**
24
31
 
25
32
  Provide a **GitHub URL** or **package name** and I'll resolve it to source and compile a best-effort SKILL.md.
@@ -50,41 +57,88 @@ If no `@version` suffix is present, proceed as today — version will be auto-de
50
57
  - Extract org/repo from URL
51
58
  - Set `resolved_url` to the GitHub URL
52
59
  - Set `repo_name` to the repo name (last path segment)
53
- - Skip to step 4 (Detect Language)
60
+ - Skip to step 3a (Verify Target Version Tag), then step 4 (Detect Language)
54
61
 
55
- **If input is a package name:**
62
+ **If input is a package-name-like token** (no whitespace, matches `[@a-zA-Z0-9._/-]+(@<semver>)?`, e.g. `lodash`, `@scope/name`, `requests==2.31`, `cognee@0.5.0`):
56
63
  - Proceed to step 3 (Registry Resolution)
57
64
 
65
+ **Otherwise — input looks like free-form prose, not a target:**
66
+
67
+ The user typed something like "I want a skill that helps with onboarding" or "build me a brainstorming workflow" — quick-skill cannot resolve that to a GitHub repository. Instead of falling through to a registry-failure HARD HALT, redirect with a sibling-skill suggestion:
68
+
69
+ "**This input looks like a description, not a package or URL.** Quick Skill needs a package name (e.g. `lodash`, `@vercel/og`, `requests`) or a GitHub URL (e.g. `https://github.com/lodash/lodash`).
70
+
71
+ If you are describing a skill you want to **create from scratch** rather than compile from existing source:
72
+
73
+ - Run `/skf-create-skill` with a skill brief — full pipeline with provenance tracking and AST-verified exports
74
+ - Or use `bmad-agent-builder` for an interactive skill design session
75
+
76
+ Otherwise, paste the package name or GitHub URL of the library you want to wrap, and quick-skill will resolve it."
77
+
78
+ **GATE [default: HALT]** — In headless mode, emit the same redirect message and HALT with **exit code 3 (resolution-failure)** per the SKILL.md exit-code map. Before exiting, emit the error result contract per SKILL.md "Result Contract on HARD HALT" (`phase: "resolve-target"`, `error.code: "resolution-failure"`, `skill_package: null`). Do not attempt registry lookups against prose input; that wastes ~3-4 round trips and produces a less actionable error message than the redirect above.
79
+
58
80
  ### 3. Registry Resolution
59
81
 
60
- Load {registryResolutionData} for resolution patterns.
82
+ Run the shared resolver against the deterministic registries (npm → PyPI → crates.io). The resolver does the HTTP+JSON+GitHub-URL-extraction work; the LLM only handles the web-search fallback below when needed.
83
+
84
+ **Resolve `{packageResolver}`** from `{packageResolverProbeOrder}`; first existing path wins. If no candidate exists, fall back to the LLM walk of {registryResolutionData} for the full chain.
61
85
 
62
- **Execute the fallback chain in order — stop at first success:**
86
+ ```bash
87
+ python3 {packageResolver} {package_name} --timeout 10
88
+ ```
63
89
 
64
- 1. **npm registry:** Fetch `https://registry.npmjs.org/{package_name}` extract `repository.url`
65
- 2. **PyPI registry:** Fetch `https://pypi.org/pypi/{package_name}/json` — extract `info.project_urls.Source` or `info.home_page`
66
- 3. **crates.io registry:** Fetch `https://crates.io/api/v1/crates/{package_name}` — extract `crate.repository`
67
- 4. **Web search fallback:** Search `"{package_name} github repository"` — look for GitHub URL
90
+ The resolver emits JSON with `status` (`"ok"` or `"fallthrough"`), `resolved_url`, `repo_owner`, `repo_name`, `registry_used`, `registries_tried`, and a per-registry `registry_outcomes` map. Exit 0 means ok; exit 1 means fallthrough.
68
91
 
69
- **If all methods fail HARD HALT:**
92
+ - **On `status: "ok"`**capture `resolved_url`, `repo_name`, and `registry_used` from the JSON. Proceed to §3a.
93
+ - **On `status: "fallthrough"`** — the deterministic chain returned no GitHub URL (every registry replied with 404 / no-github-link / timeout). Fall back to the web-search step from {registryResolutionData} §4: search `"{package_name} github repository"` with a 15s timeout and look for a GitHub URL in the top results. If found, set `resolved_url` and proceed. If web search also returns nothing, HARD HALT below.
94
+
95
+ **If all methods fail — HARD HALT (exit code 3, resolution-failure):**
70
96
 
71
97
  "**Resolution failed.** Could not resolve `{package_name}` to a GitHub repository.
72
98
 
73
- Please check:
99
+ Check:
74
100
  - Is the package name spelled correctly?
75
101
  - Is it a private package?
76
102
  - Is the source hosted on a non-GitHub platform?
77
103
 
78
104
  **Provide the GitHub URL directly to continue.**"
79
105
 
80
- Wait for corrected input. Loop back to step 2.
106
+ In interactive mode, wait for corrected input and loop back to step 2. In headless mode, emit the error result contract per SKILL.md "Result Contract on HARD HALT" (`phase: "resolve-target"`, `error.code: "resolution-failure"`, `skill_package: null`) and exit 3.
107
+
108
+ ### 3a. Verify Target Version Tag (when applicable)
109
+
110
+ Skip this section if `target_version` is null (auto-detect path — version comes from manifest read in step-03).
111
+
112
+ When the user explicitly supplied `@version` in §1b, verify the tag exists in the resolved repo before extraction. Otherwise step-03 silently reads from the default branch while metadata records the requested version — a quiet provenance bug where the SKILL.md claims version 0.5.0 but the exports actually came from main.
113
+
114
+ Probe both with-and-without v-prefix (the v-prefix is conventional but not universal across ecosystems):
115
+
116
+ ```bash
117
+ gh api repos/{owner}/{repo}/git/ref/tags/{target_version} --silent \
118
+ || gh api repos/{owner}/{repo}/git/ref/tags/v{target_version} --silent
119
+ ```
120
+
121
+ **If a matching tag is found** — set `source_ref` to the matching ref (with v-prefix when that variant matched). Step-03's ref-aware source reading uses this value to fetch from the tagged commit. Proceed to §4.
122
+
123
+ **If no tag matches** — HARD HALT with **exit code 3 (resolution-failure)**:
124
+
125
+ "**Tag `{target_version}` not found in `{owner}/{repo}`.**
126
+
127
+ The version was parsed from your `@version` suffix but does not exist as a tag in the resolved repository. Quick-skill cannot extract from a version with no commit pointer — the result would be sourced from the default branch but labelled `{target_version}` in metadata.
128
+
129
+ Recent tags in this repo:
130
+ {list top 5 from `gh api repos/{owner}/{repo}/tags --paginate=false`, or "(none — repo has no tags; omit @version to auto-detect from default branch)"}
131
+
132
+ Re-run with one of these tags, or omit the `@version` suffix to auto-detect from the default branch."
133
+
134
+ Before exiting, emit the error result contract per SKILL.md "Result Contract on HARD HALT" (`phase: "resolve-target"`, `error.code: "resolution-failure"`, `error.details: {requested_version: "{target_version}", available_tags: [...top 5]}`, `skill_package: null`). In headless mode, exit immediately; do not loop.
81
135
 
82
136
  ### 4. Detect Language
83
137
 
84
138
  Determine primary language from:
85
139
 
86
- 1. **User-provided language hint** (overrides detection)
87
- 2. **Manifest file presence** (check via GitHub API or web browsing):
140
+ 1. **User-provided language hint** (overrides detection — skip the ambiguity gate below).
141
+ 2. **Manifest-presence scan** check the repo root for ALL of these (priority order = first hit wins on auto-pick):
88
142
  - `package.json` → JavaScript/TypeScript
89
143
  - `pyproject.toml` or `setup.py` → Python
90
144
  - `Cargo.toml` → Rust
@@ -92,7 +146,21 @@ Determine primary language from:
92
146
  - `pom.xml` → Java (or Kotlin if `src/main/kotlin/` is present)
93
147
  - `build.gradle.kts` or `build.gradle` → Kotlin (or Java if only `src/main/java/` is present)
94
148
 
95
- Set `language` to detected language.
149
+ Collect every match into `detected_languages`.
150
+
151
+ 3. **Single-language case** (`len(detected_languages) <= 1`) — set `language` to the detected value (or HALT in step-01 §3 if zero matches).
152
+
153
+ 4. **Multi-language case** (`len(detected_languages) > 1`) — surface the choice rather than silently picking the first match. Multi-language repos (Python + JS bindings, or monorepos with mixed manifests) otherwise produce a skill for whichever manifest probe hits first, with no signal that the user might have wanted the other one.
154
+
155
+ "**`{repo_name}` has manifests for multiple languages:** {detected_languages}.
156
+
157
+ Primary guess: **{first_match}** (manifest-priority order). If you wanted a different language, abort and re-run with `--language-hint <lang>` or with the optional language hint at step-01 §1.
158
+
159
+ Select: [C] Continue with `{first_match}` · [A] Abort"
160
+
161
+ - **IF C** — log "user accepted multi-manifest pick: `{first_match}`" and set `language` to the first match.
162
+ - **IF A** — HARD HALT with **exit code 3 (resolution-failure)**: "Aborted to disambiguate language. Re-run with a `language_hint`." Before exiting, emit the error result contract per SKILL.md "Result Contract on HARD HALT" (`phase: "resolve-target"`, `error.code: "resolution-failure"`, `error.details: {detected_languages: [...], auto_pick: "{first_match}"}`, `skill_package: null`).
163
+ - **GATE [default: C]** — Headless mode auto-proceeds with the manifest-priority pick; record `detected_languages` and `language_resolution: "auto-picked-first"` in the extraction context so the result contract surfaces the ambiguity downstream.
96
164
 
97
165
  ### 5. Confirm Resolution
98
166
 
@@ -4,6 +4,8 @@ nextStepFile: './step-03-quick-extract.md'
4
4
 
5
5
  # Step 2: Ecosystem Check
6
6
 
7
+ Communicate with the user in `{communication_language}`.
8
+
7
9
  ## STEP GOAL:
8
10
 
9
11
  To query the agentskills.io ecosystem for an existing official skill matching the resolved target, preventing unnecessary duplication. This is an advisory gate — it never blocks the workflow on failure.
@@ -16,8 +18,6 @@ To query the agentskills.io ecosystem for an existing official skill matching th
16
18
 
17
19
  ## MANDATORY SEQUENCE
18
20
 
19
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
20
-
21
21
  ### 1. Query Ecosystem
22
22
 
23
23
  Search for an existing official skill matching `{repo_name}` in the agentskills.io ecosystem.
@@ -83,5 +83,5 @@ An official skill already exists. You can:
83
83
 
84
84
  ## CRITICAL STEP COMPLETION NOTE
85
85
 
86
- ONLY WHEN ecosystem check completes (match with user choice, no-match, or skip) will you load and read fully `{nextStepFile}` to execute source extraction.
86
+ ONLY WHEN ecosystem check completes (match with user choice, no-match, or skip) will you load and read fully `{nextStepFile}` to proceed to source extraction.
87
87
 
@@ -1,9 +1,14 @@
1
1
  ---
2
2
  nextStepFile: './step-04-compile.md'
3
+ publicApiExtractorProbeOrder:
4
+ - '{project-root}/_bmad/skf/shared/scripts/skf-extract-public-api.py'
5
+ - '{project-root}/src/shared/scripts/skf-extract-public-api.py'
3
6
  ---
4
7
 
5
8
  # Step 3: Quick Extract
6
9
 
10
+ Communicate with the user in `{communication_language}`.
11
+
7
12
  ## STEP GOAL:
8
13
 
9
14
  To read the resolved GitHub repository source and extract the public API surface using surface-level source reading (no AST). Produces an extraction inventory of exports, descriptions, and manifest data for compilation.
@@ -16,10 +21,10 @@ To read the resolved GitHub repository source and extract the public API surface
16
21
 
17
22
  ## MANDATORY SEQUENCE
18
23
 
19
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
20
-
21
24
  **Ref-aware source reading:** When `source_ref` is set from tag resolution (see step-01), append `?ref={source_ref}` to all GitHub API content and tree requests (e.g., `gh api repos/{owner}/{repo}/contents/{path}?ref={source_ref}`) to read from the tagged version. When using web browsing, use the tagged URL format (e.g., `github.com/{owner}/{repo}/blob/{source_ref}/{path}`). This ensures extraction reads from the same source version resolved during tag resolution.
22
25
 
26
+ **Parallel-fetch directive:** §1 (README), §2 (manifest), and §3 (entry-point exports) read independent files from the same `?ref={source_ref}` and are safe to issue as one batched tool-call message rather than three sequential round trips. For multi-module Maven (`<modules>`) and multi-project Gradle (`include(...)`) builds, also fetch all submodule `pom.xml` / `build.gradle[.kts]` files in parallel rather than serially per module — N module fetches collapse to O(1) wall-clock time.
27
+
23
28
  ### 1. Read README
24
29
 
25
30
  Read `README.md` from the repository root via web browsing.
@@ -32,59 +37,74 @@ Extract:
32
37
 
33
38
  If README is unavailable, note and continue.
34
39
 
35
- ### 2. Read Manifest File
40
+ ### 1.5. Repo-Shape Sniff
36
41
 
37
- Based on detected language, read the primary manifest file:
42
+ After the README has loaded, classify the repo shape from the available signals before committing further effort to extraction. Quick-skill is designed to wrap a library; non-library repos sail through silently today and produce low-quality skills the user only notices via the description field after compilation.
38
43
 
39
- - **JavaScript/TypeScript:** `package.json` extract name, version, description, main, exports, dependencies
40
- - **Python:** `pyproject.toml` or `setup.py` — extract project name, version, description, dependencies
41
- - **Rust:** `Cargo.toml` — extract package name, version, description, dependencies
42
- - **Go:** `go.mod` — extract module path, require list
43
- - **Java (Maven):** `pom.xml` — extract `<groupId>`, `<artifactId>`, `<version>`, `<description>`, direct `<dependencies>`. For multi-module projects, also enumerate `<modules><module>` entries and read each submodule's `pom.xml` (treat each as a logical unit in the extraction inventory).
44
- - **Kotlin / Java (Gradle):** `build.gradle.kts` or `build.gradle` — extract `group`, `version`, `description` (when declared), and top-level `dependencies { }` block. For multi-project builds, read `settings.gradle[.kts]` for `include(...)` entries and repeat per subproject.
44
+ **Classify as one of:**
45
45
 
46
- Extract:
47
- - **Package metadata:** name, version, description
48
- - **Entry points:** main, exports, module fields
49
- - **Key dependencies:** direct dependencies list
46
+ - **library** (default) — README has installation / usage / API content; manifest at root with publishable metadata. Proceed normally.
47
+ - **awesome-list** README H1 contains "awesome" (case-insensitive) or `awesome-` is in the repo name; README body is dominated by curated bullet links of the form `- [name](url) — desc`; no manifest at root.
48
+ - **docs-site / website** — README is short (under ~50 non-empty lines) and primarily points elsewhere ("See https://… for docs"); root has no manifest, or only a docs-framework manifest (e.g. `docusaurus.config.js`, `astro.config.mjs`, `mkdocs.yml`).
49
+ - **examples-only / tutorial** README explicitly labels the repo as examples or a tutorial ("Code examples for…", "Tutorial: …", "Learn X by building Y"); typically no published package; many small standalone files instead of a single API surface.
50
+
51
+ **If a non-library shape is detected** — soft-warn and gate before continuing:
52
+
53
+ "**Heads up — `{repo_name}` looks like a `{shape}` repo, not a library.**
54
+
55
+ Quick-skill is designed to wrap a library's public API. The compiled SKILL.md will likely have a thin Description and an empty Key Exports list. You can continue anyway, or abort and pick a target library.
56
+
57
+ Select: [C] Continue anyway · [A] Abort"
58
+
59
+ - **IF C** — log "user accepted `{shape}` shape" and proceed to §2. Set `extraction_inventory.repo_shape` to the detected shape so the result contract carries the signal for automators.
60
+ - **IF A** — HARD HALT with **exit code 3 (resolution-failure)** per the SKILL.md exit-code map: "Aborted. `{shape}` repos are best wrapped manually with `/skf-create-skill` from a brief, not auto-extracted." Before exiting, emit the error result contract per SKILL.md "Result Contract on HARD HALT" (`phase: "quick-extract"`, `error.code: "resolution-failure"`, `error.details: {repo_shape: "{shape}"}`, `skill_package: null`).
61
+
62
+ **GATE [default: C]** — In headless mode, log "headless: detected `{shape}` repo, continuing anyway" and proceed; the result contract's `summary.repo_shape` carries the signal so automators can flag low-quality outputs without re-parsing logs.
63
+
64
+ ### 2. Fetch Source Files
65
+
66
+ Fetch the manifest file and the top-level entry-point file(s) for the detected language. The helper invoked in §3 does pure parsing — no I/O — so this step does the fetch work using `gh api` (preferred when source_ref is set) or web browsing.
67
+
68
+ | Language | Manifest | Entry-point files (quick mode) |
69
+ | --- | --- | --- |
70
+ | JavaScript / TypeScript | `package.json` | `index.{js,ts}`, `src/index.{ts,js}`, or the file pointed to by the `main` field |
71
+ | Python | `pyproject.toml` or `setup.py` | `__init__.py`, `src/{package}/__init__.py` |
72
+ | Rust | `Cargo.toml` | `src/lib.rs` |
73
+ | Go | `go.mod` | top-level `*.go` files (3–5 best-effort) |
74
+ | Java (Maven) | `pom.xml` | top-level `*.java` files under `src/main/java/<groupId-as-path>/` (3–5 best-effort) |
75
+ | Kotlin (Gradle) | `build.gradle.kts` or `build.gradle` | top-level `*.kt` files under `src/main/kotlin/` (3–5 best-effort); also fetch `settings.gradle[.kts]` for `include(...)` entries when present |
50
76
 
51
- ### 3. Scan Top-Level Exports
77
+ **If `scope_hint` provided:** focus the entry-point fetch on the specified directories instead of repo root.
52
78
 
53
- Based on language and entry points from manifest, read the primary export files:
79
+ For multi-module Maven (`<modules>`) and multi-project Gradle (`include(...)`) builds, fetch the parent manifest first, then loop §2+§3 per module. Sub-module fetches are safe to issue as one batched tool-call message — N module fetches collapse to O(1) wall-clock time.
54
80
 
55
- **JavaScript/TypeScript:**
56
- - Read `index.js`, `index.ts`, `src/index.ts`, or `main` field from package.json
57
- - Extract: `export` statements, `module.exports` assignments
58
- - Pattern: lines matching `export (const|function|class|default|type|interface)`
81
+ ### 3. Parse Manifest and Scan Exports
59
82
 
60
- **Python:**
61
- - Read `__init__.py` or `src/{package}/__init__.py`
62
- - Extract: `__all__` list, top-level function/class definitions
63
- - Pattern: lines matching `def |class |__all__`
83
+ Run the shared extractor against the contents fetched in §2. The helper does manifest parse + export scan in one invocation and emits a structured envelope ready to feed §4's inventory.
64
84
 
65
- **Rust:**
66
- - Read `src/lib.rs`
67
- - Extract: `pub fn`, `pub struct`, `pub enum`, `pub trait` declarations
68
- - Pattern: lines matching `pub (fn|struct|enum|trait|mod)`
85
+ **Resolve `{publicApiExtractor}`** from `{publicApiExtractorProbeOrder}`; first existing path wins. If no candidate exists, fall back to in-prompt parsing (the legacy per-language regex tables that this section replaces).
69
86
 
70
- **Go:**
71
- - Read exported functions from top-level `.go` files
72
- - Extract: capitalized function names (Go export convention)
73
- - Pattern: lines matching `func [A-Z]`
87
+ Build the input payload from §2's fetched files and pipe it to the helper:
74
88
 
75
- **Java:**
76
- - Read `src/main/java/**/*.java` (focus on top-level packages declared in the manifest's `groupId`)
77
- - Extract: public classes, public methods, and framework annotations that mark API surfaces (Spring, Jakarta EE, CDI)
78
- - Pattern: lines matching `@(RestController|Service|Component|Configuration|Controller|Repository|Bean)|public (class|interface|enum|record) |public .* \(`
79
- - **Multi-module Maven:** iterate the `<module>` entries discovered in §2 and repeat the scan per module, reading each `{module}/src/main/java/**/*.java`
89
+ ```bash
90
+ echo '{"language":"<lang>","manifest":{"path":"<rel>","content":"<...>"},"entries":[{"path":"<rel>","content":"<...>"},...],"mode":"quick"}' \
91
+ | python3 {publicApiExtractor} --mode quick
92
+ ```
93
+
94
+ Where `<lang>` is one of `js`, `ts`, `javascript`, `typescript`, `python`, `rust`, `go`, `java`, `kotlin`. The helper accepts arbitrarily many `entries` items and aggregates exports across them.
95
+
96
+ The helper emits JSON on stdout with:
97
+
98
+ - `package_name`, `version`, `description` — parsed from the manifest
99
+ - `exports[]` — `{name, type, source_file}` per discovered top-level public symbol
100
+ - `dependencies[]` — declared direct dependencies
101
+ - `modules[]` — for Maven `<modules>` and Gradle `include(...)`, the names of sub-modules to iterate (loop §2+§3 per entry)
102
+ - `extra` — language-specific extras (e.g. `group_id` for Maven)
103
+ - `warnings[]` — manifest parse failures or scanner errors (advisory only; the envelope is still valid)
80
104
 
81
- **Kotlin:**
82
- - Read `src/main/kotlin/**/*.kt` (Kotlin defaults to `public` visibility — omit `internal`/`private` declarations)
83
- - Extract: top-level `fun`, `class`, `object`, `interface` declarations
84
- - Pattern: lines matching `^(fun |class |object |interface |data class |sealed class |@(RestController|Service|Component|Configuration|Controller))`
85
- - **Multi-project Gradle:** iterate the `include(...)` entries discovered in §2 and repeat the scan per subproject
105
+ Capture the helper's output into the extraction context. The shape of the envelope is the same for every language; §4 builds the inventory from it without per-language branching.
86
106
 
87
- **If scope_hint provided:** Focus reading on the specified directories instead of root.
107
+ **Multi-module loop:** when `modules[]` is non-empty, fetch each sub-module's manifest + entry-point files (§2) and re-invoke the helper per module (§3), aggregating `exports[]` across all module envelopes. The aggregated `exports[]`, `dependencies[]`, and a single resolved `package_name` (from the parent manifest) feed §4.
88
108
 
89
109
  ### 4. Build Extraction Inventory
90
110
 
@@ -107,6 +127,29 @@ extraction_inventory:
107
127
  - Use README description and features as fallback content
108
128
  - Note: "No exports detected — SKILL.md will be based on README content only"
109
129
 
130
+ ### 4.5. Zero-Exports Soft Gate (rescue mode)
131
+
132
+ Run this gate **only when** `extraction_inventory.exports.length == 0` AND `extraction_inventory.description` is empty (no usable README content either). When either is non-empty, the README-fallback in §4 produces a usable skill and this section is skipped.
133
+
134
+ When both are empty, the compiled SKILL.md would be effectively empty — no API surface to document and no description to fall back on. Offer the user a chance to retry with hints before producing a degenerate output:
135
+
136
+ "**Extraction yielded zero exports and no README description.**
137
+
138
+ The compiled SKILL.md would be effectively empty — no API surface to document and no description to fall back on.
139
+
140
+ Common causes:
141
+ - Wrong scope (extraction read the repo root, but the public API lives in a subdir)
142
+ - Wrong language (manifest probe picked the test/build language, not the lib language)
143
+ - Repo lays out exports unconventionally (e.g., not in `src/index.*` or `lib.rs`)
144
+
145
+ Select: [R] Retry with new hints · [P] Proceed anyway (low-confidence skill) · [A] Abort"
146
+
147
+ - **IF R** — prompt for new `scope_hint` ("New scope hint (e.g. `src/server/`):") and optional new `language_hint` ("New language hint (or empty to keep `{language}`):"). Update the extraction context with the new hints, then **re-execute step-03 from §1** with the new values. Discards the prior empty inventory.
148
+ - **IF P** — log "user accepted zero-exports outcome" and proceed to §5. The compiled skill will be README-content-only with confidence `low`. Record `zero_exports_rescue: "user-accepted"` in the inventory so the result contract summary surfaces it.
149
+ - **IF A** — HARD HALT with **exit code 3 (resolution-failure)**: "Aborted. Run `/skf-create-skill` from a brief if you want a guided extraction with provenance tracking." Before exiting, emit the error result contract per SKILL.md "Result Contract on HARD HALT" (`phase: "quick-extract"`, `error.code: "resolution-failure"`, `error.details: {exports_found: 0, description_empty: true, language: "{language}", scope: "{scope_hint or 'entire repo'}"}`, `skill_package: null`).
150
+
151
+ **GATE [default: P]** — In headless mode, log "headless: zero exports + empty description, proceeding with low-confidence skill" and proceed; record `zero_exports_rescue: "auto-proceeded"` in the result contract summary so batch automators can re-queue these targets with stricter hints downstream. [P] preserves the pre-rescue behaviour for unattended pipelines.
152
+
110
153
  ### 5. Report Extraction Summary
111
154
 
112
155
  "**Extraction complete:**
@@ -1,10 +1,15 @@
1
1
  ---
2
- nextStepFile: './step-05-validate.md'
2
+ nextStepFile: './step-05-write-and-validate.md'
3
3
  skillTemplateData: 'assets/skill-template.md'
4
+ quickMetadataRendererProbeOrder:
5
+ - '{project-root}/_bmad/skf/shared/scripts/skf-render-quick-metadata.py'
6
+ - '{project-root}/src/shared/scripts/skf-render-quick-metadata.py'
4
7
  ---
5
8
 
6
9
  # Step 4: Compile
7
10
 
11
+ Communicate with the user in `{communication_language}`. Compile generated content (descriptions, usage notes, summaries) in `{document_output_language}`.
12
+
8
13
  ## STEP GOAL:
9
14
 
10
15
  To assemble the best-effort SKILL.md document, context-snippet.md in Vercel-aligned indexed format, and metadata.json with `source_authority: community` from the extraction inventory. Present compiled output for review before validation.
@@ -17,8 +22,6 @@ To assemble the best-effort SKILL.md document, context-snippet.md in Vercel-alig
17
22
 
18
23
  ## MANDATORY SEQUENCE
19
24
 
20
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
21
-
22
25
  ### 1. Load Skill Template
23
26
 
24
27
  Load {skillTemplateData} to understand:
@@ -50,8 +53,8 @@ description: >
50
53
 
51
54
  **Required sections (after frontmatter):**
52
55
  - **Overview:** Package name, repository, language, source authority, generation date
53
- - **Description:** From extraction_inventory.description (README-derived)
54
- - **Key Exports:** From extraction_inventory.exports — list each with name, type, brief description
56
+ - **Description:** From `{overrides.description}` if set (subject to the same length/voice checks as extracted descriptions); otherwise from extraction_inventory.description (README-derived)
57
+ - **Key Exports:** From `{overrides.exports}` if set (comma-separated names parsed and trimmed; empty items skipped); otherwise from extraction_inventory.exports — list each with name, type, brief description
55
58
  - **Usage Patterns:** From extraction_inventory.usage_patterns (README examples)
56
59
 
57
60
  **Optional sections (include when data available):**
@@ -65,7 +68,9 @@ description: >
65
68
 
66
69
  ### 3. Generate Context Snippet
67
70
 
68
- Create context-snippet.md in Vercel-aligned indexed format (~80-120 tokens):
71
+ **If `{overrides.skip_snippet}` is true** — skip generation and note in the §5 preview: "context-snippet.md skipped per `--skip-snippet` override." Step-05 §2 will skip the corresponding write; step-05 §5 advisory snippet validation will report a "skipped" entry.
72
+
73
+ Otherwise, create context-snippet.md in Vercel-aligned indexed format (~80-120 tokens):
69
74
 
70
75
  ```
71
76
  [{skill_name} v{version}]|root: skills/{skill_name}/
@@ -82,53 +87,37 @@ Create context-snippet.md in Vercel-aligned indexed format (~80-120 tokens):
82
87
 
83
88
  ### 4. Generate Metadata JSON
84
89
 
85
- Generate metadata.json following the exact structure defined in {skillTemplateData} metadata.json section. Populate fields from extraction_inventory:
86
-
87
- ```json
88
- {
89
- "name": "{skill_name}",
90
- "version": "{extraction_inventory.version or 1.0.0}",
91
- "description": "{brief description of the skill}",
92
- "skill_type": "single",
93
- "source_authority": "community",
94
- "source_repo": "{resolved_url}",
95
- "source_root": "{resolved_source_path}",
96
- "source_commit": "{commit_sha_if_available}",
97
- "source_package": "{package_name from manifest}",
98
- "language": "{language}",
99
- "generated_by": "quick-skill",
100
- "generation_date": "{current ISO date}",
101
- "confidence_tier": "Quick",
102
- "spec_version": "1.3",
103
- "exports": ["{export_1}", "{export_2}"],
104
- "confidence_distribution": {
105
- "t1": 0,
106
- "t1_low": "{number of exports found — must be integer, not string}",
107
- "t2": 0,
108
- "t3": 0
109
- },
110
- "tool_versions": {
111
- "ast_grep": null,
112
- "qmd": null,
113
- "skf": "{skf_version}" // Resolution chain: _bmad/skf/package.json → npm require → _bmad/skf/VERSION → "unknown"
114
- },
115
- "stats": {
116
- "exports_documented": "{number of exports found}",
117
- "exports_public_api": "{number of exports found}",
118
- "exports_internal": 0,
119
- "exports_total": "{number of exports found}",
120
- "public_api_coverage": 1.0,
121
- "total_coverage": 1.0,
122
- "scripts_count": 0,
123
- "assets_count": 0
124
- },
125
- "dependencies": [],
126
- "compatibility": "{semver-range or null}"
127
- }
90
+ Run the shared renderer against the assembled state. The helper applies the constants, echoes input-derived fields, computes export counts and the ISO 8601 UTC timestamp, and emits the canonical envelope per `{skillTemplateData}` § "metadata.json Format".
91
+
92
+ **Resolve `{quickMetadataRenderer}`** from `{quickMetadataRendererProbeOrder}`; first existing path wins. If no candidate exists, fall back to in-prompt rendering using the constants + input-derived rules previously documented in this section (the helper replaces but does not eliminate those rules — they live in the helper now).
93
+
94
+ **Probe `tool_versions.skf` first** (the helper expects it as input — the filesystem walk stays here because the helper does no I/O):
95
+
96
+ 1. Read `{project-root}/_bmad/skf/package.json` take `version`
97
+ 2. If absent, read `{project-root}/_bmad/skf/VERSION`
98
+ 3. If absent, set to `"unknown"`
99
+
100
+ Build the input payload from the extraction inventory + step-01 resolution + the probed `tool_versions.skf` and pipe it to the renderer:
101
+
102
+ ```bash
103
+ echo '{"name":"<name>","version":"<v>","description":"<desc>","language":"<lang>","source_repo":"<url>","source_root":"<path or empty>","source_commit":"<source_ref or empty>","source_package":"<package or name>","exports":[{"name":"...","type":"..."}],"dependencies":["..."],"compatibility":"<semver-range or empty>","language_hint":<hint or null>,"scope_hint":<hint or null>,"skf_version":"<probed>"}' \
104
+ | python3 {quickMetadataRenderer}
128
105
  ```
129
106
 
107
+ The renderer emits the rendered metadata.json on stdout. Capture the output as `metadata` for the §5 preview and step-05 §2's deliverable write.
108
+
109
+ **Schema is canonical in the renderer.** When `{skillTemplateData}` adds a field, the renderer is updated to populate it (constants gain a row; input-derived fields gain a payload key). Do not hand-edit the rendered envelope here — the helper is the single source of truth for the population logic.
110
+
130
111
  ### 5. Present Compiled Output for Review
131
112
 
113
+ **If `{headless_mode}` is true** — skip the inline preview (no human reviewer reads it) and emit a one-line summary instead:
114
+
115
+ "Compiled: SKILL.md ({section_count} sections, {export_count} exports), context-snippet.md (~{snippet_token_count} tokens), metadata.json (version {version}, confidence {confidence}). Auto-approving [C]."
116
+
117
+ Then proceed directly to §6 — the GATE default action takes over.
118
+
119
+ **Otherwise (interactive mode):**
120
+
132
121
  "**Compilation complete. Review before validation:**
133
122
 
134
123
  ---
@@ -154,25 +143,29 @@ Generate metadata.json following the exact structure defined in {skillTemplateDa
154
143
  **Extraction confidence:** {confidence}
155
144
  **Exports documented:** {count}
156
145
 
157
- Review the output above. When ready, continue to validation."
146
+ Review the output above, then choose: [C] continue to validation, [E] edit the description, [S] adjust scope and re-extract, or [Q] quit without writing."
158
147
 
159
148
  ### 6. Present MENU OPTIONS
160
149
 
161
- Display: **Select:** [C] Continue to Validation
150
+ Display: **Select:** [C] Continue to Validation · [E] Edit description · [S] Adjust scope and re-extract · [Q] Quit without writing
162
151
 
163
152
  #### Menu Handling Logic:
164
153
 
165
- - IF C: Load, read entire file, then execute {nextStepFile}
166
- - IF Any other: Help user adjust compiled output, then redisplay menu
154
+ - **IF C** Load, read entire file, then execute {nextStepFile}.
155
+ - **IF E** Ask the user for a replacement description ("New description (1–1024 chars):"). Update SKILL.md frontmatter `description` and `metadata.json.description` in the in-memory compiled output, then re-render the §5 preview and redisplay this menu. Do not re-run extraction.
156
+ - **IF S** — Ask the user for an adjusted `scope_hint` ("New scope (e.g. `src/server/`, `packages/core/`):") and optionally a `language_hint`. Update the extraction context with the new hints, then load `./step-03-quick-extract.md` to re-extract. The new extraction returns to §1 of this step on completion. Discards the prior compiled output.
157
+ - **IF Q** — HARD HALT with **exit code 6 (compile-cancelled)** per the SKILL.md exit-code map: "Compilation cancelled. No files written." Before exiting, emit the error result contract per SKILL.md "Result Contract on HARD HALT" (`phase: "compile"`, `error.code: "compile-cancelled"`, `skill_package: null`). Do not proceed to validation; do not write any artifacts.
158
+ - **IF Any other** — Help the user adjust the compiled output (treated as a free-form revision request), then redisplay the menu.
167
159
 
168
160
  #### EXECUTION RULES:
169
161
 
170
162
  - ALWAYS halt and wait for user input after presenting compiled output
171
163
  - **GATE [default: C]** — If `{headless_mode}`: auto-proceed with [C] Continue, log: "headless: auto-approve compiled output"
172
- - ONLY proceed to validation when user selects 'C'
173
- - User can request changes to the compiled output before proceeding
164
+ - ONLY proceed to validation when the user selects 'C' (or headless auto-approve)
165
+ - [E] is local: re-renders the preview without re-running extraction
166
+ - [S] is the heavy option: it discards the compiled output and re-runs step-03 with new hints
174
167
 
175
168
  ## CRITICAL STEP COMPLETION NOTE
176
169
 
177
- ONLY WHEN the user reviews the compiled output and selects 'C' will you load and read fully `{nextStepFile}` to execute validation.
170
+ ONLY WHEN the user reviews the compiled output and selects 'C' (or [Q] HALTs / [S] re-runs / [E] redisplays) will you load and read fully `{nextStepFile}` to execute validation.
178
171