skill-builder
Use BEFORE creating or updating any skill OR command - decomposes goal into forward steps via backward reasoning
From catnpx claudepluginhub cowwoc/cat --plugin catThis skill uses the workspace's default tool permissions.
Skill Builder
Purpose
Design or update skills and commands by reasoning backward from the goal to required preconditions, then converting to forward-execution steps.
When to Use
- Creating a new skill or command
- Updating an existing skill or command that has unclear or failing steps
- Any procedure where the goal is clear but the path is not
Note: Both skills/ and commands/ are agent-facing prompt files that define behavior.
Use skill-builder for BOTH types.
Core Principle
Backward chaining: Start with what you want to be true, repeatedly ask "what must be true for this?", until you reach conditions you can directly achieve. Then reverse to get executable steps.
GOAL ← requires ← requires ← ... ← ATOMIC_ACTION
↓
(reverse)
↓
ATOMIC_ACTION → produces → produces → ... → GOAL
Procedure
Step 1: State the Goal
Write a single, verifiable statement of the desired end state.
Format:
GOAL: [Observable condition that indicates success]
Criteria for good goal statements:
- Observable: Can be verified by inspection or test
- Specific: No ambiguity about what "done" means
- Singular: One condition (decompose compound goals first)
Examples:
GOAL: All right-side │ characters in the box align vertically
GOAL: The function returns the correct sum for all test cases
GOAL: The user sees a confirmation message after submission
Step 2: Backward Decomposition
For each condition (starting with the goal), ask: "What must be true for this?"
Format:
CONDITION: [what we want]
REQUIRES: [what must be true for the condition]
REQUIRES: [another thing that must be true]
Rules:
- Each REQUIRES is a necessary precondition
- Multiple REQUIRES under one CONDITION means ALL must be true (AND)
- Continue decomposing until you reach atomic conditions
Atomic condition: A condition that can be directly achieved by a single action or is a given input/fact.
Example decomposition:
GOAL: Right borders align
REQUIRES: All lines have identical display width
REQUIRES: Each line follows the formula: width = content + padding + borders
REQUIRES: padding = max_content - this_content
REQUIRES: max_content is known
REQUIRES: display_width calculated for all content items
REQUIRES: emoji widths handled correctly
ATOMIC: Use width table (emoji → width mapping)
REQUIRES: all content items identified
ATOMIC: List all content strings
REQUIRES: this_content display_width is known
(same as above - shared requirement)
REQUIRES: borders add fixed width (4)
ATOMIC: Use "│ " prefix (2) and " │" suffix (2)
Step 3: Identify Leaf Nodes
Extract all ATOMIC conditions from the decomposition tree. These are your starting points.
Format:
LEAF NODES (atomic conditions):
1. [First atomic condition]
2. [Second atomic condition]
...
Step 4: Build Dependency Graph
Determine the order in which conditions can be satisfied based on their dependencies.
Rules:
- A condition can only be satisfied after ALL its REQUIRES are satisfied
- Conditions with no REQUIRES (leaf nodes) can be done first
- Multiple conditions at the same level can be done in parallel (or any order)
Format:
DEPENDENCY ORDER:
Level 0 (no dependencies): [atomic conditions]
Level 1 (depends on L0): [conditions requiring only L0]
Level 2 (depends on L1): [conditions requiring L0 and/or L1]
...
Level N: GOAL
Step 5: Extract Reusable Functions
Scan the decomposition tree for patterns that should become functions.
Extract a function when:
- Same logic appears multiple times in the tree (even with different inputs)
- Recursive structure: The same pattern applies at multiple nesting levels
- Reusable calculation: A computation that transforms input → output cleanly
Function identification signals:
Pattern A: Repeated subtree
REQUIRES: X for item A
REQUIRES: Y for A
ATOMIC: Z
REQUIRES: X for item B ← Same structure, different input
REQUIRES: Y for B
ATOMIC: Z
→ Extract: function X(item) that does Y and Z
Pattern B: Recursive structure
REQUIRES: process outer container
REQUIRES: process inner container ← Same pattern, nested
REQUIRES: process innermost ← Same pattern again
→ Extract: function process(container) that calls itself for nested containers
Pattern C: Transform chain
REQUIRES: result C
REQUIRES: intermediate B from A
REQUIRES: input A
→ Extract: function transform(A) → C
Function definition format:
FUNCTIONS:
function_name(inputs) → output
Purpose: [what it computes]
Logic: [derived from the decomposition subtree]
Used by: [which steps will call this]
Composition rules:
- Functions can call other functions
- Order function definitions so dependencies come first
- For recursive functions, define the base case and recursive case
Deriving logic for variable-length inputs:
When a function operates on a collection of arbitrary length, derive the algorithm by:
- Minimum case: Solve for the smallest valid input (often length 1)
- Next increment: Solve for length 2 (or next meaningful size)
- Generalize: Identify the pattern that extends to length N
Example: max_content_width(contents[])
Length 1: contents = ["Hello"]
max = display_width("Hello") = 5
→ For single item, max is just that item's width
Length 2: contents = ["Hello", "World!"]
w1 = display_width("Hello") = 5
w2 = display_width("World!") = 6
max = larger of w1, w2 = 6
→ For two items, compare and take larger
Length N: contents = [c1, c2, ..., cN]
→ Pattern: compare each item's width, keep the largest
→ General: max(display_width(c) for c in contents)
Example: build_box(contents[])
Length 1: contents = ["Hi"]
Lines needed: top border, one content line, bottom border
Width: display_width("Hi") + 4 = 6
→ Single item: frame around one line
Length 2: contents = ["Hi", "Bye"]
Lines needed: top, content1, content2, bottom
Width: max(display_width("Hi"), display_width("Bye")) + 4
→ Two items: both must fit in same width frame
Length N:
→ Pattern: all content lines share same width (the maximum)
→ General: find max width, pad each line to that width, add frame
This technique prevents over-generalization and ensures the algorithm handles edge cases.
Example - Box alignment functions:
FUNCTIONS:
display_width(text) → integer
Purpose: Calculate terminal display width of text
Logic: Sum character widths (emoji=2, others=1)
Used by: max_content_width, padding calculation
max_content_width(contents[]) → integer
Purpose: Find maximum display width among content items
Logic: max(display_width(c) for c in contents)
Used by: box_width, padding calculation
box_width(contents[]) → integer
Purpose: Calculate total box width including borders
Logic: max_content_width(contents) + 4
Used by: border construction, nested box embedding
build_box(contents[]) → string[]
Purpose: Construct complete box with aligned borders
Logic:
1. mw = max_content_width(contents)
2. for each content: line = "│ " + content + padding(mw - display_width(content)) + " │"
3. top = "╭" + "─"×(mw+2) + "╮"
4. bottom = "╰" + "─"×(mw+2) + "╯"
5. return [top] + lines + [bottom]
Used by: outer box construction, nested box construction (recursive)
Step 6: Convert to Forward Steps
Transform the dependency order into executable procedure steps, using extracted functions.
Rules:
- Start with Level 0 (leaf nodes)
- Progress through levels toward the goal
- Each step should be a concrete action, not a state description
- Call functions instead of repeating logic
- For recursive operations, the step invokes the function which handles recursion internally
- Include verification where applicable
Format:
PROCEDURE:
1. [Action to achieve leaf condition 1]
2. [Action to achieve leaf condition 2]
3. Call function_a(inputs) to achieve Level 1 condition
4. For each item: call function_b(item) ← Function handles repeated application
5. Call function_c(nested_structure) ← Function handles recursion internally
...
N. [Final action that achieves the GOAL]
VERIFICATION:
- [How to confirm the goal is met]
Composing functions in steps:
# Instead of inline logic:
BAD: "Calculate width by counting characters and adding 1 for each emoji"
GOOD: "Call display_width(text) to get the terminal width"
# Instead of repeated steps:
BAD: "Calculate width for item 1, then for item 2, then for item 3..."
GOOD: "For each content item, call display_width(item)"
# Instead of manual recursion:
BAD: "Build inner box, then embed in outer box, checking alignment..."
GOOD: "Call build_box(contents) - function handles nesting internally"
Step 7: Write the Skill
Structure the skill document with:
- Frontmatter: YAML with name and trigger-oriented description
- Purpose: The goal statement
- Functions: Reusable calculations extracted in Step 5
- Procedure: The forward steps from Step 6, calling functions as needed
- Verification: How to confirm success
Frontmatter description must be trigger-oriented:
The description tells WHEN to invoke the skill, not just what it does.
Format: "[WHEN to use] - [what it does]"
Examples:
"MANDATORY: Load BEFORE rendering any box output"
"Use BEFORE creating or updating any skill - decomposes goal into forward steps"
"MANDATORY: Use for approval gate reviews - transforms git diff into table"
"Use when user requests git rebase - provides automatic backup and recovery"
Trigger patterns:
MANDATORY: Load BEFORE [action]- Must be loaded before doing somethingMANDATORY: Use for [situation]- Required for specific scenariosUse BEFORE [action]- Should be used before doing somethingUse when [condition]- Triggered by a specific situationUse instead of [alternative]- Replaces a dangerous or complex operation
Template Output
---
name: [skill-name]
description: "[WHEN to use] - [what it does]"
---
# [Skill Name]
## Purpose
[Goal statement from Step 1]
---
## Prerequisites
[Any atomic conditions that are external inputs or assumptions]
---
## Functions
List functions in dependency order (functions with no dependencies first).
### function_name(inputs) → output
**Purpose**: [What it computes]
**Definition**:
[Algorithm or formula derived from decomposition]
**Example**:
function_name(example_input) = expected_output
### composed_function(inputs) → output
**Purpose**: [Higher-level operation that uses other functions]
**Definition**:
- intermediate = other_function(inputs)
- result = transform(intermediate)
- return result
### recursive_function(structure) → output
**Purpose**: [Operation that applies to nested structures]
**Definition**:
Base case: if structure is atomic, return direct_result Recursive case:
- Process current level
- For each nested element: recursive_function(element)
- Combine results
---
## Procedure
Steps call functions rather than duplicating logic.
### Step 1: Require pre-computed results (if computation was extracted)
**MANDATORY:** Check context for "PRE-COMPUTED [SKILL-NAME]".
If found:
1. Output the pre-computed content **directly without preamble or commentary**
2. Skip to Step N (output/verification)
**Silent output rule (M194):** When outputting pre-computed content, do NOT:
- Announce "I can see the pre-computed display..."
- Explain "Let me output this exactly as provided..."
- Add any text before the pre-computed content
Simply output the content. The user doesn't need to know about internal computation mechanisms.
If NOT found: **FAIL immediately**.
```bash
"${CLAUDE_PLUGIN_ROOT}/scripts/check-hooks-loaded.sh" "results" "the skill"
if [[ $? -eq 0 ]]; then
echo "ERROR: Pre-computed results not found."
echo "Hook precompute-{skill-name}.sh should have provided these."
fi
Output the error and STOP. Do NOT attempt manual computation.
Step 2: [Gather inputs] (only if no extraction)
[Collect data needed by functions]
Step 3: [Apply function]
Call function_name(inputs) to compute [result].
MANDATORY CALCULATION GATE:
Before proceeding, you MUST show:
-
List each item with its computed value:
[item]: [computation] = [result] -
State aggregate if applicable:
[aggregate] = [value]
BLOCKING: Do NOT proceed until calculations are written out.
Step N: [Final assembly / output]
[Combine results to achieve goal]
Verification
- [Checkable condition that confirms goal is met]
---
## Example 1: Simple Skill (Rectangle Calculator)
**Task**: Create a skill for calculating the area of a rectangle from user input.
### Step 1: Goal
GOAL: Output displays the correct area of the rectangle
### Step 2: Decomposition
GOAL: Output displays correct area REQUIRES: area value is correct REQUIRES: area = width × height REQUIRES: width is a valid number REQUIRES: width input is parsed ATOMIC: Read width from user REQUIRES: parsing succeeds ATOMIC: Validate width is numeric REQUIRES: height is a valid number REQUIRES: height input is parsed ATOMIC: Read height from user REQUIRES: parsing succeeds ATOMIC: Validate height is numeric REQUIRES: output is displayed ATOMIC: Print result to screen
### Step 3: Leaf Nodes
- Read width from user
- Validate width is numeric
- Read height from user
- Validate height is numeric
- Print result to screen
### Step 4: Dependency Order
Level 0: Read width, Read height Level 1: Validate width (needs width), Validate height (needs height) Level 2: Calculate area = width × height (needs both validated) Level 3: Print result (needs area)
### Step 5: Extract Functions
FUNCTIONS: validate_number(input) → number or error Purpose: Parse and validate numeric input Logic: Parse input; if not positive number, return error Used by: width validation, height validation
Note: This function is identified because validation logic appears twice (for width and height) with identical structure.
### Step 6: Forward Steps
- Read width from user
- Call validate_number(width); abort if error
- Read height from user
- Call validate_number(height); abort if error
- Calculate area = width × height
- Print "Area: {area}"
### Step 7: Resulting Skill
```markdown
# Rectangle Area Calculator
## Purpose
Output displays the correct area of the rectangle.
## Functions
### validate_number(input) → number or error
Parse input and validate it is a positive number.
parsed = parse_as_number(input) if parsed is NaN or parsed <= 0: return error("Must be a positive number") return parsed
## Procedure
### Step 1: Get width
Read width value from user input.
### Step 2: Validate width
Call `validate_number(width)`. If error, display message and stop.
### Step 3: Get height
Read height value from user input.
### Step 4: Validate height
Call `validate_number(height)`. If error, display message and stop.
### Step 5: Calculate
Compute area = width × height.
### Step 6: Display
Output "Area: {area}".
## Verification
- [ ] Output matches expected area for test inputs
Example 2: Function Extraction (Box Alignment)
Task: Create a skill for rendering aligned boxes with emoji support.
Step 1: Goal
GOAL: All right-side │ characters align vertically
Step 2: Decomposition
GOAL: Right borders align
REQUIRES: All lines have identical display width
REQUIRES: line_width = content_width + padding + 4 (borders)
REQUIRES: padding = max_content_width - content_width
REQUIRES: max_content_width is known
REQUIRES: display_width calculated for ALL content items ← Repeated operation
REQUIRES: emoji widths handled correctly
ATOMIC: Use width lookup (emoji → 2, other → 1)
REQUIRES: content_width is known for THIS item
REQUIRES: display_width calculated for this item ← Same as above!
REQUIRES: borders are fixed width (4)
ATOMIC: Use "│ " prefix + " │" suffix
Step 3: Leaf Nodes
1. Width lookup table (emoji → 2, other → 1)
2. Border constants ("│ " = 2, " │" = 2)
Step 4: Dependency Order
Level 0: Width lookup table, border constants
Level 1: display_width for each content item (uses lookup)
Level 2: max_content_width (uses all display_widths)
Level 3: padding for each item (uses max and item width)
Level 4: construct each line (uses padding)
Level 5: assemble box (uses all lines)
Step 5: Extract Functions
FUNCTIONS:
display_width(text) → integer
Purpose: Calculate terminal display width
Logic: sum(2 if char is emoji else 1 for char in text)
Used by: max_content_width, padding calculation
max_content_width(contents[]) → integer
Purpose: Find widest content item
Logic: max(display_width(c) for c in contents)
Used by: padding calculation, border construction
box_width(contents[]) → integer
Purpose: Total box width including borders
Logic: max_content_width(contents) + 4
Used by: border construction
Step 6: Forward Steps
1. List all content items
2. For each item: call display_width(item)
3. Call max_content_width(contents) to get max
4. For each item: padding = max - display_width(item)
5. Construct each line: "│ " + content + " "×padding + " │"
6. Construct top: "╭" + "─"×(max+2) + "╮"
7. Construct bottom: "╰" + "─"×(max+2) + "╯"
8. Assemble: [top] + lines + [bottom]
Step 7: Resulting Skill
# Box Alignment
## Purpose
All right-side │ characters align vertically.
## Functions
### display_width(text) → integer
Calculate terminal display width of a string.
width = 0 for each char in text: if char in [☑️, 🔄, 🔳, 📊, ...]: width += 2 else: width += 1 return width
### max_content_width(contents[]) → integer
Find maximum display width among all content items.
return max(display_width(c) for c in contents)
### box_width(contents[]) → integer
Calculate total box width including borders.
return max_content_width(contents) + 4
## Procedure
### Step 1: List content
Identify all strings that will appear in the box.
### Step 2: Calculate widths
For each content item, call `display_width(item)`.
### Step 3: Find maximum
Call `max_content_width(contents)`.
### Step 4: Construct lines
For each content:
padding = max - display_width(content)
line = "│ " + content + " "×padding + " │"
### Step 5: Construct borders
top = "╭" + "─"×(max+2) + "╮"
bottom = "╰" + "─"×(max+2) + "╯"
### Step 6: Assemble
Output: [top] + [all lines] + [bottom]
## Verification
- [ ] All right │ characters are in the same column
Handling Complex Cases
Multiple Paths (OR conditions)
When a condition can be satisfied by alternative approaches:
CONDITION: User is authenticated
OPTION A:
REQUIRES: Valid session token exists
OPTION B:
REQUIRES: Valid API key provided
IMPORTANT - Fail-Fast, Not Fallback:
When designing skills with multiple paths, distinguish between:
- Legitimate alternatives (user choice): Present options for user to select
- Fallback patterns (degraded operation): AVOID - these hide failures
# BAD - Fallback hides hook failure
If pre-computed exists: use it
Else: compute manually (error-prone!)
# GOOD - Fail-fast exposes problems
If pre-computed exists: use it
Else: FAIL with "Hook failed - check precompute-status-display.sh"
Why fail-fast is better:
- Fallback to manual computation defeats the purpose of extraction
- Silent degradation makes debugging harder
- Errors in fallback path are often worse than no output
- Forces fixing the root cause (broken hook) rather than masking it
Shared Dependencies
When multiple branches require the same condition, note it once and reference it:
REQUIRES: display_width calculated for all items
(see: emoji width calculation above)
Verification Gates (M191 Prevention)
When a skill has steps that produce intermediate calculations or data that the final output depends on, add verification gates that require showing the intermediate work before proceeding.
Why gates matter: Without explicit gates, agents may:
- Mentally acknowledge a step without executing it
- Write approximate output instead of calculated output
- Skip straight to the final result, causing errors
Identify gate candidates during decomposition:
GOAL: Output is correct
REQUIRES: Final output uses calculated values ← Gate candidate
REQUIRES: Intermediate values are computed
REQUIRES: Input data is collected
When the decomposition shows a REQUIRES that transforms data (calculates, computes, derives), that transformation should have a gate that makes the result visible.
Gate format:
**MANDATORY CALCULATION GATE (reference):**
Before proceeding to [next step], you MUST show explicit [calculations/results]:
1. **List each [item] with its [derived value]:**
[item1]: [explicit breakdown] = [result] [item2]: [explicit breakdown] = [result]
2. **State the [aggregate value]:**
[aggregate_name] = [value] (from [derivation])
**BLOCKING:** Do NOT [produce output] until these [calculations/results] are written out.
[Explanation of what goes wrong if skipped].
Gate placement in procedure:
- Place gates AFTER the calculation step, BEFORE the step that uses the results
- Use "MANDATORY" and "BLOCKING" keywords
- Reference a mistake ID if the gate prevents a known issue
Example - Box alignment gate:
### Step 3: Calculate maximum width
Call `max_content_width(all_content_items)`.
**MANDATORY CALCULATION GATE (M191):**
Before proceeding to Step 4, you MUST show explicit width calculations:
1. **List each content item with its display_width:**
"Hello 👋": 7 chars + 1 emoji(2) = 8 "World": 5 chars = 5
2. **State max_content_width:**
max_content_width = 8
**BLOCKING:** Do NOT render any box output until calculations are written out.
Hand-writing approximate output without calculation causes alignment errors.
### Step 4: Build output
[Uses the calculated values from Step 3]
No Embedded Box Drawings in Skills (M217)
Critical rule: Skills MUST NOT contain embedded box-drawing examples in their instructions or templates. Embedded boxes cause agents to manually render similar output instead of using handler functions.
Important distinction: This rule applies to skills that output boxes to users. Documentation diagrams in skills that do not produce boxes (e.g., state machine diagrams in tdd-implementation, architecture flowcharts) are acceptable because:
- They illustrate concepts for human readers, not templates for agent output
- The agent is not asked to recreate or render them
- They don't trigger the "copy this pattern" failure mode
The failure pattern:
- Skill document shows example box output:
╭──────────────────────╮ │ Example Header │ ├──────────────────────┤ │ Content here │ ╰──────────────────────╯ - Agent sees this pattern and attempts to recreate it manually
- Manual rendering produces misaligned, incorrect boxes
- Handler functions (which would produce correct output) go unused
Correct approach for skills that produce boxes:
-
Reference handler functions, not visual examples:
# BAD - Embedded box causes manual rendering Display the result in this format: ╭──────────────────────╮ │ {content} │ ╰──────────────────────╯ # GOOD - References function without visual example Use `build_box(content)` to render the result. The function handles all alignment and border construction. -
For circle/rating patterns, use lookup tables not examples:
# BAD - Embedded pattern causes manual typing Display ratings like: ●●●●○ (4/5) or ●●○○○ (2/5) # GOOD - Lookup table with explicit "do not type manually" Rating circle patterns (do not hand-type, copy from table): - 5 → ●●●●● - 4 → ●●●●○ - 3 → ●●●○○ - 2 → ●●○○○ - 1 → ●○○○○ -
For output format documentation, describe structure not rendering:
# BAD - Shows rendered output The status display looks like: ╭────────────────────────────────╮ │ 📊 Progress: [████░░░░] 40% │ ╰────────────────────────────────╯ # GOOD - Describes structure, references handler The status display contains: - Header with emoji and title - Progress bar showing percentage - All rendering via `build_status_box()` function
Verification during skill creation:
- No box-drawing characters (╭╮╰╯│├┤┬┴┼─) appear in instruction examples
- No formatted table examples with borders appear in skill text
- Visual patterns (circles, bars, etc.) use lookup tables with "do not hand-type" warning
- All display rendering references handler functions by name
Output Artifact Gates (M192 Prevention)
See also: workflow-output.md for clean output standards including pre-computation patterns and subagent batching strategies.
Critical insight: Calculation gates alone are insufficient. When a skill produces structured output (boxes, tables, formatted text), the gate must require showing the exact artifact strings that will appear in the output, not just the numeric calculations.
The failure pattern (M192):
- Agent correctly calculates widths, counts, positions
- Agent understands the formula for constructing output
- Agent re-types the output from memory instead of copying calculated artifacts
- Output has subtle errors despite correct calculations
Solution: Add a second gate that requires explicit artifact construction:
### Step 4: Construct lines
For each item, apply the formula and **record the exact result string**:
build_line("📊 Status", 20) = "│ 📊 Status │" (padding: 10) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This exact string goes to output
**MANDATORY BUILD RESULTS GATE (M192):**
Before writing final output, verify:
- [ ] Each artifact (line, cell, row) has an explicit string recorded above
- [ ] Padding/spacing counts are noted in parentheses
- [ ] Final output will COPY these exact strings (no re-typing)
**BLOCKING:** If Step 4 does not contain explicit artifact strings, STOP and complete
Step 4 before proceeding. Re-typing output causes errors even when calculations are correct.
Key distinctions:
| Calculation Gate (M191) | Artifact Gate (M192) |
|---|---|
| Shows numeric values | Shows exact output strings |
| "max_width = 20" | "│ content │" |
| Prevents wrong math | Prevents wrong assembly |
| Required BEFORE construction | Required AFTER construction, BEFORE output |
When to add artifact gates:
- Output has precise formatting (aligned columns, borders, spacing)
- Small errors in spacing/padding break the result
- The construction formula combines multiple values
Recursive Structures
For problems with recursive structure (e.g., nested boxes), the decomposition will show the same pattern at multiple levels. Extract this as a function that can be applied recursively.
Identifying recursive patterns in decomposition:
GOAL: Render nested structure correctly
REQUIRES: Outer container rendered correctly
REQUIRES: Inner container rendered correctly ← Same pattern!
REQUIRES: Innermost container rendered correctly ← Same pattern again!
ATOMIC: Base case - no more nesting
Converting to recursive function:
FUNCTION: render_container(container) → rendered_output
Base case: if container has no children
return render_leaf(container)
Recursive case:
1. For each child: rendered_child = render_container(child) ← Recursive call
2. Combine rendered children with container frame
3. Return combined result
Order of operations for nested structures:
1. Process innermost elements first (base cases)
2. Work outward, combining results
3. Final step produces the outermost result
This is "inside-out" construction - the decomposition reveals this naturally
because inner elements are REQUIRES for outer elements.
Example - Nested boxes:
Decomposition shows:
REQUIRES: Outer box contains inner boxes correctly
REQUIRES: Each inner box is self-consistent
REQUIRES: Inner box borders align
(same requirements as any box - recursive!)
Function:
build_box(contents[]) → string[]
For each content item:
if content is itself a box structure:
inner_lines = build_box(content.items) ← Recursive call
add inner_lines to processed_contents
else:
add content string to processed_contents
return construct_box_frame(processed_contents)
Procedure step:
"Call build_box(root_contents) to construct the complete nested structure"
Extracting Computation to Hook-Based Precomputation (M192/M215 Prevention)
Critical insight: When a skill contains functions that perform deterministic computation (algorithms, formulas, calculations), these MUST be extracted to hooks that run BEFORE the skill. The skill should NEVER invoke scripts directly - this shows Bash tools to users and defeats the purpose of extraction.
The correct pattern (see plugin/hooks/skill_handlers/):
- Hook runs automatically when skill is invoked (via UserPromptSubmit or skill handler)
- Hook pre-computes ALL possible outputs the skill might need
- Hook returns pre-computed content via
additionalContext - Skill receives pre-computed content and outputs it directly (no script invocation)
Identify computation candidates during function extraction (Step 5):
For each function identified, ask:
1. Is the output deterministic given the inputs?
2. Could the agent get the wrong result by "thinking" instead of computing?
3. Does the function involve precise formatting, counting, or arithmetic?
If YES to all three → Extract to hook-based precomputation
Computation candidate signals:
| Signal | Example | Why Extract? |
|---|---|---|
| Counting characters/widths | display_width(text) | Agent may miscount emojis |
| Arithmetic with variables | padding = max - width | Agent may compute incorrectly |
| Building formatted strings | "│ " + content + spaces + " │" | Agent may mis-space |
| Aggregating over collections | max(widths) | Agent may miss items |
Non-candidates (keep in skill):
| Type | Example | Why Keep? |
|---|---|---|
| Reasoning/judgment | "Identify atomic conditions" | Requires understanding |
| Pattern matching | "Find repeated subtrees" | Requires semantic analysis |
| Decision making | "Choose appropriate level" | Requires context |
Anti-pattern (M215): Skill invokes a script after reasoning completes.
# WRONG - Shows Bash tools to users, defeats extraction purpose
1. Skill reasons about content
2. Skill invokes render-output.py via Bash ← User sees Bash tool
3. Script returns formatted output
4. Skill outputs result
# CORRECT - Hook runs BEFORE skill, user sees nothing
1. Hook detects skill invocation (via UserPromptSubmit or handler)
2. Hook pre-computes ALL possible outputs
3. Hook returns pre-computed content via additionalContext
4. Skill receives pre-computed content, reasons about which to use, outputs directly
MANDATORY: Planning Verification Checklist (M198)
BLOCKING: Before recommending ANY approach for a skill-builder rewrite, complete this checklist:
extraction_verification:
# Check each computation candidate signal against the skill
signals_present:
counting_chars_widths: true|false # display_width, strlen, emoji counting
arithmetic_with_vars: true|false # padding = max - width, calculations
building_formatted_strings: true|false # "│ " + content + " │", table rows
aggregating_collections: true|false # max(widths), sum, counts
# If ANY signal is true, extraction is REQUIRED
extraction_required: true|false
# Verify recommendation matches requirement
recommendation_valid:
if_extraction_required_and_recommending_simplified: INVALID
if_extraction_required_and_recommending_hooks: VALID
if_no_extraction_and_recommending_simplified: VALID
Anti-pattern (M198): Recommending "simplified rewrite without hooks" based on content type (ASCII vs emoji) rather than computation type. The extraction criteria are about WHAT OPERATIONS the skill performs (counting, arithmetic, formatting), NOT what characters appear in the output.
Anti-pattern (M203): Acknowledging extraction is required, then overriding with case-specific reasoning. Common override patterns to REJECT:
- "HOWEVER, consider that this only runs once..."
- "BUT for this specific case, hooks are overkill..."
- "Given the static content, we can simplify..."
- "The complexity cost outweighs the benefit..."
If signals show extraction_required: true, the determination is FINAL. Do not add "HOWEVER" exceptions. The methodology exists precisely because case-specific reasoning leads to errors. Frequency of execution, content type, and perceived complexity are NOT valid override reasons.
Anti-pattern (M214): Conflating content generation with display rendering. These are independent:
| Dimension | Question | If YES |
|---|---|---|
| Content | Does content require reasoning/judgment? | Content stays in skill |
| Display | Does output need formatted rendering (boxes, tables, alignment)? | Rendering extracted to hook |
Common error: "The content is reasoning-based, so extraction_required: false." Correct analysis: Content and display are orthogonal. Even if CONTENT requires reasoning, DISPLAY rendering (box characters, padding, alignment) is deterministic and should be extracted.
Correct workflow for mixed cases (hook pre-computes all variants):
- Hook detects skill invocation
- Hook gathers all data that could be displayed (from files, state, etc.)
- Hook pre-renders ALL possible output variants
- Hook returns pre-computed variants via additionalContext
- Agent reasons about which variant(s) to use
- Agent outputs the selected pre-computed content directly
Example - research executive summary:
# WRONG analysis (led to M214):
"The content requires reasoning (identifying approaches), so extraction_required: false"
# WRONG workflow (led to M215):
1. Agent reasons about stakeholder findings → produces approach data
2. Agent calls render-research-summary.py via Bash ← User sees Bash tool!
3. Script returns formatted box output
4. Agent outputs pre-rendered result
# CORRECT analysis:
content_generation: reasoning-based # Selection/synthesis - stays in skill
display_rendering: deterministic # Box layout, alignment - extract to hook
# CORRECT workflow:
1. Hook detects /cat:research invocation
2. Hook reads all stakeholder data from PLAN.md Research section
3. Hook pre-renders executive summary boxes for each option/approach
4. Hook returns ALL pre-rendered variants via additionalContext
5. Agent reasons about findings, selects relevant pre-computed sections
6. Agent outputs selected pre-computed content (no Bash invocation)
Example - Token-report skill:
# WRONG analysis (led to M198):
"Table contents are ASCII-only, so hooks are overkill"
# CORRECT analysis:
signals_present:
counting_chars_widths: true # Column widths must be calculated
arithmetic_with_vars: true # padding = column_width - content_width
building_formatted_strings: true # "│ Type │ Description..."
aggregating_collections: true # Total tokens = sum of subagent tokens
extraction_required: true # 4/4 signals present
recommendation: "Full hook-based pre-computation (Approach A)"
Template-Based Precomputation (M216)
For mixed cases where content is reasoning-based but output structure is known:
Even when the exact content is unknown until the skill reasons about it, the OUTPUT TYPE/STRUCTURE is often known ahead of time. The hook can pre-compute templates that the skill fills in.
Key insight: You know:
- The box will be N characters wide
- Each line needs "│ " prefix and " │" suffix
- The top/bottom borders use specific characters
- Section headers follow a pattern
Template-based workflow:
- Hook pre-computes structural elements at fixed width:
- Box borders (top, bottom, dividers)
- Line template with padding placeholder
- Section header templates
- Hook returns templates via additionalContext
- Skill reasons about content (option names, descriptions, etc.)
- Skill fills templates with content, extending to more lines as needed
- Skill outputs using pre-computed structural elements
Example - Research executive summary templates:
# Hook pre-computes templates at fixed width (e.g., 76 chars inner width)
BOX_WIDTH = 76
def precompute_templates():
return {
'top_border': '╭' + '─' * (BOX_WIDTH + 2) + '╮',
'bottom_border': '╰' + '─' * (BOX_WIDTH + 2) + '╯',
'divider': '├' + '─' * (BOX_WIDTH + 2) + '┤',
'line_template': '│ {content:<' + str(BOX_WIDTH) + '} │',
'header_template': '│ {icon} {title:<' + str(BOX_WIDTH - 4) + '} │',
'empty_line': '│' + ' ' * (BOX_WIDTH + 2) + '│',
}
Skill uses templates:
### Step 1: Require pre-computed templates
**MANDATORY:** Check context for "PRE-COMPUTED RESEARCH TEMPLATES".
Templates include:
- `top_border`: Use once at start
- `bottom_border`: Use once at end
- `line_template`: Use for each content line (format with content)
- `header_template`: Use for section headers
- `divider`: Use between sections
- `empty_line`: Use for visual spacing
### Step 2: Reason about content
Synthesize stakeholder findings to identify approaches, tradeoffs, etc.
(This is the reasoning part that cannot be pre-computed.)
### Step 3: Build output using templates
For each line of content:
1. Use `line_template.format(content=your_content)`
2. If content exceeds width, wrap to multiple lines using same template
Output structure:
{top_border} {header_template.format(icon='📋', title='Executive Summary')} {divider} {line_template.format(content='Option 1: ...')} {line_template.format(content=' Description: ...')} {empty_line} {line_template.format(content='Option 2: ...')} ... {bottom_border}
Benefits of template approach:
- Borders/structure computed once with correct widths
- Skill can extend to any number of lines
- No risk of miscounting padding (template handles it)
- Works when content is unknown but structure is known
When to use each approach:
| Scenario | Approach |
|---|---|
| All content known ahead of time | Full precomputation (status_handler pattern) |
| Content unknown, structure known | Template precomputation |
| Content unknown, structure varies | Plain text (avoid boxes) |
When computation candidates exist, generate two artifacts:
1. Skill Handler (Python in plugin/hooks/skill_handlers/):
Create a handler that pre-computes ALL possible outputs before the skill runs.
See status_handler.py for the canonical example.
# plugin/hooks/skill_handlers/{skill_name}_handler.py
"""Handler for /cat:{skill-name} precomputation."""
from pathlib import Path
from . import register_handler
class SkillNameHandler:
"""Handler for /cat:{skill-name} skill."""
def handle(self, context: dict) -> str | None:
"""Pre-compute all possible outputs before skill runs."""
project_root = context.get("project_root")
if not project_root:
return None
# 1. Gather all data the skill might need
data = self._collect_data(project_root)
if not data:
return None
# 2. Pre-render ALL possible output variants
variants = self._render_all_variants(data)
# 3. Return pre-computed content via additionalContext
return f"""PRE-COMPUTED {SKILL_NAME} OUTPUT:
{variants['main_output']}
VARIANT A (if applicable):
{variants['variant_a']}
VARIANT B (if applicable):
{variants['variant_b']}
INSTRUCTION: Output the appropriate pre-computed section. Do not recalculate."""
def _collect_data(self, project_root):
# Read files, gather state, etc.
pass
def _render_all_variants(self, data):
# Pre-render boxes, tables, formatted output
# Include ALL variants the skill might need
pass
# Register handler - this makes it run when skill is invoked
_handler = SkillNameHandler()
register_handler("{skill-name}", _handler)
Key patterns from status_handler.py:
display_width(text)- calculates terminal width with emoji supportbuild_line(content, max_width)- builds single box line with paddingbuild_border(max_width, is_top)- builds top/bottom borderbuild_inner_box(header, content_items)- builds nested box structures
2. Skill Preamble (add to generated skill - FAIL-FAST, not fallback):
### Step 1: Require pre-computed results
**MANDATORY:** Check context for "PRE-COMPUTED {SKILL-NAME}".
If found:
1. Output the pre-computed content **directly without preamble**
2. If skill requires reasoning to select variants, select appropriate section
3. Skip to verification step
If NOT found: **FAIL immediately**.
```bash
"${CLAUDE_PLUGIN_ROOT}/scripts/check-hooks-loaded.sh" "results" "the skill"
if [[ $? -eq 0 ]]; then
echo "ERROR: Pre-computed results not found."
echo "The handler ({skill-name}_handler.py) should have provided these."
echo "Check:"
echo "1. Handler is registered in skill_handlers/__init__.py"
echo "2. Handler file exists in plugin/hooks/skill_handlers/"
echo "3. Handler ran without errors"
fi
Output the error and STOP. Do NOT attempt manual computation.
**Why fail-fast?** Manual computation was extracted precisely because agents
cannot do it reliably. Falling back to manual defeats the purpose and hides
handler failures.
For mixed cases (reasoning + display):
When the skill requires reasoning to generate content but display is deterministic:
# The hook pre-computes ALL possible display formats
# The skill reasons about which to use and outputs directly
def _render_all_variants(self, data):
variants = {}
# Pre-render each possible stakeholder perspective
for stakeholder in ['architect', 'security', 'performance', ...]:
variants[f'{stakeholder}_box'] = self._build_stakeholder_box(
data.get(stakeholder, {})
)
# Pre-render summary tables for different groupings
variants['by_priority'] = self._build_priority_table(data)
variants['by_category'] = self._build_category_table(data)
# Pre-render option comparison boxes
for i, option in enumerate(data.get('options', [])):
variants[f'option_{i}_box'] = self._build_option_box(option)
return variants
The skill then reasons about the data, decides what to show, and outputs the appropriate pre-computed sections.
Example - Box alignment extraction:
Functions identified in Step 5:
display_width(text) → integer ← COMPUTATION CANDIDATE
max_content_width(items) → int ← COMPUTATION CANDIDATE
build_line(content, max) → string ← COMPUTATION CANDIDATE
Generate:
1. plugin/hooks/skill_handlers/box_handler.py
- Implements display_width, build_line, build_border
- Pre-computes complete box output
- Returns via additionalContext
2. Skill preamble - REQUIRES pre-computed output (fail-fast)
Result: Agent receives exact pre-computed box, outputs directly.
No Bash tools shown to user. If handler fails, skill fails immediately.
Decision flow during Step 5:
For each function:
Is it deterministic? ─────No────→ Keep in skill (reasoning required)
│
Yes
│
Could agent compute wrong? ─No─→ Keep in skill (trivial/reliable)
│
Yes
│
Extract to skill handler (plugin/hooks/skill_handlers/)
│
Handler pre-computes ALL variants BEFORE skill runs
│
Skill REQUIRES pre-computed result (fail-fast if missing)
Checklist Before Finalizing Skill
- Frontmatter description is trigger-oriented (WHEN to use, not just what it does)
- Goal is observable and verifiable
- All REQUIRES chains end in ATOMIC conditions
- Dependency order has no cycles
- Repeated patterns extracted as functions
- Recursive structures have function with base case + recursive case
- Variable-length functions derived via min-case → increment → generalize
- Functions listed in dependency order (no forward references)
- Forward steps call functions (no duplicated logic)
- Computation candidates identified and extracted to skill handler (M192/M215)
- Skill handler created in
plugin/hooks/skill_handlers/for deterministic functions - Handler pre-computes ALL possible output variants BEFORE skill runs
- Skill NEVER invokes scripts via Bash - user sees no tool calls (M215)
- Skill REQUIRES pre-computed results - FAIL-FAST if missing (no fallback)
- No "if not found, continue to manual..." patterns (fail-fast principle)
- Calculation gates added for transformation steps (M191)
- Artifact gates added when output has precise formatting (M192)
- Gates use MANDATORY and BLOCKING keywords
- Calculation gates require explicit numeric results before construction
- Artifact gates require explicit output strings before final assembly
- No embedded box drawings in skill instructions or examples (M217)
- Box-drawing characters only appear in handler code, not skill text
- Visual patterns use lookup tables with "do not hand-type" warnings
- Display rendering references handler functions by name
- Verification criteria exist for the goal