bmad-module-skill-forge 8.0.0 → 9.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/docs/_data/pinned.yaml +7 -7
- package/docs/verifying-a-skill.md +9 -9
- package/docs/workflows.md +1 -1
- package/package.json +1 -1
- package/src/shared/scripts/skf-extract-public-api.py +15 -8
- package/src/shared/scripts/skf-verify-provenance-completeness.py +331 -105
- package/src/skf-analyze-source/SKILL.md +1 -1
- package/src/skf-audit-skill/SKILL.md +1 -1
- package/src/skf-brief-skill/SKILL.md +1 -1
- package/src/skf-campaign/SKILL.md +1 -1
- package/src/skf-create-skill/SKILL.md +1 -1
- package/src/skf-create-stack-skill/SKILL.md +1 -1
- package/src/skf-drop-skill/SKILL.md +1 -1
- package/src/skf-export-skill/SKILL.md +1 -1
- package/src/skf-forger/SKILL.md +1 -1
- package/src/skf-quick-skill/SKILL.md +1 -1
- package/src/skf-refine-architecture/SKILL.md +1 -1
- package/src/skf-rename-skill/SKILL.md +1 -1
- package/src/skf-setup/SKILL.md +1 -1
- package/src/skf-test-skill/SKILL.md +1 -1
- package/src/skf-test-skill/assets/output-section-formats.md +2 -2
- package/src/skf-test-skill/customize.toml +10 -0
- package/src/skf-test-skill/references/coverage-check-branches.md +2 -2
- package/src/skf-test-skill/references/coverage-check.md +8 -4
- package/src/skf-test-skill/references/init.md +1 -1
- package/src/skf-test-skill/references/inventory-contract.md +11 -7
- package/src/skf-test-skill/references/report.md +2 -1
- package/src/skf-test-skill/references/scoring-rules.md +2 -2
- package/src/skf-test-skill/references/signature-comparison-rules.md +7 -5
- package/src/skf-test-skill/references/source-access-protocol.md +1 -1
- package/src/skf-test-skill/scripts/gap-ledger.py +105 -53
- package/src/skf-test-skill/scripts/load-coverage-inputs.py +336 -59
- package/src/skf-test-skill/scripts/reconcile-coverage.py +46 -18
- package/src/skf-test-skill/scripts/score-signatures.py +527 -130
- package/src/skf-test-skill/scripts/validate-inventory.py +528 -68
- package/src/skf-test-skill/templates/test-report-template.md +4 -3
- package/src/skf-update-skill/SKILL.md +1 -1
- package/src/skf-verify-stack/SKILL.md +1 -1
package/docs/_data/pinned.yaml
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
# Enforced two ways: (1) release.yaml bumps this line in the same commit
|
|
33
33
|
# as package.json + marketplace.json on every release; (2) validate-docs-drift.js
|
|
34
34
|
# cross-checks this value against package.json.version and fails on mismatch.
|
|
35
|
-
skf_version: "
|
|
35
|
+
skf_version: "9.0.0"
|
|
36
36
|
|
|
37
37
|
# Path to the oh-my-skills repo, resolved relative to this repo's root.
|
|
38
38
|
# Override at runtime with the OMS environment variable if oh-my-skills
|
|
@@ -51,8 +51,8 @@ skills:
|
|
|
51
51
|
source_commit: "ba3631f2ed363a6ea50d649c34c56885af6b36fe"
|
|
52
52
|
source_ref: "v1.6.2"
|
|
53
53
|
upstream_commit_date: "2026-09-29"
|
|
54
|
-
# null: the strict SKF
|
|
55
|
-
# (
|
|
54
|
+
# null: the strict SKF 8.0.0 re-test of the version that ships
|
|
55
|
+
# (20261107T112455Z-eec853d0) is blocked by the hard gate, so it records no score.
|
|
56
56
|
test_score: null
|
|
57
57
|
test_mode: "naive"
|
|
58
58
|
confidence_tier: "Deep"
|
|
@@ -66,7 +66,7 @@ skills:
|
|
|
66
66
|
source_commit: "5fe4f37a526e51516655832ec9814e7ca24ade9a"
|
|
67
67
|
source_ref: "v1.0.0"
|
|
68
68
|
upstream_commit_date: "2026-04-22"
|
|
69
|
-
# strict SKF
|
|
69
|
+
# strict SKF 8.0.0 re-test of the version that ships (20261107T115927Z-5db1615d)
|
|
70
70
|
test_score: 100.0
|
|
71
71
|
test_mode: "naive"
|
|
72
72
|
confidence_tier: "Deep"
|
|
@@ -80,8 +80,8 @@ skills:
|
|
|
80
80
|
source_commit: "e486d3826bcd40c52db1c766966d1c8ec16df6cb"
|
|
81
81
|
source_ref: "v10.3.5"
|
|
82
82
|
upstream_commit_date: "2026-04-07"
|
|
83
|
-
# strict SKF
|
|
84
|
-
test_score:
|
|
83
|
+
# strict SKF 8.0.0 re-test of the version that ships (20261107T112449Z-0cc4693c)
|
|
84
|
+
test_score: 100.0
|
|
85
85
|
test_mode: "naive"
|
|
86
86
|
confidence_tier: "Deep"
|
|
87
87
|
source_authority: "community"
|
|
@@ -94,7 +94,7 @@ skills:
|
|
|
94
94
|
source_commit: "a5ffb45be05335d2c547436664cfbfb8c22d04df"
|
|
95
95
|
source_ref: "HEAD"
|
|
96
96
|
upstream_commit_date: "2026-03-22"
|
|
97
|
-
# strict SKF
|
|
97
|
+
# strict SKF 8.0.0 re-test of the version that ships (20261107T112452Z-65994952)
|
|
98
98
|
test_score: 100.0
|
|
99
99
|
test_mode: "naive"
|
|
100
100
|
confidence_tier: "Deep"
|
|
@@ -44,13 +44,13 @@ Provenance maps live in `forge-data/{skill}/{version}/provenance-map.json` in th
|
|
|
44
44
|
|
|
45
45
|
This is the published entry for cognee's `search` in oh-my-skills' [`forge-data/oms-cognee/1.6.2/provenance-map.json`](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.6.2/provenance-map.json), shortened to its first two parameters (`search` takes 27). It points at line 41, where `async def search(` is defined at cognee v1.6.2's commit (`ba3631f2`), and the skill's `SKILL.md` cites `[AST:cognee/api/v1/search/search.py:L41]`. Its labels follow the tool that read it: an ast-grep match gives `T1`, `ast-grep`, the node kind its rule matched and an `[AST:]` citation. An export read by eye instead, because ast-grep could not parse its file, an ast-grep call that failed or timed out left its file unread, or no rule matched it, gets `T1-low`, `source-read`, no node kind and an `[SRC:]` citation, at every forge tier.
|
|
46
46
|
|
|
47
|
-
The 1.6.2 map passes the [line, label and citation checks](#workflow-time-enforcement) that SKF 3.0.0 added. Under SKF 4.0.0 a strict test passed the skill at 99%. SKF 7.0.0's Test Skill found real errors in it, all since fixed by hand against the pinned source, and still blocks it on two false positives: it compares the skill's forms of the lightweight `cognee.pipelines.run_pipeline` with the dataset orchestrator of the same name ([#918](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/918)). So the skill has no score (see [the scores](#the-scores-including-the-ones-we-lose)). The older map next to it, [`forge-data/oms-cognee/1.0.0/provenance-map.json`](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.0.0/provenance-map.json), was compiled before labels followed the tool and still shows what those checks catch. It records `search` as `source-read` but labels it `T1`, and it points at line 26, the blank line above the definition, while that version's `SKILL.md` cites `[AST:cognee/api/v1/search/search.py:L27]`. A source read is `T1-low` with an `[SRC:]` citation, and its line is 27, the `def` line at v1.0.0.
|
|
47
|
+
The 1.6.2 map passes the [line, label and citation checks](#workflow-time-enforcement) that SKF 3.0.0 added. Under SKF 4.0.0 a strict test passed the skill at 99%. SKF 7.0.0's Test Skill found real errors in it, all since fixed by hand against the pinned source, and SKF 8.0.0 still blocks it on two false positives: it compares the skill's forms of the lightweight `cognee.pipelines.run_pipeline` with the dataset orchestrator of the same name ([#918](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/918)). So the skill has no score (see [the scores](#the-scores-including-the-ones-we-lose)). The older map next to it, [`forge-data/oms-cognee/1.0.0/provenance-map.json`](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.0.0/provenance-map.json), was compiled before labels followed the tool and still shows what those checks catch. It records `search` as `source-read` but labels it `T1`, and it points at line 26, the blank line above the definition, while that version's `SKILL.md` cites `[AST:cognee/api/v1/search/search.py:L27]`. A source read is `T1-low` with an `[SRC:]` citation, and its line is 27, the `def` line at v1.0.0.
|
|
48
48
|
|
|
49
49
|
### 3. Visit the upstream repo at the pinned commit
|
|
50
50
|
|
|
51
51
|
Open `{source_repo}` at `{source_commit}`, jump to `{source_file}` line `{source_line}`. The signature in `SKILL.md` should match what you see in the source.
|
|
52
52
|
|
|
53
|
-
If it doesn't, **that's a bug**. For a skill you built, Test Skill (`@Ferris TS`) rates a signature mismatch as a Critical gap, which stops the test at its [hard gate](#hard-gate). It compares only what a signature writes: a component by its props alone, so `FC<P>` and `(props: P) => JSX.Element` are one signature, and a type or a return type the skill does not write, or type arguments it leaves out (`dict` against `dict[str, Any]`), are not compared. When the skill documents a name in several places, such as a brief usage in `SKILL.md` and a full props list in a reference file, each form that lists parameters
|
|
53
|
+
If it doesn't, **that's a bug**. For a skill you built, Test Skill (`@Ferris TS`) rates a signature mismatch as a Critical gap, which stops the test at its [hard gate](#hard-gate). It compares only what a signature writes: a component by its props alone, so `FC<P>` and `(props: P) => JSX.Element` are one signature, and a type or a return type the skill does not write, or type arguments it leaves out (`dict` against `dict[str, Any]`), are not compared. When the skill documents a name in several places, such as a brief usage in `SKILL.md` and a full props list in a reference file, each form that lists parameters, gives a usage or states a return type is compared, and a `const` holding a component, such as a `forwardRef(...)` result, is compared by the usage or declaration the skill writes for it. When the source declares two functions of one name, a form is compared only with the one its own `[AST:]` or `[SRC:]` citation names, at a line or in a range that holds its line or a later one before the next declaration the extraction records in that file. Test Skill reads that citation on the form's line, or on the next non-blank line unless that line writes the name itself, and, for a form in a code block, in a comment just above it in the block or, when the block documents no other form of the name, in a comment on the block's first non-blank line or on the first non-blank line after the block, unless that line writes the name. A line that writes two forms of the name that differ, such as a table row that quotes the other function's form as a contrast, gives none of those forms a citation. A form whose citations name neither function, or both, is compared as before, with the declarations the package exports. Two differences a caller never meets are rated lower and still count as matching: a renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass arguments by position, is a Low gap, and an optional parameter left out of a signature the skill labels partial (such as `Props (simplified)`) is an Info observation. A renamed Python parameter stays Critical, because Test Skill cannot see which parameters a caller must pass by position, and so does a renamed Vue component prop, which callers set by name. Update Skill (`@Ferris US`) with `--from-test-report` repairs the gaps a test report lists. If you think SKF extracted it wrong, [open an issue](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/new/choose). Falsifiability isn't a feature. It's the whole deal.
|
|
54
54
|
|
|
55
55
|
### Workflow-time enforcement
|
|
56
56
|
|
|
@@ -95,14 +95,14 @@ SKF 5.0.0 to 7.0.0 put every signature rule in one file that every judge reads,
|
|
|
95
95
|
|
|
96
96
|
Every fix was made by hand against the pinned source. Each round's fixes were reviewed before its test, except the last one-row storybook correction.
|
|
97
97
|
|
|
98
|
-
Today, on a strict SKF
|
|
98
|
+
Today, on a strict SKF 8.0.0 test of the version each one ships, three skills pass, all at 100%, and one is blocked. On unchanged text, 8.0.0 raised oms-storybook-react-vite from 98.23% to 100%, because a star re-export from another package now adds no name ([#926](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/926)), and a written rule ([#932](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/932)) now settles the three oms-uitripled forms its 7.0.0 pass rested on without recording them; uitripled stays at 100%. The first 8.0.0 run of oms-cocoindex counted `ExceptionContext`, named only inside another export's type, as undocumented (99.37%), a reader's call the inventory contract does not settle ([#967](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/967)); oh-my-skills then documented it. Every pass still rests on calls the reader or the judge makes where the rules leave the case open, and each report now writes them down, with how it counted them: storybook's turns on how the split-body check reads two different exports that share a name, uitripled's on an em-dash placeholder in a Default cell read as empty, and cocoindex's on a call to a decorator instance compared with its `__call__`. The judges also still read some cases differently from run to run: three oms-uitripled lines were matches in one 7.0.0 run and Critical gaps in the next, with no change to the lines. Every report, the failing ones included, is kept: in the skill's `forge-data/` folder, or, for the reverted oms-storybook-react-vite repair, in oh-my-skills' git history. Even a passing report names the drift it found:
|
|
99
99
|
|
|
100
100
|
| Skill | Score | What the report discloses |
|
|
101
101
|
|---|---|---|
|
|
102
|
-
| [oms-cocoindex](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cocoindex/1.0.0/test-report-oms-cocoindex-
|
|
103
|
-
| [oms-cognee](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.6.2/test-report-oms-cognee-
|
|
104
|
-
| [oms-storybook-react-vite](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-storybook-react-vite/10.3.5/test-report-oms-storybook-react-vite-
|
|
105
|
-
| [oms-uitripled](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-uitripled/0.1.0/test-report-oms-uitripled-
|
|
102
|
+
| [oms-cocoindex](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cocoindex/1.0.0/test-report-oms-cocoindex-20261107T115927Z-5db1615d.md) | **100%** | 72/72 exports of the root barrel (since 7.0.0 the brief's `scope.exclude` applies before the source is read as one Python package, not a Cargo workspace), 48/48 signatures. Type Coverage is 1/1: the root exports Test Skill restores when the brief's doc globs match no file carry no kind, so the category counts a single type ([#913](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/913)). The report records how it read `@coco.fn(...)`, a call to a decorator instance, compared with the instance's `__call__`. The first 8.0.0 run counted `ExceptionContext`, named only inside another export's type, as undocumented (99.37%; [#967](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/967)); oh-my-skills documented it. Left open: 11 Medium stale-documentation gaps on connector exports that the reconcile counts outside the surface ([#912](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/912)), a brief glob that matches no file and a barrel count. |
|
|
103
|
+
| [oms-cognee](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-cognee/1.6.2/test-report-oms-cognee-20261107T112455Z-eec853d0.md) | **blocked** | 2 Critical false positives: the test compares the skill's two forms of the lightweight `cognee.pipelines.run_pipeline` with the orchestrator of the same name, the only `run_pipeline` its surface holds ([#918](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/918); the fix, [#956](https://github.com/armelhbobdad/bmad-module-skill-forge/pull/956), is not in 8.0.0). Every other check passes: 92/92 exports of the `cognee/__init__.py` barrel at pinned commit `ba3631f2` (v1.6.2), 74 of 75 signature names and 35/35 types. External Validation is 90%: skill-check finds the `SKILL.md` body over its 5,000-token budget. |
|
|
104
|
+
| [oms-storybook-react-vite](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-storybook-react-vite/10.3.5/test-report-oms-storybook-react-vite-20261107T112449Z-0cc4693c.md) | **100%** | Counted on the brief's `scope.tier_a_include` set: 370/370 exports, 180/180 signatures (66 of them Testing Library bindings whose parameters are typed in another package, counted as matching without comparison under 8.0.0's rule, [#925](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/925)), 128/128 types. Under 7.0.0 the run read `export type * from '@vitest/spy'` as 19 names, 13 of them undocumented (98.23%); 8.0.0 counts none ([#926](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/926)). The pass turns on how the split-body check reads two different exports that share a name, core's and React's `composeStories`: read as one export, they would be a High gap ([#927](https://github.com/armelhbobdad/bmad-module-skill-forge/issues/927)). |
|
|
105
|
+
| [oms-uitripled](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-uitripled/0.1.0/test-report-oms-uitripled-20261107T112452Z-65994952.md) | **100%** | 327/327 exports against the stored count for the brief's `scope.include` union, 269/269 signatures and 42/42 types. The report records the call the pass rests on, an em-dash placeholder in two props tables' Default column read as an empty cell as Create Skill's own format writes it (read as a value, two Critical gaps), and one the 100% rests on: seven primitives named only on import lines, counted as documented (without them, Export Coverage would be 97.86%). |
|
|
106
106
|
|
|
107
107
|
Perfection is suspicious. Visible fallibility is trustworthy. SKF writes down the edges it can't score cleanly, so you can read them and decide for yourself whether the remaining coverage is enough for your use case.
|
|
108
108
|
|
|
@@ -118,7 +118,7 @@ Removing the stored count made the next run name every undocumented export. It m
|
|
|
118
118
|
|
|
119
119
|
A strict SKF 4.0.0 test of the version that shipped then ([report](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-storybook-react-vite/10.3.5/test-report-oms-storybook-react-vite-20261103T131516Z-6e2b972a.md)) covered all of those fixes. It was blocked on 4 Critical gaps: two compared the React portable-stories wrappers with core's functions, and two were older real errors no earlier run had flagged. So oh-my-skills published no score instead of quoting the earlier number.
|
|
120
120
|
|
|
121
|
-
SKF 7.0.0 measures the skill against the names its brief lists. Its first test found 63.78% of them documented. The next rounds documented the 134 missing names and fixed the errors the tests and reviews found, by hand against the pinned source. A strict test of the version that ships
|
|
121
|
+
SKF 7.0.0 measures the skill against the names its brief lists. Its first test found 63.78% of them documented. The next rounds documented the 134 missing names and fixed the errors the tests and reviews found, by hand against the pinned source. A strict SKF 7.0.0 test of the version that ships passed at 98.23%, and the same text passes a strict SKF 8.0.0 test at 100% ([report](https://github.com/armelhbobdad/oh-my-skills/blob/main/forge-data/oms-storybook-react-vite/10.3.5/test-report-oms-storybook-react-vite-20261107T112449Z-0cc4693c.md)). Nothing was hidden: every report on the way is kept.
|
|
122
122
|
|
|
123
123
|
That's the pattern SKF asks you to trust: when scoring can't reach 100%, the report says so, cites the line, and leaves a fingerprint for the next audit.
|
|
124
124
|
|
|
@@ -135,7 +135,7 @@ The score is a weighted sum of up to five categories. The weights below are the
|
|
|
135
135
|
| Category | Weight | What it measures |
|
|
136
136
|
|---|---|---|
|
|
137
137
|
| **Export Coverage** | 36% | Percentage of source exports documented in `SKILL.md` |
|
|
138
|
-
| **Signature Accuracy** | 22% | Documented function signatures match actual source signatures (parameter names, types, order, return types). A renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass every argument by position, still matches, and so does a signature the skill labels partial that leaves out only optional parameters. A component is compared by its props alone, a type or return type a signature does not write is not compared, and each form a name is documented in is compared on its own |
|
|
138
|
+
| **Signature Accuracy** | 22% | Documented function signatures match actual source signatures (parameter names, types, order, return types). A renamed parameter in a TypeScript, JavaScript, Go, Rust, Java, C or C++ file, whose callers pass every argument by position, still matches, and so does a signature the skill labels partial that leaves out only optional parameters. A component is compared by its props alone, a type or return type a signature does not write is not compared, and each form a name is documented in is compared on its own, with the declaration its own citation names when the source has two of that name |
|
|
139
139
|
| **Type Coverage** | 14% | Types and interfaces referenced in exports are fully documented |
|
|
140
140
|
| **Coherence** | 18% | Cross-references resolve, integration patterns are complete (contextual mode only) |
|
|
141
141
|
| **External Validation** | 10% | skill-check quality score (0–100), averaged with the Tessl Review score (0–100) when you opt in to Tessl Review |
|
package/docs/workflows.md
CHANGED
|
@@ -597,7 +597,7 @@ To run these without questions, pass their inputs as flags. Analyze Source takes
|
|
|
597
597
|
|
|
598
598
|
Every workflow ships a `customize.toml` with settings you can change. Do not edit that file: updates overwrite it. Put your changes in `_bmad/custom/<skill-name>.toml` (shared with your team) or `_bmad/custom/<skill-name>.user.toml` (just for you), for example `_bmad/custom/skf-test-skill.toml`. Start the file with a `[workflow]` line, as the bundled file does: a setting above it is ignored without a warning. A single value replaces the default; a list adds to it.
|
|
599
599
|
|
|
600
|
-
These overrides are read by BMAD Method's customization script, which the BMAD Method installer adds. Every workflow, and Ferris, runs that script through `uv` when it starts. When the script cannot run, the run prints one warning, `[activation/warn] customization_resolver_unavailable: <reason>`, and uses the bundled defaults, ignoring `_bmad/custom/`. A workflow also adds a warning naming `customization_resolver_unavailable` and its reason to the run's warnings, in the result envelope and result file of a run that writes them (Campaign logs it in its decision log, a `--quiet` or headless Setup Forge run shows it only in its envelope, and an interactive Export Skill run only prints it). In a project with SKF alone, that script is missing, so every run warns `customization_resolver_unavailable: not found` and uses the bundled defaults. On a BMAD core older than 6.11.0, whose script reads the overrides of the nearest folder holding `_bmad` or `.git`, from the workflow's own folder up, SKF still applies them when that folder is this project, and otherwise warns `customization_resolver_unavailable: BMAD core before 6.11.0: ...`, naming the folder that script would read and the upgrade to 6.11.0 or later.
|
|
600
|
+
These overrides are read by BMAD Method's customization script, which the BMAD Method installer adds. Every workflow, and Ferris, runs that script through `uv` when it starts. When the script cannot run, the run prints one warning, `[activation/warn] customization_resolver_unavailable: <reason>`, and uses the bundled defaults, ignoring `_bmad/custom/`. A workflow also adds a warning naming `customization_resolver_unavailable` and its reason to the run's warnings, in the result envelope and result file of a run that writes them (Campaign logs it in its decision log, a `--quiet` or headless Setup Forge run shows it only in its envelope, and an interactive Export Skill run only prints it). In a project with SKF alone, that script is missing, so every run warns `customization_resolver_unavailable: not found` and uses the bundled defaults. When SKF's own helper is missing, as in an incomplete SKF install, the run warns `customization_resolver_unavailable: SKF helper skf-resolve-customization.py not found` instead: reinstall SKF. On a BMAD core older than 6.11.0, whose script reads the overrides of the nearest folder holding `_bmad` or `.git`, from the workflow's own folder up, SKF still applies them when that folder is this project, and otherwise warns `customization_resolver_unavailable: BMAD core before 6.11.0: ...`, naming the folder that script would read and the upgrade to 6.11.0 or later.
|
|
601
601
|
|
|
602
602
|
Ferris reads `_bmad/custom/skf-forger.toml` and `_bmad/custom/skf-forger.user.toml` the same way when he starts, under an `[agent]` line, as his bundled file does (a `[workflow]` table there is ignored). An override there can add `activation_steps_prepend`, which runs once his config is loaded, `persistent_facts` (a `!` entry drops each earlier entry it names) and `activation_steps_append`, which runs after his greeting. His name, title and the other agent roster values stay as shipped, and he has no `on_complete`. When the script cannot run, the warning line opens his greeting.
|
|
603
603
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "bmad-module-skill-forge",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "9.0.0",
|
|
5
5
|
"description": "BMAD module — Turn code and docs into instructions AI agents can actually follow. Progressive capability tiers (Quick/Forge/Forge+/Deep).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"bmad",
|
|
@@ -457,8 +457,9 @@ them). A namespace with `members` is diffed as its members: each is found
|
|
|
457
457
|
when a recipe found its name, or its `local` one, in its file (one with
|
|
458
458
|
no file by its name alone, as a public name is), and has no row of its
|
|
459
459
|
own when a public name (or another namespace's member) has its name and
|
|
460
|
-
file;
|
|
461
|
-
|
|
460
|
+
file; a member's own outside_scope row names its namespace (`namespace`);
|
|
461
|
+
one with null `members`, and a member that is such a module, is no gap.
|
|
462
|
+
In a family with no entry point, or only empty ones, every name the
|
|
462
463
|
recipes found is public. A JS/TS module exports, besides the names the
|
|
463
464
|
recipes match, those its column-0 `export` statements declare in a form
|
|
464
465
|
no recipe matches, read from its text: `export let`, `export declare
|
|
@@ -582,7 +583,8 @@ Output JSON (stdout, or -o):
|
|
|
582
583
|
"local", "file", "line"}, ...], # by name, then file
|
|
583
584
|
"internal": [{"name", "language", "source_file", "source_line"}, ...],
|
|
584
585
|
"extraction_gaps": [{"name", "language", "entry", "file", "line"}, ...],
|
|
585
|
-
"outside_scope": [{"name", "language", "entry", "file", "line"
|
|
586
|
+
"outside_scope": [{"name", "language", "entry", "file", "line",
|
|
587
|
+
"namespace"}, ...]}, # namespace: a member's row only
|
|
586
588
|
"reexport_targets": [{"name", "language", "local", "from", "entry",
|
|
587
589
|
"file", "line", "via"}, ...],
|
|
588
590
|
"counts": {"exports_public_api": N, # (family, name, file) keys
|
|
@@ -4439,9 +4441,11 @@ def entry_point_diff(exports: list[dict], surfaces: dict[str, _Surface], entries
|
|
|
4439
4441
|
`members` has each member diffed in its place, found when a recipe
|
|
4440
4442
|
found its name, or its `local` one, in its file, and given no row of
|
|
4441
4443
|
its own when a declared name has its name and file (or another
|
|
4442
|
-
namespace gave it one)
|
|
4443
|
-
|
|
4444
|
-
|
|
4444
|
+
namespace gave it one), else a row that names the namespace
|
|
4445
|
+
(`namespace`, which only an outside_scope row writes out); one whose
|
|
4446
|
+
members are unknown (None), or a member that is such a module
|
|
4447
|
+
(`opaque`), is no gap. In a family with no entry point, or only empty
|
|
4448
|
+
ones, every export is public."""
|
|
4445
4449
|
statuses: dict[str, str] = {}
|
|
4446
4450
|
public: dict[tuple[str, str], dict] = {}
|
|
4447
4451
|
internal: list[dict] = []
|
|
@@ -4495,7 +4499,7 @@ def entry_point_diff(exports: list[dict], surfaces: dict[str, _Surface], entries
|
|
|
4495
4499
|
continue
|
|
4496
4500
|
given.add(key)
|
|
4497
4501
|
row = {"name": member["name"], "entry": info["entry"], "file": member["file"],
|
|
4498
|
-
"line": member["line"]}
|
|
4502
|
+
"line": member["line"], "namespace": info["name"]}
|
|
4499
4503
|
(gaps if member["file"] is None or member["file"] in in_scope else outside).append((family, row))
|
|
4500
4504
|
return statuses, public, internal, gaps, outside, others
|
|
4501
4505
|
|
|
@@ -4946,8 +4950,11 @@ def run_full(args: argparse.Namespace) -> tuple[dict, int]:
|
|
|
4946
4950
|
"extraction_gaps": [{"name": info["name"], "language": family, "entry": info["entry"],
|
|
4947
4951
|
"file": info["file"], "line": info["line"]}
|
|
4948
4952
|
for family, info in sorted(gaps, key=lambda g: (g[1]["name"], g[0]))],
|
|
4953
|
+
# a namespace's member names its namespace (#969); a declared name's row keeps five keys
|
|
4949
4954
|
"outside_scope": [{"name": info["name"], "language": family, "entry": info["entry"],
|
|
4950
|
-
"file": info["file"], "line": info["line"]
|
|
4955
|
+
"file": info["file"], "line": info["line"],
|
|
4956
|
+
**({"namespace": info["namespace"]} if isinstance(info.get("namespace"), str)
|
|
4957
|
+
and info["namespace"] else {})}
|
|
4951
4958
|
for family, info in sorted(outside, key=lambda g: (g[1]["name"], g[0]))],
|
|
4952
4959
|
},
|
|
4953
4960
|
"reexport_targets": [
|