# Skill Preferences Pattern

This document provides a reusable template for adding preferences capability to AO skills.

## Overview

All AO skills should support user preferences for customization. The preferences pattern enables:
- Automatic loading of saved preferences at skill start
- Optional creation of preferences after first run
- Update preferences via "preferences" trigger word
- Per-skill preferences stored in skill directory

## Universal Pattern (3 Components)

Every skill with preferences needs these three components:

### 1. Step 0: Check for Preferences (at skill start)
### 2. Preferences Creation Offer (after first successful run)
### 3. Preferences Update Trigger (when user says "preferences")

---

## Component 1: Step 0 - Load Preferences

**Location:** Add as first step in skill's Procedure section, before any other steps.

**Template (copy-paste into skill):**

````markdown
### Step 0: Check for Preferences

**At the start of the skill, check for `preferences.md` next to this SKILL.md:**

**CRITICAL: File Path Resolution**

The skill implementation must resolve the preferences.md file path **relative to the location of this SKILL.md file**, not relative to the current working directory or .agent/ops/ folder.

```
# Pseudocode for path resolution:
SKILL_DIR = directory_containing_this_file  # e.g., .ao/skills/ao-planning/
PREFERENCES_PATH = SKILL_DIR / "preferences.md"

IF file_exists(PREFERENCES_PATH):
    content = read_file(PREFERENCES_PATH)
    Parse frontmatter to extract: {field1}, {field2}, ...
    Use stored preferences for {behavior descriptions}
    Log: "✓ Loaded preferences from {PREFERENCES_PATH}"
ELSE:
    Log: "No preferences found, will proceed with defaults"
    Proceed to Step 1
```

**Implementation Notes:**
- Resolve skill directory dynamically (do NOT hardcode paths)
- Verify file can be read before attempting to parse
- Log all path operations for debugging
- If preferences file is corrupt or unreadable, warn user and proceed with defaults

**Preference Fields for this Skill:**
- `{field_name}` ({type}) — {description}
- `{field_name}` ({type}) — {description}
...
````

**Customization:**
- Replace `{field1}, {field2}` with actual preference field names
- Replace `{behavior descriptions}` with what these preferences control
- Add the specific preference fields list at the end

---

## Component 2: Preferences Creation Offer

**Location:** Add as new step AFTER main workflow completes successfully, BEFORE final completion message.

**Template (copy-paste into skill):**

````markdown
### Step {N}: Offer Preferences Creation (if not exist)

If `preferences.md` does NOT exist after completing the main workflow, offer to create it:

> "Would you like me to save preferences for this skill? This will remember your choices for future runs."

**If user says "Yes":**

Conduct a structured interview (one question at a time) using `ao-interview` skill:

#### Interview Questions for {Skill Name}

1. **{Preference 1 Name}**
   > "{Question text for preference 1?}"
   >
   > Options: {option1} | {option2} | {option3}
   > Default: {default_value}

2. **{Preference 2 Name}**
   > "{Question text for preference 2?}"
   >
   > Options: {options}
   > Default: {default_value}

...

{N}. **Additional Preferences**
   > "Is there anything else I should remember for this skill?"
   >
   > Examples: {examples of custom preferences}
   > Or leave blank if no other preferences

**After interview, create `preferences.md` with ROBUST PATH HANDLING:**

**CRITICAL PATH RESOLUTION for Writing:**

```
# Pseudocode for creating preferences file:
SKILL_DIR = directory_containing_this_SKILL.md  # .ao/skills/{skill-name}/
PREFERENCES_PATH = SKILL_DIR / "preferences.md"

TRY:
    content = generate_preferences_markdown(responses)
    write_file(PREFERENCES_PATH, content)
    IF file_exists(PREFERENCES_PATH):
        Log: "✓ Preferences saved to {PREFERENCES_PATH}"
    ELSE:
        Error: "Failed to verify file was created at {PREFERENCES_PATH}"
        Fallback: Ask user to save manually
CATCH permission_error:
    Error: "Cannot write to {PREFERENCES_PATH} — check permissions"
    Ask user if alternative location acceptable
```

**Template for preferences.md:**

```yaml
---
name: {skill-name} preferences
description: User preferences for {skill-name}
{field_1}: {value}
{field_2}: {value}
date_created: {today's date}
date_updated: {today's date}
---

# {Skill Name} Preferences

## Summary

These preferences customize how {skill-name} operates.

## Settings

**{Setting 1 Name}**: {value}
{description of what this controls}

**{Setting 2 Name}**: {value}
{description of what this controls}

...

### Additional Notes
{any additional preferences from user}

---

*These preferences are stored in `.ao/skills/{skill-name}/preferences.md`*
*Update these preferences anytime by asking "Update my preferences for {skill-name}"*
```

