bmad-module-skill-forge 1.1.0 → 1.2.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.
@@ -7,7 +7,7 @@ description: Initialize forge environment, detect tools, and set capability tier
7
7
 
8
8
  ## Overview
9
9
 
10
- Initializes the forge environment by detecting available tools, determining the capability tier (Quick/Forge/Forge+/Deep), writing persistent configuration, and optionally indexing the project for deep search. This is a fully autonomous workflow — no user interaction is required during execution.
10
+ Initializes the forge environment by detecting available tools, determining the capability tier (Quick/Forge/Forge+/Deep), and writing persistent configuration to `{project-root}/_bmad/_memory/forger-sidecar/`. When `ccc` (cocoindex-code) is available, also augments `.cocoindex_code/settings.yml` with SKF exclusion patterns and creates or refreshes the project's semantic-search index. On Deep tier, reconciles the QMD collection registry; whenever ccc is available, reconciles the CCC index registry as well. The workflow is autonomous with one optional gate orphaned QMD collection removal in step 3 (Deep tier only; default action: Keep) — which auto-resolves to the default when `{headless_mode}` is true.
11
11
 
12
12
  ## Role
13
13
 
@@ -17,7 +17,7 @@ You are a system executor performing environment resolution. Run each step in se
17
17
 
18
18
  These rules apply to every step in this workflow:
19
19
 
20
- - Fully autonomous — all steps auto-proceed with no user interaction until the final report
20
+ - Autonomous with one optional gate (step 3 orphan-removal prompt; default: Keep) — all other steps auto-proceed with no user interaction until the final report
21
21
  - Read each step file completely before taking any action
22
22
  - Follow the mandatory sequence in each step exactly — do not skip, reorder, or optimize
23
23
  - Only load one step file at a time — never preload future steps
@@ -29,9 +29,9 @@ These rules apply to every step in this workflow:
29
29
  | # | Step | File | Auto-proceed |
30
30
  |---|------|------|--------------|
31
31
  | 1 | Detect Tools & Set Tier | steps-c/step-01-detect-and-tier.md | Yes |
32
- | 1b | CCC Index | steps-c/step-01b-ccc-index.md | Yes |
32
+ | 1b | CCC Index (only when ccc is available) | steps-c/step-01b-ccc-index.md | Yes |
33
33
  | 2 | Write Config | steps-c/step-02-write-config.md | Yes |
34
- | 3 | QMD Hygiene | steps-c/step-03-auto-index.md | Yes |
34
+ | 3 | QMD + CCC Registry Hygiene | steps-c/step-03-auto-index.md | Yes |
35
35
  | 4 | Report | steps-c/step-04-report.md | Yes |
36
36
  | 5 | Workflow Health Check | steps-c/step-05-health-check.md | Yes |
37
37
 
@@ -39,17 +39,32 @@ These rules apply to every step in this workflow:
39
39
 
40
40
  | Aspect | Detail |
41
41
  |--------|--------|
42
- | **Inputs** | (none — fully autonomous) |
42
+ | **Inputs** | (none) |
43
+ | **Flags** | `--headless` / `-H` (skip prompts, auto-resolve gates to defaults); `--require-tier=<Quick\|Forge\|Forge+\|Deep>` (halt with failure if calculated tier does not satisfy the requirement) |
43
44
  | **Gates** | One optional: orphaned QMD collection removal (step 3, Deep tier only; default: Keep) |
44
- | **Outputs** | forge-tier.yaml, preferences.yaml, forge-data directories |
45
- | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true |
45
+ | **Outputs** | `forger-sidecar/forge-tier.yaml`, `forger-sidecar/preferences.yaml`, `{forge_data_folder}/`; when ccc is available, `.cocoindex_code/settings.yml` (exclusion patterns merged) and the project ccc index |
46
+ | **Headless** | All gates auto-resolve with default action when `{headless_mode}` is true. step-04 emits a single-line `SKF_SETUP_RESULT_JSON: {…}` envelope after the human-readable banner so pipelines can parse the outcome without reading forge-tier.yaml. Schema documented in `steps-c/step-04-report.md` §4. |
47
+ | **Failure modes** | `--require-tier` not satisfied → step-04 prints a "REQUIRED TIER NOT MET" block, the JSON envelope sets `"require_tier_satisfied": false`, and the workflow halts before step-05. |
48
+ | **Exit codes** | The workflow runs as an LLM-driven sequence rather than a CLI process, so "exit code" describes the agent's terminal state for a calling pipeline that reads `SKF_SETUP_RESULT_JSON`. **0 — success** (no JSON `error` field, all writes completed, `require_tier_satisfied` is `true` or `null`). **1 — required-tier failure** (`require_tier_satisfied` is `false`; envelope still emitted with full state for diagnosis). **2 — write failure** (forge-tier.yaml or preferences.yaml could not be written; envelope `error` field names the path and reason). Pipelines should branch on the JSON `require_tier_satisfied` and `error` fields rather than process exit codes. |
46
49
 
