437 lines
14 KiB
Dart
437 lines
14 KiB
Dart
import "skill_loader.dart";
|
|
import "skill_types.dart";
|
|
|
|
// ported from old_repo/skills/bundled/*.ts
|
|
// each skill has a name, description, and a prompt template
|
|
// the prompts are trimmed down versions - dynamic sections (schema generation,
|
|
// keybinding tables) are replaced with static placeholders since we dont have
|
|
// the TS runtime context here
|
|
|
|
const _simplifyPrompt = """
|
|
# Simplify: Code Review and Cleanup
|
|
|
|
Review all changed files for reuse, quality, and efficiency. Fix any issues found.
|
|
|
|
## Phase 1: Identify Changes
|
|
|
|
Run `git diff` (or `git diff HEAD` if there are staged changes) to see what changed. If there are no git changes, review the most recently modified files that the user mentioned or that you edited earlier in this conversation.
|
|
|
|
## Phase 2: Launch Three Review Agents in Parallel
|
|
|
|
Use the Agent tool to launch all three agents concurrently in a single message. Pass each agent the full diff so it has the complete context.
|
|
|
|
### Agent 1: Code Reuse Review
|
|
|
|
For each change:
|
|
|
|
1. **Search for existing utilities and helpers** that could replace newly written code.
|
|
2. **Flag any new function that duplicates existing functionality.**
|
|
3. **Flag any inline logic that could use an existing utility.**
|
|
|
|
### Agent 2: Code Quality Review
|
|
|
|
Review the same changes for hacky patterns:
|
|
|
|
1. **Redundant state**
|
|
2. **Parameter sprawl**
|
|
3. **Copy-paste with slight variation**
|
|
4. **Leaky abstractions**
|
|
5. **Stringly-typed code**
|
|
6. **Unnecessary nesting**
|
|
7. **Unnecessary comments**
|
|
|
|
### Agent 3: Efficiency Review
|
|
|
|
Review the same changes for efficiency:
|
|
|
|
1. **Unnecessary work**
|
|
2. **Missed concurrency**
|
|
3. **Hot-path bloat**
|
|
4. **Recurring no-op updates**
|
|
5. **Unnecessary existence checks**
|
|
6. **Memory leaks**
|
|
7. **Overly broad operations**
|
|
|
|
## Phase 3: Fix Issues
|
|
|
|
Wait for all three agents to complete. Aggregate their findings and fix each issue directly. If a finding is a false positive or not worth addressing, note it and move on.
|
|
|
|
When done, briefly summarize what was fixed (or confirm the code was already clean).
|
|
""";
|
|
|
|
const _updateConfigPrompt = """
|
|
# Update Config Skill
|
|
|
|
Modify Claude Code configuration by updating settings.json files.
|
|
|
|
## When Hooks Are Required (Not Memory)
|
|
|
|
If the user wants something to happen automatically in response to an EVENT, they need a **hook** configured in settings.json. Memory/preferences cannot trigger automated actions.
|
|
|
|
**These require hooks:**
|
|
- "Before compacting, ask me what to preserve" → PreCompact hook
|
|
- "After writing files, run prettier" → PostToolUse hook with Write|Edit matcher
|
|
- "When I run bash commands, log them" → PreToolUse hook with Bash matcher
|
|
- "Always run tests after code changes" → PostToolUse hook
|
|
|
|
**Hook events:** PreToolUse, PostToolUse, PreCompact, PostCompact, Stop, Notification, SessionStart
|
|
|
|
## CRITICAL: Read Before Write
|
|
|
|
**Always read the existing settings file before making changes.** Merge new settings with existing ones - never replace the entire file.
|
|
|
|
## Settings File Locations
|
|
|
|
| File | Scope | Use For |
|
|
|------|-------|---------|
|
|
| `~/.claude/settings.json` | Global | Personal preferences for all projects |
|
|
| `.claude/settings.json` | Project | Team-wide hooks, permissions, plugins |
|
|
| `.claude/settings.local.json` | Project | Personal overrides for this project |
|
|
|
|
## Workflow
|
|
|
|
1. **Clarify intent** - Ask if the request is ambiguous
|
|
2. **Read existing file** - Use Read tool on the target settings file
|
|
3. **Merge carefully** - Preserve existing settings, especially arrays
|
|
4. **Edit file** - Use Edit tool (if file doesn't exist, ask user to create it first)
|
|
5. **Confirm** - Tell user what was changed
|
|
|
|
## Common Mistakes to Avoid
|
|
|
|
1. **Replacing instead of merging** - Always preserve existing settings
|
|
2. **Wrong file** - Ask user if scope is unclear
|
|
3. **Invalid JSON** - Validate syntax after changes
|
|
4. **Forgetting to read first** - Always read before write
|
|
""";
|
|
|
|
const _keybindingsPrompt = """
|
|
# Keybindings Skill
|
|
|
|
Create or modify `~/.claude/keybindings.json` to customize keyboard shortcuts.
|
|
|
|
## CRITICAL: Read Before Write
|
|
|
|
**Always read `~/.claude/keybindings.json` first** (it may not exist yet). Merge changes with existing bindings — never replace the entire file.
|
|
|
|
## File Format
|
|
|
|
```json
|
|
{
|
|
"\$schema": "https://www.schemastore.org/claude-code-keybindings.json",
|
|
"bindings": [
|
|
{
|
|
"context": "Chat",
|
|
"bindings": {
|
|
"ctrl+e": "chat:externalEditor"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
## Keystroke Syntax
|
|
|
|
**Modifiers** (combine with `+`): `ctrl`, `alt`, `shift`, `meta`
|
|
|
|
**Special keys**: `escape`, `enter`, `tab`, `space`, `backspace`, `delete`, arrow keys
|
|
|
|
**Chords**: Space-separated keystrokes, e.g. `ctrl+k ctrl+s`
|
|
|
|
## How User Bindings Interact with Defaults
|
|
|
|
- User bindings are **additive** — appended after the default bindings
|
|
- To **move** a binding: unbind the old key (`null`) AND add the new binding
|
|
- Set a key to `null` to remove its default binding
|
|
|
|
## Behavioral Rules
|
|
|
|
1. Only include contexts the user wants to change
|
|
2. Validate that actions and contexts are from the known lists
|
|
3. Warn the user if they choose a key that conflicts with reserved shortcuts
|
|
4. When adding a new binding, the new binding is additive
|
|
5. To fully replace a default binding, unbind old AND add new
|
|
""";
|
|
|
|
const _debugPrompt = """
|
|
# Debug Skill
|
|
|
|
Help the user debug an issue they're encountering in this current Claude Code session.
|
|
|
|
## Session Debug Log
|
|
|
|
The debug log for the current session is at: `~/.claude/debug/<session-id>.txt`
|
|
|
|
Read the debug log and look for errors, warnings, and failure patterns.
|
|
|
|
## Instructions
|
|
|
|
1. Review the user's issue description
|
|
2. Look for [ERROR] and [WARN] entries, stack traces, and failure patterns in the debug log
|
|
3. Explain what you found in plain language
|
|
4. Suggest concrete fixes or next steps
|
|
|
|
## Settings
|
|
|
|
Settings are in:
|
|
* user - `~/.claude/settings.json`
|
|
* project - `.claude/settings.json`
|
|
* local - `.claude/settings.local.json`
|
|
""";
|
|
|
|
const _rememberPrompt = """
|
|
# Memory Review
|
|
|
|
## Goal
|
|
Review the user's memory landscape and produce a clear report of proposed changes, grouped by action type. Do NOT apply changes — present proposals for user approval.
|
|
|
|
## Steps
|
|
|
|
### 1. Gather all memory layers
|
|
Read CLAUDE.md and CLAUDE.local.md from the project root (if they exist). Your auto-memory content is already in your system prompt — review it there.
|
|
|
|
### 2. Classify each auto-memory entry
|
|
|
|
| Destination | What belongs there |
|
|
|---|---|
|
|
| **CLAUDE.md** | Project conventions for all contributors |
|
|
| **CLAUDE.local.md** | Personal instructions for this user only |
|
|
| **Stay in auto-memory** | Working notes, temporary context |
|
|
|
|
### 3. Identify cleanup opportunities
|
|
- **Duplicates**: entries already in CLAUDE.md or CLAUDE.local.md
|
|
- **Outdated**: entries contradicted by newer entries
|
|
- **Conflicts**: contradictions between any two layers
|
|
|
|
### 4. Present the report
|
|
1. **Promotions** — entries to move, with destination and rationale
|
|
2. **Cleanup** — duplicates, outdated entries, conflicts to resolve
|
|
3. **Ambiguous** — entries where user input is needed
|
|
4. **No action needed** — entries that should stay put
|
|
|
|
## Rules
|
|
- Present ALL proposals before making any changes
|
|
- Do NOT modify files without explicit user approval
|
|
- Ask about ambiguous entries — don't guess
|
|
""";
|
|
|
|
const _skillifyPrompt = """
|
|
# Skillify
|
|
|
|
You are capturing this session's repeatable process as a reusable skill.
|
|
|
|
## Your Task
|
|
|
|
### Step 1: Analyze the Session
|
|
Before asking questions, analyze the session to identify:
|
|
- What repeatable process was performed
|
|
- What inputs/parameters were needed
|
|
- The distinct steps (in order)
|
|
- The success criteria for each step
|
|
- Where the user corrected or steered you
|
|
- What tools and permissions were needed
|
|
|
|
### Step 2: Interview the User
|
|
|
|
Use AskUserQuestion for ALL questions.
|
|
|
|
**Round 1:** Suggest a name and description, confirm high-level goals.
|
|
|
|
**Round 2:** Present steps, suggest arguments if needed, ask about inline vs forked context, and where to save (repo or personal).
|
|
|
|
**Round 3:** For each step, clarify what it produces, how success is verified, and whether human confirmation is needed.
|
|
|
|
**Round 4:** Confirm when to invoke, trigger phrases, and any gotchas.
|
|
|
|
### Step 3: Write the SKILL.md
|
|
|
|
Format:
|
|
```markdown
|
|
---
|
|
name: skill-name
|
|
description: one-line description
|
|
allowed-tools:
|
|
- Bash(git:*)
|
|
when_to_use: Use when...
|
|
argument-hint: "\$arg_name"
|
|
arguments:
|
|
- arg_name
|
|
context: fork
|
|
---
|
|
|
|
# Skill Title
|
|
|
|
## Goal
|
|
...
|
|
|
|
## Steps
|
|
|
|
### 1. Step Name
|
|
What to do.
|
|
|
|
**Success criteria**: How to know this step is done.
|
|
```
|
|
|
|
### Step 4: Confirm and Save
|
|
|
|
Show the complete SKILL.md content before writing. Ask for confirmation with AskUserQuestion. After writing, tell the user where it was saved and how to invoke it.
|
|
""";
|
|
|
|
const _stuckPrompt = """
|
|
# /stuck — diagnose frozen/slow Claude Code sessions
|
|
|
|
The user thinks another Claude Code session on this machine is frozen, stuck, or very slow. Investigate and post a report to #claude-code-feedback.
|
|
|
|
## What to look for
|
|
|
|
Scan for other Claude Code processes. Process names are typically `claude` (installed) or `cli` (native dev build).
|
|
|
|
Signs of a stuck session:
|
|
- **High CPU (≥90%) sustained** — likely an infinite loop
|
|
- **Process state `D`** — I/O hang
|
|
- **Process state `T`** — user probably hit Ctrl+Z
|
|
- **Process state `Z`** — zombie
|
|
- **Very high RSS (≥4GB)** — possible memory leak
|
|
|
|
## Investigation steps
|
|
|
|
1. List all Claude Code processes:
|
|
```
|
|
ps -axo pid=,pcpu=,rss=,etime=,state=,comm=,command= | grep -E '(claude|cli)' | grep -v grep
|
|
```
|
|
|
|
2. For anything suspicious, gather more context:
|
|
- Child processes: `pgrep -lP <pid>`
|
|
- Check debug log: `~/.claude/debug/<session-id>.txt`
|
|
|
|
## Report
|
|
|
|
Only post to Slack if you found something stuck. If everything looks healthy, tell the user directly.
|
|
|
|
If stuck, post to **#claude-code-feedback** using Slack MCP tool with a two-message structure:
|
|
1. **Top-level**: hostname, version, terse symptom
|
|
2. **Thread reply**: full diagnostic dump
|
|
""";
|
|
|
|
// registry - holds all skills keyed by name
|
|
class SkillRegistry {
|
|
SkillRegistry._();
|
|
|
|
static final SkillRegistry instance = SkillRegistry._();
|
|
|
|
final _skills = <String, Skill>{};
|
|
|
|
void register(Skill skill) {
|
|
_skills[skill.name] = skill;
|
|
for (final alias in skill.aliases) {
|
|
_skills[alias] = skill;
|
|
}
|
|
}
|
|
|
|
// looks up a skill by name or alias
|
|
Skill? lookup(String name) => _skills[name];
|
|
|
|
// all registered skills (deduped by name)
|
|
List<Skill> get all {
|
|
final seen = <String>{};
|
|
final result = <Skill>[];
|
|
for (final skill in _skills.values) {
|
|
if (seen.add(skill.name)) {
|
|
result.add(skill);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// merge in user and project skills, user skills take precedence over project
|
|
// bundled skills take lowest precedence
|
|
void mergeExternalSkills(List<Skill> skills) {
|
|
for (final skill in skills) {
|
|
// external skills override bundled ones with same name
|
|
_skills[skill.name] = skill;
|
|
}
|
|
}
|
|
}
|
|
|
|
// registers the built-in bundled skills
|
|
// mirrors old_repo/skills/bundled/index.ts initBundledSkills()
|
|
void registerBundledSkills() {
|
|
final reg = SkillRegistry.instance;
|
|
|
|
reg.register(const Skill(
|
|
name: "update-config",
|
|
description: "Use this skill to configure the Claude Code harness via settings.json. Automated behaviors require hooks configured in settings.json. Also use for permissions, env vars, hook troubleshooting, or any changes to settings.json files.",
|
|
source: SkillSource.bundled,
|
|
promptTemplate: _updateConfigPrompt,
|
|
allowedTools: ["Read"],
|
|
userInvocable: true,
|
|
));
|
|
|
|
reg.register(const Skill(
|
|
name: "keybindings-help",
|
|
description: "Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json.",
|
|
source: SkillSource.bundled,
|
|
promptTemplate: _keybindingsPrompt,
|
|
allowedTools: ["Read"],
|
|
userInvocable: false,
|
|
));
|
|
|
|
reg.register(const Skill(
|
|
name: "simplify",
|
|
description: "Review changed code for reuse, quality, and efficiency, then fix any issues found.",
|
|
source: SkillSource.bundled,
|
|
promptTemplate: _simplifyPrompt,
|
|
userInvocable: true,
|
|
));
|
|
|
|
reg.register(const Skill(
|
|
name: "debug",
|
|
description: "Enable debug logging for this session and help diagnose issues",
|
|
source: SkillSource.bundled,
|
|
promptTemplate: _debugPrompt,
|
|
allowedTools: ["Read", "Grep", "Glob"],
|
|
argumentHint: "[issue description]",
|
|
disableModelInvocation: true,
|
|
userInvocable: true,
|
|
));
|
|
|
|
reg.register(const Skill(
|
|
name: "remember",
|
|
description: "Review auto-memory entries and propose promotions to CLAUDE.md, CLAUDE.local.md, or shared memory. Also detects outdated, conflicting, and duplicate entries across memory layers.",
|
|
source: SkillSource.bundled,
|
|
promptTemplate: _rememberPrompt,
|
|
whenToUse: "Use when the user wants to review, organize, or promote their auto-memory entries.",
|
|
userInvocable: true,
|
|
));
|
|
|
|
reg.register(const Skill(
|
|
name: "skillify",
|
|
description: "Capture this session's repeatable process into a skill.",
|
|
source: SkillSource.bundled,
|
|
promptTemplate: _skillifyPrompt,
|
|
allowedTools: ["Read", "Write", "Edit", "Glob", "Grep", "AskUserQuestion", "Bash(mkdir:*)"],
|
|
argumentHint: "[description of the process you want to capture]",
|
|
disableModelInvocation: true,
|
|
userInvocable: true,
|
|
));
|
|
|
|
// stuck is ant-only in the original but we register it here anyway
|
|
// the caller can filter by checking USER_TYPE env var
|
|
reg.register(const Skill(
|
|
name: "stuck",
|
|
description: "[ANT-ONLY] Investigate frozen/stuck/slow Claude Code sessions on this machine.",
|
|
source: SkillSource.bundled,
|
|
promptTemplate: _stuckPrompt,
|
|
userInvocable: true,
|
|
));
|
|
}
|
|
|
|
// loads user and project skills and merges them into the registry
|
|
Future<void> loadAndMergeExternalSkills() async {
|
|
final userSkills = await loadUserSkills();
|
|
final projectSkills = await loadProjectSkills();
|
|
|
|
// project skills override user skills for same name
|
|
final all = [...userSkills, ...projectSkills];
|
|
SkillRegistry.instance.mergeExternalSkills(all);
|
|
}
|