bmad-module-skill-forge 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +6 -4
  3. package/docs/_data/pinned.yaml +1 -1
  4. package/docs/bmad-synergy.md +2 -2
  5. package/docs/getting-started.md +1 -1
  6. package/docs/skill-model.md +26 -32
  7. package/docs/troubleshooting.md +13 -1
  8. package/docs/why-skf.md +5 -4
  9. package/docs/workflows.md +53 -0
  10. package/package.json +2 -2
  11. package/src/knowledge/ccc-bridge.md +1 -1
  12. package/src/shared/references/output-contract-schema.md +10 -0
  13. package/src/shared/scripts/schemas/skf-setup-result-envelope.v1.json +134 -0
  14. package/src/shared/scripts/skf-detect-tools.py +359 -0
  15. package/src/shared/scripts/skf-emit-result-envelope.py +419 -0
  16. package/src/shared/scripts/skf-extract-public-api.py +505 -0
  17. package/src/shared/scripts/skf-forge-tier-rw.py +413 -0
  18. package/src/shared/scripts/skf-merge-ccc-exclusions.py +324 -0
  19. package/src/shared/scripts/skf-preflight.py +14 -4
  20. package/src/shared/scripts/skf-qmd-classify-collections.py +212 -0
  21. package/src/shared/scripts/skf-render-quick-metadata.py +192 -0
  22. package/src/shared/scripts/skf-resolve-package.py +264 -0
  23. package/src/shared/scripts/skf-validate-frontmatter.py +9 -3
  24. package/src/shared/scripts/skf-validate-output.py +24 -7
  25. package/src/skf-create-skill/steps-c/step-06-validate.md +1 -1
  26. package/src/skf-quick-skill/SKILL.md +178 -10
  27. package/src/skf-quick-skill/assets/skill-template.md +5 -1
  28. package/src/skf-quick-skill/references/registry-resolution.md +2 -0
  29. package/src/skf-quick-skill/steps-c/step-01-resolve-target.md +84 -16
  30. package/src/skf-quick-skill/steps-c/step-02-ecosystem-check.md +3 -3
  31. package/src/skf-quick-skill/steps-c/step-03-quick-extract.md +86 -43
  32. package/src/skf-quick-skill/steps-c/step-04-compile.md +49 -56
  33. package/src/skf-quick-skill/steps-c/step-05-write-and-validate.md +164 -0
  34. package/src/skf-quick-skill/steps-c/{step-06-write.md → step-06-finalize.md} +15 -7
  35. package/src/skf-quick-skill/steps-c/step-07-health-check.md +5 -3
  36. package/src/skf-setup/SKILL.md +25 -10
  37. package/src/skf-setup/references/tier-rules.md +2 -2
  38. package/src/skf-setup/steps-c/step-01-detect-and-tier.md +58 -71
  39. package/src/skf-setup/steps-c/step-01b-ccc-index.md +49 -66
  40. package/src/skf-setup/steps-c/step-02-write-config.md +59 -86
  41. package/src/skf-setup/steps-c/step-03-auto-index.md +73 -91
  42. package/src/skf-setup/steps-c/step-04-report.md +135 -11
  43. package/src/skf-setup/steps-c/step-05-health-check.md +4 -2
  44. package/src/skf-test-skill/steps-c/step-01-init.md +4 -4
  45. package/src/skf-quick-skill/steps-c/step-05-validate.md +0 -193
@@ -1,111 +1,98 @@
1
1
  ---
2
2
  nextStepFile: './step-01b-ccc-index.md'
3
- tierRulesData: 'references/tier-rules.md'
3
+ # Resolve `{detectToolsHelper}` by probing `{detectToolsProbeOrder}` in order
4
+ # (installed SKF module path first, src/ dev-checkout fallback); first existing
5
+ # path wins. HALT if neither resolves — the script is the source of truth for
6
+ # tool detection and tier calculation; no fallback to prose-driven probes.
7
+ detectToolsProbeOrder:
8
+ - '{project-root}/_bmad/skf/shared/scripts/skf-detect-tools.py'
9
+ - '{project-root}/src/shared/scripts/skf-detect-tools.py'
4
10
  ---
5
11
 
12
+ <!-- Config: communicate in {communication_language}. The first-run preamble below is user-visible — render it in the user's language. -->
13
+
6
14
  # Step 1: Detect Tools and Determine Tier
7
15
 
8
16
  ## STEP GOAL:
9
17
 
10
- Verify availability of the four forge tools (ast-grep, gh, qmd, ccc), read any existing configuration for re-run comparison, check for tier override, and calculate the capability tier.
18
+ Verify availability of the four forge tools (ast-grep, gh, qmd, ccc), read any existing configuration for re-run comparison, check for tier override, and calculate the capability tier — all via `{detectToolsHelper}` so the deterministic work is done once, by a tested script, never by the LLM.
11
19
 
12
20
  ## Rules
13
21
 
14
22
  - Focus only on tool detection and tier calculation — do not write any files (Step 02)
15
- - Do not skip any tool checkall 4 must be verified
16
- - Tool command failures are not errors — they indicate unavailability
23
+ - Never reimplement tool probes or the tier rules in prose the script is authoritative
24
+ - Tool command failures are not errors — they indicate unavailability (the script swallows them)
17
25
 