47
50
  ## On Activation
48
51
 
49
- 1. Load config from `{project-root}/_bmad/skf/config.yaml` and resolve:
52
+ 1. **Probe `uv` runtime.** Run `uv --version`. Every step in this workflow invokes shared Python helpers via `uv run` (PEP 723 inline metadata is what auto-resolves `pyyaml` for the helpers that need it — see `docs/getting-started.md`). If `uv` is missing, halt now with a single cohesive diagnostic rather than letting five separate steps each fail with `uv: command not found`:
53
+
54
+ "**Setup cannot proceed: `uv` is not installed.** SKF helpers depend on `uv` to auto-resolve their Python dependencies. Install it from <https://docs.astral.sh/uv/getting-started/installation/> and re-run `/skf-setup`. (See the Prerequisites section in <https://docs.astral.sh/uv/getting-started/installation/> or the SKF docs at `docs/getting-started.md` for details.)"
55
+
56
+ 2. Load config from `{project-root}/_bmad/skf/config.yaml` and resolve:
50
57
  - `project_name` (from installer-generated config.yaml, not module.yaml), `output_folder`, `user_name`, `communication_language`, `document_output_language`
51
58
  - `skills_output_folder`, `forge_data_folder`, `sidecar_path`
52
59
 
53
- 2. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in preferences.yaml. Default: false.
60
+ **If the file does not exist**, halt with a single cohesive "wrong directory" diagnostic rather than failing later with a generic file-read error from a downstream step:
61
+
62
+ "**Setup cannot proceed: `{project-root}/_bmad/skf/config.yaml` was not found.** This directory does not appear to be an SKF-initialised project. From the project root, run `npx bmad-module-skill-forge install` (or `npx bmad-method install` and add SKF as a custom module — see `docs/getting-started.md`), then re-run `/skf-setup`. If you ARE in the right project but the file was deleted, restore it from version control or re-run the SKF installer."
63
+
64
+ **If the file exists but is malformed YAML**, halt and surface the parser error along with the file path so the user can fix it directly: "**Setup cannot proceed: `{project-root}/_bmad/skf/config.yaml` is not valid YAML.** Parser error: {error_message}. Open the file and repair the YAML, or restore from version control."
65
+
66
+ 3. **Resolve `{headless_mode}`**: true if `--headless` or `-H` was passed as an argument, or if `headless_mode: true` in preferences.yaml. Default: false.
67
+
68
+ 4. **Resolve `{require_tier}`**: parse `--require-tier=<value>` from the invocation arguments. Accept exactly `Quick`, `Forge`, `Forge+`, or `Deep` (case-sensitive). If absent or unparseable, leave as null (no tier requirement).
54
69
 
55
- 3. Load, read the full file, and then execute `./steps-c/step-01-detect-and-tier.md` to begin the workflow.
70
+ 5. Load, read the full file, and then execute `./steps-c/step-01-detect-and-tier.md` to begin the workflow.
@@ -7,9 +7,9 @@
7
7
  | ast-grep | `ast-grep --version` | Returns version string without error |
8
8
  | gh | `gh --version` | Returns version string without error |
9
9
  | qmd | `qmd status` | Returns status indicating initialized and operational |
10
- | ccc | Step A: `ccc --help` Step B: `ccc doctor` | Step A: exits 0 (binary exists). Step B: daemon running, version string, model check OK |
10
+ | ccc | Step A: `ccc --help` Step B: `ccc doctor` | Step A: exits 0 AND help output identifies the binary as cocoindex-code. Step B: daemon healthy. See `steps-c/step-01-detect-and-tier.md` §7 for the full identity-marker procedure. |
11
11
 
12
- **Important:** Use verification commands, not existence checks (`which`, `command -v`). A tool must be functional, not just present on PATH. For daemon-based tools (ccc), verify both binary existence and daemon health.
12
+ **Important:** Use verification commands, not existence checks (`which`, `command -v`). A tool must be functional, not just present on PATH. For daemon-based tools (ccc), verify both binary identity and daemon health — a binary with the right name but the wrong implementation is a false positive, not a tool.
13
13
 
14
14
  ## Tier Calculation
15
15
 
@@ -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.