Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions .buildforce/context/_index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ version: "2.1"

# Timestamps (populated by /buildforce.extract on first run)
generated_at: "2026-02-02"
last_updated: "2026-02-02"
last_updated: "2026-02-09"

# ============================================================================
# CODEBASE PROFILE
Expand Down Expand Up @@ -262,10 +262,28 @@ domains:
file: architecture/extract-command.yaml
type: structural
status: extracted
depth: deep
description: Two-layer context extraction system with skill-based architecture and three unified modes
tags: [ slash-command, skill, extract, context-mining, sub-agents, claude-code-only, coverage-map, stop-hook, incremental ]
related_context: [ slash-commands, cli-architecture, upgrade-command, skills-bundling, settings-merge-utility ]

- id: settings-merge-utility
file: architecture/settings-merge-utility.yaml
type: structural
status: extracted
depth: shallow
description: TypeScript utility for merging Claude Code settings with additive deep-merge strategy
tags: [ utility, settings, claude-code, merge, hooks, permissions ]
related_context: [ upgrade-command, cli-architecture ]

- id: skills-bundling
file: architecture/skills-bundling.yaml
type: structural
status: extracted
depth: moderate
description: Claude Code-specific slash command for iterative context extraction from fresh codebases
tags: [ slash-command, extract, context-mining, sub-agents, claude-code-only, coverage-map ]
related_context: [ slash-commands, cli-architecture, upgrade-command ]
description: Release packaging system for bundling Claude Code skills with agent filtering and extract skill integration
tags: [ release-packaging, skills, claude-code, bundling, agent-filtering, extract-skill-deployment ]
related_context: [ upgrade-command, cli-architecture, settings-merge-utility, extract-command ]

conventions:
description: |
Expand Down Expand Up @@ -427,9 +445,9 @@ domains:

summary:
overall_coverage: 100
total_items: 35
extracted_items: 35
iterations_completed: 2
total_items: 37
extracted_items: 37
iterations_completed: 3

# ============================================================================
# EXTRACTION STATE
Expand All @@ -440,15 +458,17 @@ summary:
extraction:
needs_clarification:
- "Should the migration system (MigrationRunner) be documented as a separate context item?"
- "Are there plans to add automated testing? Should we document target test framework preferences?"
- "Should linting tools (ESLint/Prettier) be added to the quality gates?"
- "Should the vitest setup be completed (add vitest to package.json deps, update test script)?"
recommended_focus:
- id: build-command
reason: "Core workflow command - could benefit from moderate depth with extension points"
- id: plan-command
reason: "Core workflow command - could benefit from moderate depth with state flow details"
- id: complete-command
reason: "Core workflow command - could benefit from moderate depth with context creation details"
- id: settings-merge-utility
reason: "Key enabler for skill activation - could benefit from moderate depth documenting the hook configuration flow"
new_discoveries:
- id: migration-runner
domain: architecture
Expand All @@ -466,3 +486,7 @@ extraction:
domain: architecture
description: "Sophisticated release automation with version calculation and npm publishing"
priority: low
- id: skill-delegation-pattern
domain: conventions
description: "Convention for commands delegating to skills for multi-entry-point activation (command + hook)"
priority: medium
293 changes: 242 additions & 51 deletions .buildforce/context/architecture/extract-command.yaml

Large diffs are not rendered by default.

139 changes: 139 additions & 0 deletions .buildforce/context/architecture/settings-merge-utility.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
id: settings-merge-utility
name: Settings Merge Utility
type: structural
status: production
created: "2026-02-04"
last_updated: "2026-02-04"

summary: |
TypeScript utility for merging Claude Code settings from template configuration into
user's .claude/settings.local.json file. Uses additive deep-merge strategy that
preserves all existing user permissions and hooks while adding new entries from
templates. Deduplicates arrays using JSON.stringify comparison to ensure idempotent
operations across multiple init/upgrade cycles.

responsibilities:
- Merge template hooks/config.json into .claude/settings.local.json
- Preserve existing user permissions (allow, deny, ask arrays)
- Preserve existing user hooks (Stop, PreToolUse, PostToolUse arrays)
- Deduplicate array entries using JSON.stringify comparison
- Create settings.local.json if it doesn't exist
- Handle edge cases gracefully (missing template, malformed JSON)
- Provide debug logging when enabled
- Return detailed merge result for progress tracking

dependencies:
internal:
- init-command: "Calls mergeClaudeSettings after template extraction"
- upgrade-command: "Calls mergeClaudeSettings after template replacement"
external:
- fs-extra: "^11.2.0 - File system operations (readFile, writeFile, pathExists, ensureDir)"
- chalk: "^5.4.1 - Debug logging with colored output"

