bmad-module-skill-forge 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/docs/_data/pinned.yaml +1 -1
- package/docs/workflows.md +34 -15
- package/package.json +2 -2
- 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 +29 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +73 -0
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +14 -0
- package/src/shared/scripts/skf-recommend-scope-type.py +369 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +293 -0
- package/src/shared/scripts/skf-write-skill-brief.py +509 -0
- package/src/skf-audit-skill/steps-c/step-01-init.md +49 -3
- package/src/skf-audit-skill/steps-c/step-03-structural-diff.md +6 -5
- 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-create-skill/steps-c/step-07-generate-artifacts.md +9 -1
- package/src/skf-export-skill/steps-c/step-01-load-skill.md +11 -3
- package/src/skf-export-skill/steps-c/step-03-generate-snippet.md +8 -2
- package/src/skf-export-skill/steps-c/step-04-update-context.md +29 -0
- package/src/skf-export-skill/steps-c/step-06-summary.md +12 -0
- package/src/skf-setup/steps-c/step-01b-ccc-index.md +10 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: './step-04-confirm-brief.md'
|
|
3
3
|
scopeTemplatesFile: 'assets/scope-templates.md'
|
|
4
|
+
recommendScopeTypeScript: '{project-root}/src/shared/scripts/skf-recommend-scope-type.py'
|
|
4
5
|
advancedElicitationSkill: '/bmad-advanced-elicitation'
|
|
5
6
|
partyModeSkill: '/bmad-party-mode'
|
|
6
7
|
---
|
|
@@ -16,6 +17,8 @@ To collaboratively define the skill's inclusion and exclusion boundaries using t
|
|
|
16
17
|
- Focus only on defining scope boundaries — do not write the brief yet (Step 05)
|
|
17
18
|
- Do not make scope decisions unilaterally — user drives all scope choices
|
|
18
19
|
- Produce: scope type, include patterns, exclude patterns
|
|
20
|
+
- All user-facing output in `{communication_language}`
|
|
21
|
+
- **Re-entry from step-04 [R] revise:** prior selections (`scope.type`, `scope.include`, `scope.exclude`, `scope.notes`, `scripts_intent`, `assets_intent`, supplemental `doc_urls`) are preserved as the current state. Re-present them at each section as the existing answer; the user only re-confirms or overrides. Do not reset to the §2c template menu unless the user explicitly asks to start scope over.
|
|
19
22
|
|
|
20
23
|
## MANDATORY SEQUENCE
|
|
21
24
|
|
|
@@ -65,6 +68,10 @@ Add, remove, or confirm these URLs."
|
|
|
65
68
|
|
|
66
69
|
Wait for confirmation. Record any changes to `doc_urls`.
|
|
67
70
|
|
|
71
|
+
HEAD-check the URLs in parallel — issue all N `curl -sI --max-time 5 {url}` calls in a **single message with N parallel Bash calls**, then process the responses together. On a 4xx/5xx, DNS failure, or timeout per URL, warn `"Could not reach {url} — {status or error}."` and offer the same correct/keep choice as step-01 §3. The check is best-effort — never HALT on a failed HEAD — but the failure must surface here so it is not discovered downstream during compilation.
|
|
72
|
+
|
|
73
|
+
**On re-entry from step-04 [R]:** if `doc_urls` is byte-identical to the list that was probed on the previous pass through this subsection AND the prior per-URL probe results are still recoverable from conversation context, skip the parallel HEAD-check and reuse those results. Re-running the probes when the list has not changed wastes round-trips and can flap on transient failures. Any addition, removal, or edit to a URL invalidates the cache — re-probe the entire updated set. If the prior results are not recoverable (long session, compaction, etc.), re-probe — never cache-hit on a list whose results you cannot cite.
|
|
74
|
+
|
|
68
75
|
**If no supplemental doc_urls were collected:** Skip this subsection.
|
|
69
76
|
|
|
70
77
|
**Scope guidance for first-time users:** A well-scoped skill covers one cohesive capability with 3-8 primary functions. If the scope includes unrelated concerns (e.g., authentication AND data visualization), suggest splitting into separate briefs. If the scope is too narrow (single utility function), suggest expanding to the surrounding capability surface.
|
|
@@ -73,11 +80,41 @@ Wait for confirmation. Record any changes to `doc_urls`.
|
|
|
73
80
|
|
|
74
81
|
Load `{scopeTemplatesFile}` for the scope type options ([F], [M], [P], [C], [R]) and their descriptions.
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
**Recommend a scope type — don't present the five options as equal weight.** SKILL.md states this workflow "steers toward the smaller, sharper version when scope is unclear" — surface that opinion at decision time. Use the analysis from step-02 and the user's intent from step-01 to pick the best-fit recommendation, then present the menu with that option marked as the suggested default.
|
|
84
|
+
|
|
85
|
+
**Delegate the recommendation to `{recommendScopeTypeScript}`** instead of walking the heuristic ladder in prose. The script is the single source of truth for the five-rule ladder (component-registry → reference-app keywords → specific-modules naming/count → narrow-public-api → default full-library) plus the docs-only short-circuit. Both the interactive recommendation and the §6 headless GATE invoke the same script — same inputs, same outputs, no drift.
|
|
86
|
+
|
|
87
|
+
**Fetch registry-file contents before building the payload.** Step-02 §4.1 fetches `package.json` plus the entry-point files but does not fetch `registry.ts` / `components.ts` — the deep-match branch of the component-registry rule needs those contents. Scan the tree for any of `registry.ts` / `registry.tsx` / `components.ts` / `components.tsx` (any depth). For each match, fetch its contents in **one message with N parallel Bash calls** (`gh api repos/{owner}/{repo}/contents/{path}` for GitHub, file reads for local), then base64-decode the responses together. Skip the fetch if the tree contains no registry files.
|
|
88
|
+
|
|
89
|
+
Build the payload and invoke:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
echo '{
|
|
93
|
+
"intent": "<combined intent + scope_hint text from step-01>",
|
|
94
|
+
"module_count": <count from step-02 §4.3>,
|
|
95
|
+
"export_count": <count from step-02 §4.3>,
|
|
96
|
+
"tree": [<flat list of repo-relative file paths from step-02 §1>],
|
|
97
|
+
"entry_files": [{"path": "<registry path>", "content": "<contents>"}, ...],
|
|
98
|
+
"source_type": "source",
|
|
99
|
+
"mode": "interactive"
|
|
100
|
+
}' | uv run {recommendScopeTypeScript}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`entry_files` carries the registry contents fetched above; omit when no registry files exist in the tree. `mode: "interactive"` activates the content-inspection branch of the component-registry rule (10+ entries or `Component[]` annotation); the headless GATE in §6 uses `mode: "headless"` which falls back to presence-only matching. `source_type: "docs-only"` short-circuits to `docs-only` regardless of the other signals.
|
|
104
|
+
|
|
105
|
+
The script returns `{scope_type, matched_heuristic, signals, rationale}`. Use `rationale` directly — it already names the specific signals that fired.
|
|
106
|
+
|
|
107
|
+
Present:
|
|
108
|
+
|
|
109
|
+
"**Recommended scope type: [{letter}] {Name}** — {rationale from the script}.
|
|
110
|
+
|
|
111
|
+
How broadly should this skill cover the library?
|
|
112
|
+
|
|
113
|
+
{full menu from `{scopeTemplatesFile}` with the recommended letter marked, e.g. '[F] Full Library', '[M] Specific Modules', '[P] Public API Only ← recommended', '[C] Component Library', '[R] Reference App'}
|
|
77
114
|
|
|
78
|
-
|
|
115
|
+
Press Enter to accept the recommendation, or pick a different letter."
|
|
79
116
|
|
|
80
|
-
Wait for user selection.
|
|
117
|
+
Wait for user selection. Empty input or just Enter accepts the recommendation; any of the five letters overrides.
|
|
81
118
|
|
|
82
119
|
### 3. Define Boundaries Based on Selection
|
|
83
120
|
|
|
@@ -128,19 +165,25 @@ Record the response as `scripts_intent` and `assets_intent` in the brief. Defaul
|
|
|
128
165
|
|
|
129
166
|
### 6. Present MENU OPTIONS
|
|
130
167
|
|
|
131
|
-
Display: **Select an Option:** [A] Advanced Elicitation [P] Party Mode [C] Continue to Brief Confirmation
|
|
168
|
+
Display: **Select an Option:** [A] Advanced Elicitation [P] Party Mode [C] Continue to Brief Confirmation [X] Cancel and exit
|
|
132
169
|
|
|
133
170
|
#### Menu Handling Logic:
|
|
134
171
|
|
|
135
172
|
- IF A: Invoke {advancedElicitationSkill}, and when finished redisplay the menu
|
|
136
173
|
- IF P: Invoke {partyModeSkill}, and when finished redisplay the menu
|
|
137
174
|
- IF C: Load, read entire file, then execute {nextStepFile}
|
|
175
|
+
- IF X: Treat as user-cancellation. Display `"Cancelled — no brief was written."` and HALT (exit code 6, `halt_reason: "user-cancelled"`). Cancellation here is non-destructive — no files have been written yet. `[X]` is interactive-only; the headless GATE never reaches this branch.
|
|
138
176
|
- IF Any other comments or queries: help user respond then [Redisplay Menu Options](#6-present-menu-options)
|
|
139
177
|
|
|
140
178
|
#### EXECUTION RULES:
|
|
141
179
|
|
|
142
180
|
- ALWAYS halt and wait for user input after presenting menu
|
|
143
|
-
- **GATE [default: C]** — If `{headless_mode}`:
|
|
181
|
+
- **GATE [default: C]** — If `{headless_mode}`: consume the headless inputs from step-01 in priority order:
|
|
182
|
+
- If `scope_type` was supplied, use it (must match one of the six valid types) and skip the §2c template menu.
|
|
183
|
+
- Otherwise auto-select via `{recommendScopeTypeScript}` — invoke the script with the **same payload shape** documented in §2c but with `mode: "headless"` (presence-only matching for the component-registry rule, since `entry_files` may not be available without an interactive context). Use the returned `scope_type` and log `"headless: scope_type={value} from heuristic={matched_heuristic}"`. The script's docs-only short-circuit handles `source_type=docs-only` automatically.
|
|
184
|
+
- If `include`/`exclude` were supplied, use them verbatim (split on comma) instead of running the boundary prompts in §3.
|
|
185
|
+
- If `scripts_intent`/`assets_intent` were supplied, record them and skip §5b; otherwise default to `detect`.
|
|
186
|
+
- Log: `"headless: scope_type={value} include={n} exclude={n} scripts_intent={value} assets_intent={value}"`.
|
|
144
187
|
- ONLY proceed to next step when user selects 'C'
|
|
145
188
|
- After other menu items execution, return to this menu
|
|
146
189
|
- User can chat or ask questions — always respond and then redisplay menu
|
|
@@ -16,16 +16,15 @@ To present the complete skill brief in human-readable format, highlighting all f
|
|
|
16
16
|
|
|
17
17
|
- Focus only on presenting and confirming — do not write files yet (Step 05)
|
|
18
18
|
- Do not proceed without explicit user approval (P2 confirmation gate)
|
|
19
|
+
- All user-facing output in `{communication_language}`
|
|
19
20
|
|
|
20
21
|
## MANDATORY SEQUENCE
|
|
21
22
|
|
|
22
23
|
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
|
|
23
24
|
|
|
24
|
-
### 1.
|
|
25
|
+
### 1. Assemble Complete Brief
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
### 2. Assemble Complete Brief
|
|
27
|
+
Use the values already accepted in steps 01-03 directly — do not re-load `{briefSchemaFile}` here. The 18 fields below are all in conversation; the schema is only consulted in §4 if an inline adjustment needs a specific field's validation rule cited.
|
|
29
28
|
|
|
30
29
|
Compile all gathered data from steps 01-03 into the complete brief:
|
|
31
30
|
|
|
@@ -48,11 +47,11 @@ Compile all gathered data from steps 01-03 into the complete brief:
|
|
|
48
47
|
- **assets_intent:** {detect/none/description from step 03, or "detect" if not explicitly set}
|
|
49
48
|
- **source_authority:** {official/community/internal from step 01 — default "community"}
|
|
50
49
|
|
|
51
|
-
###
|
|
50
|
+
### 2. Present Brief for Review
|
|
52
51
|
|
|
53
|
-
Using the
|
|
52
|
+
Using the format below:
|
|
54
53
|
|
|
55
|
-
"**
|
|
54
|
+
"**Review the complete skill brief before I write it.**
|
|
56
55
|
|
|
57
56
|
---
|
|
58
57
|
|
|
@@ -62,7 +61,7 @@ Skill Brief: {name}
|
|
|
62
61
|
|
|
63
62
|
Target: {source_repo}
|
|
64
63
|
Language: {language}
|
|
65
|
-
Forge Tier: {forge_tier}
|
|
64
|
+
Forge Tier: {forge_tier} — {tier_gloss}
|
|
66
65
|
Description: {description}
|
|
67
66
|
|
|
68
67
|
Scope: {scope.type}
|
|
@@ -79,9 +78,8 @@ Doc URLs:
|
|
|
79
78
|
Supplemental Docs:
|
|
80
79
|
{doc_urls, one per line with labels}
|
|
81
80
|
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
Assets: {assets_intent}
|
|
81
|
+
Scripts: {scripts_intent} — {scripts_gloss}
|
|
82
|
+
Assets: {assets_intent} — {assets_gloss}
|
|
85
83
|
|
|
86
84
|
Source Authority: {source_authority}
|
|
87
85
|
|
|
@@ -95,17 +93,31 @@ Created: {created}
|
|
|
95
93
|
Created by: {created_by}
|
|
96
94
|
```
|
|
97
95
|
|
|
98
|
-
---
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
Glosses (substitute the matching one-liner for `{tier_gloss}`, `{scripts_gloss}`, `{assets_gloss}` above so the user can decode each value at a glance):
|
|
99
|
+
|
|
100
|
+
- **Forge tier glosses** —
|
|
101
|
+
- `Quick`: text-only extraction; AST and semantic discovery off
|
|
102
|
+
- `Forge`: AST-grep on; semantic and re-ranking off
|
|
103
|
+
- `Forge+`: AST-grep + ccc semantic discovery; re-ranking off
|
|
104
|
+
- `Deep`: full pipeline — AST + ccc + qmd portfolio search + LLM re-ranking
|
|
105
|
+
|
|
106
|
+
- **`scripts_intent` / `assets_intent` glosses** —
|
|
107
|
+
- `detect`: SKF will scan source for the standard `scripts/`/`bin/`/`tools/`/`cli/` (or `assets/`/`templates/`/`schemas/`/`configs/`) directories during create-skill and decide automatically
|
|
108
|
+
- `none`: no script/asset packaging — create-skill will skip the detection pass
|
|
109
|
+
- free-text (anything else): a description of what to package; create-skill treats it as the user's spec
|
|
110
|
+
|
|
111
|
+
(For `docs-only` and `public-api` scope types the scripts/assets prompt is skipped in step-03 §5b — the values default to `detect` but the create-skill detection pass also no-ops for these scope types, so the gloss just clarifies that the recorded value will not actually fire any scan.)"
|
|
99
112
|
|
|
100
|
-
###
|
|
113
|
+
### 3. Highlight Items Needing Attention
|
|
101
114
|
|
|
102
115
|
Flag any fields that may need review:
|
|
103
116
|
|
|
104
117
|
{If language was overridden or low confidence:}
|
|
105
118
|
"**Note:** Language was {auto-detected / manually overridden}."
|
|
106
119
|
|
|
107
|
-
|
|
108
|
-
"**Note:** Description was derived from your stated intent. Adjust if needed."
|
|
120
|
+
"**Description:** synthesized and confirmed in step-01 §7b. This is the text agents read when deciding whether to route to your skill — refine here if you want to tighten it now that the full brief is visible."
|
|
109
121
|
|
|
110
122
|
{If forge tier was defaulted:}
|
|
111
123
|
"**Note:** Forge tier defaulted to Quick (no forge-tier.yaml found)."
|
|
@@ -123,16 +135,17 @@ You can:
|
|
|
123
135
|
- Revise scope boundaries by selecting [R]
|
|
124
136
|
- Proceed to write by selecting [C]"
|
|
125
137
|
|
|
126
|
-
###
|
|
138
|
+
### 4. Handle Inline Adjustments
|
|
127
139
|
|
|
128
140
|
If the user requests changes to specific fields (name, description, version, etc.):
|
|
141
|
+
- If the adjustment requires explaining a field's validation rule or allowed values, load `{briefSchemaFile}` now (otherwise skip the read — the common path does not need it)
|
|
129
142
|
- Make the adjustment
|
|
130
143
|
- Re-present the updated brief
|
|
131
144
|
- Return to the menu
|
|
132
145
|
|
|
133
|
-
###
|
|
146
|
+
### 5. Present MENU OPTIONS
|
|
134
147
|
|
|
135
|
-
Display: **Select an Option:** [R] Revise Scope [A] Advanced Elicitation [P] Party Mode [C] Approve and Write
|
|
148
|
+
Display: **Select an Option:** [R] Revise Scope [A] Advanced Elicitation [P] Party Mode [C] Approve and Write [X] Cancel and exit
|
|
136
149
|
|
|
137
150
|
#### Menu Handling Logic:
|
|
138
151
|
|
|
@@ -140,7 +153,8 @@ Display: **Select an Option:** [R] Revise Scope [A] Advanced Elicitation [P] Par
|
|
|
140
153
|
- IF A: Invoke {advancedElicitationSkill}, and when finished redisplay the menu
|
|
141
154
|
- IF P: Invoke {partyModeSkill}, and when finished redisplay the menu
|
|
142
155
|
- IF C: Load, read entire file, then execute {nextStepFile}
|
|
143
|
-
- IF
|
|
156
|
+
- IF X: Treat as user-cancellation. Display `"Cancelled — no brief was written."` and HALT (exit code 6, `halt_reason: "user-cancelled"`). Cancellation here is non-destructive — step-05 has not run, no skill-brief.yaml file exists yet. `[X]` is interactive-only; the headless GATE never reaches this branch.
|
|
157
|
+
- IF Any other comments or queries: help user respond, apply any field adjustments, re-present brief if changed, then [Redisplay Menu Options](#5-present-menu-options)
|
|
144
158
|
|
|
145
159
|
#### EXECUTION RULES:
|
|
146
160
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
briefSchemaFile: 'assets/skill-brief-schema.md'
|
|
3
|
+
versionResolutionFile: 'references/version-resolution.md'
|
|
4
|
+
qmdRegistrationFile: 'references/qmd-collection-registration.md'
|
|
3
5
|
nextStepFile: './step-06-health-check.md'
|
|
6
|
+
writeSkillBriefScript: '{project-root}/src/shared/scripts/skf-write-skill-brief.py'
|
|
7
|
+
emitBriefEnvelopeScript: '{project-root}/src/shared/scripts/skf-emit-brief-result-envelope.py'
|
|
8
|
+
forgeTierRwScript: '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
|
|
9
|
+
forgeTierFile: '{sidecar_path}/forge-tier.yaml'
|
|
4
10
|
---
|
|
5
11
|
|
|
6
12
|
# Step 5: Write Brief
|
|
@@ -15,145 +21,136 @@ To generate the complete skill-brief.yaml from the approved brief data and write
|
|
|
15
21
|
- Do not change any field values without user request — the brief was already approved
|
|
16
22
|
- Create the output directory if it doesn't exist
|
|
17
23
|
- Chains to the local health-check step via `{nextStepFile}` after completion — the user-facing success summary is NOT the terminal step
|
|
24
|
+
- All user-facing output in `{communication_language}`; written artifact (`description`, `notes`) in `{document_output_language}`
|
|
25
|
+
- **Determinism delegation:** YAML rendering, version-precedence, atomic write, the headless result envelope, and the QMD-collection registry mutation are all delegated to shared SKF scripts. The LLM's job in this step is to assemble inputs, branch on script results, and surface user-facing prose — not to render YAML, JSON envelopes, or YAML-mutation diffs in the model.
|
|
18
26
|
|
|
19
27
|
## MANDATORY SEQUENCE
|
|
20
28
|
|
|
21
29
|
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise unless user explicitly requests a change.
|
|
22
30
|
|
|
23
|
-
### 1.
|
|
31
|
+
### 1. Reference the Schema (LLM context only)
|
|
24
32
|
|
|
25
|
-
Load `{briefSchemaFile}` to
|
|
33
|
+
`{briefSchemaFile}` and `{versionResolutionFile}` document the brief contract for human readers. The deterministic enforcement of that contract lives in `{writeSkillBriefScript}` and its JSON Schema artifact at `src/shared/scripts/schemas/skill-brief.v1.json`. Load `{briefSchemaFile}` only if you need to explain a specific field to the user during inline adjustments — otherwise skip the read; the script is the source of truth.
|
|
26
34
|
|
|
27
|
-
### 2.
|
|
35
|
+
### 2. Resolve Output Path
|
|
28
36
|
|
|
29
|
-
|
|
37
|
+
Resolve the target write path:
|
|
38
|
+
- Primary: `{forge_data_folder}/{skill-name}/skill-brief.yaml`
|
|
39
|
+
- Fallback (when `{forge_data_folder}` is not set or doesn't exist): `{output_folder}/forge-data/{skill-name}/skill-brief.yaml` and inform user "**Note:** forge_data_folder not configured. Writing to {output_folder}/forge-data/{skill-name}/ instead."
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
- Fall back to `{output_folder}/forge-data/{skill-name}/`
|
|
33
|
-
- Inform user: "**Note:** forge_data_folder not configured. Writing to {output_folder}/forge-data/{skill-name}/ instead."
|
|
41
|
+
The script's atomic-write helper creates parent directories as needed (`mkdir -p`) — no separate mkdir call required.
|
|
34
42
|
|
|
35
|
-
###
|
|
43
|
+
### 2b. Existing Brief — Overwrite Policy
|
|
36
44
|
|
|
37
|
-
|
|
45
|
+
Before writing, check whether the resolved target path already exists.
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
- Otherwise, use the auto-detected source version from step 02, or `1.0.0` if none was detected.
|
|
47
|
+
**Interactive (`{headless_mode}` is false):**
|
|
41
48
|
|
|
42
|
-
|
|
49
|
+
If the file exists, present:
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
"**An existing brief was found at `{path}`.**
|
|
52
|
+
Overwrite it with the brief you just approved? [Y/N]"
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
name: "{approved skill name}"
|
|
49
|
-
version: "{resolved version — target_version if set, else detected source version, else 1.0.0}"
|
|
50
|
-
source_type: "{source or docs-only}"
|
|
51
|
-
source_repo: "{approved source repo or doc site URL}"
|
|
52
|
-
language: "{approved language}"
|
|
53
|
-
description: "{approved description}"
|
|
54
|
-
forge_tier: "{approved forge tier}"
|
|
55
|
-
created: "{current date}"
|
|
56
|
-
created_by: "{user_name}"
|
|
57
|
-
scope:
|
|
58
|
-
type: "{approved scope type}"
|
|
59
|
-
include:
|
|
60
|
-
- "{approved include patterns}"
|
|
61
|
-
exclude:
|
|
62
|
-
- "{approved exclude patterns}"
|
|
63
|
-
notes: "{approved scope notes or empty string}"
|
|
64
|
-
---
|
|
65
|
-
```
|
|
54
|
+
- **[Y]** Overwrite — proceed to §3.
|
|
55
|
+
- **[N]** Cancel — emit a single-line stderr log `brief-skill: overwrite-cancelled at {path}` and HALT with exit code 5 (do not chain to step-06; the run produced no new artifact).
|
|
66
56
|
|
|
67
|
-
**
|
|
57
|
+
**Headless (`{headless_mode}` is true):**
|
|
68
58
|
|
|
69
|
-
|
|
70
|
-
Include the `target_version` field in the generated YAML — its value MUST be identical to the `version` field above:
|
|
71
|
-
```yaml
|
|
72
|
-
target_version: "{target_version — same value as version}"
|
|
73
|
-
```
|
|
59
|
+
If the file exists:
|
|
74
60
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```yaml
|
|
78
|
-
doc_urls:
|
|
79
|
-
- url: "{documentation URL}"
|
|
80
|
-
label: "{page label}"
|
|
81
|
-
```
|
|
82
|
-
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.
|
|
61
|
+
- If `force` was supplied as a headless argument: log `"headless: force-overwriting existing brief at {path}"` and proceed to §3.
|
|
62
|
+
- Otherwise: emit the error envelope per §4b with `halt_reason: "overwrite-cancelled"`, then HALT with exit code 5.
|
|
83
63
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
scripts_intent: "{none or description}"
|
|
88
|
-
```
|
|
64
|
+
If the file does not exist, proceed normally.
|
|
65
|
+
|
|
66
|
+
### 3. Write the Brief
|
|
89
67
|
|
|
90
|
-
**
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
|
|
68
|
+
Assemble the brief context as a **flat** JSON object — every approved value is a top-level key, scope is split across four `scope_*` keys instead of nested, and every optional field is passed as `null` when not set rather than conditionally omitted. This eliminates the "decide what to omit" cognitive load that previously made this the most expensive HALT-typo site in the workflow:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"name": "{approved skill name}",
|
|
73
|
+
"target_version": "{target_version from step 01, or null}",
|
|
74
|
+
"detected_version": "{auto-detected version from step 02, or null}",
|
|
75
|
+
"source_type": "{source or docs-only}",
|
|
76
|
+
"source_repo": "{approved source repo or doc site URL}",
|
|
77
|
+
"language": "{approved language}",
|
|
78
|
+
"description": "{approved description}",
|
|
79
|
+
"forge_tier": "{Quick|Forge|Forge+|Deep}",
|
|
80
|
+
"created": "{current ISO date YYYY-MM-DD}",
|
|
81
|
+
"created_by": "{user_name}",
|
|
82
|
+
"scope_type": "{approved scope type}",
|
|
83
|
+
"scope_include": ["{approved include patterns}"],
|
|
84
|
+
"scope_exclude": ["{approved exclude patterns}"],
|
|
85
|
+
"scope_notes": "{approved scope notes or empty string}",
|
|
86
|
+
"doc_urls": null | [{"url": "...", "label": "..."}],
|
|
87
|
+
"scripts_intent": null | "{detect|none|free-text}",
|
|
88
|
+
"assets_intent": null | "{detect|none|free-text}",
|
|
89
|
+
"source_authority": null | "{official|community|internal}"
|
|
90
|
+
}
|
|
94
91
|
```
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
Pipe it into the writer script with the `--from-flat` flag:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
echo '<context-json>' | uv run {writeSkillBriefScript} write --target {resolved-target-path} --from-flat
|
|
99
97
|
```
|
|
100
98
|
|
|
101
|
-
|
|
99
|
+
The script translates flat → nested internally, drops the null optional fields, and runs the same schema validation and atomic write as before — pass every key always, the writer decides what reaches the YAML.
|
|
102
100
|
|
|
103
|
-
|
|
101
|
+
The script:
|
|
102
|
+
- Validates the context against `src/shared/scripts/schemas/skill-brief.v1.json`
|
|
103
|
+
- Applies the version-precedence rule from `{versionResolutionFile}`
|
|
104
|
+
- Enforces the `target_version == version` invariant (refuses to write a brief that violates it)
|
|
105
|
+
- Renders YAML in canonical key order (byte-stable across runs)
|
|
106
|
+
- Atomically writes the file via temp + fsync + rename (no half-written file ever visible)
|
|
107
|
+
- Emits a JSON success envelope on stdout: `{"status":"ok","brief_path":"…","version":"…","bytes":…,"warnings":[…]}`
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
**On script failure (non-zero exit):**
|
|
110
|
+
- Exit 1 (validation/invariant): The error JSON on stderr names the offending field. This indicates a context-assembly bug, not a user error — surface the message to the user, log it, then HALT.
|
|
111
|
+
- Interactive: **HALT** — display the error JSON's `message` field.
|
|
112
|
+
- Headless: emit the error envelope per §4b with `halt_reason: "input-invalid"`, then `exit 2`.
|
|
113
|
+
- Exit 2 (I/O failure): The atomic write failed (target unwritable, disk full, etc.).
|
|
114
|
+
- Interactive: **HALT** — "**Error:** Failed to write skill-brief.yaml. Check that the directory is writable and try again."
|
|
115
|
+
- Headless: emit the error envelope per §4b with `halt_reason: "write-failed"`, then `exit 4`.
|
|
106
116
|
|
|
107
|
-
|
|
117
|
+
**On success:** capture `brief_path` and `version` from the response envelope — both are needed for §4b and §6.
|
|
108
118
|
|
|
109
|
-
**
|
|
119
|
+
**Draft cleanup.** After a successful write, remove `{forge_data_folder}/{skill-name}/.brief-draft.json` if it exists (`rm -f` — silent on absent). The draft was a step-01 §7 checkpoint covering the in-flight workflow window; once the brief is written it is no longer meaningful. In headless mode this rm is a no-op (drafts are only written interactively).
|
|
110
120
|
|
|
111
|
-
|
|
121
|
+
### 4b. Headless Result Envelope (Canonical)
|
|
112
122
|
|
|
113
|
-
|
|
123
|
+
This section is the canonical envelope-emission reference for the workflow. Every headless emission — the success terminal here and every HARD HALT in step-01/02/05 — uses this contract. Remote sites point here instead of restating it.
|
|
124
|
+
|
|
125
|
+
**Success (this call site only — emitted from §3 directly):**
|
|
114
126
|
|
|
115
|
-
Create a QMD collection targeting only the brief file:
|
|
116
127
|
```bash
|
|
117
|
-
|
|
118
|
-
|
|
128
|
+
echo '{"status":"success","brief_path":"<from §3 response>","skill_name":"<name>","version":"<from §3 response>","language":"<language>","scope_type":"<scope.type>","halt_reason":null}' | \
|
|
129
|
+
uv run {emitBriefEnvelopeScript} emit
|
|
119
130
|
```
|
|
120
131
|
|
|
121
|
-
|
|
132
|
+
**Error (used by every HARD HALT site):**
|
|
133
|
+
|
|
122
134
|
```bash
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
qmd embed
|
|
135
|
+
echo '{"status":"error","skill_name":"<name>","halt_reason":"<reason>"}' | \
|
|
136
|
+
uv run {emitBriefEnvelopeScript} emit --target stderr
|
|
126
137
|
```
|
|
127
138
|
|
|
128
|
-
|
|
139
|
+
When the HALT fires before `skill_name` has been resolved (step-01 §1 pre-flight write probe, step-01 §8 input-missing on a malformed args bundle), pass the partially-gathered value or the literal `"unknown"` — the script accepts any non-empty string at this position.
|
|
129
140
|
|
|
130
|
-
|
|
131
|
-
- Run `qmd status` or `qmd collection list` and confirm `{skill-name}-brief` shows document count > 0
|
|
132
|
-
- If verification succeeds: proceed to registry update normally
|
|
133
|
-
- 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
|
|
141
|
+
The script derives `exit_code` deterministically from `halt_reason` (null→0, input-missing/input-invalid→2, forge-tier-missing/target-inaccessible/gh-auth-failed→3, write-failed→4, overwrite-cancelled→5, user-cancelled→6 [interactive-only — headless never raises this]), validates against `src/shared/scripts/schemas/skf-brief-result-envelope.v1.json`, and prints the prefixed `SKF_BRIEF_RESULT_JSON: {…}` line.
|
|
134
142
|
|
|
135
|
-
|
|
143
|
+
The script enforces the success/error halt_reason invariant (success requires null halt_reason; error requires non-null). The `user-cancelled` halt_reason is accepted for completeness (interactive `[X]` Cancel sites in step-01/03/04) but never appears on the headless code path.
|
|
136
144
|
|
|
137
|
-
|
|
145
|
+
Invocation sites (each pointed at this block, not duplicated): step-01 §1 (write-failed pre-resolution), step-01 §8 (input-missing/input-invalid GATE), step-05 §2b (overwrite-cancelled), step-05 §3 (input-invalid/write-failed from script). Step-01 §1 forge-tier-missing and step-02 §1 (target-inaccessible/gh-auth-failed) currently HALT without emitting an envelope — pre-existing gaps tracked separately, not addressed by this canonicalization.
|
|
138
146
|
|
|
139
|
-
|
|
147
|
+
When `{headless_mode}` is false, skip this section silently — no envelope is emitted.
|
|
140
148
|
|
|
141
|
-
|
|
142
|
-
- name: "{skill-name}-brief"
|
|
143
|
-
type: "brief"
|
|
144
|
-
source_workflow: "brief-skill"
|
|
145
|
-
skill_name: "{skill-name}"
|
|
146
|
-
created_at: "{current ISO date}"
|
|
147
|
-
# status: "pending" # Added only when embed verification fails
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
Write the updated forge-tier.yaml.
|
|
149
|
+
### 5. QMD Collection Registration (Deep Tier Only)
|
|
151
150
|
|
|
152
|
-
**
|
|
153
|
-
- If QMD collection creation fails: log the error. Do NOT fail the workflow — the brief file was already written successfully.
|
|
154
|
-
- If forge-tier.yaml update fails: log the error, continue.
|
|
151
|
+
**IF forge tier is Deep AND QMD tool is available:** load `{qmdRegistrationFile}` and follow the procedure there to index the brief into a QMD collection and update the forge-tier registry.
|
|
155
152
|
|
|
156
|
-
**IF forge tier is NOT Deep:**
|
|
153
|
+
**IF forge tier is NOT Deep OR QMD is not available:** skip this section silently — do not load `{qmdRegistrationFile}`. No messaging.
|
|
157
154
|
|
|
158
155
|
### 6. Display Success Summary
|
|
159
156
|
|
|
@@ -161,7 +158,7 @@ Write the updated forge-tier.yaml.
|
|
|
161
158
|
|
|
162
159
|
---
|
|
163
160
|
|
|
164
|
-
**File:** `{
|
|
161
|
+
**File:** `{brief_path from §3 response}`
|
|
165
162
|
**Skill:** {name}
|
|
166
163
|
**Language:** {language}
|
|
167
164
|
**Scope:** {scope type}
|
|
@@ -190,4 +187,3 @@ ONLY WHEN the brief file has been written and the success summary displayed will
|
|
|
190
187
|
## CRITICAL STEP COMPLETION NOTE
|
|
191
188
|
|
|
192
189
|
This step chains to the local health-check step (`{nextStepFile}`), which in turn delegates to `shared/health-check.md`. After the health check completes, the brief-skill workflow is fully done.
|
|
193
|
-
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
# `shared/health-check.md` resolves relative to the SKF module root
|
|
3
|
-
# (`_bmad/skf/` when installed, `src/`
|
|
4
|
-
# to this step file.
|
|
3
|
+
# (`{project-root}/_bmad/skf/` when installed, `{project-root}/src/`
|
|
4
|
+
# during development), NOT relative to this step file.
|
|
5
5
|
nextStepFile: 'shared/health-check.md'
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -16,7 +16,16 @@ Chain to the shared workflow self-improvement health check at `{nextStepFile}`.
|
|
|
16
16
|
- No user-facing reports, file writes, or result contracts in this step — those belong in step-05
|
|
17
17
|
- Delegate directly to `{nextStepFile}` with no additional commentary
|
|
18
18
|
- Do not attempt any other action between loading this step and executing `{nextStepFile}`
|
|
19
|
+
- All user-facing output in `{communication_language}`
|
|
19
20
|
|
|
20
21
|
## MANDATORY SEQUENCE
|
|
21
22
|
|
|
22
23
|
Load `{nextStepFile}`, read it fully, then execute it.
|
|
24
|
+
|
|
25
|
+
## Completion criteria
|
|
26
|
+
|
|
27
|
+
This is the terminal step of brief-skill. The workflow is complete when `{nextStepFile}` returns control — do not transition to any further step.
|
|
28
|
+
|
|
29
|
+
## CRITICAL STEP COMPLETION NOTE
|
|
30
|
+
|
|
31
|
+
Step 06 is the terminal stage of brief-skill. After `{nextStepFile}` returns control, the brief-skill workflow is fully complete — do not re-enter step-05 or step-06, do not load any further step file, and do not loop back into the workflow.
|
|
@@ -106,11 +106,19 @@ The helper returns non-zero (exit 2) if `{skill_group}/active` already exists as
|
|
|
106
106
|
### 5. Verify Write Completion
|
|
107
107
|
|
|
108
108
|
After all files are written, verify:
|
|
109
|
-
- All 4 deliverable artifact types exist (SKILL.md, context-snippet.md, metadata.json, at least one file in references
|
|
109
|
+
- All 4 deliverable artifact types exist (SKILL.md, context-snippet.md, metadata.json, **and** either at least one file in `references/` **or** `references/` is empty AND Tier-2 content is inline in SKILL.md — see "Empty `references/` exception" below), all 3 workspace artifacts exist (provenance-map.json, evidence-report.md, extraction-rules.yaml), plus scripts/ and assets/ files when inventories are non-empty
|
|
110
110
|
- The `active` symlink at `{skill_group}/active` resolves to `{version}`
|
|
111
111
|
- Store `ref_count` = count of files written to `references/` for use in step-08 report
|
|
112
112
|
- List each file with its path and size
|
|
113
113
|
|
|
114
|
+
**Empty `references/` exception (Tier-2 inline):** `ref_count == 0` is a valid completion state when step-06 kept Tier-2 content inline in SKILL.md — e.g., the body was already under the size limit, or `skill-check` was unavailable and the manual fallback (step-06 §3) skipped the split. In that case, append a single line to `{forge_version}/evidence-report.md` recording the inline state so downstream tooling and audits can distinguish "inline by design" from "split-body skipped due to error":
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
ref_count: 0 # Tier-2 kept inline in SKILL.md (no split performed in step-06)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
When `ref_count > 0` is expected (because step-06 ran a split) but no files were written, halt with: "Split-body produced zero reference files. Investigate step-06 output before retrying — empty `references/` after a split is never a valid state."
|
|
121
|
+
|
|
114
122
|
**If any write failed:**
|
|
115
123
|
Halt with: "Artifact generation failed: could not write `{file_path}`. Check permissions and disk space."
|
|
116
124
|
|
|
@@ -165,14 +165,22 @@ Load `{sidecar_path}/preferences.yaml` (if exists):
|
|
|
165
165
|
|
|
166
166
|
### 4b. Check Test Report (Quality Gate)
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
`skf-test-skill` writes timestamped test-report filenames (`test-report-{skill_name}-{ISO-TIMESTAMP}-{HASH}.md`) — there is no exact-name `test-report-{skill_name}.md` on disk. Locate the most recent report by glob, not by exact filename:
|
|
169
|
+
|
|
170
|
+
1. Glob `{forge_data_folder}/{skill_name}/{active_version}/test-report-{skill_name}-*.md` (i.e. `{forge_version}/test-report-{skill_name}-*.md`). Sort matches descending by the parsed ISO-timestamp segment in the filename (`YYYYMMDDTHHMMSSZ` between the skill name and the hash — `sort -r` on the filename works because the timestamp is the first variable component). Take the first match.
|
|
171
|
+
2. If the versioned glob returns nothing, fall back to the same glob at the flat path `{forge_data_folder}/{skill_name}/test-report-{skill_name}-*.md`. Pick the newest by parsed timestamp.
|
|
172
|
+
3. If neither glob returns anything, look for the stable companion `skf-test-skill-result-latest.json` in the same two directories (versioned first, then flat). Read the report path from `outputs[]` per the canonical contract documented at `shared/references/output-contract-schema.md` (resolved by skf-test-skill step-06 §4c) and load that file.
|
|
173
|
+
4. If all three lookups fail, the skill has no test report.
|
|
174
|
+
|
|
175
|
+
**If a test report is found:**
|
|
169
176
|
|
|
170
|
-
**If test report found:**
|
|
171
177
|
- Read frontmatter `testResult` and `score`
|
|
172
178
|
- If `testResult: fail`: warn: "**Warning:** This skill failed its last test (score: {score}%). Consider running `@Ferris TS` and addressing gaps before export."
|
|
173
179
|
- If `testResult: pass`: note: "Last test: **PASS** ({score}%)"
|
|
180
|
+
- Always surface the actual file picked in the message (e.g. `test-report-my-base-ui-20260507T050917Z-487606-9b2f.md`) — not the no-longer-existent `test-report-{skill_name}.md` — so an operator can navigate to the report from the log.
|
|
181
|
+
|
|
182
|
+
**If no test report found** (all three lookups returned nothing):
|
|
174
183
|
|
|
175
|
-
**If no test report found:**
|
|
176
184
|
- Warn: "**Note:** No test report found for this skill. Consider running `@Ferris TS` before export to verify completeness."
|
|
177
185
|
|
|
178
186
|
Continue to step 5 regardless — this is advisory, not blocking.
|
|
@@ -62,9 +62,15 @@ Store `{skill_root}` for use in snippet generation. The context-snippet.md writt
|
|
|
62
62
|
3. Read SKILL.md to extract: heading slugs for `#quick-start` and `#key-types`, inline summary of key types (~10 words)
|
|
63
63
|
4. **Anchor verification (split-body awareness):** For each section anchor (`#quick-start`, `#key-types`), verify the heading exists in SKILL.md. If a `references/` directory exists and `## Full` headings in SKILL.md are absent or stubs (indicating split-body, not a stack skill's structural references), rewrite the anchor to point to the reference file path (e.g., `references/{file}.md#key-types`). If the heading cannot be resolved in either location, omit that anchor line from the snippet.
|
|
64
64
|
5. Derive gotchas from: T2-future annotations in evidence report (breaking changes), async requirements, version-specific behavior.
|
|
65
|
+
|
|
66
|
+
**Detect first-export state before applying carry-forward logic.** The `[CARRIED]` one-cycle expiry is meaningful only on a *re-export*. On a first export, the prior `context-snippet.md` was authored by `create-skill` (or `update-skill`) from the evidence report inside the same forge cycle — those gotchas are freshly derived, not "left over from a previous export." Treating them as carry-forward primes them for premature expiry on the second export.
|
|
67
|
+
|
|
68
|
+
Read `{skills_output_folder}/.export-manifest.json` (the same file step-04 §4a will rewrite). If the skill name is **absent** from `exports`, OR present with no resolvable `last_exported` for any version under `versions`, this is a first export — set `is_first_export = true`. Otherwise `is_first_export = false`. (Step-04 reads the manifest authoritatively for the rebuild; this read is the lightweight probe step-03 needs to choose the right branch below.)
|
|
69
|
+
|
|
65
70
|
- **If new gotchas are derived:** Use them (they supersede any prior gotchas). Write as `|gotchas: {pitfall-1}, {pitfall-2}` with no marker.
|
|
66
|
-
- **If NO new gotchas are derived
|
|
67
|
-
- **If NO new gotchas are derived
|
|
71
|
+
- **If NO new gotchas are derived AND `is_first_export == true` AND `prior_gotchas` exists:** Treat the prior gotchas as **freshly derived** by create-skill/update-skill — write them **without** the `[CARRIED]` marker. (The marker only applies to re-exports.) No warning needed; this is the normal first-export shape.
|
|
72
|
+
- **If NO new gotchas are derived BUT `prior_gotchas` exists AND `is_first_export == false` AND `prior_gotchas_already_carried == false`:** First carry-forward cycle on a re-export — preserve the prior gotchas line, prefixing the value with `[CARRIED]` so the next export can detect that expiry has been reached. Write as `|gotchas: [CARRIED] {prior gotchas content}`. Emit warning: "**Gotchas preserved from prior export (one-cycle carry-forward).** These gotchas will be DROPPED on the next export unless new gotchas are derived or you manually refresh them. Review now if they are still applicable."
|
|
73
|
+
- **If NO new gotchas are derived AND `prior_gotchas` exists AND `prior_gotchas_already_carried == true`:** Expiry reached (re-export only — first-export branch above takes precedence) — drop the gotchas line entirely. Emit warning: "**Stale gotchas dropped** — the prior gotchas were already carried forward once and cannot be derived from the current evidence report. The snippet now has no gotchas line. If the prior gotchas are still relevant, re-add them to the evidence report's T2-future section and re-run export."
|
|
68
74
|
- **If NO new gotchas derived AND no `prior_gotchas`:** Omit the gotchas line.
|
|
69
75
|
|
|
70
76
|
Generate:
|