18
26
  ## MANDATORY SEQUENCE
19
27
 
20
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
21
-
22
- ### 1. Load Tier Rules
23
-
24
- Load and read {tierRulesData} for the tool detection commands and tier calculation logic.
25
-
26
- ### 2. Check for Existing Configuration (Re-run Detection)
28
+ ### 1. Check for Existing Configuration (Re-run Detection)
27
29
 
28
30
  **Read existing forge-tier.yaml** at `{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml`:
29
- - If exists: store the current `tier` value as `{previous_tier}` and `tier_detected_at` as `{previous_detection_date}`
30
- - If not found: set `{previous_tier}` to null (first run)
31
+
32
+ - If exists: store the current `tier` value as `{previous_tier}`, `tier_detected_at` as `{previous_detection_date}`, and the `tools` map as `{previous_tools}` (for the tool-set delta detection step-04 needs to surface newly-installed tools on same-tier re-runs).
33
+ - If not found: set `{previous_tier}` to null and `{previous_tools}` to an empty map (first run).
31
34
 
32
35
  **Read existing preferences.yaml** at `{project-root}/_bmad/_memory/forger-sidecar/preferences.yaml`:
36
+
33
37
  - If exists: check for `tier_override` value
34
38
  - If not found: set `{tier_override}` to null
35
39
 
36
- ### 3. Verify Tool: ast-grep
37
-
38
- Run: `ast-grep --version`
39
-
40
- - If succeeds: record `{ast_grep: true}` and store version string
41
- - If fails (command not found or error): record `{ast_grep: false}`
42
-
43
- ### 4. Verify Tool: gh
44
-
45
- Run: `gh --version`
46
-
47
- - If succeeds: record `{gh_cli: true}` and store version string
48
- - If fails: record `{gh_cli: false}`
49
-
50
- ### 5. Verify Tool: qmd
51
-
52
- Run: `qmd status`
53
-
54
- - If succeeds and indicates operational: record `{qmd: true}`
55
- - If fails or indicates not initialized: record `{qmd: false}`
56
-
57
- ### 6. Check Optional: Security Scan (SNYK_TOKEN)
58
-
59
- Check if the `SNYK_TOKEN` environment variable is set:
60
-
61
- - If `SNYK_TOKEN` is non-empty: record `{security_scan: true}`
62
- - If `SNYK_TOKEN` is empty or unset: record `{security_scan: false}`
63
-
64
- This is informational only — security scan availability does NOT affect the tier level. It is recorded in forge-tier.yaml so that create-skill's validation step can report actionable guidance when security scanning is unavailable.
65
-
66
- ### 7. Verify Tool: ccc (cocoindex-code)
40
+ **First-run preamble** when `{previous_tier}` is null AND `{headless_mode}` is `false`, display this preamble before continuing so the user knows what is about to happen and can abort cleanly with Esc / Ctrl+C before any writes:
67
41
 
68
- **Step A Binary existence:** Run `ccc --help`
42
+ "**About to set up the forge.** This workflow will:
69
43
 
70
- - If exits 0: binary confirmed. Continue to Step B.
71
- - If fails (command not found or error): record `{ccc: false}`. Skip Step B.
44
+ - Detect available tools (ast-grep, gh, qmd, ccc) read-only probes only
45
+ - Write `{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml` (capability tier + tool state)
46
+ - Write `{project-root}/_bmad/_memory/forger-sidecar/preferences.yaml` (first-run defaults)
47
+ - Create `{forge_data_folder}/` if missing
48
+ - When ccc is available: augment `{project-root}/.cocoindex_code/settings.yml` with SKF exclusion patterns, then create or refresh the project ccc index
72
49
 
73
- **Step BDaemon health:** Run `ccc doctor`
50
+ **About tiers:** SKF picks one of four tiers (Quick / Forge / Forge+ / Deep) based on which tools are installed. **All four are fully usable** higher tiers add power, they don't fix gaps. If you're new and only have a base Python install, Quick tier is the right starting point and the report at the end will show you exactly which tools to install if you want to climb later.
74
51
 
75
- - If daemon is running and model check OK: record `{ccc: true, ccc_daemon: "healthy"}` and store version string from output
76
- - If daemon is not running: record `{ccc: true, ccc_daemon: "stopped"}` — binary available, daemon needs starting. Step-01b will handle this.
77
- - If error or timeout: record `{ccc: true, ccc_daemon: "error"}` — binary works but daemon has issues.
52
+ Press Esc or Ctrl+C now if this isn't the right project no files have been written yet."
78
53
 
79
- ccc availability gates the Forge+ tier and enhances Deep tier when present.
54
+ ### 2. Run Detection Helper
80
55
 
81
- ### 8. Calculate Tier
56
+ Build the Bash invocation. Start with `uv run {detectToolsHelper}` — `uv run` honors the script's PEP 723 inline-metadata dependency declarations (resolved automatically on first call; cached thereafter), which is why uv is documented as a runtime prerequisite in `docs/getting-started.md`. Do NOT call the script via bare `python3` — `python3` ignores PEP 723 metadata, and on a fresh interpreter without the declared deps installed system-wide the call fails with `ModuleNotFoundError`. If `{tier_override}` is non-null, append `--tier-override "{tier_override}"` (the script handles invalid values by flagging them — do NOT pre-validate). If `{require_tier}` (resolved in On Activation) is non-null, append `--require-tier "{require_tier}"`. Then execute.
82
57
 