files:
primary:
- src/utils/settings-merge.ts
secondary:
- src/commands/init/setup.ts
- src/commands/upgrade/execution.ts

interfaces:
exports:
- name: "mergeClaudeSettings"
signature: "(projectPath: string, templateConfigPath: string, options?: { debug?: boolean }) => Promise<MergeResult>"
description: "Merges template hooks configuration into user's Claude Code settings"

- name: "MergeResult"
signature: "interface { merged: boolean; skipped: boolean; reason: string; hooksAdded?: number; permissionsAdded?: number }"
description: "Result object describing what was done during merge"

design_decisions:
- decision: "Use additive-only merge strategy (never removes existing data)"
rationale: "User permissions and hooks represent accumulated approvals and customizations. Removing them would break workflows and require re-approval. Safety over cleanup."

- decision: "Deduplicate arrays using JSON.stringify comparison"
rationale: "Simple and reliable for comparing hook objects. Multiple upgrade cycles shouldn't add duplicate hooks. Performance is acceptable since arrays are small."

- decision: "Template config contains hooks content directly, not full settings structure"
rationale: "Keeps template file focused on what Buildforce needs (hooks). The utility wraps it in settings structure for merging. Separation of concerns."

- decision: "Skip merge gracefully if template config doesn't exist"
rationale: "Not all releases may include hooks. Graceful degradation prevents upgrade failures when hooks aren't needed."

- decision: "No backup file creation"
rationale: "Merge is additive-only and git tracks changes. Backup adds complexity without significant value since no data is lost."

merge_algorithm: |
1. Read template config from .buildforce/templates/hooks/config.json
- Template contains hooks content directly: { "Stop": [...], "PreToolUse": [...] }
- Wrap in settings structure: { hooks: templateContent }

2. Read existing settings from .claude/settings.local.json
- If file doesn't exist: start with empty object {}
- If file is malformed: log warning, start with empty object {}

3. Merge permissions (if present in template):
- permissions.allow: concat arrays, deduplicate
- permissions.deny: concat arrays, deduplicate
- permissions.ask: concat arrays, deduplicate

4. Merge hooks (if present in template):
- For each hook type (Stop, PreToolUse, PostToolUse):
- Concat existing array with incoming array
- Deduplicate using JSON.stringify comparison

5. Write merged result to .claude/settings.local.json
- Pretty-print with 2-space indent
- Add trailing newline

data_models:
- name: "ClaudeSettings"
description: "Claude Code settings file structure"
properties:
- name: permissions
type: "{ allow?: string[], deny?: string[], ask?: string[] }"
description: "Tool permission rules"
- name: hooks
type: "{ Stop?: unknown[], PreToolUse?: unknown[], PostToolUse?: unknown[] }"
description: "Event hook configurations"

- name: "MergeResult"
description: "Result returned by mergeClaudeSettings"
properties:
- name: merged
type: boolean
description: "True if merge was performed"
- name: skipped
type: boolean
description: "True if merge was skipped"
- name: reason
type: string
description: "Explanation of what happened"
- name: hooksAdded
type: number
description: "Count of hooks in template config"
- name: permissionsAdded
type: number
description: "Count of permissions in template config"

evolution:
- version: "1.0"
date: "2026-02-04"
changes: "Initial implementation with additive deep-merge, array deduplication, and graceful edge case handling"

related_specs:
- skills-bundling-config-concat-20260204000000

notes: |
This utility enables automatic activation of Buildforce features by ensuring
the Stop hook is configured to call the buildforce-context-extract skill.

The merge is designed to be:
- Safe: Never removes user data
- Idempotent: Multiple runs don't create duplicates
- Transparent: Debug logging shows what was done
- Resilient: Graceful handling of missing/malformed files

Future enhancements:
- Support for merging other Claude Code settings sections if needed
- Version tracking to detect when template config changes
145 changes: 145 additions & 0 deletions .buildforce/context/architecture/skills-bundling.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
id: skills-bundling
name: Skills Bundling System
type: structural
status: production
created: "2026-02-04"
last_updated: "2026-02-09"

summary: |
Release packaging functionality that bundles Claude Code skills from src/templates/skills/
into .claude/skills/ in release ZIP packages. Skills are stored as directories containing
SKILL.md files with YAML frontmatter for agent filtering and skill configuration.
Follows the same pattern as generate_agents() for consistency. Currently Claude-only feature.
Primary consumer: buildforce-context-extract skill for automatic context extraction.

responsibilities:
- Bundle skill folders from src/templates/skills/ into release packages
- Support agent filtering via 'agents:' YAML frontmatter in SKILL.md
- Preserve entire skill folder structure (SKILL.md plus any additional files)
- Deploy skills to .claude/skills/ directory in target project
- Exclude skills folder from .buildforce/templates/ copy (avoid duplication)
- Log filtering decisions during package generation