**Verification:** After writing, verify:
- [ ] File exists at correct path
- [ ] File is readable
- [ ] Frontmatter is valid YAML
- [ ] All required fields present
- [ ] Log confirmation message to user
````

**Customization:**
- Replace `{N}` with actual step number
- Replace `{Skill Name}` with skill name
- Add skill-specific interview questions
- Customize preferences.md template fields

---

## Component 3: Preferences Update Trigger

**Location:** Add as new section at end of skill, typically after "Completion Checklist" and before "Anti-patterns".

**Template (copy-paste into skill):**

````markdown
## Preferences Management

### Creating Preferences (First Run)

If `preferences.md` does not exist after completing the main workflow, the skill offers to create it (see Step {N} above).

### Updating Preferences

When user asks to update preferences (e.g., "Update my preferences for this skill" or just "preferences"):

1. **Resolve preferences.md path** (same as Step 0)
   ```
   SKILL_DIR = directory_containing_this_SKILL.md
   PREFERENCES_PATH = SKILL_DIR / "preferences.md"
   ```

2. **Read current preferences.md**
   ```
   IF NOT file_exists(PREFERENCES_PATH):
       Error: "Preferences not found at {PREFERENCES_PATH}"
       Offer to create new preferences instead
   ELSE:
       Parse current settings for context
   ```

3. **Re-interview user** with same questions as creation (see Step {N} above)
   > Show current values: "Current {setting name}: {current_value}. Keep or change?"

4. **Ask at the end**: "Anything else to add or change?"

5. **Update preferences.md** with new values and update `date_updated`
   ```
   content = update_preferences_markdown(old_content, responses)
   write_file(PREFERENCES_PATH, content)
   Verify file was written successfully
   Log: "✓ Preferences updated at {PREFERENCES_PATH}"
   ```

### Trigger Keywords

Users can trigger preference updates by saying:
- "preferences"
- "update preferences"
- "update my preferences for {skill-name}"
- "change settings for {skill-name}"

The skill should recognize these and enter preferences update mode.
````

**Customization:**
- Replace `{N}` with the step number where preferences creation is offered
- Add skill-specific trigger keywords if needed

---

## Complete Example: ao-example

Here's a minimal complete example showing all three components integrated:

**File:** `.ao/skills/ao-example/SKILL.md`

````markdown
---
name: ao-example
description: "Example skill demonstrating preferences pattern"
category: utility
---

# Example Skill

## Purpose
Demonstrates how to implement preferences.

## Procedure

### Step 0: Check for Preferences

[... COMPONENT 1 from above ...]

Preference Fields for ao-example:
- `detail_level` (low/normal/high) — How much detail to show
- `auto_confirm` (yes/no) — Skip confirmation prompts

### Step 1: Do Main Work

Execute the skill's primary function using preferences if available:

```
IF detail_level == "high":
    Show verbose output
ELIF detail_level == "normal":
    Show standard output
ELSE:
    Show minimal output

IF auto_confirm:
    Proceed without asking
ELSE:
    Ask user to confirm
```

### Step 2: Offer Preferences Creation (if not exist)

[... COMPONENT 2 from above ...]

Interview Questions for ao-example:

1. **Detail Level**
   > "How much detail should I show by default?"
   > Options: low | normal | high
   > Default: normal

2. **Auto Confirm**
   > "Should I skip confirmation prompts?"
   > Options: yes | no
   > Default: no

3. **Additional Preferences**
   > "Any other preferences for this skill?"

## Completion Checklist

- [ ] Main work completed
- [ ] Preferences offered if not exist
- [ ] User satisfied with results

## Preferences Management

[... COMPONENT 3 from above ...]
````

---

## Anti-patterns (FORBIDDEN)

These patterns cause problems and must be avoided:

### Path Resolution Anti-patterns

❌ **Hardcoding absolute paths**
```python
# WRONG: Hardcoded path
preferences_path = "C:/dev/ao-ops/.ao/skills/ao-planning/preferences.md"
```

✅ **Correct: Dynamic resolution**
```python
# RIGHT: Resolve relative to skill location
skill_dir = Path(__file__).parent  # Directory containing SKILL.md
preferences_path = skill_dir / "preferences.md"
```

❌ **Using CWD (current working directory)**
```python
# WRONG: Depends on where command is run
preferences_path = Path.cwd() / ".agent" / "preferences.md"
```

❌ **Saving to .agent/ops/ folder**
```python
# WRONG: Preferences should be with skill, not in .agent/ops/
preferences_path = Path(".agent/ops/ao-planning-preferences.md")
```

### File Creation Anti-patterns