83
- **If `{tier_override}` is set and valid (Quick, Forge, Forge+, or Deep):**
84
- - Use `{tier_override}` as `{calculated_tier}`
85
- - Note that override is active for the report step
58
+ The script (see `src/shared/scripts/skf-detect-tools.py` docstring for the full `DETECT_OUTPUT_SCHEMA`) probes ast-grep / gh / qmd / ccc concurrently with two-step verification for qmd and ccc (binary-identity check + daemon-health check, including the `CocoIndex Code` identity-marker substring check that rejects PATH-shadowing aliases). It applies the 4-rule tier table, performs the tier-override sanity check (override is honored but flagged unsafe when underlying tools are missing), and evaluates `--require-tier` using a tool-prerequisite check (Deep does NOT subsume Forge+ Deep does not require ccc). Output is one JSON document on stdout.
86
59
 
87
- **If no override, apply tier rules from {tierRulesData} in order — the first matching rule wins. Do not continue checking once a match is found:**
88
- - `{ast_grep}` AND `{gh_cli}` AND `{qmd}` all true → **Deep**
89
- - `{ast_grep}` AND `{ccc}` both true, but NOT (`{gh_cli}` AND `{qmd}`) → **Forge+**
90
- - `{ast_grep}` true (regardless of ccc/gh/qmd) → **Forge**
91
- - Otherwise → **Quick**
60
+ ### 3. Parse Output and Set Context Flags
92
61
 
93
- **If `{tier_override}` is set but invalid:** ignore it, use detected tier, flag for warning in report.
62
+ From the JSON, set these context flags. Field paths are relative to the script's top-level object.
94
63
 
95
- ### 9. Auto-Proceed
64
+ From `tools`:
96
65
 
97
- "**Proceeding to CCC index check...**"
66
+ - `{ast_grep}` `tools.ast_grep.available`
67
+ - `{ast_grep_version}` ← `tools.ast_grep.version`
68
+ - `{gh_cli}` ← `tools.gh_cli.available`
69
+ - `{gh_cli_version}` ← `tools.gh_cli.version`
70
+ - `{qmd}` ← `tools.qmd.available`
71
+ - `{qmd_status}` ← `tools.qmd.status` (`"absent" | "daemon_stopped" | "healthy"` — drives the climb-hint distinction in step-04)
72
+ - `{qmd_version}` ← `tools.qmd.version`
73
+ - `{ccc}` ← `tools.ccc.available`
74
+ - `{ccc_daemon}` ← `tools.ccc.daemon` (`"healthy" | "stopped" | "error" | null`)
75
+ - `{ccc_version}` ← `tools.ccc.version`
76
+ - `{security_scan}` ← `tools.security_scan.available` (informational only — never affects tier)
98
77
 
99
- #### Menu Handling Logic:
78
+ From `tier`:
100
79
 