dependencies:
internal:
- cli-architecture: "Part of create-release-packages.sh workflow"
- upgrade-command: "Skills are replaced during upgrade"
- init-command: "Skills are deployed during init"
external:
- bash: "Shell scripting in create-release-packages.sh"
- awk: "YAML frontmatter parsing for agent filtering"
- grep: "Word boundary matching for agent name detection"

files:
primary:
- .github/workflows/scripts/create-release-packages.sh
secondary:
- src/templates/skills/buildforce-context-extract/SKILL.md
- src/commands/upgrade/execution.ts
- src/commands/init/setup.ts

interfaces:
exports:
- name: "generate_skills"
signature: "(agent: string, output_dir: string) => void"
description: "Bash function that copies skill folders to output directory with agent filtering"

design_decisions:
- decision: "Follow generate_agents() pattern for consistency"
rationale: "Agents and skills have similar bundling requirements. Reusing the proven pattern reduces complexity and ensures consistent behavior."

- decision: "Skills are Claude-only feature (for now)"
rationale: "Only Claude Code supports skills in .claude/skills/. Other AI assistants don't have equivalent skill systems. Can be extended when other agents add skill support."

- decision: "Copy entire skill folder, not just SKILL.md"
rationale: "Skills may contain additional files (examples, schemas, etc.). Preserving folder structure allows for richer skill definitions in the future."

- decision: "Support agent filtering via frontmatter"
rationale: "Same pattern as commands and agents. Allows skills to be agent-specific (e.g., a skill that only works with Claude's tool system)."

- decision: "Exclude skills from .buildforce/templates/ copy"
rationale: "Skills go directly to .claude/skills/, not to templates. Prevents duplication and confusion about where skills live."

skill_structure: |
src/templates/skills/
└── {skill-name}/
└── SKILL.md # Required - skill definition with optional frontmatter
└── [other files] # Optional - examples, schemas, etc.

SKILL.md Frontmatter (optional):
---
name: skill-name
description: Brief description
agents: [claude] # Optional - if present, only bundle for listed agents
---

Output in release package:
.claude/skills/
└── {skill-name}/
└── SKILL.md
└── [other files]

bundling_flow: |
1. create-release-packages.sh runs generate_skills() for Claude agent
2. For each directory in src/templates/skills/:
a. Check if SKILL.md exists (skip if not)
b. Read SKILL.md content and parse frontmatter
c. Check 'agents:' field - if present and doesn't include current agent, skip
d. Copy entire skill folder to output_dir/{skill-name}/
e. Log filtering/copying decisions
3. build_variant() excludes skills from .buildforce/templates/ copy
4. ZIP package contains .claude/skills/{skill-name}/SKILL.md

agent_filtering: |
Agent filtering logic (same as generate_agents):

1. Read SKILL.md content with line ending normalization
2. Extract 'agents:' field from YAML frontmatter using awk
3. If 'agents:' field exists:
- Use word boundary matching (grep -w) to check if current agent is in list
- If not found: skip skill with log message "[filter] Skipping skill X for Y"
- If found: include skill with log message "[filter] Including skill X for Y"
4. If 'agents:' field missing: include skill for all agents

Examples:
agents: [claude] → Only bundle for claude
agents: [claude, cursor] → Bundle for claude and cursor
(no agents field) → Bundle for all agents

evolution:
- version: "1.0"
date: "2026-02-04"
changes: "Initial implementation with generate_skills() function, agent filtering, and build_variant() integration for Claude"

related_specs:
- skills-bundling-config-concat-20260204000000

extract_skill_integration: |
The skills bundling system is critical to the extract skill deployment chain:
1. Source: src/templates/skills/buildforce-context-extract/SKILL.md (with YAML frontmatter)
2. Bundling: generate_skills() copies to .claude/skills/buildforce-context-extract/ in release ZIP
3. Deployment: init/upgrade extracts to user's .claude/skills/ directory
4. Activation: Stop hook (configured by settings-merge) calls the skill after conversations
5. Command: extract.md thin wrapper also delegates to the skill for manual invocation

The SKILL.md frontmatter includes:
- name: buildforce-context-extract
- user-invocable: true
- context: fork
- allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, Task]

notes: |
Skills bundling enables the full auto-context-extractor workflow by ensuring
the buildforce-context-extract skill is deployed to .claude/skills/ during
init and upgrade.

The skills system works in tandem with settings merge:
1. Skills bundling deploys the skill to .claude/skills/
2. Settings merge configures the Stop hook to call the skill

Without both pieces, the skill exists but never activates.

Future enhancements:
- Support skills bundling for other agents when they add skill support
- Add skill version tracking
- Support skill dependencies (one skill requiring another)
Loading