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.
Files changed (40) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/docs/_data/pinned.yaml +1 -1
  3. package/docs/workflows.md +34 -15
  4. package/package.json +2 -2
  5. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +58 -0
  6. package/src/shared/scripts/schemas/skill-brief.v1.json +77 -0
  7. package/src/shared/scripts/schemas/workspace-detection.v1.json +44 -0
  8. package/src/shared/scripts/skf-detect-language.py +277 -0
  9. package/src/shared/scripts/skf-detect-workspaces.py +427 -0
  10. package/src/shared/scripts/skf-emit-brief-result-envelope.py +257 -0
  11. package/src/shared/scripts/skf-extract-public-api.py +29 -0
  12. package/src/shared/scripts/skf-forge-tier-rw.py +73 -0
  13. package/src/shared/scripts/skf-merge-ccc-exclusions.py +14 -0
  14. package/src/shared/scripts/skf-recommend-scope-type.py +369 -0
  15. package/src/shared/scripts/skf-validate-brief-inputs.py +293 -0
  16. package/src/shared/scripts/skf-write-skill-brief.py +509 -0
  17. package/src/skf-audit-skill/steps-c/step-01-init.md +49 -3
  18. package/src/skf-audit-skill/steps-c/step-03-structural-diff.md +6 -5
  19. package/src/skf-brief-skill/SKILL.md +41 -12
  20. package/src/skf-brief-skill/assets/description-voice-examples.md +19 -0
  21. package/src/skf-brief-skill/assets/scope-templates.md +5 -0
  22. package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -40
  23. package/src/skf-brief-skill/references/draft-checkpoint.md +46 -0
  24. package/src/skf-brief-skill/references/headless-args.md +22 -0
  25. package/src/skf-brief-skill/references/headless-source-authority-detection.md +26 -0
  26. package/src/skf-brief-skill/references/portfolio-similarity-check.md +35 -0
  27. package/src/skf-brief-skill/references/qmd-collection-registration.md +52 -0
  28. package/src/skf-brief-skill/references/version-resolution.md +46 -0
  29. package/src/skf-brief-skill/steps-c/step-01-gather-intent.md +164 -14
  30. package/src/skf-brief-skill/steps-c/step-02-analyze-target.md +118 -50
  31. package/src/skf-brief-skill/steps-c/step-03-scope-definition.md +48 -5
  32. package/src/skf-brief-skill/steps-c/step-04-confirm-brief.md +33 -19
  33. package/src/skf-brief-skill/steps-c/step-05-write-brief.md +93 -97
  34. package/src/skf-brief-skill/steps-c/step-06-health-check.md +11 -2
  35. package/src/skf-create-skill/steps-c/step-07-generate-artifacts.md +9 -1
  36. package/src/skf-export-skill/steps-c/step-01-load-skill.md +11 -3
  37. package/src/skf-export-skill/steps-c/step-03-generate-snippet.md +8 -2
  38. package/src/skf-export-skill/steps-c/step-04-update-context.md +29 -0
  39. package/src/skf-export-skill/steps-c/step-06-summary.md +12 -0
  40. package/src/skf-setup/steps-c/step-01b-ccc-index.md +10 -3
@@ -156,6 +156,35 @@ Instead of globbing `{skills_output_folder}/*/context-snippet.md`, resolve snipp
156
156
 
157
157
  **If no snippets pass the filter:** Generate managed section with zero skills — header only, no skill entries.
158
158
 
