bmad-module-skill-forge 1.8.0 → 2.0.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.
Files changed (136) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +10 -5
  3. package/docs/_data/pinned.yaml +2 -2
  4. package/docs/agents.md +11 -2
  5. package/docs/architecture.md +5 -4
  6. package/docs/bmad-synergy.md +42 -3
  7. package/docs/campaign.md +172 -0
  8. package/docs/examples.md +7 -3
  9. package/docs/forge-auto.md +90 -0
  10. package/docs/getting-started.md +9 -0
  11. package/docs/how-it-works.md +6 -4
  12. package/docs/index.md +4 -3
  13. package/docs/skill-model.md +1 -1
  14. package/docs/troubleshooting.md +17 -1
  15. package/docs/verifying-a-skill.md +9 -3
  16. package/docs/why-skf.md +1 -1
  17. package/docs/workflows.md +73 -27
  18. package/package.json +2 -2
  19. package/src/module-help.csv +2 -1
  20. package/src/shared/_known-workarounds.yaml +155 -0
  21. package/src/shared/data/language-corpora.json +32 -0
  22. package/src/shared/references/pipeline-contracts.md +14 -2
  23. package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +6 -1
  24. package/src/shared/scripts/schemas/skill-brief.v1.json +6 -1
  25. package/src/shared/scripts/skf-derive-assembly-shape.py +107 -0
  26. package/src/shared/scripts/skf-detect-docs.py +417 -0
  27. package/src/shared/scripts/skf-detect-language.py +5 -3
  28. package/src/shared/scripts/skf-emit-brief-result-envelope.py +12 -1
  29. package/src/shared/scripts/skf-emit-result-envelope.py +17 -0
  30. package/src/shared/scripts/skf-extract-public-api.py +53 -0
  31. package/src/shared/scripts/skf-language-corpora.py +100 -0
  32. package/src/shared/scripts/skf-merge-doc-urls.py +244 -0
  33. package/src/shared/scripts/skf-preapply.py +192 -0
  34. package/src/shared/scripts/skf-shape-detect.py +1114 -0
  35. package/src/shared/scripts/skf-validate-brief-schema.py +2 -5
  36. package/src/shared/scripts/skf-validate-pins.py +368 -0
  37. package/src/shared/scripts/skf-write-skill-brief.py +21 -5
  38. package/src/skf-analyze-source/SKILL.md +26 -20
  39. package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
  40. package/src/skf-analyze-source/references/init.md +30 -0
  41. package/src/skf-analyze-source/references/step-auto-scope.md +677 -0
  42. package/src/skf-analyze-source/references/step-shape-detect.md +86 -0
  43. package/src/skf-audit-skill/SKILL.md +1 -0
  44. package/src/skf-audit-skill/assets/drift-report-template.md +6 -0
  45. package/src/skf-audit-skill/references/init.md +1 -1
  46. package/src/skf-audit-skill/references/report.md +4 -0
  47. package/src/skf-audit-skill/references/severity-classify.md +1 -1
  48. package/src/skf-audit-skill/references/step-doc-drift.md +147 -0
  49. package/src/skf-brief-skill/SKILL.md +7 -3
  50. package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -1
  51. package/src/skf-brief-skill/references/analyze-target.md +7 -4
  52. package/src/skf-brief-skill/references/confirm-brief.md +0 -1
  53. package/src/skf-brief-skill/references/gather-intent.md +22 -2
  54. package/src/skf-brief-skill/references/scope-definition.md +2 -1
  55. package/src/skf-brief-skill/references/step-auto-brief.md +188 -0
  56. package/src/skf-brief-skill/references/step-auto-validate.md +209 -0
  57. package/src/skf-brief-skill/references/write-brief.md +2 -3
  58. package/src/skf-campaign/SKILL.md +190 -0
  59. package/src/skf-campaign/assets/campaign-state-schema.json +191 -0
  60. package/src/skf-campaign/customize.toml +73 -0
  61. package/src/skf-campaign/manifest.yaml +11 -0
  62. package/src/skf-campaign/references/campaign-directive-spec.md +121 -0
  63. package/src/skf-campaign/references/health-check.md +35 -0
  64. package/src/skf-campaign/references/step-01-setup.md +122 -0
  65. package/src/skf-campaign/references/step-02-strategy.md +97 -0
  66. package/src/skf-campaign/references/step-03-pins.md +56 -0
  67. package/src/skf-campaign/references/step-04-provenance.md +63 -0
  68. package/src/skf-campaign/references/step-05-skill-loop.md +103 -0
  69. package/src/skf-campaign/references/step-06-batch.md +87 -0
  70. package/src/skf-campaign/references/step-07-capstone.md +63 -0
  71. package/src/skf-campaign/references/step-08-verify.md +75 -0
  72. package/src/skf-campaign/references/step-09-refine.md +83 -0
  73. package/src/skf-campaign/references/step-10-export.md +106 -0
  74. package/src/skf-campaign/references/step-11-maintenance.md +84 -0
  75. package/src/skf-campaign/references/step-resume.md +114 -0
  76. package/src/skf-campaign/scripts/.gitkeep +0 -0
  77. package/src/skf-campaign/scripts/campaign-deps.py +235 -0
  78. package/src/skf-campaign/scripts/campaign-parse-manifest.py +119 -0
  79. package/src/skf-campaign/scripts/campaign-provenance.py +247 -0
  80. package/src/skf-campaign/scripts/campaign-render-kickoff.py +158 -0
  81. package/src/skf-campaign/scripts/campaign-report.py +249 -0
  82. package/src/skf-campaign/scripts/campaign-validate-pins.py +174 -0
  83. package/src/skf-campaign/scripts/campaign-validate-state.py +207 -0
  84. package/src/skf-campaign/templates/campaign-brief-template.yaml +34 -0
  85. package/src/skf-campaign/templates/campaign-report-template.md +54 -0
  86. package/src/skf-campaign/templates/kickoff-template.md +48 -0
  87. package/src/skf-create-skill/SKILL.md +4 -1
  88. package/src/skf-create-skill/assets/compile-assembly-rules.md +22 -0
  89. package/src/skf-create-skill/assets/skill-sections.md +2 -0
  90. package/src/skf-create-skill/references/compile.md +5 -2
  91. package/src/skf-create-skill/references/extract.md +9 -1
  92. package/src/skf-create-skill/references/extraction-patterns.md +3 -1
  93. package/src/skf-create-skill/references/generate-artifacts.md +11 -2
  94. package/src/skf-create-skill/references/health-check.md +2 -2
  95. package/src/skf-create-skill/references/report.md +17 -1
  96. package/src/skf-create-skill/references/source-resolution-protocols.md +1 -1
  97. package/src/skf-create-skill/references/step-auto-shard.md +110 -0
  98. package/src/skf-create-skill/references/step-doc-rot.md +109 -0
  99. package/src/skf-create-skill/references/step-doc-sources.md +123 -0
  100. package/src/skf-create-skill/references/sub/fetch-docs.md +29 -0
  101. package/src/skf-create-skill/references/validate.md +12 -3
  102. package/src/skf-drop-skill/SKILL.md +2 -2
  103. package/src/skf-drop-skill/references/execute.md +20 -9
  104. package/src/skf-drop-skill/references/report.md +2 -0
  105. package/src/skf-drop-skill/references/select.md +7 -2
  106. package/src/skf-forger/SKILL.md +15 -4
  107. package/src/skf-refine-architecture/SKILL.md +2 -1
  108. package/src/skf-refine-architecture/references/compile.md +1 -1
  109. package/src/skf-refine-architecture/references/report.md +1 -1
  110. package/src/skf-rename-skill/SKILL.md +2 -2
  111. package/src/skf-rename-skill/references/execute.md +5 -4
  112. package/src/skf-rename-skill/references/rebuild-context.md +2 -2
  113. package/src/skf-rename-skill/references/select.md +3 -3
  114. package/src/skf-setup/SKILL.md +1 -1
  115. package/src/skf-setup/references/auto-index.md +3 -1
  116. package/src/skf-setup/references/detect-and-tier.md +4 -0
  117. package/src/skf-setup/references/report.md +4 -1
  118. package/src/skf-setup/references/tier-rules.md +1 -1
  119. package/src/skf-test-skill/SKILL.md +8 -7
  120. package/src/skf-test-skill/customize.toml +2 -1
  121. package/src/skf-test-skill/references/coverage-check.md +10 -0
  122. package/src/skf-test-skill/references/external-validators.md +1 -1
  123. package/src/skf-test-skill/references/init.md +16 -1
  124. package/src/skf-test-skill/references/report.md +5 -1
  125. package/src/skf-test-skill/references/score.md +95 -6
  126. package/src/skf-test-skill/references/source-access-protocol.md +13 -3
  127. package/src/skf-test-skill/references/step-hard-gate.md +73 -0
  128. package/src/skf-test-skill/scripts/compute-score.py +4 -3
  129. package/src/skf-test-skill/templates/test-report-template.md +1 -0
  130. package/src/skf-update-skill/customize.toml +0 -9
  131. package/src/skf-update-skill/references/detect-changes.md +33 -3
  132. package/src/skf-update-skill/references/health-check.md +2 -2
  133. package/src/skf-update-skill/references/init.md +1 -0
  134. package/src/skf-update-skill/references/re-extract.md +15 -1
  135. package/src/skf-verify-stack/references/report.md +1 -1
  136. package/tools/cli/lib/ui.js +3 -2