101
- - After tier calculation is complete, immediately load, read entire file, then execute {nextStepFile}
80
+ - `{calculated_tier}` ← `tier.calculated` the tier downstream steps act on
81
+ - `{detected_tier}` ← `tier.detected` — what would have been chosen without override
82
+ - `{tier_override_active}` ← `tier.override_applied`
83
+ - `{tier_override_invalid}` ← `tier.override_invalid`
84
+ - `{tier_override_invalid_value}` ← `tier.override_invalid_value`
85
+ - `{tier_override_invalid_suggestion}` ← `tier.override_invalid_suggestion` (closest valid tier name from a fuzzy match — `null` when no candidate cleared the cutoff or when override is valid; consumed by step-04's invalid-override note as a "did you mean ...?" hint)
86
+ - `{tier_override_unsafe}` ← `tier.override_unsafe`
87
+ - `{tier_override_unsafe_missing}` ← `tier.override_unsafe_missing` (a list — step-04 joins with `", "` for display)
102
88
 
103
- #### EXECUTION RULES:
89
+ From `require_tier`:
104
90
 
105
- - This is an auto-proceed step with no user choices
106
- - Proceed directly to next step after detection and calculation
91
+ - `{require_tier_satisfied}` `require_tier.satisfied` (`true | false | null`; null when `--require-tier` was not set)
92
+ - `{require_tier_failure_missing_tools}` `require_tier.missing_tools` (a list)
107
93
 
108
- ## CRITICAL STEP COMPLETION NOTE
94
+ **The script is the source of truth.** Every tier-rules edge case (override-honored-but-unsafe, Deep-doesn't-subsume-Forge+, qmd-binary-vs-daemon distinction, ccc-identity-marker rejection of foreign binaries) is locked into the test suite at `test/test-skf-detect-tools.py`. Substituting prose-driven logic for the script's output here will cause subtle re-run regressions that the prompt's prose is no longer detailed enough to catch.
109
95
 
110
- ONLY WHEN all 4 core tools have been verified, optional security scan checked, and the tier calculated will you load and read fully `{nextStepFile}` to execute the CCC index check step.
96
+ ### 4. Auto-Proceed
111
97
 
98
+ After context flags are populated, display "**Proceeding to CCC index check...**", then load `{nextStepFile}`, read it fully, and execute it.
@@ -1,31 +1,39 @@
1
1
  ---
2
2
  nextStepFile: './step-02-write-config.md'
3
+ # Resolve `{mergeCccExclusionsHelper}` by probing `{mergeCccExclusionsProbeOrder}`
4
+ # in order (installed SKF module path first, src/ dev-checkout fallback); first
5
+ # existing path wins. HALT if neither resolves — the script owns config-value
6
+ # validation and the set-union merge into .cocoindex_code/settings.yml; no
7
+ # fallback to prose-driven validation.
8
+ mergeCccExclusionsProbeOrder:
9
+ - '{project-root}/_bmad/skf/shared/scripts/skf-merge-ccc-exclusions.py'
10
+ - '{project-root}/src/shared/scripts/skf-merge-ccc-exclusions.py'
3
11
  ---
4
12
 
13
+ <!-- Config: communicate in {communication_language}. User-visible status messages (CCC exclusion summary, indexing progress message) render in the user's language. -->
14
+
5
15
  # Step 1b: CCC Index Verification
6
16
 
7
17
  ## STEP GOAL:
8
18
 
9
- If ccc is available (`{ccc: true}` from step-01), configure CCC exclusion patterns for SKF infrastructure directories, verify that the ccc index exists for the project root, and create or refresh it if needed. Store index state and exclusion patterns in context for step-02 to write into forge-tier.yaml.
19
+ If ccc is available (`{ccc: true}` from step-01), invoke `{mergeCccExclusionsHelper}` to validate config-driven SKF exclusion patterns and merge them into `.cocoindex_code/settings.yml`, then verify the project's ccc index exists and create or refresh it if needed. Store index state and exclusion-merge results in context for step-02 to write into forge-tier.yaml and for step-04 to surface in the JSON envelope.
10
20
 
11
21
  For Quick and Forge tiers, or when ccc is unavailable, skip silently and proceed.
12
22
 
13
23
  ## Rules
14
24
 
15
- - Focus only on ccc index verification and creation
25
+ - Focus only on ccc index verification, exclusion-pattern merge, and (re-)indexing
16
26
  - Do not display skip messages for Quick/Forge tiers
17
27
  - Do not fail the workflow if ccc indexing fails
18
- - Do not re-index if ccc index already exists and is fresh, unless new exclusion patterns were applied
28
+ - Never reimplement the exclusion-pattern validation in prose the script owns the PR #248 rules
19
29
 
20
30
  ## MANDATORY SEQUENCE
21
31
 
22
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
23
-
24
32
  ### 1. Check Eligibility
25
33
 
26
34
  Read `{ccc}` from step-01 context.
27
35
 
28
- **If `{ccc}` is false:** Set `{ccc_index_result: "none", ccc_indexed_path: null, ccc_last_indexed: null, ccc_exclude_patterns: []}`. Proceed directly to section 4 (Auto-Proceed) — no output, no messaging.
36
+ **If `{ccc}` is false:** Set `{ccc_index_result: "none", ccc_indexed_path: null, ccc_last_indexed: null, ccc_exclude_patterns: [], ccc_exclusion_warnings: [], settings_yml_written: false, settings_yml_patterns_added: 0}`. Proceed directly to section 4 (Auto-Proceed) — no output, no messaging.
29
37
 
30
38
  **If `{ccc}` is true:** Continue to section 2.
31
39
 
@@ -36,81 +44,68 @@ Read existing forge-tier.yaml at `{project-root}/_bmad/_memory/forger-sidecar/fo
36
44
  Read `staleness_threshold_hours` from `ccc_index.staleness_threshold_hours` in the existing forge-tier.yaml (default: 24 if not present or not a number). Use this value for the freshness check below.
37
45
 
38
46
  Check the `ccc_index` section:
47
+
39
48
  - If `ccc_index.indexed_path` matches `{project-root}` AND `ccc_index.status` is `"fresh"` or `"created"`:
40
49
  - Check freshness: if `ccc_index.last_indexed` is within `staleness_threshold_hours` of now → index is fresh
41
50
  - Store `{ccc_index_result: "fresh", ccc_indexed_path: {project-root}, ccc_last_indexed: {existing timestamp}}`
42
- - Set `{needs_reindex: false}` — proceed to section 2b (exclusions must still be configured)
51
+ - Set `{needs_reindex: false}` — exclusions still get merged in section 3, which may force a re-index
43
52
 
44
53
  - If `ccc_index.indexed_path` matches `{project-root}` but timestamp is older than threshold:
45
- - Set `{needs_reindex: true}` — proceed to section 2b then section 3
54
+ - Set `{needs_reindex: true}`
46
55
 
47
56
  - If `ccc_index` is missing, has null values, or path doesn't match:
48
- - Set `{needs_reindex: true}` — proceed to section 2b then section 3
57
+ - Set `{needs_reindex: true}`
49
58
 
50
- ### 2b. Configure CCC Exclusions
59
+ ### 3. Merge SKF Exclusion Patterns
51
60
 
52
61
  SKF infrastructure and output directories must be excluded from the CCC index — they contain workflow instructions, build artifacts, and generated skills that pollute semantic search results with zero extraction value.
53
62
 
54
- **Build the SKF exclusion list:**
55
-
56
- 1. Use `{skills_output_folder}` and `{forge_data_folder}` from the workflow activation context (resolved in On Activation from `{project-root}/_bmad/skf/config.yaml`).
57
-
58
- 2. Assemble the exclusion patterns using `**/` prefix format (matching `.cocoindex_code/settings.yml` convention — e.g., `**/node_modules`):
59
- - `**/_bmad` — SKF framework module (workflows, agents, knowledge files)
60
- - `**/_bmad-output` — Build output artifacts
61
- - `**/.claude` — Claude Code configuration
62
- - `**/_skf-learn` — SKF learning materials
63
- - `**/{skills_output_folder}` — Generated skill files (from activation context)
64
- - `**/{forge_data_folder}` — Compilation workspace (from activation context)
65
-
66
- 3. Store `{ccc_exclude_patterns}` in context for step-02 to write into forge-tier.yaml.
63
+ Run the merge helper, forwarding the resolved config values from the workflow activation context. Invoke via `uv run` so PEP 723 inline metadata resolves the script's PyYAML dependency automatically (per `docs/getting-started.md`'s prereq list — uv exists for this exact purpose). Bare `python3` will fail on a fresh Python with `ModuleNotFoundError: No module named 'yaml'`.
67
64
 
68
- **Apply exclusions to settings.yml:**
65
+ ```bash
66
+ uv run {mergeCccExclusionsHelper} \
67
+ --project-root "{project-root}" \
68
+ --skills-output-folder "{skills_output_folder}" \
69
+ --forge-data-folder "{forge_data_folder}"
70
+ ```
69
71
 
70
- Check if `{project-root}/.cocoindex_code/settings.yml` exists. Set `{settings_yml_existed: true}` if it does, `{settings_yml_existed: false}` if not.
72
+ The script (see `src/shared/scripts/skf-merge-ccc-exclusions.py` docstring for the full schema) builds the SKF exclusion list (4 always-include hardcoded patterns + 2 conditional from validated config), applies the PR #248 validation rules to reject empty / absolute / glob-meta config values with actionable warnings, and performs an idempotent set-union merge into `{project-root}/.cocoindex_code/settings.yml`. User customizations are preserved. When the file does not exist yet (first-time setup before `ccc init`) the script creates it; when nothing new needs adding the script skips the write entirely (mtime preserved).
71
73
 
72
- If `{settings_yml_existed}` is true (from a previous `ccc init` run):
74
+ **Parse the JSON output and set context flags:**
73
75
 
74
- 1. Read `settings.yml`
75
- 2. For each pattern in `{ccc_exclude_patterns}`: if the pattern is NOT already present in `exclude_patterns`, append it and set `{exclusions_changed: true}`
76
- 3. If `{exclusions_changed}`: write the updated `settings.yml` back, set `{needs_reindex: true}` (new exclusions require re-indexing), display: "**CCC exclusions configured:** {count} SKF patterns applied to .cocoindex_code/settings.yml"
77
- 4. If no new patterns needed: display nothing (exclusions already configured)
76
+ - `{settings_yml_existed}` `settings_yml_existed`
77
+ - `{settings_yml_written}` `written`
78
+ - `{settings_yml_patterns_added}` `patterns_added`
79
+ - `{ccc_exclude_patterns}` ← derive from the script's behaviour: it always merges the 4 always-include patterns plus any of the 2 config-driven ones whose value passed validation. Step-02 needs this list to write `ccc_index.exclude_patterns` into forge-tier.yaml — read it from the script's `patterns_added_list` PLUS any already-present matching SKF patterns inferred from the input. Pragmatically: re-derive the list as `["**/_bmad", "**/_bmad-output", "**/.claude", "**/_skf-learn"]` plus `"**/{skills_output_folder}"` if no warning names `skills_output_folder` plus `"**/{forge_data_folder}"` if no warning names `forge_data_folder`.
80
+ - `{ccc_exclusion_warnings}` ← `warnings` (a list — step-04 folds them into the envelope's warnings array)
78
81
 
79
- This preserves any existing user customizations and default exclusions while ensuring SKF directories are filtered out.
82
+ **If `{settings_yml_written}` is true** (new patterns merged into settings.yml): set `{needs_reindex: true}` — new exclusions require re-indexing for the index to reflect them. Display: "**CCC exclusions configured:** {patterns_added} SKF patterns applied to .cocoindex_code/settings.yml"
80
83
 
81
- If `{settings_yml_existed}` is false: the exclusions will be applied after `ccc init` in section 3.
84
+ **If `{settings_yml_written}` is false** (idempotent re-run, all patterns already present): display nothing (exclusions already configured). Do NOT change `{needs_reindex}` it stays at whatever section 2 set it to.
82
85
 
83
86
  **Flow decision:**
84
- - If `{needs_reindex}` is true: proceed to section 3
85
- - If `{needs_reindex}` is false: proceed to section 4 (Auto-Proceed)
86
-
87
- ### 3. Create or Refresh CCC Index
88
87
 
89
- **If `{ccc_daemon}` is `"stopped"` or `"healthy"`:**
88
+ - If `{needs_reindex}` is true: proceed to section 4
89
+ - If `{needs_reindex}` is false: proceed to section 5 (Auto-Proceed)
90
90
 
91
- The `ccc index` command auto-starts the daemon when needed. Proceed with indexing below.
91
+ ### 4. Create or Refresh CCC Index
92
92
 
93
- **If `{ccc_daemon}` is `"error"`:**
93
+ **If `{ccc_daemon}` is `"stopped"` or `"healthy"`:** the `ccc index` command auto-starts the daemon when needed.
94
94
 
95
- Attempt indexing anyway — errors will be caught below.
95
+ **If `{ccc_daemon}` is `"error"`:** attempt indexing anyway — errors will be caught below.
96
96
 
97
97
  Run (CWD must be `{project-root}`):
98
+
98
99
  ```bash
99
100
  ccc init
100
101
  ```
101
102
 
102
- **If init fails** (project may already be initialized): continue — this is not an error.
103
-
104
- **Apply SKF exclusion patterns (if not already applied in section 2b):**
103
+ **If init fails** (project may already be initialized): continue — this is not an error. Note: when `{settings_yml_existed}` was false and `ccc init` just created the settings.yml, the merge in section 3 ran BEFORE ccc init. The script handles "no settings.yml exists" by creating one with just the SKF exclusions, which `ccc init` will then either preserve (if it merges) or overwrite (in which case the next workflow run re-merges). Either way the SKF exclusions end up in the file by the time `ccc index` runs the second time.
105
104
 
106
- If `{settings_yml_existed}` is false (first-time setup — `ccc init` just created it), apply exclusions now:
107
-
108
- 1. Read `{project-root}/.cocoindex_code/settings.yml` (created by `ccc init`)
109
- 2. For each pattern in `{ccc_exclude_patterns}`: if the pattern is NOT already present in `exclude_patterns`, append it
110
- 3. Write the updated `settings.yml` back
111
- 4. Display: "**CCC exclusions configured:** {count} SKF patterns applied to .cocoindex_code/settings.yml"
105
+ Before invoking `ccc index`, display: "**Building semantic index this can take several minutes on large codebases (1000+ files). Run `ccc status` in another terminal to monitor progress.**" so the user does not assume the workflow has hung during the long-running call.
112
106
 
113
107
  Then run:
108
+
114
109
  ```bash
115
110
  ccc index
116
111
  ```
@@ -118,29 +113,17 @@ ccc index
118
113
  **Note:** `ccc index` can take several minutes on large codebases (1000+ files). Run with an extended timeout or in background mode. Use `ccc status` to verify completion — check that `Chunks` and `Files` counts are non-zero.
119
114
 
120
115
  **If succeeds:**
116
+
121
117
  - Run `ccc status` to get file count
122
118
  - Store `{ccc_index_result: "created", ccc_indexed_path: {project-root}, ccc_last_indexed: {current ISO timestamp}, ccc_file_count: {count from ccc status}}`
123
119
  - Display: "**CCC index created.** {ccc_file_count} files indexed for semantic discovery."
124
120
 
125
121
  **If fails:**
126
- - Store `{ccc_index_result: "failed", ccc_indexed_path: null, ccc_last_indexed: null}`
122
+
123
+ - Store `{ccc_index_result: "failed", ccc_indexed_path: null, ccc_last_indexed: null, ccc_indexing_failed_reason: {error}}` (the failed-reason flag flows into step-04's envelope warnings)
127
124
  - Display: "CCC indexing failed: {error}. Extraction will use direct AST scanning — semantic pre-ranking unavailable this session."
128
125
  - Continue — this is NOT a workflow error
129
126
 
130
- ### 4. Auto-Proceed
131
-
132
- "**Proceeding to write configuration...**"
133
-
134
- #### Menu Handling Logic:
135
-
136
- - After ccc index check completes (or is skipped), immediately load, read entire file, then execute {nextStepFile}
137
-
138
- #### EXECUTION RULES:
139
-
140
- - This is an auto-proceed step with no user choices
141
- - Proceed directly to next step after ccc index verification
142
-
143
- ## CRITICAL STEP COMPLETION NOTE
144
-
145
- ONLY WHEN ccc index verification is complete (or step is skipped for ccc unavailable) will you load and read fully `{nextStepFile}` to execute the configuration write step.
127
+ ### 5. Auto-Proceed
146
128
 
129
+ After ccc index verification is complete (or skipped because ccc is unavailable), display "**Proceeding to write configuration...**", then load `{nextStepFile}`, read it fully, and execute it.
@@ -1,123 +1,96 @@
1
1
  ---
2
2
  nextStepFile: './step-03-auto-index.md'
3
+ # Resolve `{forgeTierRwHelper}` by probing `{forgeTierRwProbeOrder}` in order
4
+ # (installed SKF module path first, src/ dev-checkout fallback); first existing
5
+ # path wins. HALT if neither resolves — the script owns the canonical
6
+ # forge-tier.yaml format AND the array-preservation contract that protects
7
+ # qmd_collections / ccc_index_registry / staleness_threshold_hours from being
8
+ # lost on rewrite. NEVER fall back to inline YAML emission — drift between the
9
+ # script and a prose-rendered template will silently corrupt downstream skills.
10
+ forgeTierRwProbeOrder:
11
+ - '{project-root}/_bmad/skf/shared/scripts/skf-forge-tier-rw.py'
12
+ - '{project-root}/src/shared/scripts/skf-forge-tier-rw.py'
3
13
  ---
4
14
 
15
+ <!-- Config: communicate in {communication_language}. Halt-on-write-failure messages render in the user's language. -->
16
+
5
17
  # Step 2: Write Configuration
6
18
 
7
19
  ## STEP GOAL:
8
20
 
9
- Write the detected tool availability and calculated tier to forge-tier.yaml, create preferences.yaml with defaults if it does not exist, and ensure the forge-data/ directory is present.
21
+ Write the detected tool availability and calculated tier to `forge-tier.yaml` (preserving registry arrays from any existing file), create `preferences.yaml` with first-run defaults if it does not exist, and ensure the `forge-data/` directory is present. All file mutations go through `{forgeTierRwHelper}` so the format is locked, atomic, and array-preservation is guaranteed.
10
22
 
11
23
  ## Rules
12
24
 
13
25
  - Focus only on writing configuration files and creating directories
14
26
  - Do not re-detect tools — use results from step-01
15
- - Do not overwrite existing preferences.yaml
16
- - File write failures are errors — report clearly
27
+ - Never inline a YAML template for forge-tier.yaml or preferences.yaml — the script owns the canonical format
28
+ - File write failures are errors — report clearly and halt the workflow
17
29
 
18
30
  ## MANDATORY SEQUENCE
19
31
 
20
- **CRITICAL:** Follow this sequence exactly. Do not skip, reorder, or improvise.
21
-
22
32
  ### 1. Write forge-tier.yaml
23
33
 
24
- Write to `{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml`:
25
-
26
- ```yaml
27
- # Ferris Sidecar: Forge Tier State
28
- # Written by setup workflow
29
-
30
- # Tool availability (detected during [SF] Setup Forge)
31
- tools:
32
- ast_grep: {true/false from detection}
33
- gh_cli: {true/false from detection}
34
- qmd: {true/false from detection}
35
- ccc: {true/false from detection}
36
- ccc_daemon: {ccc_daemon from step-01 if available: "healthy"|"stopped"|"error", or ~}
37
- security_scan: {true/false — true when SNYK_TOKEN is set}
38
-
39
- # Capability tier (derived from tool availability)
40
- # Quick = no tools | Forge = + ast-grep | Forge+ = + ast-grep + ccc | Deep = + ast-grep + gh + QMD
41
- tier: {calculated_tier}
42
- tier_detected_at: {current ISO timestamp}
43
-
44
- # CCC semantic index state (managed by setup step-01b and extraction workflows)
45
- ccc_index:
46
- indexed_path: {ccc_indexed_path from step-01b, or ~}
47
- last_indexed: {ccc_last_indexed from step-01b, or ~}
48
- status: {ccc_index_result from step-01b: "fresh"|"created"|"none"|"failed"}
49
- staleness_threshold_hours: 24
50
- file_count: {ccc_file_count from step-01b, or ~}
51
- exclude_patterns: {ccc_exclude_patterns from step-01b, or []}
52
-
53
- # CCC index registry (tracks which source paths have been indexed for skill workflows)
54
- # PRESERVE existing entries on re-runs — see Note below
55
- ccc_index_registry: {preserved from existing forge-tier.yaml, or [] if first run}
56
-
57
- # QMD collection registry (populated by create-skill, consumed by audit/update-skill)
58
- # PRESERVE existing entries on re-runs — see Note below
59
- qmd_collections: {preserved from existing forge-tier.yaml, or [] if first run}
34
+ Build the JSON payload from context flags set by step-01 and step-01b. The payload must include `tools`, `tier`, and `ccc_index`; the script handles `tier_detected_at` defaulting to "now" if absent and preserves `qmd_collections`, `ccc_index_registry`, and a user-customized `ccc_index.staleness_threshold_hours` from any existing file.
35
+
36
+ Invoke via `uv run` so the script's PEP 723 PyYAML dependency resolves automatically (this is what `docs/getting-started.md`'s uv prereq exists for). Bare `python3` would fail on a fresh interpreter with `ModuleNotFoundError`.
37
+
38
+ ```bash
39
+ echo '{
40
+ "tools": {
41
+ "ast_grep": {ast_grep},
42
+ "gh_cli": {gh_cli},
43
+ "qmd": {qmd},
44
+ "ccc": {ccc},
45
+ "ccc_daemon": {ccc_daemon},
46
+ "security_scan": {security_scan}
47
+ },
48
+ "tier": "{calculated_tier}",
49
+ "ccc_index": {
50
+ "indexed_path": {ccc_indexed_path},
51
+ "last_indexed": {ccc_last_indexed},
52
+ "status": "{ccc_index_result}",
53
+ "file_count": {ccc_file_count},
54
+ "exclude_patterns": {ccc_exclude_patterns}
55
+ }
56
+ }' | uv run {forgeTierRwHelper} write-tools \
57
+ --target "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml"
60
58
  ```
61
59
 
62
- **Note on re-runs:** The `qmd_collections`, `ccc_index_registry` arrays, and `staleness_threshold_hours` value must be preserved across re-runs. Before overwriting forge-tier.yaml, read these existing values and re-inject them into the new write. These values are populated by create-skill workflows or customized by users and must not be reset. Note: `exclude_patterns` is NOT preserved it is always written fresh from `{ccc_exclude_patterns}` computed by step-01b.
63
-
64
- **This file is ALWAYS overwritten** on every run — it reflects current tool state.
65
-
66
- If the write fails, report the error and halt the workflow.
60
+ The script atomically writes the file via temp + fsync + rename (mirrors `skf-atomic-write.py`'s crash-safety contract) and returns a JSON response with `wrote`, `preserved_arrays.qmd_collections` count, `preserved_arrays.ccc_index_registry` count, and the resolved `tier`.
67
61
 
68
- ### 2. Handle preferences.yaml
62
+ **Parse the response and set context flags for step-04:**
69
63
 
70
- Check if `{project-root}/_bmad/_memory/forger-sidecar/preferences.yaml` exists:
64
+ - `{forge_tier_yaml_path}` ← `wrote`
65
+ - `{forge_tier_qmd_collections_count}` ← `preserved_arrays.qmd_collections`
66
+ - `{forge_tier_ccc_registry_count}` ← `preserved_arrays.ccc_index_registry`
71
67
 
72
- **If it does NOT exist (first run):** Create with defaults:
68
+ **If the script exits non-zero**: parse the stderr JSON `{"status":"error","message":...}`, set `{error: {phase: "step-02:write-tools", path: "{project-root}/_bmad/_memory/forger-sidecar/forge-tier.yaml", reason: <message>}}` for step-04's envelope, and halt the workflow before chaining to step-03.
73
69
 
74
- ```yaml
75
- # Ferris Sidecar: User Preferences
76
- # Created by setup workflow on first run
77
- # Edit this file to customize Ferris behavior
70
+ ### 2. Initialize preferences.yaml
78
71
 
79
- # Override detected tier (set to Quick, Forge, Forge+, or Deep to force a tier)
80
- tier_override: ~
81
-
82
- # Passive context injection (set to false to skip snippet generation and CLAUDE.md updates during export)
83
- passive_context: true
72
+ ```bash
73
+ uv run {forgeTierRwHelper} init-prefs \
74
+ --target "{project-root}/_bmad/_memory/forger-sidecar/preferences.yaml"
75
+ ```
84
76
 
85
- # Headless mode (set to true to skip confirmation gates in all workflows)
86
- headless_mode: false
77
+ The script creates the file with first-run defaults (matching the prior inline template — `tier_override: ~`, `passive_context: true`, `headless_mode: false`, `compact_greeting: false`, plus reserved-for-future-use commented fields) IF the file does not exist. When the file already exists, the script refuses to overwrite (preserves user customization) and reports `wrote: false`.
87
78
 
88
- # Compact greeting (set to true to skip the full capabilities table on session start)
89
- compact_greeting: false
79
+ **Parse the response and set context flags for step-04:**
90
80
 
91
- # Reserved for future use these fields are not yet consumed by any workflow step
92
- # output_language: ~
93
- # skill_format_version: ~
94
- # citation_style: ~
95
- # confidence_display: ~
96
- ```
81
+ - `{preferences_yaml_created}` `wrote` (true on first run, false on re-run when the file pre-existed)
97
82
 
98
- **If it DOES exist:** Do not modify. Preserve entirely.
83
+ **If the script exits non-zero**: same halt-and-report pattern as section 1, with `phase: "step-02:init-prefs"` and the matching path.
99
84
 
100
85
  ### 3. Ensure forge-data/ Directory
101
86
 
102
87
  Check if `{forge_data_folder}` directory exists:
103
88
 
104
- - If missing: create it
105
- - If exists: skip silently
89
+ - If missing: create it. Store `{forge_data_dir_created: true}` in context.
90
+ - If exists: skip silently. Store `{forge_data_dir_created: false}` in context.
106
91
 
107
- ### 4. Auto-Proceed
92
+ If the create fails (parent unwritable, disk full, permission denied), set `{error: {phase: "step-02:forge-data-dir", path: "{forge_data_folder}", reason: <error message>}}` for step-04's envelope and halt the workflow.
108
93
 
109
- "**Proceeding to QMD collection hygiene...**"
110
-
111
- #### Menu Handling Logic:
112
-
113
- - After all file operations complete successfully, immediately load, read entire file, then execute {nextStepFile}
114
-
115
- #### EXECUTION RULES:
116
-
117
- - This is an auto-proceed step with no user choices
118
- - Proceed directly to next step after configuration is written
119
-
120
- ## CRITICAL STEP COMPLETION NOTE
121
-
122
- ONLY WHEN forge-tier.yaml has been written successfully and preferences.yaml exists (created or pre-existing) will you load and read fully `{nextStepFile}` to execute the QMD hygiene step.
94
+ ### 4. Auto-Proceed
123
95
 
96
+ After forge-tier.yaml has been written successfully and preferences.yaml exists (created or pre-existing), display "**Proceeding to QMD collection hygiene...**", then load `{nextStepFile}`, read it fully, and execute it.