bmad-module-skill-forge 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +6 -4
- package/docs/_data/pinned.yaml +1 -1
- package/docs/bmad-synergy.md +2 -2
- package/docs/getting-started.md +1 -1
- package/docs/skill-model.md +26 -32
- package/docs/troubleshooting.md +13 -1
- package/docs/why-skf.md +5 -4
- package/docs/workflows.md +53 -0
- package/package.json +2 -2
- package/src/knowledge/ccc-bridge.md +1 -1
- package/src/shared/references/output-contract-schema.md +10 -0
- package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +134 -0
- package/src/shared/scripts/skf-detect-tools.py +359 -0
- package/src/shared/scripts/skf-emit-result-envelope.py +419 -0
- package/src/shared/scripts/skf-extract-public-api.py +505 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +413 -0
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +324 -0
- package/src/shared/scripts/skf-preflight.py +14 -4
- package/src/shared/scripts/skf-qmd-classify-collections.py +212 -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-frontmatter.py +9 -3
- package/src/shared/scripts/skf-validate-output.py +24 -7
- package/src/skf-create-skill/steps-c/step-06-validate.md +1 -1
- 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-setup/SKILL.md +25 -10
- package/src/skf-setup/references/tier-rules.md +2 -2
- package/src/skf-setup/steps-c/step-01-detect-and-tier.md +58 -71
- package/src/skf-setup/steps-c/step-01b-ccc-index.md +49 -66
- package/src/skf-setup/steps-c/step-02-write-config.md +59 -86
- package/src/skf-setup/steps-c/step-03-auto-index.md +73 -91
- package/src/skf-setup/steps-c/step-04-report.md +135 -11
- package/src/skf-setup/steps-c/step-05-health-check.md +4 -2
- package/src/skf-test-skill/steps-c/step-01-init.md +4 -4
- package/src/skf-quick-skill/steps-c/step-05-validate.md +0 -193
|
@@ -1,73 +1,89 @@
|
|
|
1
1
|
---
|
|
2
2
|
nextStepFile: './step-04-report.md'
|
|
3
|
+
# Resolve `{qmdClassifyHelper}` and `{forgeTierRwHelper}` by probing the
|
|
4
|
+
# corresponding `*ProbeOrder` arrays (installed SKF module path first, src/
|
|
5
|
+
# dev-checkout fallback); first existing path wins. HALT if neither resolves
|
|
6
|
+
# for the helper a section actually invokes — both scripts own classification
|
|
7
|
+
# / registry-cleanup contracts that have no prose fallback.
|
|
8
|
+
qmdClassifyProbeOrder:
|
|
9
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-qmd-classify-collections.py'
|
|
10
|
+
- '{project-root}/src/shared/scripts/skf-qmd-classify-collections.py'
|
|
11
|
+
forgeTierRwProbeOrder:
|
|
12
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-forge-tier-rw.py'
|
|
13
|
+
- '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
|
|
3
14
|
---
|
|
4
15
|
|
|
5
|
-
|
|
16
|
+
<!-- Config: communicate in {communication_language}. The orphan-removal prompt and the headless-resolution log message render in the user's language. -->
|
|
17
|
+
|
|
18
|
+
# Step 3: QMD + CCC Registry Hygiene
|
|
6
19
|
|
|
7
20
|
## STEP GOAL:
|
|
8
21
|
|
|
9
|
-
|
|
22
|
+
When the detected tier is Deep, classify live QMD collections against the `qmd_collections` registry and prompt the user before removing orphans. Whenever ccc is available (Forge+ or Deep), prune `ccc_index_registry` entries whose source paths no longer exist. All set-arithmetic and YAML mutation goes through scripts (`{qmdClassifyHelper}` and `{forgeTierRwHelper}`); the workflow only orchestrates external CLI calls (`qmd collection list`, `qmd collection remove`) and the user-prompt branch.
|
|
10
23
|
|
|
11
|
-
For Quick and Forge tiers, skip silently and proceed (QMD is not available
|
|
24
|
+
For Quick and Forge tiers, skip silently and proceed (QMD is not available; ccc registry cleanup only runs when ccc is available regardless of tier).
|
|
12
25
|
|
|
13
26
|
## Rules
|
|
14
27
|
|
|
15
|
-
- Focus only on
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- Do not
|
|
28
|
+
- Focus only on registry hygiene — no new collection creation (that belongs to create-skill)
|
|
29
|
+
- Never reimplement the forge-namespace suffix filter in prose — the classifier owns it
|
|
30
|
+
- Never silently delete collections — always prompt before `qmd collection remove`
|
|
31
|
+
- Headless runs must auto-resolve the orphan prompt to the documented default (Keep)
|
|
32
|
+
- Do not fail the workflow if hygiene encounters errors
|
|
20
33
|
|
|
21
34
|
## MANDATORY SEQUENCE
|
|
22
35
|
|
|
23
|
-
**CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
|
|
24
|
-
|
|
25
36
|
### 1. Check Tier
|
|
26
37
|
|
|
27
|
-
Read `{calculated_tier}` from context.
|
|
38
|
+
Read `{calculated_tier}` and `{ccc}` from context (set by step-01).
|
|
28
39
|
|
|
29
|
-
**If
|
|
40
|
+
**If `{calculated_tier}` is Quick or Forge AND `{ccc}` is false:** No registry hygiene needed. Set `{hygiene_result: "skipped", hygiene_healthy: 0, hygiene_orphaned_removed: 0, hygiene_orphaned_kept: 0, hygiene_stale_cleaned: 0, ccc_registry_stale_cleaned: 0, ccc_registry_stale_removed_paths: []}`. Proceed directly to section 5 (Auto-Proceed) — no output, no messaging.
|
|
30
41
|
|
|
31
|
-
**If
|
|
42
|
+
**If `{calculated_tier}` is Quick or Forge AND `{ccc}` is true:** No QMD work, but ccc registry needs pruning. Set QMD-related flags to defaults (`hygiene_result: "skipped"`, all hygiene_* counts = 0). Skip directly to section 4 (Stale Registry Cleanup), running it with the ccc-prune flag only.
|
|
32
43
|
|
|
33
|
-
**If
|
|
44
|
+
**If `{calculated_tier}` is Forge+:** Same as Quick/Forge with ccc — no QMD work (qmd unavailable at Forge+), but ccc registry hygiene runs.
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
**If `{calculated_tier}` IS Deep:** Continue to section 2.
|
|
36
47
|
|
|
37
|
-
|
|
48
|
+
### 2. Classify Live QMD Collections vs Registry
|
|
38
49
|
|
|
39
50
|
List live QMD collections:
|
|
51
|
+
|
|
40
52
|
```bash
|
|
41
53
|
qmd collection list
|
|
42
54
|
```
|
|
43
55
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
56
|
+
Parse the output into a comma-separated string of collection names and store as `{live_collections}` (raw — including any foreign collections owned by other tools sharing the QMD daemon; the classifier filters them out).
|
|
57
|
+
|
|
58
|
+
**Error handling:** If `qmd collection list` fails (daemon down, daemon errors), set `{hygiene_result: "qmd_unavailable", hygiene_healthy: 0, hygiene_orphaned_removed: 0, hygiene_orphaned_kept: 0, hygiene_stale_cleaned: 0}`, log the error, and skip directly to section 4 (which will still run the ccc-prune branch if `{ccc}` is true).
|
|
47
59
|
|
|
48
|
-
|
|
60
|
+
Run the classifier. Invoke via `uv run` so the script's PEP 723 PyYAML dependency resolves automatically (`docs/getting-started.md` documents uv as the runtime prereq for exactly this); bare `python3` would `ModuleNotFoundError` on a fresh interpreter.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uv run {qmdClassifyHelper} \
|
|
64
|
+
--live-names "{live_collections}" \
|
|
65
|
+
--registry-from-yaml "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml"
|
|
66
|
+
```
|
|
49
67
|
|
|
50
|
-
|
|
68
|
+
The script (see `src/shared/scripts/skf-qmd-classify-collections.py` docstring for the full schema) applies the forge-namespace suffix filter (`-brief | -temporal | -docs | -extraction`) to `{live_collections}` before classifying — collections owned by unrelated tools are silently excluded from the orphan / healthy / stale sets and counted under `foreign_filtered_count` for telemetry only. This is the PR #244 incident protection: collections like Hindsight memory banks that happen to live in the same QMD daemon never enter any classification that could lead to data loss.
|
|
51
69
|
|
|
52
|
-
|
|
70
|
+
**Parse the JSON output and set context flags:**
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
72
|
+
- `{hygiene_healthy}` ← `len(healthy)`
|
|
73
|
+
- `{orphaned_collections}` ← `orphaned` (the list — used in section 3)
|
|
74
|
+
- `{stale_collections}` ← `stale` (the list — used in section 4)
|
|
75
|
+
- `{foreign_filtered_count}` ← `foreign_filtered_count`
|
|
57
76
|
|
|
58
|
-
|
|
59
|
-
- These may be leftover from prior auto-indexing or manual indexing
|
|
60
|
-
- Flag for user-prompted removal
|
|
77
|
+
Set `{hygiene_result: "completed"}`.
|
|
61
78
|
|
|
62
|
-
|
|
63
|
-
- The QMD collection was deleted or lost
|
|
64
|
-
- Remove the stale entry from the registry
|
|
79
|
+
### 3. Handle Orphaned Collections
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
**If `{orphaned_collections}` is empty:** Set `{hygiene_orphaned_removed: 0, hygiene_orphaned_kept: 0}` and skip to section 4.
|
|
67
82
|
|
|
68
|
-
**If orphaned
|
|
83
|
+
**Headless gate.** If `{headless_mode}` is true, auto-resolve to the default action **Keep** without prompting: log `"Auto-decision (headless): kept {len(orphaned_collections)} orphaned forge collection(s)"`, set `{hygiene_orphaned_removed: 0, hygiene_orphaned_kept: len(orphaned_collections)}`, and skip to section 4. This matches the workflow contract (`Headless: All gates auto-resolve with default action when {headless_mode} is true`) declared in the Invocation Contract.
|
|
84
|
+
|
|
85
|
+
**If `{headless_mode}` is false**, display to the user:
|
|
69
86
|
|
|
70
|
-
Display to user:
|
|
71
87
|
"**QMD Hygiene: Found {count} orphaned collection(s) not tracked in the forge registry:**
|
|
72
88
|
|
|
73
89
|
{list orphaned collection names}
|
|
@@ -75,77 +91,43 @@ Display to user:
|
|
|
75
91
|
These collections exist in QMD but are not managed by any skill workflow. They may be from a previous auto-index run or manual creation.
|
|
76
92
|
|
|
77
93
|
**[R]emove** orphaned collections — clean up QMD
|
|
78
|
-
**[K]eep** orphaned collections — leave them as-is"
|
|
94
|
+
**[K]eep** orphaned collections — leave them as-is (default)"
|
|
95
|
+
|
|
96
|
+
**If user selects R (Remove):** For each name in `{orphaned_collections}`:
|
|
79
97
|
|
|
80
|
-
**If user selects R (Remove):**
|
|
81
|
-
For each orphaned collection:
|
|
82
98
|
```bash
|
|
83
|
-
qmd collection remove
|
|
99
|
+
qmd collection remove <name>
|
|
84
100
|
```
|
|
85
|
-
Log each removal.
|
|
86
|
-
|
|
87
|
-
**If user selects K (Keep):**
|
|
88
|
-
Skip removal. Log that orphaned collections were kept.
|
|
89
|
-
|
|
90
|
-
**If no orphaned collections:** Skip this section silently.
|
|
91
|
-
|
|
92
|
-
### 5. Handle Stale Registry Entries
|
|
93
|
-
|
|
94
|
-
**If stale registry entries found:**
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
Track the count of successful removals as `{hygiene_orphaned_removed}`. Set `{hygiene_orphaned_kept: 0}`.
|
|
97
103
|
|
|
98
|
-
|
|
104
|
+
**If user selects K (Keep) or no orphans:** Set `{hygiene_orphaned_removed: 0, hygiene_orphaned_kept: len(orphaned_collections)}`.
|
|
99
105
|
|
|
100
|
-
|
|
106
|
+
### 4. Stale Registry Cleanup
|
|
101
107
|
|
|
102
|
-
|
|
108
|
+
This section ALWAYS runs when reachable — it handles both `qmd_collections` stale entries (Deep tier) and `ccc_index_registry` stale entries (whenever ccc is true). The script's flags are mutually independent.
|
|
103
109
|
|
|
104
|
-
|
|
110
|
+
Build the invocation. Always include `--target` for the forge-tier.yaml path. Include `--qmd-live-names "{live_collections}"` ONLY when section 2 ran successfully (i.e. `{hygiene_result}` is `"completed"`); omit the flag entirely otherwise so the script skips QMD cleanup. Include `--prune-missing-ccc-paths` ONLY when `{ccc}` is true; omit it otherwise.
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
**Healthy** — path exists: no action needed.
|
|
113
|
-
|
|
114
|
-
**Stale** — path does not exist (source directory removed or moved):
|
|
115
|
-
- Remove the stale entry from `ccc_index_registry`
|
|
116
|
-
- Log: "Removed stale CCC index registry entry: {path} (path no longer exists)"
|
|
117
|
-
|
|
118
|
-
Update forge-tier.yaml with the cleaned registry.
|
|
119
|
-
|
|
120
|
-
Store `{ccc_registry_stale_cleaned: count}` in context for step-04 reporting.
|
|
121
|
-
|
|
122
|
-
**IF `tools.ccc` is false:** Skip this section silently.
|
|
123
|
-
|
|
124
|
-
### 6. Store Hygiene Results and Auto-Proceed
|
|
125
|
-
|
|
126
|
-
Store in context for step-04 reporting:
|
|
127
|
-
```
|
|
128
|
-
{hygiene_result: "completed"|"skipped"|"qmd_unavailable"}
|
|
129
|
-
{hygiene_healthy: count}
|
|
130
|
-
{hygiene_orphaned_removed: count}
|
|
131
|
-
{hygiene_orphaned_kept: count}
|
|
132
|
-
{hygiene_stale_cleaned: count}
|
|
133
|
-
{ccc_registry_stale_cleaned: count}
|
|
112
|
+
```bash
|
|
113
|
+
uv run {forgeTierRwHelper} clean-stale \
|
|
114
|
+
--target "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml" \
|
|
115
|
+
[--qmd-live-names "{live_collections}"] \
|
|
116
|
+
[--prune-missing-ccc-paths]
|
|
134
117
|
```
|
|
135
118
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
#### Menu Handling Logic:
|
|
119
|
+
The script reads the registry, computes set-difference operations (qmd: registry − live; ccc: filter where `path` does not exist on disk), and atomically rewrites forge-tier.yaml only when something actually changed (mtime preserved on idempotent re-runs). The CI ephemeral-mount caveat for ccc-registry pruning is logged in the script's WARNING message (per PR #248).
|
|
139
120
|
|
|
140
|
-
|
|
121
|
+
**Parse the JSON output and set context flags for step-04:**
|
|
141
122
|
|
|
142
|
-
|
|
123
|
+
- `{hygiene_stale_cleaned}` ← `len(qmd_removed)`
|
|
124
|
+
- `{ccc_registry_stale_cleaned}` ← `len(ccc_removed)`
|
|
125
|
+
- `{ccc_registry_stale_removed_paths}` ← `ccc_removed` (the list — step-04 folds individual paths into envelope warnings)
|
|
143
126
|
|
|
144
|
-
|
|
145
|
-
- If no orphans found, this is an auto-proceed step
|
|
146
|
-
- Proceed directly to next step after hygiene or skip
|
|
127
|
+
If `{hygiene_stale_cleaned}` > 0, display: "**Cleaned {hygiene_stale_cleaned} stale QMD registry entry/entries** (collection no longer exists in QMD)."
|
|
147
128
|
|
|
148
|
-
|
|
129
|
+
(The script already logs each ccc removal as a WARNING line; no additional display needed.)
|
|
149
130
|
|
|
150
|
-
|
|
131
|
+
### 5. Auto-Proceed
|
|
151
132
|
|
|
133
|
+
After hygiene completes (or is skipped for non-Deep tiers without ccc), display "**Proceeding to forge status report...**", then load `{nextStepFile}`, read it fully, and execute it.
|
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
---
|
|
2
2
|
tierRulesData: 'references/tier-rules.md'
|
|
3
3
|
nextStepFile: './step-05-health-check.md'
|
|
4
|
+
# Resolve `{emitEnvelopeHelper}` by probing `{emitEnvelopeProbeOrder}` in order
|
|
5
|
+
# (installed SKF module path first, src/ dev-checkout fallback); first existing
|
|
6
|
+
# path wins. HALT if neither resolves when the headless envelope must be emitted
|
|
7
|
+
# — the script is the source of truth for the SKF_SETUP_RESULT_JSON contract;
|
|
8
|
+
# never inline-render the envelope from prose (LLM schema drift is the bug
|
|
9
|
+
# this script exists to prevent).
|
|
10
|
+
emitEnvelopeProbeOrder:
|
|
11
|
+
- '{project-root}/_bmad/skf/shared/scripts/skf-emit-result-envelope.py'
|
|
12
|
+
- '{project-root}/src/shared/scripts/skf-emit-result-envelope.py'
|
|
4
13
|
---
|
|
5
14
|
|
|
15
|
+
<!-- Config: communicate in {communication_language}; emit user-visible report text (FORGE STATUS banner, climb hint, REQUIRED TIER NOT MET block, breadcrumb) in {document_output_language}. The JSON envelope from section 4 is a machine contract — its keys and enum values stay English regardless. -->
|
|
16
|
+
|
|
6
17
|
# Step 4: Forge Status Report
|
|
7
18
|
|
|
8
19
|
## STEP GOAL:
|
|
9
20
|
|
|
10
|
-
Display the forge status report with positive capability framing
|
|
21
|
+
Display the forge status report with positive capability framing, surface tier changes and tool-set deltas on re-runs, prominently flag a required-tier miss, and (when headless) emit the schema-locked `SKF_SETUP_RESULT_JSON` envelope via `{emitEnvelopeHelper}`.
|
|
11
22
|
|
|
12
23
|
## Rules
|
|
13
24
|
|
|
14
|
-
- Focus only on
|
|
25
|
+
- Focus only on display + envelope emission
|
|
15
26
|
- Do not use negative framing ("missing", "lacking", "unavailable")
|
|
16
27
|
- Do not list tools that are not available
|
|
17
28
|
- Use tier capability descriptions from tier-rules.md
|
|
29
|
+
- Never inline-render the envelope JSON — the script owns the schema; drift breaks pipelines
|
|
18
30
|
- Chains to the local health-check step via `{nextStepFile}` after completion — the user-facing status report is NOT the terminal step
|
|
19
31
|
|
|
20
|
-
##
|
|
32
|
+
## Headless Mode Display Rule
|
|
33
|
+
|
|
34
|
+
When `{headless_mode}` is `true`, sections 2 (the human-readable banner) and 3 (the REQUIRED TIER NOT MET human prose block) are **skipped entirely**. The single line of stdout in headless mode is the `SKF_SETUP_RESULT_JSON: {…}` envelope from section 4 — every signal the human banner would have surfaced is already in the envelope (`tier`, `previous_tier`, `tier_changed`, `tools`, `tools_added`, `tools_removed`, `files_written`, `tier_override_*`, `require_tier_satisfied`, `warnings`, `error`). Parent skills and CI pipelines consume one parseable line; they should not be forced to scroll past 30 lines of ASCII-art they cannot use.
|
|
35
|
+
|
|
36
|
+
When `{headless_mode}` is `false`, sections 2 and 3 display normally and section 4 is skipped (interactive runs read the human banner; envelope emission would just be log noise).
|
|
21
37
|
|
|
22
|
-
|
|
38
|
+
## MANDATORY SEQUENCE
|
|
23
39
|
|
|
24
40
|
### 1. Load Capability Descriptions
|
|
25
41
|
|
|
26
|
-
Load and read {tierRulesData} for the tier capability descriptions and re-run messages.
|
|
42
|
+
Load and read {tierRulesData} for the tier capability descriptions and re-run messages. Needed by section 2; safe to load unconditionally — the data load itself is silent.
|
|
27
43
|
|
|
28
|
-
### 2. Display Forge Status Report
|
|
44
|
+
### 2. Display Forge Status Report (skip when `{headless_mode}` is true)
|
|
29
45
|
|
|
30
46
|
**Format the report as follows:**
|
|
31
47
|
|
|
@@ -39,6 +55,17 @@ Load and read {tierRulesData} for the tier capability descriptions and re-run me
|
|
|
39
55
|
|
|
40
56
|
Tools Detected:
|
|
41
57
|
{for each tool that is available, show: tool name — version}
|
|
58
|
+
{if no tools are available: (none yet — see "Climb to next tier" below)}
|
|
59
|
+
|
|
60
|
+
{if calculated_tier is not Deep:}
|
|
61
|
+
Climb to next tier:
|
|
62
|
+
{if not tools.ast_grep: - Install ast-grep (https://ast-grep.github.io) — unlocks AST-backed code analysis (Forge tier)}
|
|
63
|
+
{if tools.ast_grep and not tools.ccc: - Install cocoindex-code (https://github.com/cocoindex-io/cocoindex-code) — adds semantic-guided precision compilation (Forge+ tier)}
|
|
64
|
+
{if tools.ast_grep and not tools.gh_cli: - Install GitHub CLI (https://cli.github.com) — required for Deep tier (cross-repository synthesis)}
|
|
65
|
+
{if tools.ast_grep and not tools.qmd and qmd_status is "absent": - Install qmd (https://github.com/tobi/qmd) — required for Deep tier (knowledge search)}
|
|
66
|
+
{if tools.ast_grep and not tools.qmd and qmd_status is "daemon_stopped": - Start the qmd daemon (already installed) — run `qmd start` (or your distribution's qmd service command) to unlock Deep tier (knowledge search)}
|
|
67
|
+
{if tools.ccc and ccc_daemon is "error": - The ccc daemon is reporting errors — run `ccc doctor` to diagnose. CCC index will fail until resolved (mirrors the qmd daemon-stopped pattern above for parity)}
|
|
68
|
+
{end if}
|
|
42
69
|
|
|
43
70
|
{if hygiene_result is "completed":}
|
|
44
71
|
QMD Registry:
|
|
@@ -63,30 +90,127 @@ Load and read {tierRulesData} for the tier capability descriptions and re-run me
|
|
|
63
90
|
{if ccc_index_result is "failed": indexing failed — semantic discovery unavailable this session}
|
|
64
91
|
{end if}
|
|
65
92
|
|
|
93
|
+
Files written this run:
|
|
94
|
+
- forge-tier.yaml — {project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml
|
|
95
|
+
{if preferences_yaml_created is true:}
|
|
96
|
+
- preferences.yaml — {project-root}/_bmad/_memory/forger-sidecar/preferences.yaml (first-run defaults)
|
|
97
|
+
{end if}
|
|
98
|
+
{if forge_data_dir_created is true:}
|
|
99
|
+
- {forge_data_folder}/ (directory created)
|
|
100
|
+
{end if}
|
|
101
|
+
{if settings_yml_written is true:}
|
|
102
|
+
- .cocoindex_code/settings.yml — {project-root}/.cocoindex_code/settings.yml ({settings_yml_patterns_added} SKF exclusion pattern(s) merged)
|
|
103
|
+
{end if}
|
|
104
|
+
{if ccc_index_result is "created":}
|
|
105
|
+
- .cocoindex_code/ ccc index — {ccc_file_count} files indexed
|
|
106
|
+
{end if}
|
|
107
|
+
|
|
66
108
|
{if tier_override is active:}
|
|
67
109
|
Note: Tier override active (set in preferences.yaml)
|
|
68
110
|
|
|
111
|
+
{if tier_override_invalid is true:}
|
|
112
|
+
Note: tier_override value "{tier_override_invalid_value}" in preferences.yaml is not valid.
|
|
113
|
+
{if tier_override_invalid_suggestion is non-null: Did you mean "{tier_override_invalid_suggestion}"?}
|
|
114
|
+
Valid values are case-sensitive: Quick, Forge, Forge+, Deep. Using detected tier {calculated_tier}.
|
|
115
|
+
|
|
116
|
+
{if tier_override_unsafe is true:}
|
|
117
|
+
Warning: tier_override is forcing {calculated_tier} but the underlying tool prerequisites are not satisfied.
|
|
118
|
+
Missing: {tier_override_unsafe_missing}. The override is honored, but downstream skills that
|
|
119
|
+
rely on the missing tool(s) will fail at runtime. Install the missing tool(s) or remove
|
|
120
|
+
the override from preferences.yaml.
|
|
121
|
+
|
|
69
122
|
{if re-run with tier change:}
|
|
70
123
|
{appropriate upgrade/downgrade message from tier-rules.md}
|
|
71
124
|
|
|
72
|
-
{if re-run with same tier:}
|
|
125
|
+
{if re-run with same tier and {tools_added} is empty and {tools_removed} is empty:}
|
|
73
126
|
{same-tier message from tier-rules.md}
|
|
74
127
|
|
|
128
|
+
{if re-run with same tier and ({tools_added} or {tools_removed} is non-empty):}
|
|
129
|
+
Tier unchanged: {calculated_tier}.
|
|
130
|
+
{if {tools_added} non-empty:} Newly detected: {comma-separated tool names from tools_added}{if ccc was added and tier is Deep: " — ccc enhances Deep tier transparently."}
|
|
131
|
+
{if {tools_removed} non-empty:} No longer detected: {comma-separated tool names from tools_removed} — re-install to restore those capabilities.
|
|
132
|
+
|
|
75
133
|
═══════════════════════════════════════
|
|
76
134
|
Forge ready. {calculated_tier} tier active.
|
|
77
135
|
═══════════════════════════════════════
|
|
136
|
+
|
|
137
|
+
{if {headless_mode} is false:}
|
|
138
|
+
Next: try [BS] Brief Skill to scope your first compilation target, or [QS] Quick Skill for a fast template-driven path. Already have a skill? [AS] Audit Skill drift-checks an existing skill against current sources.
|
|
78
139
|
```
|
|
79
140
|
|
|
141
|
+
The same-tier-with-tool-deltas branch reads `{tools_added}` and `{tools_removed}` directly from step-01's context (set from `skf-detect-tools.py` output relative to `{previous_tools}`). No LLM-side recomputation.
|
|
142
|
+
|
|
80
143
|
**Tool display rules:**
|
|
144
|
+
|
|
81
145
|
- Only show tools that ARE available with their version strings
|
|
82
146
|
- Do NOT list unavailable tools
|
|
83
147
|
- Do NOT show a "missing" column or section
|
|
84
148
|
|
|
85
|
-
### 3.
|
|
149
|
+
### 3. Display Required-Tier Failure Block (when applicable; skip when `{headless_mode}` is true)
|
|
150
|
+
|
|
151
|
+
If `{require_tier_satisfied}` is `false` AND `{headless_mode}` is `false`, display this block immediately after the status report. In headless mode the failure surfaces via the envelope's `require_tier_satisfied: false` and the synthesized `warnings` entry — the human prose block would be log noise that pipelines have to scroll past.
|
|
152
|
+
|
|
153
|
+
When the block does fire (interactive run with require-tier failure):
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
═══════════════════════════════════════
|
|
157
|
+
REQUIRED TIER NOT MET
|
|
158
|
+
═══════════════════════════════════════
|
|
159
|
+
|
|
160
|
+
Required: {require_tier}
|
|
161
|
+
Detected: {calculated_tier}
|
|
162
|
+
Missing: {require_tier_failure_missing_tools}
|
|
163
|
+
|
|
164
|
+
Install the missing tool(s) and re-run, or relax `--require-tier`.
|
|
165
|
+
See "Climb to next tier" above for install URLs.
|
|
166
|
+
═══════════════════════════════════════
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
This block exists to make pipeline failures visible without the operator parsing the JSON envelope.
|
|
170
|
+
|
|
171
|
+
### 4. Emit Headless JSON Envelope
|
|
172
|
+
|
|
173
|
+
When `{headless_mode}` is `true`, build the context payload from this step's accumulated flags and forward it to `{emitEnvelopeHelper}` on stdin. Invoke via `uv run` so the script's PEP 723 dependency declarations are honored (the canonical runtime invocation per `docs/getting-started.md`'s uv prereq — bare `python3` skips the metadata block and breaks on a fresh interpreter). The script computes derived fields (`tools_added`, `tools_removed`, `tier_changed`, `warnings`), validates the assembled envelope against the JSON Schema at `src/shared/scripts/schemas/skf-setup-result-envelope.v1.json`, and emits the single prefixed line `SKF_SETUP_RESULT_JSON: {…}` on stdout.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
echo '{
|
|
177
|
+
"tier": "{calculated_tier}",
|
|
178
|
+
"previous_tier": {previous_tier_or_null},
|
|
179
|
+
"tools": {tools_dict_from_step_01_or_skf_detect_tools_output},
|
|
180
|
+
"previous_tools": {previous_tools_or_null},
|
|
181
|
+
"config_path": "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml",
|
|
182
|
+
"ccc_index": {
|
|
183
|
+
"status": "{ccc_index_result}",
|
|
184
|
+
"indexed_path": {ccc_indexed_path_or_null},
|
|
185
|
+
"file_count": {ccc_file_count_or_null}
|
|
186
|
+
},
|
|
187
|
+
"files_written": {
|
|
188
|
+
"forge-tier.yaml": true,
|
|
189
|
+
"preferences.yaml": {preferences_yaml_created},
|
|
190
|
+
"settings.yml": {settings_yml_written},
|
|
191
|
+
"ccc_index": {ccc_index_result_was_created}
|
|
192
|
+
},
|
|
193
|
+
"tier_override_active": {tier_override_active},
|
|
194
|
+
"tier_override_invalid": {tier_override_invalid},
|
|
195
|
+
"tier_override_invalid_value": {tier_override_invalid_value_or_null},
|
|
196
|
+
"tier_override_unsafe": {tier_override_unsafe},
|
|
197
|
+
"tier_override_unsafe_missing": {tier_override_unsafe_missing_list},
|
|
198
|
+
"require_tier_satisfied": {require_tier_satisfied_or_null},
|
|
199
|
+
"require_tier_failure_missing": {require_tier_failure_missing_tools_list},
|
|
200
|
+
"qmd_status": "{qmd_status}",
|
|
201
|
+
"ccc_exclusion_warnings": {ccc_exclusion_warnings_list},
|
|
202
|
+
"ccc_registry_stale_removed": {ccc_registry_stale_removed_paths_list},
|
|
203
|
+
"ccc_indexing_failed_reason": {ccc_indexing_failed_reason_or_null},
|
|
204
|
+
"error": {error_object_or_null}
|
|
205
|
+
}' | uv run {emitEnvelopeHelper} emit
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The script's documented context-payload shape (see `src/shared/scripts/skf-emit-result-envelope.py` docstring) tolerates two `tools` shapes — bare booleans OR `skf-detect-tools.py`'s `{key: {available: bool, ...}}` output — so either step-01's normalized booleans OR the raw detect-tools output forwarded as-is will produce the correct envelope.
|
|
86
209
|
|
|
87
|
-
|
|
210
|
+
**If the script exits non-zero:** the assembled envelope failed schema validation, which means a context flag from an earlier step is malformed. Surface the error to stderr and continue (the human-readable banner is already displayed; missing JSON envelope on a headless run is a degraded but non-fatal state — the pipeline observer will see no envelope and treat that as "agent did not complete cleanly").
|
|
88
211
|
|
|
89
|
-
|
|
212
|
+
**When `{headless_mode}` is `false`, do NOT invoke the script** — interactive runs read the human-readable banner.
|
|
90
213
|
|
|
91
|
-
|
|
214
|
+
### 5. Chain to Health Check
|
|
92
215
|
|
|
216
|
+
After the forge status report (and any failure block + JSON envelope) has been displayed, load `{nextStepFile}`, read it fully, and execute it — UNLESS `{require_tier_satisfied}` is `false`, in which case halt the workflow here without chaining to step-05. The health-check step is the true terminal step on success — do not stop after the report on a passing run even though it reads as final. step-05 in turn delegates to `shared/health-check.md`; after that returns, the setup workflow is fully done.
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
# `shared/health-check.md` resolves relative to the SKF module root
|
|
3
|
-
# (`_bmad/skf/` when installed, `src/` during
|
|
4
|
-
# to this step file.
|
|
3
|
+
# (`{project-root}/_bmad/skf/` when installed, `{project-root}/src/` during
|
|
4
|
+
# development), NOT relative to this step file.
|
|
5
5
|
nextStepFile: 'shared/health-check.md'
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
<!-- Config: communicate in {communication_language}. This is a delegation-only step (no user-visible output of its own); shared/health-check.md inherits the language directive on load. -->
|
|
9
|
+
|
|
8
10
|
# Step 5: Workflow Health Check
|
|
9
11
|
|
|
10
12
|
## STEP GOAL:
|
|
@@ -91,10 +91,10 @@ SKF module (`skf init`) or run from a development checkout with src/ present.
|
|
|
91
91
|
|
|
92
92
|
Do not proceed. No partial test report is written.
|
|
93
93
|
|
|
94
|
-
**3b. Run the validator (30s timeout — deterministic validator should finish in <1s; the cap only guards against runaway python)
|
|
94
|
+
**3b. Run the validator (30s timeout — deterministic validator should finish in <1s; the cap only guards against runaway python).** Invoke via `uv run` so the script's PEP 723 PyYAML dependency resolves automatically; bare `python3` would fail with `ModuleNotFoundError: No module named 'yaml'` on a fresh interpreter (`docs/getting-started.md` documents uv as the runtime prereq for exactly this).
|
|
95
95
|
|
|
96
96
|
```bash
|
|
97
|
-
timeout 30s
|
|
97
|
+
timeout 30s uv run {frontmatterScript} {resolved_skill_package}/SKILL.md --skill-dir-name {skill_name}
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
If the command trips the 30s wall-clock (exit code `124`), set
|
|
@@ -116,7 +116,7 @@ Parse the JSON output. Per B2, treat each `status` value explicitly:
|
|
|
116
116
|
This skill will fail `npx skills add` and `npx skill-check check`. {If warn:} Consider fixing frontmatter before proceeding (run `npx skill-check check <skill-dir> --fix` to auto-fix deterministic issues). {If fail:} test-skill cannot proceed — halt and repair frontmatter, then re-run.
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
**3c. Python runtime probe.** Before the first invocation, confirm `python3`
|
|
119
|
+
**3c. Python runtime probe.** Before the first invocation, confirm both `python3` AND `uv` are on `$PATH` (`command -v python3` and `command -v uv`). Both are required: `uv run` shells through to `python3` under the hood AND honors the script's PEP 723 PyYAML dependency declaration that bare `python3` ignores. If either is missing, set `analysis_confidence: degraded` in workflow context and carry a **score cap** into step-05: `capped_score = threshold - 1` → forces auto-FAIL until the runtime is restored. Record the reason in evidence-report and the test report frontmatter (`analysisConfidence: degraded`, `toolingStatus: python3-missing` or `uv-missing` as appropriate). `uv` is a documented runtime prerequisite — see `docs/getting-started.md` for install instructions.
|
|
120
120
|
|
|
121
121
|
### 4. Load Forge Tier State
|
|
122
122
|
|
|
@@ -199,7 +199,7 @@ testResult: ''
|
|
|
199
199
|
score: ''
|
|
200
200
|
threshold: ''
|
|
201
201
|
analysisConfidence: '{full|degraded}'
|
|
202
|
-
toolingStatus: '{ok|python3-missing|frontmatter-validator-missing|frontmatter-validator-timeout}'
|
|
202
|
+
toolingStatus: '{ok|python3-missing|uv-missing|frontmatter-validator-missing|frontmatter-validator-timeout}'
|
|
203
203
|
workspaceDrift: '{not-checked|ok|overridden}'
|
|
204
204
|
testDate: '{run_id timestamp ISO-8601 UTC}'
|
|
205
205
|
stepsCompleted: ['step-01-init']
|