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.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +10 -5
- package/docs/_data/pinned.yaml +2 -2
- package/docs/agents.md +11 -2
- package/docs/architecture.md +5 -4
- package/docs/bmad-synergy.md +42 -3
- package/docs/campaign.md +172 -0
- package/docs/examples.md +7 -3
- package/docs/forge-auto.md +90 -0
- package/docs/getting-started.md +9 -0
- package/docs/how-it-works.md +6 -4
- package/docs/index.md +4 -3
- package/docs/skill-model.md +1 -1
- package/docs/troubleshooting.md +17 -1
- package/docs/verifying-a-skill.md +9 -3
- package/docs/why-skf.md +1 -1
- package/docs/workflows.md +73 -27
- package/package.json +2 -2
- package/src/module-help.csv +2 -1
- package/src/shared/_known-workarounds.yaml +155 -0
- package/src/shared/data/language-corpora.json +32 -0
- package/src/shared/references/pipeline-contracts.md +14 -2
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +6 -1
- package/src/shared/scripts/schemas/skill-brief.v1.json +6 -1
- package/src/shared/scripts/skf-derive-assembly-shape.py +107 -0
- package/src/shared/scripts/skf-detect-docs.py +417 -0
- package/src/shared/scripts/skf-detect-language.py +5 -3
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +12 -1
- package/src/shared/scripts/skf-emit-result-envelope.py +17 -0
- package/src/shared/scripts/skf-extract-public-api.py +53 -0
- package/src/shared/scripts/skf-language-corpora.py +100 -0
- package/src/shared/scripts/skf-merge-doc-urls.py +244 -0
- package/src/shared/scripts/skf-preapply.py +192 -0
- package/src/shared/scripts/skf-shape-detect.py +1114 -0
- package/src/shared/scripts/skf-validate-brief-schema.py +2 -5
- package/src/shared/scripts/skf-validate-pins.py +368 -0
- package/src/shared/scripts/skf-write-skill-brief.py +21 -5
- package/src/skf-analyze-source/SKILL.md +26 -20
- package/src/skf-analyze-source/assets/skill-brief-schema.md +1 -1
- package/src/skf-analyze-source/references/init.md +30 -0
- package/src/skf-analyze-source/references/step-auto-scope.md +677 -0
- package/src/skf-analyze-source/references/step-shape-detect.md +86 -0
- package/src/skf-audit-skill/SKILL.md +1 -0
- package/src/skf-audit-skill/assets/drift-report-template.md +6 -0
- package/src/skf-audit-skill/references/init.md +1 -1
- package/src/skf-audit-skill/references/report.md +4 -0
- package/src/skf-audit-skill/references/severity-classify.md +1 -1
- package/src/skf-audit-skill/references/step-doc-drift.md +147 -0
- package/src/skf-brief-skill/SKILL.md +7 -3
- package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -1
- package/src/skf-brief-skill/references/analyze-target.md +7 -4
- package/src/skf-brief-skill/references/confirm-brief.md +0 -1
- package/src/skf-brief-skill/references/gather-intent.md +22 -2
- package/src/skf-brief-skill/references/scope-definition.md +2 -1
- package/src/skf-brief-skill/references/step-auto-brief.md +188 -0
- package/src/skf-brief-skill/references/step-auto-validate.md +209 -0
- package/src/skf-brief-skill/references/write-brief.md +2 -3
- package/src/skf-campaign/SKILL.md +190 -0
- package/src/skf-campaign/assets/campaign-state-schema.json +191 -0
- package/src/skf-campaign/customize.toml +73 -0
- package/src/skf-campaign/manifest.yaml +11 -0
- package/src/skf-campaign/references/campaign-directive-spec.md +121 -0
- package/src/skf-campaign/references/health-check.md +35 -0
- package/src/skf-campaign/references/step-01-setup.md +122 -0
- package/src/skf-campaign/references/step-02-strategy.md +97 -0
- package/src/skf-campaign/references/step-03-pins.md +56 -0
- package/src/skf-campaign/references/step-04-provenance.md +63 -0
- package/src/skf-campaign/references/step-05-skill-loop.md +103 -0
- package/src/skf-campaign/references/step-06-batch.md +87 -0
- package/src/skf-campaign/references/step-07-capstone.md +63 -0
- package/src/skf-campaign/references/step-08-verify.md +75 -0
- package/src/skf-campaign/references/step-09-refine.md +83 -0
- package/src/skf-campaign/references/step-10-export.md +106 -0
- package/src/skf-campaign/references/step-11-maintenance.md +84 -0
- package/src/skf-campaign/references/step-resume.md +114 -0
- package/src/skf-campaign/scripts/.gitkeep +0 -0
- package/src/skf-campaign/scripts/campaign-deps.py +235 -0
- package/src/skf-campaign/scripts/campaign-parse-manifest.py +119 -0
- package/src/skf-campaign/scripts/campaign-provenance.py +247 -0
- package/src/skf-campaign/scripts/campaign-render-kickoff.py +158 -0
- package/src/skf-campaign/scripts/campaign-report.py +249 -0
- package/src/skf-campaign/scripts/campaign-validate-pins.py +174 -0
- package/src/skf-campaign/scripts/campaign-validate-state.py +207 -0
- package/src/skf-campaign/templates/campaign-brief-template.yaml +34 -0
- package/src/skf-campaign/templates/campaign-report-template.md +54 -0
- package/src/skf-campaign/templates/kickoff-template.md +48 -0
- package/src/skf-create-skill/SKILL.md +4 -1
- package/src/skf-create-skill/assets/compile-assembly-rules.md +22 -0
- package/src/skf-create-skill/assets/skill-sections.md +2 -0
- package/src/skf-create-skill/references/compile.md +5 -2
- package/src/skf-create-skill/references/extract.md +9 -1
- package/src/skf-create-skill/references/extraction-patterns.md +3 -1
- package/src/skf-create-skill/references/generate-artifacts.md +11 -2
- package/src/skf-create-skill/references/health-check.md +2 -2
- package/src/skf-create-skill/references/report.md +17 -1
- package/src/skf-create-skill/references/source-resolution-protocols.md +1 -1
- package/src/skf-create-skill/references/step-auto-shard.md +110 -0
- package/src/skf-create-skill/references/step-doc-rot.md +109 -0
- package/src/skf-create-skill/references/step-doc-sources.md +123 -0
- package/src/skf-create-skill/references/sub/fetch-docs.md +29 -0
- package/src/skf-create-skill/references/validate.md +12 -3
- package/src/skf-drop-skill/SKILL.md +2 -2
- package/src/skf-drop-skill/references/execute.md +20 -9
- package/src/skf-drop-skill/references/report.md +2 -0
- package/src/skf-drop-skill/references/select.md +7 -2
- package/src/skf-forger/SKILL.md +15 -4
- package/src/skf-refine-architecture/SKILL.md +2 -1
- package/src/skf-refine-architecture/references/compile.md +1 -1
- package/src/skf-refine-architecture/references/report.md +1 -1
- package/src/skf-rename-skill/SKILL.md +2 -2
- package/src/skf-rename-skill/references/execute.md +5 -4
- package/src/skf-rename-skill/references/rebuild-context.md +2 -2
- package/src/skf-rename-skill/references/select.md +3 -3
- package/src/skf-setup/SKILL.md +1 -1
- package/src/skf-setup/references/auto-index.md +3 -1
- package/src/skf-setup/references/detect-and-tier.md +4 -0
- package/src/skf-setup/references/report.md +4 -1
- package/src/skf-setup/references/tier-rules.md +1 -1
- package/src/skf-test-skill/SKILL.md +8 -7
- package/src/skf-test-skill/customize.toml +2 -1
- package/src/skf-test-skill/references/coverage-check.md +10 -0
- package/src/skf-test-skill/references/external-validators.md +1 -1
- package/src/skf-test-skill/references/init.md +16 -1
- package/src/skf-test-skill/references/report.md +5 -1
- package/src/skf-test-skill/references/score.md +95 -6
- package/src/skf-test-skill/references/source-access-protocol.md +13 -3
- package/src/skf-test-skill/references/step-hard-gate.md +73 -0
- package/src/skf-test-skill/scripts/compute-score.py +4 -3
- package/src/skf-test-skill/templates/test-report-template.md +1 -0
- package/src/skf-update-skill/customize.toml +0 -9
- package/src/skf-update-skill/references/detect-changes.md +33 -3
- package/src/skf-update-skill/references/health-check.md +2 -2
- package/src/skf-update-skill/references/init.md +1 -0
- package/src/skf-update-skill/references/re-extract.md +15 -1
- package/src/skf-verify-stack/references/report.md +1 -1
- 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
|
|
|
@@ -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. **
|
|
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
|
|
|
@@ -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
|
|
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
|
|
@@ -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.
|
|
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
|