@@ -0,0 +1,86 @@
1
+ ---
2
+ shapeDetectProbeOrder:
3
+ - '{project-root}/_bmad/skf/shared/scripts/skf-shape-detect.py'
4
+ - '{project-root}/src/shared/scripts/skf-shape-detect.py'
5
+ ---
6
+
7
+ # Shape Detection Reference
8
+
9
+ Reference document for invoking `skf-shape-detect.py` — the shared shape classification module. Loaded by `step-auto-scope.md` for auto-scope analysis.
10
+
11
+ ## Invocation Contract
12
+
13
+ **Resolve `{shapeDetectHelper}`** from `{shapeDetectProbeOrder}`; first existing path wins; HALT if neither resolves.
14
+
15
+ **Command:**
16
+ ```
17
+ uv run {shapeDetectHelper} --repo-url <url> \
18
+ --manifests <path1,path2,...> \
19
+ --grammar-files <g1,g2,...> --tree-paths <d1/,d2/,file,...>
20
+ ```
21
+
22
+ **Arguments:**
23
+
24
+ | Arg | Required | Description |
25
+ |-----|----------|-------------|
26
+ | `--repo-url` | Yes | Repository URL (context only — no cloning performed) |
27
+ | `--manifests` | Yes | Comma-separated local file paths to manifest files (may be empty when a tree-level signal carries the classification) |
28
+ | `--grammar-files` | No | Comma-separated repo-relative grammar files (`*.y`, `*.g4`, `*.pest`, `Grammar/python.gram`, ...) — a whole-language signal |
29
+ | `--tree-paths` | No | Comma-separated repo-relative directory (trailing `/`) and structural file signals harvested from the clone (a `compiler/` dir, a lexer+parser+ast triad) |
30
+
31
+ **Supported manifests:** `package.json`, `pyproject.toml`, `Cargo.toml`, `go.mod`, `pom.xml`, `build.gradle`, `build.gradle.kts`, `Package.swift`
32
+
33
+ ## Output Schema
34
+
35
+ JSON object on stdout:
36
+
37
+ | Field | Type | Description |
38
+ |-------|------|-------------|
39
+ | `shape` | string | `library-API` \| `reference-app` \| `language-reference` \| `stack-compose` \| `unknown` |
40
+ | `signals` | array[string] | Human-readable evidence strings |
41
+ | `confidence` | float | 0.0–1.0 |
42
+ | `export_count` | integer | Total public-facing exports detected |
43
+ | `package_count` | integer | Distinct packages detected |
44
+
45
+ ## Exit Codes
46
+
47
+ | Code | Meaning | Consumer Action |
48
+ |------|---------|-----------------|
49
+ | 0 | Shape classified (not unknown) | Use shape result for scope mapping |
50
+ | 1 | Unknown shape (no heuristic matched) | Fall back to interactive mode |
51
+ | 2 | Error (invalid args, missing/unreadable files, parse failure) | HARD HALT with `resolution-failure` |
52
+
53
+ On exit code 2, error details are written to stderr as JSON: `{"error": "message", "code": "ERROR_CODE"}`
54
+
55
+ ## Shape → Scope Type Mapping
56
+
57
+ | Shape | scope.type | Condition |
58
+ |-------|------------|-----------|
59
+ | `library-API` | `full-library` | export_count ≤ 200 |
60
+ | `library-API` | `public-api` | export_count > 200 (surface too large for full coverage) |
61
+ | `reference-app` | `reference-app` | Direct mapping — apps, CLIs, demos |
62
+ | `language-reference` | `full-library` | Language tools/parsers are library-shaped from a skill perspective. **Corpora-dependent** for a *whole-language* reference (a `grammar_file:`/`tree_triad:` signal — a compiler/interpreter): its value is the language's prose (guide/Book + std/library docs), not compiler internals, so step-auto-scope.md §6b seeds companion corpora and §6/§7 record an honest DEGRADED caveat when none are found (mirrors the §3b facet-coverage guard). A parser *library* (`parser_producer:`/`parser_dep:`) is exempt — its code is the product. |
63
+ | `stack-compose` | `full-library` | Decomposition candidate when `package_count > 3` — cohesion-checked in step-auto-scope.md §3b |
64
+ | `unknown` | N/A | Triggers fallback to interactive mode |
65
+
66
+ ## Decomposition Thresholds
67
+
68
+ When auto-scope detects a multi-package monorepo, it may recommend multi-skill decomposition instead of producing one unwieldy skill. The threshold is evaluated in step-auto-scope.md §3a.
69
+
70
+ | Threshold | Value | Signal | Decomposition Path |
71
+ |-----------|-------|--------|-------------------|
72
+ | Multi-package / monorepo | `package_count > 3` | Shape detection `package_count` | Cohesion check (§3b): merge to one skill or split per package |
73
+
74
+ `package_count > 3` makes a **monorepo** a decomposition candidate; step-auto-scope.md §3b then decides merge-vs-split. It is empirically validated (fires on real 15-, 38-, and 442-package workspaces). A *single* package with a large API surface is **not** decomposed — it produces one cohesive skill that `skf-create-skill`'s auto-shard splits into `references/` shards at the 400-line ceiling.
75
+
76
+ When neither threshold is met, the single-scope flow proceeds unchanged.
77
+
78
+ ## Heuristic Ladder
79
+
80
+ The five-shape heuristic ladder applies in order (first match wins):
81
+
82
+ 1. **language-reference** — parser/grammar/language-toolchain project. Signals, strongest first: a hand-written-compiler tree structure (a dedicated `compiler/` directory with a lexer+parser+ast triad plus a codegen/VM/type-checker member — catches rustc, TypeScript, Go); a declared grammar file (`Grammar/python.gram`, a root `parse.y`, a `*.g4` — catches CPython, Ruby); the repo's own name being a known parser/grammar tool (pest, lalrpop, lark — the producer); or a parser-generator dependency (a DSL built on antlr4/lalrpop — the consumer). Delegating consumers (formatters, linters, bundlers that depend on a parser) and markup/DSL parsers (CSS, markdown, GraphQL) are excluded.
83
+ 2. **stack-compose** — multi-ecosystem composite project. Signals: manifests from 2+ distinct ecosystems
84
+ 3. **reference-app** — application, CLI, or demo project. Signals: npm `bin` field, Rust `[[bin]]`, framework deps (next, fastapi, axum, etc.)
85
+ 4. **library-API** — library exposing a programmatic API. Signals: `main`/`module`/`exports` fields, `[lib]` target, export count
86
+ 5. **unknown** — no heuristic matched
@@ -40,6 +40,7 @@ These rules apply to every step in this workflow:
40
40
  | 3 | Structural Diff | references/structural-diff.md | Yes |