159
+ #### 4c.1 Detect Orphaned Managed-Section Rows (manifest-absent skills)
160
+
161
+ A managed-section row becomes orphaned when a `[skill-name v...]` entry already exists in the prior managed section but the skill is not in the exported skill set built in 4b — typically an externally-installed skill that was authored in a different repo and dropped into the user's `{skills_output_folder}` without going through export-skill. Strict ADR-K would silently drop such rows, but the user's managed section is load-bearing — silent removal of an installed skill is a regression an attentive operator caught manually in this run, recording a `deviations[].kind = "preserve_external_skills"` ad-hoc.
162
+
163
+ Detect this case before §5 assembly so the operator (or `{headless_mode}`) makes an explicit choice:
164
+
165
+ 1. Read the prior managed section from the **first target context file** (`target_context_files[0].context_file`). Parse the `[skill-name v...]` rows between `<!-- SKF:BEGIN -->` and `<!-- SKF:END -->` into `prior_section_rows` — a list of `{skill_name, version}` pairs. If the file does not exist or has no managed section, set `prior_section_rows = []` and skip to §5.
166
+ 2. Build `orphan_managed_rows` — every entry in `prior_section_rows` whose `skill_name` is NOT in the exported skill set built in 4b (manifest entries plus current-export targets). For each orphan, capture the original snippet line(s) verbatim from the prior section.
167
+ 3. **If `orphan_managed_rows` is non-empty**, present a halt-gate:
168
+
169
+ "**Managed-section rows present but absent from manifest:**
170
+
171
+ {list each as `- {skill_name} v{version}`}
172
+
173
+ These skills appear in the existing managed section in `{first-context-file}` but no entry exists in `.export-manifest.json` and no source draft exists under `{skills_output_folder}/{skill_name}/`. They were likely installed from a different repo and never run through export-skill in this project. Options:
174
+
175
+ - **(a) Drop** — remove these rows from the rebuilt managed section (strict ADR-K behavior). The skills' on-disk files are not touched, but they will no longer appear in any context file's managed index.
176
+ - **(b) Preserve verbatim** — copy each orphan's existing snippet line(s) into the rebuilt managed section unchanged. Records `deviations[].kind = \"preserve_external_skills\"` with the affected skill names and versions in the result contract for audit.
177
+ - **(c) Cancel** — abort export. Run export-skill against each external skill (or remove the orphan rows from the context file manually) before re-running."
178
+
179
+ 4. **Gate handling:**
180
+ - **(a) Drop:** Do not include any orphan rows in the rebuilt section. Record `orphans_dropped = [{skill_name, version}, …]` in workflow context for the §6 result contract.
181
+ - **(b) Preserve verbatim:** Append each captured snippet to the assembled section after the manifest-driven entries, preserving alphabetical order in the merged list. Append `{kind: "preserve_external_skills", skills: [{name, version}, …], rationale: "managed-section row exists but no manifest entry / no source draft"}` to the `deviations[]` array in the §6 result contract. The same orphans are written to **every** target context file in the loop (so all configured IDEs end up with consistent managed sections).
182
+ - **(c) Cancel:** HALT the workflow. Do not rewrite any context file. Do not update the manifest. Do not produce a result contract.
183
+
184
+ 5. **Headless default** (when `{headless_mode}`): auto-select **(b) Preserve verbatim**, with the same `deviations[]` entry. Emit a loud log line: `"headless: {N} managed-section rows had no manifest entry; preserving verbatim with deviations[].kind = preserve_external_skills. Run export-skill against each to migrate them into the manifest."` Silent drop under automation would regress the user's managed section without consent; cancel under automation would block the whole export over an externally-installed skill the user did not author. Preservation matches the prior-attentive-operator convention captured in `skills/export-skill-result-latest.json` deviations.
185
+
186
+ This detection runs once per export run (not per target context file) — orphan rows are inherent to the prior state of the first context file and the choice is global.
187
+
159
188
  #### 4d. Rewrite Root Paths for Target Context File
160
189
 
161
190
  The context-snippet.md files on disk contain root paths for the IDE they were originally exported to. When assembling the managed section for the current target context file, rewrite root paths if they differ from the target's `skill_root`.
@@ -132,6 +132,18 @@ No files were written. To run the export for real:
132
132
 
133
133
  Write the result contract per `shared/references/output-contract-schema.md`: the per-run record at `{skills_output_folder}/export-skill-result-{YYYYMMDD-HHmmss}.json` (UTC timestamp, resolution to seconds) and a copy at `{skills_output_folder}/export-skill-result-latest.json` (stable path for pipeline consumers — copy, not symlink). Include all context files and target managed-section files in `outputs`; include total always-on and on-trigger token counts in `summary`.
134
134
 
