bmad-module-skill-forge 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/docs/_data/pinned.yaml +1 -1
- package/docs/skill-model.md +26 -32
- package/docs/troubleshooting.md +12 -0
- package/docs/workflows.md +87 -15
- package/package.json +2 -2
- package/src/shared/references/output-contract-schema.md +10 -0
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +58 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +77 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +44 -0
- package/src/shared/scripts/skf-detect-language.py +277 -0
- package/src/shared/scripts/skf-detect-workspaces.py +427 -0
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +257 -0
- package/src/shared/scripts/skf-extract-public-api.py +534 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +73 -0
- package/src/shared/scripts/skf-recommend-scope-type.py +369 -0
- package/src/shared/scripts/skf-render-quick-metadata.py +192 -0
- package/src/shared/scripts/skf-resolve-package.py +264 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +293 -0
- package/src/shared/scripts/skf-validate-output.py +24 -7
- package/src/shared/scripts/skf-write-skill-brief.py +509 -0
- package/src/skf-brief-skill/SKILL.md +41 -12
- package/src/skf-brief-skill/assets/description-voice-examples.md +19 -0
- package/src/skf-brief-skill/assets/scope-templates.md +5 -0
- package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -40
- package/src/skf-brief-skill/references/draft-checkpoint.md +46 -0
- package/src/skf-brief-skill/references/headless-args.md +22 -0
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +26 -0
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +35 -0
- package/src/skf-brief-skill/references/qmd-collection-registration.md +52 -0
- package/src/skf-brief-skill/references/version-resolution.md +46 -0
- package/src/skf-brief-skill/steps-c/step-01-gather-intent.md +164 -14
- package/src/skf-brief-skill/steps-c/step-02-analyze-target.md +118 -50
- package/src/skf-brief-skill/steps-c/step-03-scope-definition.md +48 -5
- package/src/skf-brief-skill/steps-c/step-04-confirm-brief.md +33 -19
- package/src/skf-brief-skill/steps-c/step-05-write-brief.md +93 -97
- package/src/skf-brief-skill/steps-c/step-06-health-check.md +11 -2
- package/src/skf-quick-skill/SKILL.md +178 -10
- package/src/skf-quick-skill/assets/skill-template.md +5 -1
- package/src/skf-quick-skill/references/registry-resolution.md +2 -0
- package/src/skf-quick-skill/steps-c/step-01-resolve-target.md +84 -16
- package/src/skf-quick-skill/steps-c/step-02-ecosystem-check.md +3 -3
- package/src/skf-quick-skill/steps-c/step-03-quick-extract.md +86 -43
- package/src/skf-quick-skill/steps-c/step-04-compile.md +49 -56
- package/src/skf-quick-skill/steps-c/step-05-write-and-validate.md +164 -0
- package/src/skf-quick-skill/steps-c/{step-06-write.md → step-06-finalize.md} +15 -7
- package/src/skf-quick-skill/steps-c/step-07-health-check.md +5 -3
- package/src/skf-quick-skill/steps-c/step-05-validate.md +0 -193
|
@@ -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
|
-
|
|
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
|
-
|
|
86
|
+
```bash
|
|
87
|
+
python3 {packageResolver} {package_name} --timeout 10
|
|
88
|
+
```
|
|
63
89
|
|
|
64
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
###
|
|
40
|
+
### 1.5. Repo-Shape Sniff
|
|
36
41
|
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
47
|
-
- **
|
|
48
|
-
- **
|
|
49
|
-
- **
|
|
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
|
-
|
|
77
|
+
**If `scope_hint` provided:** focus the entry-point fetch on the specified directories instead of repo root.
|
|
52
78
|
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
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
|
|
166
|
-
- IF
|
|
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
|
-
-
|
|
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
|
|