41
41
  | 4 | Semantic Diff | references/semantic-diff.md | Yes (skip at non-Deep) |
42
42
  | 5 | Severity Classification | references/severity-classify.md | Yes |
43
+ | 5a | Doc Drift | references/step-doc-drift.md | Yes |
43
44
  | 6 | Report | references/report.md | Yes |
44
45
  | 7 | Workflow Health Check | references/health-check.md | Yes |
45
46
 
@@ -52,6 +52,12 @@ previousWorkflow: 'create-skill'
52
52
 
53
53
  ---
54
54
 
55
+ ## Documentation Drift
56
+
57
+ <!-- Appended by step-doc-drift -->
58
+
59
+ ---
60
+
55
61
  ## Remediation Suggestions
56
62
 
57
63
  <!-- Appended by report -->
@@ -252,7 +252,7 @@ When skipping, log the reason, then set the audit-ref context variables to basel
252
252
  - **[D]:** Read the prior report's findings_list (parse the Structural/Semantic/Severity sections, or the appended findings tables) and stash as `prior_findings` in workflow context. Run the new audit normally. In step 6 (report.md), after the Remediation Suggestions section, emit a `## Diff Against Prior Report` subsection summarizing added / removed / changed findings vs `prior_findings`.
253
253
  - **[R]:** Load the prior report's frontmatter (`stepsCompleted`, `drift_score`, any intermediate state). Set `{outputFile}` to the prior report path (do NOT create a new one). Determine the next un-completed step from `stepsCompleted` and skip forward to it; downstream steps append to the existing report.