135
+ **`deviations[]` field (export-skill-specific extension):** when step-04 §4c.1 detected manifest-orphan managed rows and the operator (or `{headless_mode}`) chose **(b) Preserve verbatim**, also include a `deviations[]` array in the contract. Each entry has the shape:
136
+
137
+ ```json
138
+ {
139
+ "kind": "preserve_external_skills",
140
+ "skills": [{"name": "skill-name", "version": "1.0.0"}, …],
141
+ "rationale": "managed-section row exists but no manifest entry / no source draft"
142
+ }
143
+ ```
144
+
145
+ Other workflow choices that diverge from the strict spec path may add their own entries with a distinct `kind` value — auditors then have one place to look for "what did this run choose to do differently from the canonical flow." Omit the field when there are no deviations rather than writing an empty array, so the absence is readable as "ran the canonical path."
146
+
135
147
  ### 7. Chain to Health Check
136
148
 
137
149
  ONLY WHEN the export summary, distribution instructions, and result contract are complete will you then load, read the full file, and execute `{nextStepFile}`. The health-check step is the true terminal step — do not stop here even though the summary reads as final.
@@ -60,13 +60,20 @@ Check the `ccc_index` section:
60
60
 
61
61
  SKF infrastructure and output directories must be excluded from the CCC index — they contain workflow instructions, build artifacts, and generated skills that pollute semantic search results with zero extraction value.
62
62
 
63
- Run the merge helper, forwarding the resolved config values from the workflow activation context. Invoke via `uv run` so PEP 723 inline metadata resolves the script's PyYAML dependency automatically (per `docs/getting-started.md`'s prereq list uv exists for this exact purpose). Bare `python3` will fail on a fresh Python with `ModuleNotFoundError: No module named 'yaml'`.
63
+ **Pre-process the config values before invocation.** `skills_output_folder` and `forge_data_folder` come from `{project-root}/_bmad/skf/config.yaml` as template strings literally containing `{project-root}` (e.g. `'{project-root}/skills'`, `'{project-root}/forge-data'`). The merge helper interpolates each value into a `**/{value}` glob, so passing the raw template produces the malformed pattern `**/{project-root}/skills`. Before invoking the helper:
64
+
65
+ 1. Substitute `{project-root}` in each value with the resolved absolute project root.
66
+ 2. Reduce the substituted value to its last path segment (basename) — `**/skills`, `**/forge-data` are the patterns the script is designed to emit. Sub-paths under `{project-root}` (e.g. `_bmad-output/forge-data`) keep the segments below `{project-root}` (`_bmad-output/forge-data`), not the full absolute path.
67
+
68
+ Concretely: given `skills_output_folder: '{project-root}/skills'`, pass `skills`. Given `forge_data_folder: '{project-root}/_bmad-output/forge-data'`, pass `_bmad-output/forge-data`. The script's PR #248 validator rejects unresolved placeholders (`{`, `}`) and absolute paths (`/abs/...`) as a backstop, but the step is responsible for delivering values the script can interpolate cleanly — surfacing a warning and silently dropping the pattern is worse than getting the pre-processing right here.
69
+
70
+ Run the merge helper, forwarding the **resolved** config values. Invoke via `uv run` so PEP 723 inline metadata resolves the script's PyYAML dependency automatically (per `docs/getting-started.md`'s prereq list — uv exists for this exact purpose). Bare `python3` will fail on a fresh Python with `ModuleNotFoundError: No module named 'yaml'`.
64
71
 
65
72
  ```bash
66
73
  uv run {mergeCccExclusionsHelper} \
67
74
  --project-root "{project-root}" \
68
- --skills-output-folder "{skills_output_folder}" \
69
- --forge-data-folder "{forge_data_folder}"
75
+ --skills-output-folder "{resolved_skills_basename}" \
76
+ --forge-data-folder "{resolved_forge_data_path}"
70
77
  ```
71
78
 
72
79
  The script (see `src/shared/scripts/skf-merge-ccc-exclusions.py` docstring for the full schema) builds the SKF exclusion list (4 always-include hardcoded patterns + 2 conditional from validated config), applies the PR #248 validation rules to reject empty / absolute / glob-meta config values with actionable warnings, and performs an idempotent set-union merge into `{project-root}/.cocoindex_code/settings.yml`. User customizations are preserved. When the file does not exist yet (first-time setup before `ccc init`) the script creates it; when nothing new needs adding the script skips the write entirely (mtime preserved).