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,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.
|
|
@@ -23,6 +23,12 @@ These rules apply to every step in this workflow:
|
|
|
23
23
|
- Only load one step file at a time — never preload future steps
|
|
24
24
|
- Always communicate in `{communication_language}`
|
|
25
25
|
- If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
|
|
26
|
+
- If `{headless_mode}` is true, emit a single-line JSON progress event to **stderr** at each step's entry and exit so pipeline schedulers can stream live progress instead of post-mortem-parsing the result contract:
|
|
27
|
+
- entry: `{"step":N,"name":"<slug>","status":"start"}`
|
|
28
|
+
- exit (just before chaining to nextStepFile): `{"step":N,"name":"<slug>","status":"done"}`
|
|
29
|
+
- on HARD HALT: `{"step":N,"name":"<slug>","status":"halt","exit":<code>}` instead of "done"
|
|
30
|
+
|
|
31
|
+
`N` is the step number (1–7) and `<slug>` is the kebab portion of the filename after the number — `resolve-target`, `ecosystem-check`, `quick-extract`, `compile`, `write-and-validate`, `finalize`, `health-check`. One line per event; do not pretty-print.
|
|
26
32
|
|
|
27
33
|
## Stages
|
|
28
34
|
|
|
@@ -32,25 +38,187 @@ These rules apply to every step in this workflow:
|
|
|
32
38
|
| 2 | Ecosystem Check | steps-c/step-02-ecosystem-check.md | Yes |
|
|
33
39
|
| 3 | Quick Extract | steps-c/step-03-quick-extract.md | Yes |
|
|
34
40
|
| 4 | Compile | steps-c/step-04-compile.md | No (review) |
|
|
35
|
-
| 5 | Write & Validate | steps-c/step-05-validate.md | Yes |
|
|
36
|
-
| 6 | Finalize | steps-c/step-06-
|
|
41
|
+
| 5 | Write & Validate | steps-c/step-05-write-and-validate.md | Yes |
|
|
42
|
+
| 6 | Finalize | steps-c/step-06-finalize.md | Yes |
|
|
37
43
|
| 7 | Workflow Health Check | steps-c/step-07-health-check.md | Yes |
|
|
38
44
|
|
|
39
45
|
## Invocation Contract
|
|
40
46
|
|
|
41
47
|
| Aspect | Detail |
|
|
42
48
|
|--------|--------|
|
|
43
|
-
| **Inputs** | target (GitHub URL or package name) [required], language_hint [optional], scope_hint [optional] |
|
|
44
|
-
| **
|
|
45
|
-
| **
|
|
49
|
+
| **Inputs** | target (GitHub URL or package name) [required for single-target mode], language_hint [optional], scope_hint [optional] |
|
|
50
|
+
| **Overrides** | `--description`, `--exports`, `--skip-snippet`, `--no-active-pointer`, `--batch <file>`, `--fail-fast` — see On Activation step 3 |
|
|
51
|
+
| **Gates** | step-01: Input Gate [use args]; step-02: Choice Gate [P] (if match); step-04: Review Gate [C/E/S/Q] |
|
|
52
|
+
| **Outputs** | SKILL.md, context-snippet.md, metadata.json, active pointer, result contract (timestamped + `-latest` copy). Snippet and active pointer can be skipped per overrides. |
|
|
46
53
|
| **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true |
|
|
54
|
+
| **Exit codes** | See "Exit Codes" below |
|
|
55
|
+
|
|
56
|
+
## Exit Codes
|
|
57
|
+
|
|
58
|
+
Every HARD HALT in this workflow exits with a stable, documented code so headless automators can branch on the failure class without grepping message text:
|
|
59
|
+
|
|
60
|
+
| Code | Meaning | Raised by |
|
|
61
|
+
| ---- | ---------------------- | ----------------------------------------------------------- |
|
|
62
|
+
| 0 | success | step-07 (terminal) |
|
|
63
|
+
| 3 | resolution-failure | step-01 §2c (prose input), step-01 §3 (registry chain failed) |
|
|
64
|
+
| 4 | write-failure | step-05 §2 (deliverable write failed) |
|
|
65
|
+
| 5 | overwrite-cancelled | step-05 §1 (user selected [N]) |
|
|
66
|
+
| 6 | compile-cancelled | step-04 §6 (user selected [Q]) |
|
|
67
|
+
| 7 | finalize-blocked | step-06 §1 (active-pointer flip refused — non-link in place) |
|
|
68
|
+
|
|
69
|
+
Reserved: `validator-missing` may be promoted from advisory log to fatal exit code in a future revision; consumers should not assume code 8+ is unused.
|
|
70
|
+
|
|
71
|
+
## Result Contract on HARD HALT
|
|
72
|
+
|
|
73
|
+
In addition to the success-variant result contract written by step-06 §3, every HARD HALT must surface an **error variant** so headless automators don't silently break when `quick-skill-result-latest.json` is missing on failed runs.
|
|
74
|
+
|
|
75
|
+
**Always (every HARD HALT, regardless of phase)** — emit a single line on **stderr**:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
SKF_QUICK_SKILL_RESULT_JSON: {"status":"error","exit_code":<N>,"phase":"<slug>","error":{"code":"<class>","message":"<short>"},"outputs":{},"summary":{},"skill_package":"<path-or-null>"}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
One line, no pretty-print. Matches the prefix-and-envelope convention used by `skf-emit-result-envelope.py`.
|
|
82
|
+
|
|
83
|
+
**Additionally, when `{skill_package}` is known** (HALT at step-05 §1 onward) — write the same JSON object (without the `SKF_QUICK_SKILL_RESULT_JSON: ` prefix) to disk:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
{skill_package}/quick-skill-result-{YYYYMMDD-HHmmss}.json
|
|
87
|
+
{skill_package}/quick-skill-result-latest.json (copy, not symlink)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
so consumers that hardcode the `-latest.json` path see a deterministic file even on failed runs. HALTs at step-01/02/03/04 cannot write to disk because `{skill_package}` is computed only in step-05 §1; for those, the stderr envelope plus exit code is the contract.
|
|
91
|
+
|
|
92
|
+
**Schema:**
|
|
93
|
+
|
|
94
|
+
| Field | Type | Notes |
|
|
95
|
+
| --------------- | -------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `status` | string | always `"error"` for HARD HALTs |
|
|
97
|
+
| `exit_code` | integer | matches the Exit Codes table |
|
|
98
|
+
| `phase` | string | step slug where the HALT occurred (e.g. `resolve-target`, `compile`) |
|
|
99
|
+
| `error.code` | string | one of: `resolution-failure`, `write-failure`, `overwrite-cancelled`, `compile-cancelled`, `finalize-blocked` |
|
|
100
|
+
| `error.message` | string | the user-facing message that was displayed |
|
|
101
|
+
| `error.details` | any | optional — phase-specific context (e.g. the failed file path) |
|
|
102
|
+
| `outputs` | object | empty `{}` on early HALTs; partial when files were already written |
|
|
103
|
+
| `summary` | object | empty `{}` on early HALTs |
|
|
104
|
+
| `skill_package` | string \| null | absolute path when known, `null` when HALT preceded step-05 §1 |
|
|
47
105
|
|
|
48
106
|
## On Activation
|
|
49
107
|
|
|
50
|
-
1.
|
|
51
|
-
- `project_name`, `output_folder`, `user_name`, `communication_language`, `document_output_language`
|
|
52
|
-
-
|
|
108
|
+
1. Read `{project-root}/_bmad/skf/config.yaml` and `{forger_root}/preferences.yaml` in parallel (one batched tool-call message — they are independent files), then resolve:
|
|
109
|
+
- From config: `project_name`, `output_folder`, `user_name`, `communication_language`, `document_output_language`, `skills_output_folder`, `forge_data_folder`
|
|
110
|
+
- From preferences: `headless_mode` (default false)
|
|
111
|
+
|
|
112
|
+
2. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in `preferences.yaml`. Default: false.
|
|
113
|
+
|
|
114
|
+
3. **Parse CLI overrides** — capture optional override flags into the workflow context as `{overrides}`. Each override is opt-in; when omitted, the workflow runs as today.
|
|
115
|
+
|
|
116
|
+
| Flag | Effect |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `--description "<string>"` | Override the LLM-derived description in step-04 §2 (used in SKILL.md frontmatter and metadata.json). Subject to the same agentskills.io length (1–1024 chars) and voice (third-person) checks as extracted descriptions. |
|
|
119
|
+
| `--exports "<name1,name2,...>"` | Override the extracted export list. Parse as comma-separated; trim whitespace per item; skip empty items. Used in step-04 §2 Key Exports and the count-derived metadata stats. |
|
|
120
|
+
| `--skip-snippet` | Skip context-snippet.md generation in step-04 §3 and its write in step-05 §2. Artifact omitted from `outputs`; step-05 §5 advisory snippet validation reports a "skipped" entry. |
|
|
121
|
+
| `--no-active-pointer` | Skip the active-pointer flip in step-06 §1. Deliverables still land in `{skill_package}` but `{skill_group}/active` is not updated. Useful for batch automators that flip pointers in a separate stage. |
|
|
122
|
+
| `--batch <file>` | Run the workflow against a list of targets from a text file rather than a single argument. Implies `--headless` (gates cannot be human-driven across N targets). See "Batch Mode" below for input format and summary contract. Single-target overrides above apply globally to every target in the batch. |
|
|
123
|
+
| `--fail-fast` | Only meaningful with `--batch`. Abort the whole batch on the first per-target failure instead of recording the failure in the summary and proceeding to the next target. |
|
|
124
|
+
|
|
125
|
+
4. **If `--batch` is set**, force `{headless_mode} = true` (log "headless: coerced by --batch" if it was false), read the batch file, and parse the target list per "Batch Mode" below. Continue at step 5; the batch loop documented in "Batch Mode" wraps the step-01 → step-07 pipeline that follows.
|
|
126
|
+
|
|
127
|
+
5. Load, read the full file, and then execute `./steps-c/step-01-resolve-target.md` to begin the workflow. (In batch mode, control returns here for each subsequent target after step-07 completes; see "Batch Mode" below.)
|
|
128
|
+
|
|
129
|
+
## Batch Mode
|
|
130
|
+
|
|
131
|
+
When `--batch <file>` is supplied, quick-skill processes a list of targets from a text file in sequence rather than a single target from arguments. Designed for unattended bulk runs (CI pipelines, mass-rebuilds, the skf-batch-skills meta-workflow when it lands).
|
|
132
|
+
|
|
133
|
+
### Input format
|
|
134
|
+
|
|
135
|
+
One target per line. Empty lines and lines starting with `#` (after optional leading whitespace) are ignored. Each non-empty line has the same shape as the single-target `target` argument, with optional space-separated per-line modifiers:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
# A batch input file.
|
|
139
|
+
lodash
|
|
140
|
+
@vercel/og
|
|
141
|
+
cognee@0.5.0
|
|
142
|
+
https://github.com/foo/bar
|
|
143
|
+
https://github.com/foo/bar@2.1.0-beta
|
|
144
|
+
|
|
145
|
+
# Per-line modifiers — overrides for THIS target only:
|
|
146
|
+
lodash language=javascript scope=src/
|
|
147
|
+
cognee@0.5.0 language=python scope=cognee/api/
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Recognised per-line modifiers:
|
|
151
|
+
|
|
152
|
+
| Modifier | Effect (this target only) |
|
|
153
|
+
| --- | --- |
|
|
154
|
+
| `language=<lang>` | Sets `language_hint` for this target — same effect as the optional `language_hint` input on a single-target run. |
|
|
155
|
+
| `scope=<path>` | Sets `scope_hint` for this target — same effect as the optional `scope_hint` input on a single-target run. |
|
|
156
|
+
|
|
157
|
+
Per-line modifiers shadow the global `--description` / `--exports` / `--skip-snippet` / `--no-active-pointer` overrides only when those override fields are not set. Global overrides apply to every target unless a future modifier extends per-line override syntax.
|
|
158
|
+
|
|
159
|
+
### Execution
|
|
160
|
+
|
|
161
|
+
`--batch` implies `--headless`. The batch loop runs the full quick-skill pipeline (steps 1–7) for each target in file order:
|
|
162
|
+
|
|
163
|
+
1. Set `target`, `target_version`, `language_hint`, `scope_hint` from the batch line into the workflow context.
|
|
164
|
+
2. Execute steps 1–7 per the normal pipeline.
|
|
165
|
+
3. After step-07 completes (success or HARD HALT), record the per-target outcome (target, status, exit_code, skill_package, error.code) into the batch result list.
|
|
166
|
+
4. If `--fail-fast` is set and the target failed, exit the batch loop immediately. Otherwise continue with the next target.
|
|
167
|
+
|
|
168
|
+
Per-target output lands in `{skill_package}/` as today, with the per-target result contract at `{skill_package}/quick-skill-result-latest.json` (success or error variant per "Result Contract on HARD HALT" above).
|
|
169
|
+
|
|
170
|
+
### Batch summary contract
|
|
171
|
+
|
|
172
|
+
After the last target completes (or `--fail-fast` triggers an early exit), write the batch summary at:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
{skills_output_folder}/_batch/quick-skill-batch-{YYYYMMDD-HHmmss}.json
|
|
176
|
+
{skills_output_folder}/_batch/quick-skill-batch-latest.json (copy, not symlink)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Schema:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"skill": "skf-quick-skill",
|
|
184
|
+
"mode": "batch",
|
|
185
|
+
"status": "success | partial | failed",
|
|
186
|
+
"timestamp": "<ISO 8601 UTC>",
|
|
187
|
+
"input_file": "<path passed to --batch>",
|
|
188
|
+
"targets_total": 0,
|
|
189
|
+
"succeeded": 0,
|
|
190
|
+
"failed": 0,
|
|
191
|
+
"fail_fast_triggered": false,
|
|
192
|
+
"results": [
|
|
193
|
+
{
|
|
194
|
+
"target": "<line from batch file>",
|
|
195
|
+
"status": "success | error",
|
|
196
|
+
"exit_code": 0,
|
|
197
|
+
"skill_package": "<absolute path or null>",
|
|
198
|
+
"error_code": null
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`status` resolves as: `"success"` when `failed == 0`; `"partial"` when `failed > 0 && succeeded > 0`; `"failed"` when `succeeded == 0`. `fail_fast_triggered` is `true` only when `--fail-fast` aborted the loop early — `targets_total` then reflects the count actually attempted, not the file's line count.
|
|
205
|
+
|
|
206
|
+
### Headless events
|
|
207
|
+
|
|
208
|
+
Batch mode emits per-target boundary events on stderr in addition to the per-step events documented in Workflow Rules:
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
{"batch":<n>,"target":"<target>","status":"start"}
|
|
212
|
+
{"batch":<n>,"target":"<target>","status":"done","exit":<code>}
|
|
213
|
+
{"batch":<n>,"target":"<target>","status":"fail","exit":<code>,"error_code":"<class>"}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`<n>` is the 1-based index of the target in the parsed list. After the loop ends, emit one final batch-summary event:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
{"batch_summary":true,"targets_total":N,"succeeded":K,"failed":M,"status":"<...>","fail_fast_triggered":<bool>}
|
|
220
|
+
```
|
|
53
221
|
|
|
54
|
-
|
|
222
|
+
### Exit code
|
|
55
223
|
|
|
56
|
-
|
|
224
|
+
The batch process exits with code `0` when `failed == 0`, otherwise with the exit code of the first failed target (so automators that already branch on the single-target exit-code map continue to work without batch-specific handling). When `--fail-fast` triggers, the exit code is the failing target's code.
|
|
@@ -101,6 +101,10 @@ Indexed format targeting ~80-120 tokens per skill:
|
|
|
101
101
|
"assets_count": 0
|
|
102
102
|
},
|
|
103
103
|
"dependencies": [],
|
|
104
|
-
"compatibility": "{semver-range}"
|
|
104
|
+
"compatibility": "{semver-range}",
|
|
105
|
+
"provenance": {
|
|
106
|
+
"language_hint": "{language_hint or null}",
|
|
107
|
+
"scope_hint": "{scope_hint or null}"
|
|
108
|
+
}
|
|
105
109
|
}
|
|
106
110
|
```
|
|
@@ -13,6 +13,8 @@ When the user provides a package name instead of a GitHub URL, use this fallback
|
|
|
13
13
|
|
|
14
14
|
Try each registry in order. Stop at first success.
|
|
15
15
|
|
|
16
|
+
**Per-call timeout:** apply a 10s timeout to each registry HTTP call (15s for the web-search fallback) so a single hung registry cannot stall the workflow under hostile network conditions. Treat a timeout as a soft failure and fall through to the next entry in the chain.
|
|
17
|
+
|
|
16
18
|
#### 1. npm Registry (JavaScript/TypeScript)
|
|
17
19
|
|
|
18
20
|
```
|