254
254
 
255
- > **Note (resumability):** the existing template frontmatter captures `stepsCompleted` and `drift_score` but does not currently persist intermediate findings_list between stages. If [R] is selected and stepsCompleted indicates the prior run halted after structural-diff or semantic-diff, the appended sections in the report body (`## Structural Drift`, `## Semantic Drift`, `## Severity Classification`) serve as the implicit intermediate state — re-parse them on resume rather than re-running completed stages. **TODO:** if resumability proves unreliable in practice, extend the report frontmatter to carry an explicit `intermediate_findings` block (out of scope for this PR).
255
+ > **Note (resumability):** the existing template frontmatter captures `stepsCompleted` and `drift_score` but does not currently persist intermediate findings_list between stages. If [R] is selected and stepsCompleted indicates the prior run halted after structural-diff or semantic-diff, the appended sections in the report body (`## Structural Drift`, `## Semantic Drift`, `## Severity Classification`) serve as the implicit intermediate state — re-parse them on resume rather than re-running completed stages. **Design note:** explicit mid-stage resume state is intentionally not persisted today — if resumability proves unreliable in practice, a future enhancement is to extend the report frontmatter to carry an explicit `intermediate_findings` block.
256
256
 
257
257
  - **Other input:** help user, redisplay the gate.
258
258
 
@@ -27,6 +27,10 @@ Update the ## Audit Summary section at the top of {outputFile} with final calcul
27
27
  - Fill in severity count table from Step 05 classification summary
28
28
  - Set overall drift score
29
29
  - Add total findings count
30
+ - Include doc drift summary from `doc_drift_summary` context (set by step 5a):
31
+ - If `changed > 0`: "**Doc Drift:** {changed} of {total_tracked} tracked doc(s) have changed since compile. Consider re-running CS to update doc_sources."
32
+ - If `fetch_failed > 0`: "{fetch_failed} doc URL(s) could not be reached during audit."
33
+ - If `skipped_entirely`: no mention in summary (already noted in the doc drift section)
30
34
 
31
35
  ### 2. Generate Remediation Suggestions
32
36
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- nextStepFile: 'report.md'
2
+ nextStepFile: 'step-doc-drift.md'
3
3
  outputFile: '{forge_version}/drift-report-{timestamp}.md'
4
4
  severityRulesFile: 'references/severity-rules.md'
5
5
  ---
