bmad-module-skill-forge 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/docs/_data/pinned.yaml +1 -1
- package/docs/workflows.md +34 -15
- package/package.json +2 -2
- package/src/shared/scripts/schemas/skf-brief-result-envelope.v1.json +58 -0
- package/src/shared/scripts/schemas/skill-brief.v1.json +77 -0
- package/src/shared/scripts/schemas/workspace-detection.v1.json +44 -0
- package/src/shared/scripts/skf-detect-language.py +277 -0
- package/src/shared/scripts/skf-detect-workspaces.py +427 -0
- package/src/shared/scripts/skf-emit-brief-result-envelope.py +257 -0
- package/src/shared/scripts/skf-extract-public-api.py +29 -0
- package/src/shared/scripts/skf-forge-tier-rw.py +73 -0
- package/src/shared/scripts/skf-merge-ccc-exclusions.py +14 -0
- package/src/shared/scripts/skf-recommend-scope-type.py +369 -0
- package/src/shared/scripts/skf-validate-brief-inputs.py +293 -0
- package/src/shared/scripts/skf-write-skill-brief.py +509 -0
- package/src/skf-audit-skill/steps-c/step-01-init.md +49 -3
- package/src/skf-audit-skill/steps-c/step-03-structural-diff.md +6 -5
- package/src/skf-brief-skill/SKILL.md +41 -12
- package/src/skf-brief-skill/assets/description-voice-examples.md +19 -0
- package/src/skf-brief-skill/assets/scope-templates.md +5 -0
- package/src/skf-brief-skill/assets/skill-brief-schema.md +1 -40
- package/src/skf-brief-skill/references/draft-checkpoint.md +46 -0
- package/src/skf-brief-skill/references/headless-args.md +22 -0
- package/src/skf-brief-skill/references/headless-source-authority-detection.md +26 -0
- package/src/skf-brief-skill/references/portfolio-similarity-check.md +35 -0
- package/src/skf-brief-skill/references/qmd-collection-registration.md +52 -0
- package/src/skf-brief-skill/references/version-resolution.md +46 -0
- package/src/skf-brief-skill/steps-c/step-01-gather-intent.md +164 -14
- package/src/skf-brief-skill/steps-c/step-02-analyze-target.md +118 -50
- package/src/skf-brief-skill/steps-c/step-03-scope-definition.md +48 -5
- package/src/skf-brief-skill/steps-c/step-04-confirm-brief.md +33 -19
- package/src/skf-brief-skill/steps-c/step-05-write-brief.md +93 -97
- package/src/skf-brief-skill/steps-c/step-06-health-check.md +11 -2
- package/src/skf-create-skill/steps-c/step-07-generate-artifacts.md +9 -1
- package/src/skf-export-skill/steps-c/step-01-load-skill.md +11 -3
- package/src/skf-export-skill/steps-c/step-03-generate-snippet.md +8 -2
- package/src/skf-export-skill/steps-c/step-04-update-context.md +29 -0
- package/src/skf-export-skill/steps-c/step-06-summary.md +12 -0
- package/src/skf-setup/steps-c/step-01b-ccc-index.md +10 -3
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: skf-brief-skill
|
|
3
|
-
description: Design a skill scope through guided discovery. Use when the user requests to "create a skill brief" or "brief a skill.
|
|
3
|
+
description: Design a skill scope through guided discovery. Use when the user requests to "create a skill brief" or "brief a skill".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Brief Skill
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
Helps the user define what to skill — target repo, scope, language, inclusion/exclusion patterns — and produces a skill-brief.yaml that drives create-skill. This is the first step in the skill creation pipeline
|
|
10
|
+
Helps the user define what to skill — target repo, scope, language, inclusion/exclusion patterns — and produces a `skill-brief.yaml` that drives create-skill. This is the first step in the skill creation pipeline; the brief is the input contract for create-skill, which performs the actual compilation.
|
|
11
|
+
|
|
12
|
+
A good skill brief sets a tight, cohesive boundary: one capability with 3-8 primary functions, an unambiguous public API surface, and a description short enough to fit in a registry row. Briefs that try to cover several unrelated concerns (e.g. authentication *and* data visualization) compile into skills that no agent can route to confidently — a brief covering too much is a worse failure mode than a brief covering too little, and this workflow steers toward the smaller, sharper version when scope is unclear.
|
|
13
|
+
|
|
14
|
+
Brief-skill is split from create-skill so the scoping conversation runs *once*, on cheap signals (manifests, top-level exports, intent), without paying for AST extraction. Compilation is expensive; scoping decisions are cheap to revise. Keeping them in separate workflows lets a user iterate on the brief, share it for review, and re-run create-skill against the same brief whenever the upstream version moves.
|
|
11
15
|
|
|
12
16
|
## Role
|
|
13
17
|
|
|
@@ -20,9 +24,19 @@ These rules apply to every step in this workflow:
|
|
|
20
24
|
- Read each step file completely before taking any action
|
|
21
25
|
- Follow the mandatory sequence in each step exactly — do not skip, reorder, or optimize
|
|
22
26
|
- Only load one step file at a time — never preload future steps
|
|
23
|
-
-
|
|
27
|
+
- **Lazy-load references and assets:** `references/*.md` and `assets/*.md` files are loaded inside the section that needs them, not at step entry. If a section is skipped (e.g. `version-resolution.md` when `{extractPublicApiScript}` already returned a version, `scope-templates.md` for the `docs-only` branch that bypasses §2c), do not load that file. Each unnecessary load costs context (~5-10 KB per reference) and biases the LLM toward consulting material the current path does not need.
|
|
28
|
+
- Always communicate in `{communication_language}` (the language for user-facing prose). Written artifact text — the `description`, `notes`, and other free-form fields persisted into `skill-brief.yaml` — is in `{document_output_language}`; per-step rules call this out where it applies (see step-05). The two values may be the same.
|
|
24
29
|
- If `{headless_mode}` is true, auto-proceed through confirmation gates with their default action and log each auto-decision
|
|
25
30
|
|
|
31
|
+
## On Activation
|
|
32
|
+
|
|
33
|
+
1. Load config from `{project-root}/_bmad/skf/config.yaml` and resolve:
|
|
34
|
+
- `project_name`, `output_folder`, `user_name`, `communication_language`, `forge_data_folder`, `sidecar_path`
|
|
35
|
+
|
|
36
|
+
2. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in preferences.yaml. Default: false.
|
|
37
|
+
|
|
38
|
+
3. Load, read the full file, and execute `./steps-c/step-01-gather-intent.md`.
|
|
39
|
+
|
|
26
40
|
## Stages
|
|
27
41
|
|
|
28
42
|
| # | Step | File | Auto-proceed |
|
|
@@ -32,22 +46,37 @@ These rules apply to every step in this workflow:
|
|
|
32
46
|
| 3 | Scope Definition | steps-c/step-03-scope-definition.md | No (interactive) |
|
|
33
47
|
| 4 | Confirm Brief | steps-c/step-04-confirm-brief.md | No (confirm) |
|
|
34
48
|
| 5 | Write Brief | steps-c/step-05-write-brief.md | Yes |
|
|
35
|
-
| 6 | Workflow Health Check | steps-c/step-06-health-check.md | Yes |
|
|
49
|
+
| 6 | Workflow Health Check (terminal) | steps-c/step-06-health-check.md | Yes |
|
|
36
50
|
|
|
37
51
|
## Invocation Contract
|
|
38
52
|
|
|
39
53
|
| Aspect | Detail |
|
|
40
54
|
|--------|--------|
|
|
41
|
-
| **Inputs** | target_repo [required], skill_name [required], scope_hint [optional], language_hint [optional] |
|
|
55
|
+
| **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] |
|
|
42
56
|
| **Gates** | step-01: Input Gate [use args] | step-03: Confirm Gate [C] | step-04: Confirm Gate [C] |
|
|
43
|
-
| **Outputs** | skill-brief.yaml |
|
|
44
|
-
| **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true |
|
|
57
|
+
| **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 |
|
|
58
|
+
| **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) |
|
|
59
|
+
| **Exit codes** | See "Exit Codes" below |
|
|
45
60
|
|
|
46
|
-
##
|
|
61
|
+
## Exit Codes
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
- `project_name`, `output_folder`, `user_name`, `communication_language`, `forge_data_folder`, `sidecar_path`
|
|
63
|
+
Every HARD HALT in this workflow exits with a stable code so headless automators can branch on the failure class without grepping message text:
|
|
50
64
|
|
|
51
|
-
|
|
65
|
+
| Code | Meaning | Raised by |
|
|
66
|
+
| ---- | -------------------- | ------------------------------------------------------------------------------------------ |
|
|
67
|
+
| 0 | success | step-06 (terminal) |
|
|
68
|
+
| 2 | input-missing / input-invalid | step-01 GATE — required headless arg absent (`target_repo`, `skill_name`, or `doc_urls` when `source_type=docs-only`) → `input-missing`; enum violation, malformed semver, non-kebab `skill_name`, or step-05 brief-context schema validation failure → `input-invalid` |
|
|
69
|
+
| 3 | resolution-failure | step-01 §1 (`forge-tier.yaml` missing); step-02 §1 (target inaccessible / `gh auth` fails) |
|
|
70
|
+
| 4 | write-failure | step-01 §1 pre-flight write probe (data folder unwritable: read-only mount, disk full, permissions denied); step-05 §4 (write to `{forge_data_folder}/{skill-name}/skill-brief.yaml` failed) |
|
|
71
|
+
| 5 | overwrite-cancelled | step-05 §2 (existing brief, `force` not supplied) |
|
|
72
|
+
| 6 | user-cancelled | any interactive menu in step-01/03/04 (user selected `[X]` Cancel and exit) |
|
|
73
|
+
|
|
74
|
+
## Result Contract (Headless)
|
|
75
|
+
|
|
76
|
+
When `{headless_mode}` is true, step-05 emits a single-line JSON envelope on **stdout** before chaining to step-06, and every HARD HALT emits the same envelope shape on **stderr** with `status: "error"`:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
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}
|
|
80
|
+
```
|
|
52
81
|
|
|
53
|
-
|
|
82
|
+
`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.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Description Voice Examples
|
|
2
|
+
|
|
3
|
+
Loaded by step-01 §7b only. The five examples below show the *range* of acceptable voices for the `description` field — they vary in lead, structure, and trigger phrasing on purpose. The point is to anchor the LLM to "two facts must come through (what the skill is, when to use it); everything else is voice — do not template-stamp."
|
|
4
|
+
|
|
5
|
+
## Examples
|
|
6
|
+
|
|
7
|
+
> Render Markdown to HTML using the marked library. Use when the user pastes raw Markdown and wants formatted output, or asks how to convert MD files in a build pipeline.
|
|
8
|
+
|
|
9
|
+
> Stripe API client for Node.js — payment intents, subscriptions, customer portal, webhooks. Triggers on tasks involving Stripe-managed payments, subscription billing, or webhook event handling.
|
|
10
|
+
|
|
11
|
+
> Charts and visualizations powered by D3.js. Reach for this when the user asks to plot data, build interactive graphs, or wants bare D3 control instead of a React-charts abstraction.
|
|
12
|
+
|
|
13
|
+
> Lint Python code with Ruff. Use when the user wants to add or configure Ruff in a Python project, debug rule selectors, or understand why a specific check fired.
|
|
14
|
+
|
|
15
|
+
> Date and time arithmetic via Luxon — parsing, formatting, time zones, durations, intervals. Use when working with dates in ways that exceed `Date.toISOString()` but you don't want a full Moment.js footprint.
|
|
16
|
+
|
|
17
|
+
## Notes on Voice
|
|
18
|
+
|
|
19
|
+
Each example leads differently (verb / noun / "Charts and..." / verb / noun-phrase) and matches the trigger phrasing ("Use when...", "Triggers on...", "Reach for this when...") to the voice rather than copy-pasting a single template. Compose in that spirit using the gathered material — the target repo, the user's intent, the version if set, and any scope hints — but **do not template-stamp**.
|
|
@@ -7,26 +7,31 @@ Present these options to the user for selection:
|
|
|
7
7
|
**[F] Full Library** — Include everything. Best for smaller, focused libraries.
|
|
8
8
|
- All public exports, all modules
|
|
9
9
|
- Exclude only tests, build artifacts, and internal utilities
|
|
10
|
+
- *Looks like:* `marked` (single-purpose Markdown→HTML); `nanoid` (id generator); `zod` (validation library)
|
|
10
11
|
|
|
11
12
|
**[M] Specific Modules** — Select which modules to include. Best for large libraries where only some parts are relevant.
|
|
12
13
|
- You choose which modules/directories
|
|
13
14
|
- Fine-grained control over what's in and out
|
|
15
|
+
- *Looks like:* `lodash` skill scoped to just `lodash/array` and `lodash/string`; `aws-sdk` skill scoped to just S3 and DynamoDB
|
|
14
16
|
|
|
15
17
|
**[P] Public API Only** — Include only the public-facing API surface. Best for libraries with a clear public/private boundary.
|
|
16
18
|
- Entry points and exported interfaces only
|
|
17
19
|
- Internal implementation excluded
|
|
20
|
+
- *Looks like:* `stripe` (payment intents, subscriptions, webhooks — not internal HTTP plumbing); `redis` client (connection + commands, not protocol parsers)
|
|
18
21
|
|
|
19
22
|
**[C] Component Library** — Optimized for UI component libraries with registries, props-based APIs, and design system variants.
|
|
20
23
|
- Component registry as primary API surface (not individual exports)
|
|
21
24
|
- Props interfaces as API contracts (not function signatures)
|
|
22
25
|
- Auto-exclude demo/example/story files (with user confirmation)
|
|
23
26
|
- Variant consolidation across design systems
|
|
27
|
+
- *Looks like:* `shadcn-ui` (Button, Dialog, Form... 50+ components); Material-UI; Carbon Design System
|
|
24
28
|
|
|
25
29
|
**[R] Reference App** — Whole-app pattern-reference skill. Use when the source is a working example app and the skill's value is **wiring patterns** (lifecycle, IPC, build-config, distribution) rather than a public library API.
|
|
26
30
|
- Pattern surface as primary API slot (not individual exports)
|
|
27
31
|
- Adoption Steps as primary workflow format (not API-call chains)
|
|
28
32
|
- Tier 2 organized as `references/pattern-*.md` groupings (not per-function)
|
|
29
33
|
- Export-count stats are pattern-surface proxies, not library exports
|
|
34
|
+
- *Looks like:* a Tauri starter app (window setup + IPC bridge + build config); a Next.js auth example (route handlers + middleware + session storage wiring)
|
|
30
35
|
|
|
31
36
|
## Boundary Definitions by Scope Type
|
|
32
37
|
|
|
@@ -171,46 +171,7 @@ scope:
|
|
|
171
171
|
|
|
172
172
|
## Human-Readable Presentation Format
|
|
173
173
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
```
|
|
177
|
-
Skill Brief: {name}
|
|
178
|
-
====================
|
|
179
|
-
|
|
180
|
-
Target: {source_repo}
|
|
181
|
-
Language: {language}
|
|
182
|
-
Forge Tier: {forge_tier}
|
|
183
|
-
Description: {description}
|
|
184
|
-
|
|
185
|
-
Scope: {scope.type}
|
|
186
|
-
Include: {scope.include patterns, one per line}
|
|
187
|
-
Exclude: {scope.exclude patterns, one per line}
|
|
188
|
-
Notes: {scope.notes}
|
|
189
|
-
|
|
190
|
-
{If source_type is "docs-only":}
|
|
191
|
-
Source Type: docs-only
|
|
192
|
-
Doc URLs:
|
|
193
|
-
{doc_urls, one per line with labels}
|
|
194
|
-
|
|
195
|
-
{If source_type is "source" AND supplemental doc_urls collected:}
|
|
196
|
-
Supplemental Docs:
|
|
197
|
-
{doc_urls, one per line with labels}
|
|
198
|
-
|
|
199
|
-
{If scripts_intent or assets_intent was explicitly set (not default "detect"):}
|
|
200
|
-
Scripts: {scripts_intent}
|
|
201
|
-
Assets: {assets_intent}
|
|
202
|
-
|
|
203
|
-
Source Authority: {source_authority}
|
|
204
|
-
|
|
205
|
-
{If target_version is set:}
|
|
206
|
-
Target Version: {target_version} (user-specified)
|
|
207
|
-
Detected Version: {detected_version or "N/A"}
|
|
208
|
-
{Else:}
|
|
209
|
-
Version: {version}
|
|
210
|
-
{End if}
|
|
211
|
-
Created: {created}
|
|
212
|
-
Created by: {created_by}
|
|
213
|
-
```
|
|
174
|
+
The runtime template lives in `steps-c/step-04-confirm-brief.md` §2 — that is the single source of truth for how the brief is rendered for user confirmation (brief-skill step-04 only; analyze-source batch generation does not render). If the rendering format needs to change, edit the step file. This asset documents the data contract; the step owns the presentation.
|
|
214
175
|
|
|
215
176
|
## Validation Rules
|
|
216
177
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Draft Checkpoint Lifecycle
|
|
2
|
+
|
|
3
|
+
The `.brief-draft.json` file at `{forge_data_folder}/{skill-name}/.brief-draft.json` is a step-01 in-flight-state checkpoint. It exists only while the workflow has progressed past §7 but not yet completed step-05 — once the final brief writes successfully, step-05 §4 removes it.
|
|
4
|
+
|
|
5
|
+
**Headless mode skips this entire lifecycle** — the run completes in a single invocation, so no resume is meaningful and no checkpoint is written.
|
|
6
|
+
|
|
7
|
+
The two halves of the lifecycle (resume on entry, write on §7 confirmation) form a pair. This file documents both so a single load covers them.
|
|
8
|
+
|
|
9
|
+
## Half 1 — Resume Check (loaded from §6 after name confirmation)
|
|
10
|
+
|
|
11
|
+
After the skill name is confirmed in §6, check for an in-progress draft at `{forge_data_folder}/{name}/.brief-draft.json`. **Only present the resume prompt** when the file exists AND no `skill-brief.yaml` sits beside it (a finished brief uses the same dir; if a finished brief exists, the draft is stale and step-05's overwrite gate is the right control point).
|
|
12
|
+
|
|
13
|
+
When the precondition is met, present:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
**An in-progress draft for `{name}` was found** (last updated: {mtime}).
|
|
17
|
+
[Y] Resume from the saved draft (jump to §8 with prior answers restored)
|
|
18
|
+
[N] Start fresh (delete the draft and re-gather)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### `[Y]` — Resume
|
|
22
|
+
|
|
23
|
+
Load the JSON and restore the captured fields: `target_repo`, `source_type`, `source_authority`, `target_version`, `doc_urls`, `intent`, `scope_hint`, `description`, `forge_tier`, `tier_source`. Then jump directly to §8 — **skip the rest of §6, all of §7, and all of §7b**.
|
|
24
|
+
|
|
25
|
+
The skip rule for §7b is load-bearing: re-running §7b would overwrite the user's previously accepted `description` with a fresh candidate synthesized from the seed material. The restored `description` is authoritative.
|
|
26
|
+
|
|
27
|
+
The user can still revise any field at step-04 §3 if a refinement is needed after the full brief is visible.
|
|
28
|
+
|
|
29
|
+
### `[N]` — Start fresh
|
|
30
|
+
|
|
31
|
+
Delete `.brief-draft.json` and continue forward to §7 — the collision check and portfolio-similarity check have already executed earlier in §6 (before the resume prompt fired) and do not repeat. §7 / §7b then proceed in their normal order.
|
|
32
|
+
|
|
33
|
+
## Half 2 — Checkpoint Write (loaded from §7 after summary confirmation)
|
|
34
|
+
|
|
35
|
+
After the user confirms the §7 summary, persist the captured state atomically. Write a single JSON object with all of:
|
|
36
|
+
|
|
37
|
+
- `target_repo`, `source_type`, `source_authority`
|
|
38
|
+
- `target_version` (if set)
|
|
39
|
+
- `doc_urls` (if collected)
|
|
40
|
+
- `intent`, `scope_hint`
|
|
41
|
+
- `description` (the §7b accepted text)
|
|
42
|
+
- `forge_tier`, `tier_source` (for diagnostics)
|
|
43
|
+
|
|
44
|
+
Atomic-write protocol: write to `.brief-draft.json.tmp` first, then `mv .brief-draft.json.tmp .brief-draft.json`. The rename is atomic on a single filesystem; a partial write never becomes visible as `.brief-draft.json`.
|
|
45
|
+
|
|
46
|
+
The file is removed by step-05 §4 after the final brief writes successfully.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Headless Argument Table
|
|
2
|
+
|
|
3
|
+
Loaded by step-01 §8 only when `{headless_mode}` is true. Canonical operator-facing documentation for the argument set consumed at step-01's GATE; the `{validateBriefInputsScript}` enforces these rules deterministically (its `KNOWN_FIELDS` set must stay in sync with this table).
|
|
4
|
+
|
|
5
|
+
| Argument | Required | Default | Notes |
|
|
6
|
+
|----------|----------|---------|-------|
|
|
7
|
+
| `target_repo` | yes | — | HALT (exit 2, `halt_reason: "input-missing"`) if absent |
|
|
8
|
+
| `skill_name` | yes | — | HALT (exit 2, `halt_reason: "input-missing"`) if absent; HALT (exit 2, `halt_reason: "input-invalid"`) if non-kebab |
|
|
9
|
+
| `source_type` | no | `source` | If `docs-only`, `doc_urls` becomes required |
|
|
10
|
+
| `doc_urls` | conditional | — | Required when `source_type=docs-only` (HALT exit 2, `halt_reason: "input-missing"` if empty). List of `url` or `url,label` |
|
|
11
|
+
| `source_authority` | no | detected | `official` / `community` / `internal`. When absent and `target_repo` is a GitHub URL, step-01 §8 GATE probes `gh api user` and compares its login to the URL owner — match → `official`, otherwise → `community`. Local-path or `gh api user` failure → `community`. Forced to `community` when `source_type=docs-only` |
|
|
12
|
+
| `target_version` | no | — | Auto-detected in step-02 if absent. Full X.Y.Z semver required (HALT exit 2, `halt_reason: "input-invalid"` on partial forms like `1`, `1.2`, `v2`) |
|
|
13
|
+
| `scope_hint` | no | — | Free-text steering for §5 |
|
|
14
|
+
| `language_hint` | no | — | Overrides language detection in step-02/03 |
|
|
15
|
+
| `scope_type` | no | heuristic | `full-library` / `specific-modules` / `public-api` / `component-library` / `reference-app` / `docs-only`. When absent and `source_type=source`, step-03 §2c runs five signal-driven heuristics (component-registry presence, reference-app keywords, specific-module intent, narrow public API) and uses the first match; falls back to `full-library` only if no heuristic fires. `source_type=docs-only` always short-circuits to `docs-only` |
|
|
16
|
+
| `include` | no | — | Comma-separated globs (used by step-03 §3) |
|
|
17
|
+
| `exclude` | no | — | Comma-separated globs (used by step-03 §3) |
|
|
18
|
+
| `scripts_intent` | no | `detect` | `detect` / `none` / free-text |
|
|
19
|
+
| `assets_intent` | no | `detect` | `detect` / `none` / free-text |
|
|
20
|
+
| `intent` | no | — | Free-text used to derive `description` in §7b |
|
|
21
|
+
| `force` | no | — | Overwrite existing brief without prompting (consumed in step-05 §2b) |
|
|
22
|
+
| `preset` | no | — | Name of a preset YAML file at `{sidecar_path}/brief-presets/{preset}.yaml`. Loaded at step-01 §8 GATE and merged as defaults; explicit args override preset values. Useful for repeated patterns (e.g. briefing 5 SaaS API SDKs with the same `source_authority`/`scope_type`/`scripts_intent`). The preset file is YAML containing any subset of the headless args above; unknown fields are ignored with a warning |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Headless Source-Authority Detection
|
|
2
|
+
|
|
3
|
+
Loaded by step-01 §8 only when **all three** preconditions hold:
|
|
4
|
+
|
|
5
|
+
1. `{headless_mode}` is true
|
|
6
|
+
2. `source_authority` is absent from the validator's `normalized` output (the validator intentionally leaves it absent so detection can run here — when it was supplied in args, the supplied value wins and detection does not run)
|
|
7
|
+
3. `source_type=source` AND `target_repo` is a GitHub URL
|
|
8
|
+
|
|
9
|
+
When any precondition is unmet, skip this entire procedure: docs-only forces `community` in §3.2; local-path targets default to `community` directly; non-GitHub source URLs are not classifiable from `gh api user` and also default to `community`.
|
|
10
|
+
|
|
11
|
+
## Procedure
|
|
12
|
+
|
|
13
|
+
Probe the operator's GitHub login:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gh api user --jq .login
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Compare the result to the `owner` segment of `target_repo` (URL pattern `https://github.com/<owner>/<repo>`) — **lower-case both values before comparing**. GitHub owner matching is case-insensitive but the API preserves case in responses, so a literal-string comparison would miss the match.
|
|
20
|
+
|
|
21
|
+
| Outcome | Set | Rationale |
|
|
22
|
+
|---------|-----|-----------|
|
|
23
|
+
| login matches owner (case-insensitive) | `source_authority: "official"` | The operator is the repo's GitHub owner |
|
|
24
|
+
| login does not match | `source_authority: "community"` | The operator is a downstream consumer |
|
|
25
|
+
| `gh api user` errors (unauthenticated, network failure, missing binary) | `source_authority: "community"` (fallback) | Log `"warn: source-authority detection skipped — gh api user failed"` |
|
|
26
|
+
| Local-path target | `source_authority: "community"` (no probe) | Comparison does not apply |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Portfolio-Similarity Check
|
|
2
|
+
|
|
3
|
+
Loaded by step-01 §6 only when **all three** preconditions hold:
|
|
4
|
+
|
|
5
|
+
1. Forge tier is `Deep`
|
|
6
|
+
2. `tools.qmd` is true in `forge-tier.yaml`
|
|
7
|
+
3. The flow is interactive (the headless path skips this check entirely — it would either need to HALT on duplicates [over-aggressive for an automator] or silently log [no operator to act on it]; either choice has a side effect on the QMD index that is best avoided headlessly)
|
|
8
|
+
|
|
9
|
+
This check catches **semantic near-duplicates** that exact-name collision misses (e.g. `markdown-renderer` proposed when `marked` already exists, or `auth-gateway` when `auth-middleware` already exists). Exact-name collision is handled separately at §6 before this check runs.
|
|
10
|
+
|
|
11
|
+
## Procedure
|
|
12
|
+
|
|
13
|
+
The brief portfolio is already indexed in QMD collections — one `{skill-name}-brief` collection per existing brief, registered by step-05 §5 of every prior Deep-tier run. The qmd CLI does not support glob-style collection selection, so enumerate first then query per collection in **a single message with N parallel Bash calls**:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 1. Enumerate brief collections (one per existing brief)
|
|
17
|
+
qmd collection list | awk '/-brief$/{print $1}'
|
|
18
|
+
|
|
19
|
+
# 2. For each collection, query the proposed name + intent text:
|
|
20
|
+
qmd query "{name} {synthesized-or-intent-text}" -c {collection-name} -n 1 --min-score 0.6
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Aggregate the top hits across all `-brief` collections; keep the 3 highest-scoring across the union. If any results come back, surface them as a heads-up — *not* a HALT:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
**Heads up — these existing briefs look semantically close to `{name}`:**
|
|
27
|
+
1. {existing-name} (similarity: {score}) — {existing-description}
|
|
28
|
+
2. {existing-name} (similarity: {score}) — {existing-description}
|
|
29
|
+
|
|
30
|
+
Continue with `{name}`, or pick a different name?
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Failure modes
|
|
34
|
+
|
|
35
|
+
On any QMD failure (binary missing, collection list empty, any per-collection query times out): log `"warn: portfolio-similarity check skipped — qmd query failed: {error}"` and continue silently — never HALT. Quick / Forge / Forge+ tiers do not run this check (qmd is Deep-tier-only per the canonical tier definition: `Deep = + ast-grep + gh + QMD` in `skf-forge-tier-rw.py`).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# QMD Collection Registration (Deep Tier)
|
|
2
|
+
|
|
3
|
+
Loaded by step-05 §5 only when forge tier is Deep AND QMD is available. Skipped silently otherwise.
|
|
4
|
+
|
|
5
|
+
Index the skill brief into a QMD collection so portfolio-level searches can find existing briefs and avoid duplicate skill creation across large monorepos.
|
|
6
|
+
|
|
7
|
+
## Collection Creation
|
|
8
|
+
|
|
9
|
+
Create a QMD collection targeting only the brief file:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
qmd collection add {forge_data_folder}/{skill-name} --name {skill-name}-brief --mask "skill-brief.yaml"
|
|
13
|
+
qmd embed
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If the collection already exists (re-briefing): remove and recreate for atomic replace:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
qmd collection remove {skill-name}-brief
|
|
20
|
+
qmd collection add {forge_data_folder}/{skill-name} --name {skill-name}-brief --mask "skill-brief.yaml"
|
|
21
|
+
qmd embed
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Embed Verification
|
|
25
|
+
|
|
26
|
+
After `qmd embed` completes, verify the collection was embedded:
|
|
27
|
+
|
|
28
|
+
- Run `qmd status` or `qmd collection list` and confirm `{skill-name}-brief` shows document count > 0
|
|
29
|
+
- If verification succeeds: proceed to registry update with no `status` field
|
|
30
|
+
- If verification fails: log warning "QMD embed verification failed for {skill-name}-brief — collection may not be searchable yet", proceed to registry update but include `status: "pending"` in the entry
|
|
31
|
+
|
|
32
|
+
## Registry Update (Delegated to Script)
|
|
33
|
+
|
|
34
|
+
Build the entry JSON and pipe it to the `register-qmd-collection` subcommand:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
echo '{
|
|
38
|
+
"name": "{skill-name}-brief",
|
|
39
|
+
"type": "brief",
|
|
40
|
+
"source_workflow": "brief-skill",
|
|
41
|
+
"skill_name": "{skill-name}",
|
|
42
|
+
"created_at": "{current ISO date}"
|
|
43
|
+
// include "status": "pending" only when embed verification failed
|
|
44
|
+
}' | uv run {forgeTierRwScript} register-qmd-collection --target {forgeTierFile}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The script handles the upsert deterministically (replace existing entry with same `name`, else append) and preserves all other forge-tier state (tools, tier, ccc_index, ccc_index_registry, other qmd_collections entries) — no need to reason about YAML re-rendering or section comments.
|
|
48
|
+
|
|
49
|
+
## Error Handling
|
|
50
|
+
|
|
51
|
+
- If `qmd embed` or `qmd collection add` fails: log the error. Do NOT fail the workflow — the brief file was already written successfully.
|
|
52
|
+
- If the `register-qmd-collection` script call fails: log the error JSON, continue. The brief is the user-visible artifact; the registry entry is a portfolio-search optimisation.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Version Resolution
|
|
2
|
+
|
|
3
|
+
Single source of truth for how brief-skill resolves the `version` field of `skill-brief.yaml`. Loaded by step-02 §4b (auto-detect, fallback path only when the language is not script-supported) and step-05 §3 (resolve & write) so both operate on the same precedence rules and invariant. Step-01 §3b references this file in prose for human-readable rationale but does not load it — that step only collects `target_version` and validates its shape with an inline regex.
|
|
4
|
+
|
|
5
|
+
**Aligned with** `assets/skill-brief-schema.md` "Version Detection" section. If you change one, change the other.
|
|
6
|
+
|
|
7
|
+
## Detection Algorithm
|
|
8
|
+
|
|
9
|
+
For the detected source language, attempt the lookups in order. Stop at the first match.
|
|
10
|
+
|
|
11
|
+
- **Python:** `pyproject.toml` `[project] version` (static) → if `dynamic = ["version"]`, check `__init__.py` for `__version__` → `_version.py` if exists → `setup.py` `version=` → `git describe --tags --abbrev=0`
|
|
12
|
+
- **JavaScript / TypeScript:** root `package.json` (`"version"`). If the root has `"private": true` with a `"workspaces"` array or lacks a `"version"` field, fall back to a primary workspace package's `package.json` (e.g. `code/core/package.json`, or the first matching `packages/*/package.json`). For GitHub sources, prefer `gh api repos/{owner}/{repo}/releases/latest` → `tag_name` when a non-pre-release tag exists, over a default-branch pre-release. Treat a version containing `-alpha`, `-beta`, `-rc`, `-next`, or `-canary` as a pre-release.
|
|
13
|
+
- **Rust:** `Cargo.toml` `[package] version` (static). If `version = { workspace = true }`, resolve from workspace root `Cargo.toml` → `git describe --tags --abbrev=0`.
|
|
14
|
+
- **Go:** version tag from `go.mod`, or `git describe --tags --abbrev=0`.
|
|
15
|
+
|
|
16
|
+
For remote GitHub sources, fetch version-bearing files via `gh api repos/{owner}/{repo}/contents/{file}` (decode base64). For local sources, read the file directly.
|
|
17
|
+
|
|
18
|
+
If every step fails or returns a non-semver value, the detected version is `null` — the resolver below falls back to `"1.0.0"`.
|
|
19
|
+
|
|
20
|
+
**Pre-release handling:** preserve detected pre-release tags (`1.0.0-beta.0`, `2.0.0-rc.1`) verbatim. Do not strip them.
|
|
21
|
+
|
|
22
|
+
## Precedence — Resolving the `version` Field
|
|
23
|
+
|
|
24
|
+
The brief's `version` field is resolved from three candidate sources, in priority order:
|
|
25
|
+
|
|
26
|
+
1. **`target_version`** — collected interactively in step-01 §3b or supplied as a headless argument. When present, this value wins outright. The auto-detection above still runs for informational purposes (the user sees both "Target version" and "Detected version" side-by-side at the analysis summary), but the brief's `version` field is set from `target_version`.
|
|
27
|
+
2. **Auto-detected version** — from §"Detection Algorithm" above. Used when `target_version` is absent.
|
|
28
|
+
3. **Default** — `"1.0.0"` when both of the above fail or yield a non-semver value.
|
|
29
|
+
|
|
30
|
+
## Invariant
|
|
31
|
+
|
|
32
|
+
When `target_version` is set, the written brief MUST satisfy:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
brief.target_version == brief.version
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Step-05 §3 enforces this by setting both fields to the same string when `target_version` is present. Downstream tooling (e.g. `skf-create-skill`) can distinguish "user-requested" from "auto-detected" by the presence of `target_version` without re-deriving provenance — but the values themselves are identical. Different values are a contract violation and a bug.
|
|
39
|
+
|
|
40
|
+
## Step-Level Responsibilities
|
|
41
|
+
|
|
42
|
+
| Step | Responsibility |
|
|
43
|
+
|------|----------------|
|
|
44
|
+
| 01 §3b | Collect `target_version` (interactive prompt, or headless arg). Do not auto-detect — that is step 02's job. |
|
|
45
|
+
| 02 §4b | Run the detection algorithm regardless of whether `target_version` is set. If `target_version` is set and the detected version differs, surface the disagreement to the user — but the precedence above is unchanged: `target_version` wins. |
|
|
46
|
+
| 05 §3 | Apply the precedence rules and write `version`. If `target_version` is set, also write the `target_version` field with the identical value. Enforce the invariant. |
|