❌ **Not verifying file was created**
```python
# WRONG: Assume write succeeded
write_file(preferences_path, content)
# ... continue without checking
```

✅ **Correct: Verify and log**
```python
# RIGHT: Verify and inform user
write_file(preferences_path, content)
if preferences_path.exists():
    print(f"✓ Preferences saved to {preferences_path}")
else:
    print(f"❌ Failed to create {preferences_path}")
```

❌ **Not logging file path**
```python
# WRONG: User doesn't know where file was saved
write_file(preferences_path, content)
print("Preferences saved")
```

✅ **Correct: Log full path**
```python
# RIGHT: Show exactly where file is
write_file(preferences_path, content)
print(f"✓ Preferences saved to {preferences_path}")
```

### Consent Anti-patterns

❌ **Creating preferences without asking**
```python
# WRONG: Automatically save preferences
create_preferences(preferences_path, defaults)
```

✅ **Correct: Ask first**
```python
# RIGHT: Offer, wait for consent
response = ask_user("Save preferences for future runs?")
if response.lower() in ["yes", "y"]:
    create_preferences(preferences_path, settings)
```

❌ **Overwriting without warning**
```python
# WRONG: Silently overwrite existing preferences
write_file(preferences_path, new_content)
```

✅ **Correct: Confirm before overwrite**
```python
# RIGHT: Warn if overwriting
if preferences_path.exists():
    confirm = ask_user("Preferences exist. Overwrite?")
    if not confirm:
        return
write_file(preferences_path, new_content)
```

### Interview Anti-patterns

❌ **Asking all questions at once**
```markdown
> "What's your detail level, auto-confirm preference, and output format?"
```

✅ **Correct: One question at a time**
```markdown
> "What detail level? (low/normal/high)"
[wait for response]
> "Should I auto-confirm? (yes/no)"
[wait for response]
```

❌ **Not showing current values when updating**
```markdown
> "What detail level?" [no context about current setting]
```

✅ **Correct: Show current value**
```markdown
> "Current detail level: high. Keep or change?"
```

---

## Testing Checklist

For each skill updated with preferences, test:

### Test 1: First Run (No Preferences)
- [ ] Skill runs normally without preferences
- [ ] Skill uses default behaviors
- [ ] Skill offers to create preferences at end
- [ ] User can decline and skill still completes

### Test 2: Preferences Creation
- [ ] Interview asks questions one at a time
- [ ] Interview waits for each response
- [ ] preferences.md created at correct path (.ao/skills/{skill-name}/)
- [ ] File contents valid YAML
- [ ] File contains all expected fields
- [ ] Confirmation message shows file path

### Test 3: Subsequent Run (With Preferences)
- [ ] Skill loads preferences automatically (Step 0)
- [ ] Log message confirms: "✓ Loaded preferences from {path}"
- [ ] Skill behavior respects preference settings
- [ ] No offer to create preferences (already exist)

### Test 4: Preferences Update
- [ ] User says "preferences" or equivalent
- [ ] Skill enters update mode
- [ ] Current values shown for each setting
- [ ] User can change some values and keep others
- [ ] preferences.md updated with new values
- [ ] `date_updated` field changed
- [ ] Confirmation message shows update successful

### Test 5: Path Resolution
- [ ] preferences.md exists in skill directory, not .agent/ops/
- [ ] Path resolution works regardless of CWD
- [ ] No hardcoded paths in logs or errors
- [ ] Relative paths display correctly

### Test 6: Error Handling
- [ ] Corrupted preferences.md → warn, use defaults
- [ ] Missing write permission → error with clear message
- [ ] Invalid YAML → warn, offer to recreate

---

## Implementation Checklist

When adding preferences to a skill:

- [ ] Identify preference-worthy settings for this skill
- [ ] Add Step 0 (load preferences) as first procedure step
- [ ] Add preferences creation offer as late step (after main work)
- [ ] Add Preferences Management section at end
- [ ] Customize interview questions for this skill
- [ ] Test all 6 test cases above
- [ ] Document preference fields in Step 0
- [ ] Add trigger keywords to Preferences Management section

---

## Reference Implementation

See `.ao/skills/ao-plan-preview/SKILL.md` for the most complete reference implementation of this pattern.

Key sections to examine:
- **Step 0** (lines ~37-74): Preferences loading with robust path handling
- **Step 3.5** (lines ~110-130): Additional interview questions  
- **Preferences Management** (lines ~320-420): Creation and update procedures
- **Anti-patterns** (lines ~430-445): What NOT to do

---

## Questions?

If you need help adding preferences to a skill, reference this document and the ao-plan-preview implementation. The pattern is designed to be copy-paste friendly with clear customization points marked as `{placeholders}`.