@@ -0,0 +1,147 @@
1
+ ---
2
+ nextStepFile: 'report.md'
3
+ outputFile: '{forge_version}/drift-report-{timestamp}.md'
4
+ ---
5
+
6
+ <!-- Config: communicate in {communication_language}. -->
7
+
8
+ # Step 5a: Documentation Drift
9
+
10
+ ## STEP GOAL:
11
+
12
+ Compare documentation content hashes stored at compile time (in `doc_sources` within metadata.json) against the current upstream state. Produce a drift section that reports which tracked docs have changed, which are unreachable, and which were never hashed. This step is informational — doc drift does not affect the source code drift score.
13
+
14
+ ## Rules
15
+
16
+ - Auto-proceed step — no user interaction
17
+ - Graceful failure — if doc fetching fails for any URL, mark as `fetch_failed`, do not block the audit
18
+ - Do not classify severity — doc drift is informational alongside source drift
19
+ - If no `doc_sources` in metadata, skip with notice and auto-proceed
20
+ - Never abort the audit pipeline on any failure in this step
21
+
22
+ ## MANDATORY SEQUENCE
23
+
24
+ ### 1. Check for doc_sources
25
+
26
+ Check the skill metadata loaded at init (step 1 §3 — Load Skill Artifacts) for a `doc_sources` array.
27
+
28
+ **If `doc_sources` is absent or metadata lacks the field:**
29
+
30
+ Append to {outputFile}:
31
+
32
+ ```markdown
33
+ ## Documentation Drift
34
+
35
+ No doc_sources recorded — skip doc drift check. This skill was compiled before doc tracking was available. Recompile with the current CS pipeline to enable doc drift detection.
36
+ ```
37
+
38
+ Set `doc_drift_summary = { skipped_entirely: true }` in workflow context. Update {outputFile} frontmatter: append `'doc-drift'` to `stepsCompleted`. Auto-proceed to {nextStepFile}.
39
+
40
+ **If `doc_sources` is present but an empty array:**
41
+
42
+ Append to {outputFile}:
43
+
44
+ ```markdown
45
+ ## Documentation Drift
46
+
47
+ No documentation sources tracked. The `doc_sources` array is empty — no drift check to perform.
48
+ ```
49
+
50
+ Set `doc_drift_summary = { total_tracked: 0, skipped_entirely: false }` in workflow context. Update {outputFile} frontmatter: append `'doc-drift'` to `stepsCompleted`. Auto-proceed to {nextStepFile}.
51
+
52
+ **If `doc_sources` is present and non-empty:** Continue to §2.
53
+
54
+ ### 2. Fetch and Hash Each Tracked Doc
55
+
56
+ For each entry in `doc_sources`:
57
+
58
+ 1. Read `url` and `content_hash` from the entry
59
+ 2. **If `content_hash` is `null`:** Skip this entry — there is no baseline to compare against. Record it for the report as a null-hash entry. Do not attempt to fetch the URL.
60
+ 3. **If `content_hash` is non-null:** Attempt HTTP GET of the URL with a reasonable timeout (10s)
61
+ - **On success:** Compute `sha256:{hexdigest}` of the response body bytes (UTF-8 encoding). Compare against the stored `content_hash`. If they differ, record as drifted. If they match, record as unchanged.
62
+ - **On failure (network error, timeout, non-200 status):** Record the entry as `status: "fetch_failed"` with the failure reason. Do not report as drift.
63
+
64
+ If URL fetching is unavailable in the current environment, skip doc drift check entirely with:
65
+
66
+ ```markdown
67
+ ## Documentation Drift
68
+
69
+ Doc drift check skipped — URL fetching unavailable in current environment.
70
+ ```
71
+
72
+ Set `doc_drift_summary = { skipped_entirely: true }` and auto-proceed.
73
+
74
+ ### 3. Build Drift Findings
75
+
76
+ Categorize results:
77
+ - **changed:** entries where `content_hash` differs from newly computed hash
78
+ - **unchanged:** entries where hashes match
79
+ - **fetch_failed:** entries where the URL could not be reached
80
+ - **skipped_null_hash:** entries where `content_hash` was `null`
81
+
82
+ Compute totals:
83
+ - `total_tracked` = length of `doc_sources`
84
+ - `changed` = count of drifted entries
85
+ - `unchanged` = count of matching entries
86
+ - `fetch_failed` = count of fetch failures
87
+ - `skipped_null_hash` = count of null-hash entries
88
+
89
+ ### 4. Append to Drift Report
90
+
91
+ Append the `## Documentation Drift` section to {outputFile}.
92
+
93
+ **When drift detected:**
94
+
95
+ ```markdown
96
+ ## Documentation Drift
97
+
98
+ | URL | Old Hash | New Hash | Detected At |
99
+ |-----|----------|----------|-------------|
100
+ | {url} | `{old_hash}` | `{new_hash}` | {ISO-8601 timestamp} |
101
+
102
+ **{changed} of {total_tracked} tracked documentation source(s) have changed since compile.**
103
+ ```
104
+
105
+ Include rows for ALL entries, in order:
106
+ - Drifted entries: show old and new hash
107
+ - Unchanged entries: omit from table (only drifted entries appear)
108
+ - Fetch-failed entries: `| {url} | \`{old_hash}\` | _(fetch failed: {reason})_ | {timestamp} |`
109
+ - Null-hash entries: `| {url} | _(not recorded)_ | — | — |`
110
+
111
+ Fetch-failed entries are clearly marked and excluded from the drift count.
112
+
113
+ **When no drift detected:**
114
+
115
+ ```markdown
116
+ ## Documentation Drift
117
+
118
+ No documentation drift detected. All {total_tracked} tracked documentation source(s) match their compile-time hashes.
119
+ ```
120
+
121
+ If some entries were fetch_failed or skipped_null_hash, append a note after the main message listing those entries.
122
+
123
+ ### 5. Store Context and Auto-Proceed
124
+
125
+ Store `doc_drift_summary` in workflow context for report.md to reference:
126
+
127
+ ```
128
+ doc_drift_summary = {
129
+ total_tracked: N,
130
+ changed: N,
131
+ unchanged: N,
132
+ fetch_failed: N,
133
+ skipped_null_hash: N,
134
+ skipped_entirely: false
135
+ }
136
+ ```
137
+
138
+ Update {outputFile} frontmatter:
139
+ - Append `'doc-drift'` to `stepsCompleted`
140
+
141
+ Display: "**Documentation drift check complete. {changed} of {total_tracked} source(s) drifted. Proceeding to report generation...**"
142
+
143
+ Load, read the full file, then execute {nextStepFile}.
144
+
145
+ ## CRITICAL STEP COMPLETION NOTE
146
+
147
+ ONLY WHEN the ## Documentation Drift section has been appended to {outputFile} and workflow context updated will you then load and read fully `{nextStepFile}` to begin final report generation.
@@ -73,17 +73,21 @@ These rules apply to every step in this workflow:
73
73
  | # | Step | File | Auto-proceed |
74
74
  |---|------|------|--------------|
75
75
  | 1 | Gather Intent | references/gather-intent.md | No (interactive) |
76
+ | 1a | Auto-Brief Generation (auto mode only) | references/step-auto-brief.md | Yes |
77
+ | 1b | Auto-Brief Validation (auto mode only) | references/step-auto-validate.md | No (interactive gate — headless auto-approves) |
76
78
  | 2 | Analyze Target | references/analyze-target.md | Yes |
77
79
  | 3 | Scope Definition | references/scope-definition.md | No (interactive) |
78
80
  | 4 | Confirm Brief | references/confirm-brief.md | No (confirm) |
79
81
  | 5 | Write Brief | references/write-brief.md | Yes |
80
82
  | 6 | Workflow Health Check (terminal) | references/health-check.md | Yes |
81
83
 
84
+ Stages 1a-1b are conditional — they replace stages 2-5 when BS is invoked with the `[auto]` flag via pipeline context. The routing decision is made in stage 1 (gather-intent.md §1b). In auto mode, the chain is: gather-intent.md §1 (forge tier) → §1b (auto check) → step-auto-brief.md → step-auto-validate.md → health-check.md (on [A]pprove or [E]dit) or → confirm-brief.md → write-brief.md → health-check.md (on [R]eject).
85
+
82
86
  ## Invocation Contract
83
87
 
84
88
  | Aspect | Detail |
85
89
  |--------|--------|
86
- | **Inputs** | `target_repo` [required], `skill_name` [required], `scope_hint` [optional], `language_hint` [optional], `target_version` [optional], `source_authority` [optional: official/community/internal, default community], `source_type` [optional: source/docs-only, default source], `doc_urls` [optional: list of `url[,label]` for source_type=docs-only or supplemental], `scope_type` [optional: full-library/specific-modules/public-api/component-library/reference-app/docs-only], `include` [optional: comma-separated globs], `exclude` [optional: comma-separated globs], `scripts_intent` [optional: detect/none/free-text, default detect], `assets_intent` [optional: detect/none/free-text, default detect], `intent` [optional: free-text used to derive description], `force` [optional: overwrite existing brief without prompting], `from_brief` [optional: path to a pre-authored `skill-brief.yaml` to *ratify* — when supplied it is the source of truth, `target_repo`/`skill_name` become optional/derived-from-brief, and the run mirrors the interactive §3.1a ratify path: schema-validate, skip analyze-target/scope-definition, write through the canonical writer in place] |
90
+ | **Inputs** | `target_repo` [required], `skill_name` [required], `scope_hint` [optional], `language_hint` [optional], `target_version` [optional], `source_authority` [optional: official/community/internal, default community], `source_type` [optional: source/docs-only, default source], `doc_urls` [optional: list of `url[,label]` for source_type=docs-only or supplemental], `scope_type` [optional: full-library/specific-modules/public-api/component-library/reference-app/docs-only], `include` [optional: comma-separated globs], `exclude` [optional: comma-separated globs], `scripts_intent` [optional: detect/none/free-text, default detect], `assets_intent` [optional: detect/none/free-text, default detect], `intent` [optional: free-text used to derive description], `force` [optional: overwrite existing brief without prompting], `from_brief` [optional: path to a pre-authored `skill-brief.yaml` to *ratify* — when supplied it is the source of truth, `target_repo`/`skill_name` become optional/derived-from-brief, and the run mirrors the interactive §3.1a ratify path: schema-validate, skip analyze-target/scope-definition, write through the canonical writer in place], `[auto]` [optional: bracket modifier passed via pipeline context — when present, BS loads the upstream brief from `brief_path` in pipeline data, enriches it with doc detection, and writes through the canonical writer; requires `brief_path` from AN's `SKF_ANALYZE_RESULT_JSON`] |
87
91
  | **Gates** | step 1: Input Gate [use args] | step 3: Confirm Gate [C] | step 4: Confirm Gate [C] |
88
92
  | **Outputs** | `skill-brief.yaml` at `{forge_data_folder}/{skill-name}/skill-brief.yaml`; final `SKF_BRIEF_RESULT_JSON` line on stdout when `{headless_mode}` is true |
89
93
  | **Headless** | All gates auto-resolve with heuristic-driven or default action when `{headless_mode}` is true; pre-supplied inputs consumed at the gates that would otherwise prompt; absent `source_authority` and `scope_type` are resolved by signal-driven detection (see `references/headless-args.md`); existing briefs are preserved unless `--force` was supplied (HALT with `overwrite-cancelled` otherwise); supplying `from_brief <path>` instead routes the step 1 GATE to a ratify path that schema-validates the pre-authored brief, skips analyze-target/scope-definition, and writes through the canonical writer (overwriting in place, no `--force` needed) rather than deriving a new brief |
@@ -108,7 +112,7 @@ Every HARD HALT in this workflow exits with a stable code so headless automators
108
112
  When `{headless_mode}` is true, step 5 emits a single-line JSON envelope on **stdout** before chaining to step 6, and every HARD HALT emits the same envelope shape on **stderr** with `status: "error"`:
109
113
 
110
114
  ```
111
- SKF_BRIEF_RESULT_JSON: {"status":"success|error","brief_path":"…|null","skill_name":"…","version":"…|null","language":"…|null","scope_type":"…|null","exit_code":0,"halt_reason":null}
115
+ SKF_BRIEF_RESULT_JSON: {"status":"success|error","brief_path":"…|null","skill_name":"…","version":"…|null","language":"…|null","scope_type":"…|null","exit_code":0,"halt_reason":null,"mode":"auto|null"}
112
116
  ```
113
117
 
114
- `status` is `"success"` on the terminal happy path, `"error"` on any HALT. `halt_reason` is one of: `null` (success), `"input-missing"`, `"input-invalid"`, `"forge-tier-missing"`, `"target-inaccessible"`, `"gh-auth-failed"`, `"write-failed"`, `"overwrite-cancelled"`, `"user-cancelled"`. `exit_code` matches the table above.
118
+ `status` is `"success"` on the terminal happy path, `"error"` on any HALT. `halt_reason` is one of: `null` (success), `"input-missing"`, `"input-invalid"`, `"forge-tier-missing"`, `"target-inaccessible"`, `"gh-auth-failed"`, `"write-failed"`, `"overwrite-cancelled"`, `"user-cancelled"`. `exit_code` matches the table above. `mode` is `"auto"` when BS was invoked with the `[auto]` flag (pipeline auto-brief generation), `null` otherwise (interactive or headless-without-auto).
@@ -19,7 +19,7 @@
19
19
  | Field | Type | Constraint | Description |
20
20
  |--------------------|--------|--------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
21
21
  | source_type | string | `source` or `docs-only` | Default `source`. When `docs-only`: `source_repo` optional, `doc_urls` required |
22
- | doc_urls | array | `{url, label}` objects | Documentation URLs for T3 content. Required when `source_type: "docs-only"` |
22
+ | doc_urls | array | `{url, label, source?}` objects | Documentation URLs for T3 content. Required when `source_type: "docs-only"`. Optional `source` provenance enum: `language-registry` (registry-guaranteed corpus) \| `readme-detection` \| `homepage` \| `pages-api` \| `docs-folder` |
23
23
  | `scripts_intent` | string | `detect` / `none` / free-text | Describes whether scripts should be extracted. Values: `detect` (auto-detect from source — default when absent), `none` (skip scripts), or a free-text description of expected scripts (e.g., "CLI validation tools in bin/"). |
24
24
  | `assets_intent` | string | `detect` / `none` / free-text | Describes whether assets should be extracted. Values: `detect` (auto-detect from source — default when absent), `none` (skip assets), or a free-text description of expected assets (e.g., "JSON schemas in schemas/"). |
25
25
  | `target_version` | string | Semantic version (`X.Y.Z` or `X.Y.Z-prerelease`) | User-specified target version. When present, overrides auto-detection and becomes the skill's version. Recommended for docs-only skills where auto-detection is unavailable. |
@@ -10,6 +10,9 @@ detectWorkspacesProbeOrder:
10
10
  detectLanguageProbeOrder:
11
11
  - '{project-root}/_bmad/skf/shared/scripts/skf-detect-language.py'
12
12
  - '{project-root}/src/shared/scripts/skf-detect-language.py'
13
+ emitBriefEnvelopeProbeOrder:
14
+ - '{project-root}/_bmad/skf/shared/scripts/skf-emit-brief-result-envelope.py'
15
+ - '{project-root}/src/shared/scripts/skf-emit-brief-result-envelope.py'
13
16
  ---
14
17
 
15
18
  <!-- Config: communicate in {communication_language}. -->
@@ -56,10 +59,10 @@ To analyze the target repository by resolving its location, reading its structur
56
59
 
57
60
  **On API failure (non-200 from `gh api`):**
58
61
 
59
- Distinguish the failure class before reporting:
60
- - Auto-run `gh auth status` and capture its output. If it reports an unauthenticated state or expired token: HALT (exit code 3, `halt_reason: "gh-auth-failed"`) — "**Error:** GitHub CLI is not authenticated. `gh auth status` says: `{captured output}`. Run `gh auth login` and retry."
61
- - If `gh auth status` reports authenticated but the call still failed (404/403): HALT (exit code 3, `halt_reason: "target-inaccessible"`) — "**Error:** Cannot access repository at `{url}`. The CLI is authenticated but the API returned `{status}`. Check the URL and that the account has access to private repositories if applicable."
62
- - If `gh auth status` itself fails to run (binary missing): HALT (exit code 3, `halt_reason: "gh-auth-failed"`) — "**Error:** `gh` CLI not found on PATH. Install it from <https://cli.github.com> and re-run."
62
+ Distinguish the failure class before reporting. In headless mode, every branch below emits the error envelope per **step 5 §4b** with its stated `halt_reason` before the HALT (pass the resolved `{skill_name}`, or the `"unknown"` placeholder documented in §4b if it is not yet set):
63
+ - Auto-run `gh auth status` and capture its output. If it reports an unauthenticated state or expired token: emit the error envelope per **step 5 §4b** with `halt_reason: "gh-auth-failed"`, then HALT (exit code 3, `halt_reason: "gh-auth-failed"`) — "**Error:** GitHub CLI is not authenticated. `gh auth status` says: `{captured output}`. Run `gh auth login` and retry."
64
+ - If `gh auth status` reports authenticated but the call still failed (404/403): emit the error envelope per **step 5 §4b** with `halt_reason: "target-inaccessible"`, then HALT (exit code 3, `halt_reason: "target-inaccessible"`) — "**Error:** Cannot access repository at `{url}`. The CLI is authenticated but the API returned `{status}`. Check the URL and that the account has access to private repositories if applicable."
65
+ - If `gh auth status` itself fails to run (binary missing): emit the error envelope per **step 5 §4b** with `halt_reason: "gh-auth-failed"`, then HALT (exit code 3, `halt_reason: "gh-auth-failed"`) — "**Error:** `gh` CLI not found on PATH. Install it from <https://cli.github.com> and re-run."
63
66
 
64
67
  **For local paths:**
65
68
  - Verify the directory exists
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  nextStepFile: 'write-brief.md'
3
3
  reviseStepFile: 'scope-definition.md'
4
- briefSchemaFile: 'assets/skill-brief-schema.md'
5
4
  advancedElicitationSkill: '/bmad-advanced-elicitation'
6
5
  partyModeSkill: '/bmad-party-mode'
7
6
  ---
@@ -2,7 +2,6 @@
2
2
  nextStepFile: 'analyze-target.md'
3
3
  ratifyTargetFile: 'confirm-brief.md'
4
4
  forgeTierFile: '{sidecar_path}/forge-tier.yaml'
5
- descriptionVoiceExamplesFile: 'assets/description-voice-examples.md'
6
5
  headlessArgsFile: 'references/headless-args.md'
7
6
  headlessSourceAuthorityDetectionFile: 'references/headless-source-authority-detection.md'
8
7
  portfolioSimilarityCheckFile: 'references/portfolio-similarity-check.md'
@@ -61,8 +60,23 @@ Attempt to load `{forgeTierFile}`:
61
60
 
62
61
  **If not found:**
63
62
  - "**Cannot proceed.** forge-tier.yaml not found at `{forgeTierFile}`. Run the **setup** workflow first to configure your forge tier (Quick/Forge/Forge+/Deep)."
63
+ - In headless mode, emit the error envelope per **step 5 §4b** with `halt_reason: "forge-tier-missing"` (`skill_name` is not yet resolved here — use the `"unknown"` placeholder convention documented in §4b).
64
64
  - HALT (exit code 3, `halt_reason: "forge-tier-missing"`) — do not proceed.
65
65
 
66
+ ### 1b. Auto Mode Check
67
+
68
+ **Check for `[auto]` flag:** If `[auto]` was passed as a bracket modifier in the pipeline context (e.g., `BS[auto]`), set `{auto_mode}` = true.
69
+
70
+ **IF `{auto_mode}` is true:**
71
+
72
+ 1. **Load upstream brief path:** Read `brief_path` from the pipeline data context (passed by the forger from AN's `SKF_ANALYZE_RESULT_JSON` `brief_paths[]`). If `brief_path` is not available, HARD HALT with exit code 2 (`input-missing`): "**Auto mode requires `brief_path` in pipeline context — AN must run before BS[auto].**"
73
+ 2. **Load source repo:** Read `source_repo` from the pipeline data context (the target repo URL or path, forwarded by the forger). If not available, attempt to extract it from the upstream brief at `brief_path`.
74
+ 3. "**Auto mode activated — bypassing interactive brief workflow.**"
75
+ 4. **Route to auto-brief:** Load, read fully, then execute `references/step-auto-brief.md`. **STOP HERE** — do not continue to §2 or any subsequent section.
76
+
77
+ **IF `{auto_mode}` is NOT true:**
78
+ Continue to §2 as normal — the entire interactive flow below is unchanged.
79
+
66
80
  ### 2. Welcome and Explain
67
81
 
68
82
  "**Welcome to Brief Skill — the skill scoping workflow.**
@@ -92,8 +106,12 @@ This section has four sub-flows. Execute exactly one branch — 3.1a *or* 3.2 *o
92
106
 
93
107
  #### 3.1 Collect target
94
108
 
109
+ **Open-floor opening.** Lead with an open invitation so an expert can state everything in one breath rather than being walked through seven discrete prompts — costs almost nothing token-wise and sharply improves the conversational feel of this, the most question-heavy mode. A first-timer who pastes only a bare URL still gets the full guided sequence below, unchanged.
110
+
95
111
  "**What repository or documentation do you want to create a skill for?**
96
112
 
113
+ Tell me everything you have — the repo or docs, what you want to skill and why, any scope or version thoughts. Or just paste a URL and we'll go from there.
114
+
97
115
  Provide one of:
98
116
  - A **GitHub URL** (e.g., `https://github.com/org/repo`)
99
117
  - A **local path** (e.g., `/path/to/project`)
@@ -104,7 +122,7 @@ Or type `cancel` / `exit` / `[X]` to leave without writing anything.
104
122
 
105
123
  **Target:**"
106
124
 
107
- Wait for user response. Branch on the response:
125
+ Wait for user response. **Parse the response for any of the fields the later sections collect** — `target_version` (§3b), intent (§4), scope hints (§5), source authority (§3.3), a proposed name (§6) — and pre-fill every field the user covered, holding them in workflow context. Sections §3b/§4/§5/§6/§7b then **acknowledge a pre-filled field instead of re-asking** ("I noted you're targeting v4.0.0"), and prompt only for the gaps. An expert who stated it all collapses to the §3.1 target branch plus the §7b description confirmation; a bare URL falls through to the full sequence. Then branch on the response for the target itself:
108
126
 
109
127
  - Empty input, `cancel`, `exit`, `[X]`, `q`, or `:q` → 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 by step 1. Headless mode never reaches this branch (the GATE in §8 short-circuits the interactive sub-flows).
110
128
  - Path that resolves to an existing `skill-brief.yaml` (file path ending in `skill-brief.yaml` that exists, OR a directory containing a `skill-brief.yaml`) → §3.1a
@@ -253,6 +271,8 @@ Take your time — the more context you share, the better the brief."
253
271
 
254
272
  Wait for user response. Ask follow-up questions if intent is unclear.
255
273
 
274
+ **Capture, don't interrupt.** If the user volunteers an out-of-scope aside while answering — "the v3 API is totally different", "we're deprecating the auth module next quarter" — do not redirect the conversation to chase it. Silently note it as a candidate `scope.notes` line (carried forward into the brief's `scope.notes` at step 3) and continue the current prompt. These unprompted asides are often the most useful scoping signal; the cost of losing them when the conversation moves on is higher than the cost of one stored line.
275
+
256
276
  ### 5. Capture Scope Hints
257
277
 
258
278
  If the user mentioned scope preferences in their intent response, acknowledge them:
@@ -1,6 +1,5 @@
1
1
  ---
2
2
  nextStepFile: 'confirm-brief.md'
3
- scopeTemplatesFile: 'assets/scope-templates.md'
4
3
  recommendScopeTypeProbeOrder:
5
4
  - '{project-root}/_bmad/skf/shared/scripts/skf-recommend-scope-type.py'
6
5
  - '{project-root}/src/shared/scripts/skf-recommend-scope-type.py'
@@ -120,6 +119,8 @@ How broadly should this skill cover the library?
120
119
 
121
120
  Press Enter to accept the recommendation, or pick a different letter."
122
121
 
122
+ **First-timer reassurance (interactive only, never-briefed user — the §4 first-timer rail fired in step 01).** Append one line so the harder scope-type call doesn't stall a first-timer: "The recommended type is almost always right — accept it and re-scope from step 4 if the analysis surprises you." Repeat users and headless skip this line.
123
+
123
124
  Wait for user selection. Empty input or just Enter accepts the recommendation; any of the five letters overrides.
124
125
 
125
126
  ### 3. Define Boundaries Based on Selection