Add /diff and /template commands
- /diff command to compare config with backups - Shows added/removed/changed files - JSON-aware comparison for config files - List available backups - /template command for session templates - Built-in templates: daily-standup, code-review, troubleshoot, deploy - Each template includes checklist, initial commands, prompt - Create custom templates interactively or non-interactively - Updated shell completions with 21 aliases total - Test suite now covers 29 tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -69,6 +69,18 @@ _claude_agent_info() {
|
|||||||
COMPREPLY=($(compgen -W "--tree --list" -- "${cur}"))
|
COMPREPLY=($(compgen -W "--tree --list" -- "${cur}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_claude_diff() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
|
COMPREPLY=($(compgen -W "--backup --list --json" -- "${cur}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
_claude_template() {
|
||||||
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
|
||||||
|
COMPREPLY=($(compgen -W "--list --create --use --delete" -- "${cur}"))
|
||||||
|
}
|
||||||
|
|
||||||
_claude_memory_add() {
|
_claude_memory_add() {
|
||||||
local cur="${COMP_WORDS[COMP_CWORD]}"
|
local cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
local prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||||
@@ -108,6 +120,8 @@ complete -F _claude_upgrade upgrade.sh
|
|||||||
complete -F _claude_workflow workflow-info.py
|
complete -F _claude_workflow workflow-info.py
|
||||||
complete -F _claude_skill_info skill-info.py
|
complete -F _claude_skill_info skill-info.py
|
||||||
complete -F _claude_agent_info agent-info.py
|
complete -F _claude_agent_info agent-info.py
|
||||||
|
complete -F _claude_diff config-diff.py
|
||||||
|
complete -F _claude_template session-template.py
|
||||||
|
|
||||||
# Alias completions for convenience
|
# Alias completions for convenience
|
||||||
alias claude-validate='~/.claude/automation/validate-setup.sh'
|
alias claude-validate='~/.claude/automation/validate-setup.sh'
|
||||||
@@ -130,8 +144,10 @@ alias claude-upgrade='~/.claude/automation/upgrade.sh'
|
|||||||
alias claude-workflow='python3 ~/.claude/automation/workflow-info.py'
|
alias claude-workflow='python3 ~/.claude/automation/workflow-info.py'
|
||||||
alias claude-skill='python3 ~/.claude/automation/skill-info.py'
|
alias claude-skill='python3 ~/.claude/automation/skill-info.py'
|
||||||
alias claude-agent='python3 ~/.claude/automation/agent-info.py'
|
alias claude-agent='python3 ~/.claude/automation/agent-info.py'
|
||||||
|
alias claude-diff='python3 ~/.claude/automation/config-diff.py'
|
||||||
|
alias claude-template='python3 ~/.claude/automation/session-template.py'
|
||||||
|
|
||||||
echo "Claude Code completions loaded. Available aliases:"
|
echo "Claude Code completions loaded. Available aliases:"
|
||||||
echo " claude-{validate,status,backup,restore,clean,memory-add,memory-list,search}"
|
echo " claude-{validate,status,backup,restore,clean,memory-add,memory-list,search}"
|
||||||
echo " claude-{history,install,test,maintenance,log,debug,export,mcp,upgrade}"
|
echo " claude-{history,install,test,maintenance,log,debug,export,mcp,upgrade}"
|
||||||
echo " claude-{workflow,skill,agent}"
|
echo " claude-{workflow,skill,agent,diff,template}"
|
||||||
|
|||||||
@@ -131,6 +131,24 @@ _claude_agent_info() {
|
|||||||
'*:name:'
|
'*:name:'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Config diff completion
|
||||||
|
_claude_diff() {
|
||||||
|
_arguments \
|
||||||
|
'--backup[Backup file]:file:_files' \
|
||||||
|
'--list[List backups]' \
|
||||||
|
'--json[JSON output]'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Template completion
|
||||||
|
_claude_template() {
|
||||||
|
_arguments \
|
||||||
|
'--list[List templates]' \
|
||||||
|
'--create[Create template]:name:' \
|
||||||
|
'--use[Use template]:name:' \
|
||||||
|
'--delete[Delete template]:name:' \
|
||||||
|
'--non-interactive[Non-interactive mode]'
|
||||||
|
}
|
||||||
|
|
||||||
# Register completions
|
# Register completions
|
||||||
compdef _memory_add memory-add.py
|
compdef _memory_add memory-add.py
|
||||||
compdef _memory_list memory-list.py
|
compdef _memory_list memory-list.py
|
||||||
@@ -145,6 +163,8 @@ compdef _claude_upgrade upgrade.sh
|
|||||||
compdef _claude_workflow workflow-info.py
|
compdef _claude_workflow workflow-info.py
|
||||||
compdef _claude_skill_info skill-info.py
|
compdef _claude_skill_info skill-info.py
|
||||||
compdef _claude_agent_info agent-info.py
|
compdef _claude_agent_info agent-info.py
|
||||||
|
compdef _claude_diff config-diff.py
|
||||||
|
compdef _claude_template session-template.py
|
||||||
|
|
||||||
# Aliases
|
# Aliases
|
||||||
alias claude-validate='~/.claude/automation/validate-setup.sh'
|
alias claude-validate='~/.claude/automation/validate-setup.sh'
|
||||||
@@ -167,8 +187,10 @@ alias claude-upgrade='~/.claude/automation/upgrade.sh'
|
|||||||
alias claude-workflow='python3 ~/.claude/automation/workflow-info.py'
|
alias claude-workflow='python3 ~/.claude/automation/workflow-info.py'
|
||||||
alias claude-skill='python3 ~/.claude/automation/skill-info.py'
|
alias claude-skill='python3 ~/.claude/automation/skill-info.py'
|
||||||
alias claude-agent='python3 ~/.claude/automation/agent-info.py'
|
alias claude-agent='python3 ~/.claude/automation/agent-info.py'
|
||||||
|
alias claude-diff='python3 ~/.claude/automation/config-diff.py'
|
||||||
|
alias claude-template='python3 ~/.claude/automation/session-template.py'
|
||||||
|
|
||||||
echo "Claude Code completions loaded (zsh)"
|
echo "Claude Code completions loaded (zsh)"
|
||||||
echo " Aliases: claude-{validate,status,backup,restore,clean,memory-add,memory-list,search}"
|
echo " Aliases: claude-{validate,status,backup,restore,clean,memory-add,memory-list,search}"
|
||||||
echo " claude-{history,install,test,maintenance,log,debug,export,mcp,upgrade}"
|
echo " claude-{history,install,test,maintenance,log,debug,export,mcp,upgrade}"
|
||||||
echo " claude-{workflow,skill,agent}"
|
echo " claude-{workflow,skill,agent,diff,template}"
|
||||||
|
|||||||
294
automation/config-diff.py
Executable file
294
automation/config-diff.py
Executable file
@@ -0,0 +1,294 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Compare current configuration with backup or default.
|
||||||
|
Usage: python3 config-diff.py [--backup FILE] [--default] [--json]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
import tempfile
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
|
CLAUDE_DIR = Path.home() / ".claude"
|
||||||
|
BACKUP_DIR = CLAUDE_DIR / "backups"
|
||||||
|
|
||||||
|
|
||||||
|
def load_json_safe(path: Path) -> Optional[Dict]:
|
||||||
|
"""Load JSON file safely."""
|
||||||
|
try:
|
||||||
|
with open(path) as f:
|
||||||
|
return json.load(f)
|
||||||
|
except (FileNotFoundError, json.JSONDecodeError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_files() -> List[Path]:
|
||||||
|
"""Get list of configuration files to compare."""
|
||||||
|
patterns = [
|
||||||
|
"CLAUDE.md",
|
||||||
|
"VERSION",
|
||||||
|
".claude-plugin/plugin.json",
|
||||||
|
"hooks/hooks.json",
|
||||||
|
"state/*.json",
|
||||||
|
"state/**/*.json",
|
||||||
|
]
|
||||||
|
|
||||||
|
files = []
|
||||||
|
for pattern in patterns:
|
||||||
|
files.extend(CLAUDE_DIR.glob(pattern))
|
||||||
|
|
||||||
|
return sorted(files)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_backup(backup_path: Path) -> Path:
|
||||||
|
"""Extract backup to temporary directory."""
|
||||||
|
temp_dir = Path(tempfile.mkdtemp())
|
||||||
|
|
||||||
|
with tarfile.open(backup_path, "r:gz") as tar:
|
||||||
|
tar.extractall(temp_dir)
|
||||||
|
|
||||||
|
return temp_dir
|
||||||
|
|
||||||
|
|
||||||
|
def compare_files(current: Path, other: Path) -> Dict:
|
||||||
|
"""Compare two files and return differences."""
|
||||||
|
result = {
|
||||||
|
"current_exists": current.exists(),
|
||||||
|
"other_exists": other.exists(),
|
||||||
|
"same": False,
|
||||||
|
"diff": None
|
||||||
|
}
|
||||||
|
|
||||||
|
if not current.exists() and not other.exists():
|
||||||
|
result["same"] = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
if not current.exists() or not other.exists():
|
||||||
|
return result
|
||||||
|
|
||||||
|
# Compare content
|
||||||
|
try:
|
||||||
|
current_content = current.read_text()
|
||||||
|
other_content = other.read_text()
|
||||||
|
|
||||||
|
if current_content == other_content:
|
||||||
|
result["same"] = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
# For JSON files, do structural comparison
|
||||||
|
if current.suffix == ".json":
|
||||||
|
try:
|
||||||
|
current_json = json.loads(current_content)
|
||||||
|
other_json = json.loads(other_content)
|
||||||
|
|
||||||
|
# Find differences
|
||||||
|
result["diff"] = {
|
||||||
|
"type": "json",
|
||||||
|
"added": [],
|
||||||
|
"removed": [],
|
||||||
|
"changed": []
|
||||||
|
}
|
||||||
|
|
||||||
|
# Simple key comparison for top-level
|
||||||
|
current_keys = set(current_json.keys()) if isinstance(current_json, dict) else set()
|
||||||
|
other_keys = set(other_json.keys()) if isinstance(other_json, dict) else set()
|
||||||
|
|
||||||
|
result["diff"]["added"] = list(current_keys - other_keys)
|
||||||
|
result["diff"]["removed"] = list(other_keys - current_keys)
|
||||||
|
|
||||||
|
# Check for changed values
|
||||||
|
for key in current_keys & other_keys:
|
||||||
|
if current_json.get(key) != other_json.get(key):
|
||||||
|
result["diff"]["changed"].append(key)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Text comparison
|
||||||
|
current_lines = current_content.split("\n")
|
||||||
|
other_lines = other_content.split("\n")
|
||||||
|
|
||||||
|
result["diff"] = {
|
||||||
|
"type": "text",
|
||||||
|
"current_lines": len(current_lines),
|
||||||
|
"other_lines": len(other_lines),
|
||||||
|
"line_diff": len(current_lines) - len(other_lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
result["error"] = str(e)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_backup() -> Optional[Path]:
|
||||||
|
"""Get the most recent backup file."""
|
||||||
|
if not BACKUP_DIR.exists():
|
||||||
|
return None
|
||||||
|
|
||||||
|
backups = list(BACKUP_DIR.glob("*.tar.gz"))
|
||||||
|
if not backups:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return max(backups, key=lambda p: p.stat().st_mtime)
|
||||||
|
|
||||||
|
|
||||||
|
def compare_with_backup(backup_path: Path) -> Dict[str, Dict]:
|
||||||
|
"""Compare current config with a backup."""
|
||||||
|
temp_dir = extract_backup(backup_path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
results = {}
|
||||||
|
config_files = get_config_files()
|
||||||
|
|
||||||
|
for current_file in config_files:
|
||||||
|
rel_path = current_file.relative_to(CLAUDE_DIR)
|
||||||
|
backup_file = temp_dir / rel_path
|
||||||
|
|
||||||
|
results[str(rel_path)] = compare_files(current_file, backup_file)
|
||||||
|
|
||||||
|
# Check for files in backup that don't exist in current
|
||||||
|
for backup_file in temp_dir.rglob("*.json"):
|
||||||
|
rel_path = backup_file.relative_to(temp_dir)
|
||||||
|
current_file = CLAUDE_DIR / rel_path
|
||||||
|
|
||||||
|
if str(rel_path) not in results:
|
||||||
|
results[str(rel_path)] = compare_files(current_file, backup_file)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# Cleanup temp directory
|
||||||
|
import shutil
|
||||||
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
def format_results(results: Dict[str, Dict], json_output: bool = False) -> str:
|
||||||
|
"""Format comparison results."""
|
||||||
|
if json_output:
|
||||||
|
return json.dumps(results, indent=2)
|
||||||
|
|
||||||
|
lines = ["\n📊 Configuration Diff\n"]
|
||||||
|
|
||||||
|
# Group by status
|
||||||
|
same = []
|
||||||
|
added = []
|
||||||
|
removed = []
|
||||||
|
changed = []
|
||||||
|
|
||||||
|
for path, info in results.items():
|
||||||
|
if info.get("same"):
|
||||||
|
same.append(path)
|
||||||
|
elif info.get("current_exists") and not info.get("other_exists"):
|
||||||
|
added.append(path)
|
||||||
|
elif not info.get("current_exists") and info.get("other_exists"):
|
||||||
|
removed.append(path)
|
||||||
|
else:
|
||||||
|
changed.append((path, info))
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
lines.append(f"Unchanged: {len(same)}")
|
||||||
|
lines.append(f"Added: {len(added)}")
|
||||||
|
lines.append(f"Removed: {len(removed)}")
|
||||||
|
lines.append(f"Changed: {len(changed)}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Details
|
||||||
|
if added:
|
||||||
|
lines.append("=== Added Files ===")
|
||||||
|
for path in added:
|
||||||
|
lines.append(f" + {path}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
if removed:
|
||||||
|
lines.append("=== Removed Files ===")
|
||||||
|
for path in removed:
|
||||||
|
lines.append(f" - {path}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
lines.append("=== Changed Files ===")
|
||||||
|
for path, info in changed:
|
||||||
|
lines.append(f" ~ {path}")
|
||||||
|
diff = info.get("diff", {})
|
||||||
|
if diff:
|
||||||
|
if diff.get("type") == "json":
|
||||||
|
if diff.get("added"):
|
||||||
|
lines.append(f" Added keys: {', '.join(diff['added'][:5])}")
|
||||||
|
if diff.get("removed"):
|
||||||
|
lines.append(f" Removed keys: {', '.join(diff['removed'][:5])}")
|
||||||
|
if diff.get("changed"):
|
||||||
|
lines.append(f" Changed keys: {', '.join(diff['changed'][:5])}")
|
||||||
|
elif diff.get("type") == "text":
|
||||||
|
lines.append(f" Lines: {diff['other_lines']} -> {diff['current_lines']}")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Compare configuration with backup")
|
||||||
|
parser.add_argument("--backup", "-b", type=str,
|
||||||
|
help="Backup file to compare with (default: latest)")
|
||||||
|
parser.add_argument("--list", "-l", action="store_true",
|
||||||
|
help="List available backups")
|
||||||
|
parser.add_argument("--json", "-j", action="store_true",
|
||||||
|
help="Output as JSON")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.list:
|
||||||
|
if not BACKUP_DIR.exists():
|
||||||
|
print("No backups directory found.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
backups = sorted(BACKUP_DIR.glob("*.tar.gz"),
|
||||||
|
key=lambda p: p.stat().st_mtime, reverse=True)
|
||||||
|
|
||||||
|
if not backups:
|
||||||
|
print("No backups found.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
print("\n📦 Available Backups\n")
|
||||||
|
for b in backups[:10]:
|
||||||
|
mtime = datetime.fromtimestamp(b.stat().st_mtime)
|
||||||
|
size = b.stat().st_size
|
||||||
|
if size > 1024 * 1024:
|
||||||
|
size_str = f"{size / 1024 / 1024:.1f}M"
|
||||||
|
else:
|
||||||
|
size_str = f"{size / 1024:.1f}K"
|
||||||
|
print(f" {b.name:<40} {size_str:<8} {mtime:%Y-%m-%d %H:%M}")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Get backup to compare
|
||||||
|
if args.backup:
|
||||||
|
backup_path = Path(args.backup)
|
||||||
|
if not backup_path.exists():
|
||||||
|
backup_path = BACKUP_DIR / args.backup
|
||||||
|
if not backup_path.exists():
|
||||||
|
print(f"Backup not found: {args.backup}")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
backup_path = get_latest_backup()
|
||||||
|
if not backup_path:
|
||||||
|
print("No backups found. Create one with: claude-backup")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(f"Comparing with: {backup_path.name}")
|
||||||
|
|
||||||
|
results = compare_with_backup(backup_path)
|
||||||
|
print(format_results(results, args.json))
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
257
automation/session-template.py
Executable file
257
automation/session-template.py
Executable file
@@ -0,0 +1,257 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Manage session templates for common workflows.
|
||||||
|
Usage: python3 session-template.py [--list|--create NAME|--use NAME|--delete NAME]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Optional
|
||||||
|
|
||||||
|
CLAUDE_DIR = Path.home() / ".claude"
|
||||||
|
TEMPLATES_DIR = CLAUDE_DIR / "state" / "personal-assistant" / "templates"
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_templates_dir():
|
||||||
|
"""Ensure templates directory exists."""
|
||||||
|
TEMPLATES_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def load_template(name: str) -> Optional[Dict]:
|
||||||
|
"""Load a template by name."""
|
||||||
|
template_file = TEMPLATES_DIR / f"{name}.json"
|
||||||
|
try:
|
||||||
|
with open(template_file) as f:
|
||||||
|
return json.load(f)
|
||||||
|
except (FileNotFoundError, json.JSONDecodeError):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def save_template(name: str, template: Dict):
|
||||||
|
"""Save a template."""
|
||||||
|
ensure_templates_dir()
|
||||||
|
template_file = TEMPLATES_DIR / f"{name}.json"
|
||||||
|
with open(template_file, "w") as f:
|
||||||
|
json.dump(template, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
def list_templates():
|
||||||
|
"""List available templates."""
|
||||||
|
ensure_templates_dir()
|
||||||
|
|
||||||
|
templates = list(TEMPLATES_DIR.glob("*.json"))
|
||||||
|
|
||||||
|
if not templates:
|
||||||
|
print("\nNo templates found. Create one with --create <name>")
|
||||||
|
print("\nBuilt-in templates you can create:")
|
||||||
|
print(" - daily-standup: Morning status check and planning")
|
||||||
|
print(" - code-review: Review code changes")
|
||||||
|
print(" - troubleshoot: Debug an issue")
|
||||||
|
print(" - deploy: Deployment workflow")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n📋 Session Templates ({len(templates)})\n")
|
||||||
|
|
||||||
|
for t in sorted(templates):
|
||||||
|
template = load_template(t.stem)
|
||||||
|
if template:
|
||||||
|
desc = template.get("description", "No description")
|
||||||
|
category = template.get("category", "general")
|
||||||
|
print(f" {t.stem}")
|
||||||
|
print(f" Category: {category}")
|
||||||
|
print(f" {desc}")
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def create_template(name: str, interactive: bool = True):
|
||||||
|
"""Create a new template."""
|
||||||
|
ensure_templates_dir()
|
||||||
|
|
||||||
|
# Check for built-in templates
|
||||||
|
builtin_templates = {
|
||||||
|
"daily-standup": {
|
||||||
|
"name": "daily-standup",
|
||||||
|
"description": "Morning status check and planning",
|
||||||
|
"category": "routine",
|
||||||
|
"context_level": "moderate",
|
||||||
|
"initial_commands": ["/status"],
|
||||||
|
"checklist": [
|
||||||
|
"Check system health",
|
||||||
|
"Review pending items",
|
||||||
|
"Check calendar",
|
||||||
|
"Plan priorities"
|
||||||
|
],
|
||||||
|
"prompt_template": "Good morning! Let's start with a quick status check and plan the day."
|
||||||
|
},
|
||||||
|
"code-review": {
|
||||||
|
"name": "code-review",
|
||||||
|
"description": "Review code changes in a project",
|
||||||
|
"category": "development",
|
||||||
|
"context_level": "comprehensive",
|
||||||
|
"initial_commands": [],
|
||||||
|
"checklist": [
|
||||||
|
"Understand the change scope",
|
||||||
|
"Check for issues",
|
||||||
|
"Verify tests",
|
||||||
|
"Suggest improvements"
|
||||||
|
],
|
||||||
|
"prompt_template": "Please review the recent changes in {project}. Focus on {focus_areas}."
|
||||||
|
},
|
||||||
|
"troubleshoot": {
|
||||||
|
"name": "troubleshoot",
|
||||||
|
"description": "Debug an issue systematically",
|
||||||
|
"category": "debugging",
|
||||||
|
"context_level": "comprehensive",
|
||||||
|
"initial_commands": ["/debug"],
|
||||||
|
"checklist": [
|
||||||
|
"Gather symptoms",
|
||||||
|
"Check logs",
|
||||||
|
"Identify root cause",
|
||||||
|
"Implement fix",
|
||||||
|
"Verify resolution"
|
||||||
|
],
|
||||||
|
"prompt_template": "I'm experiencing {issue}. Let's debug this systematically."
|
||||||
|
},
|
||||||
|
"deploy": {
|
||||||
|
"name": "deploy",
|
||||||
|
"description": "Deploy application to environment",
|
||||||
|
"category": "operations",
|
||||||
|
"context_level": "moderate",
|
||||||
|
"initial_commands": ["/k8s:cluster-status"],
|
||||||
|
"checklist": [
|
||||||
|
"Verify cluster health",
|
||||||
|
"Check application readiness",
|
||||||
|
"Perform deployment",
|
||||||
|
"Verify deployment",
|
||||||
|
"Monitor for issues"
|
||||||
|
],
|
||||||
|
"prompt_template": "Deploy {app} to {environment}."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if name in builtin_templates:
|
||||||
|
template = builtin_templates[name]
|
||||||
|
save_template(name, template)
|
||||||
|
print(f"✓ Created template from built-in: {name}")
|
||||||
|
print(f" {template['description']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
if interactive:
|
||||||
|
print(f"\nCreating new template: {name}\n")
|
||||||
|
|
||||||
|
template = {
|
||||||
|
"name": name,
|
||||||
|
"created": datetime.now().isoformat(),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get details interactively
|
||||||
|
template["description"] = input("Description: ").strip() or "No description"
|
||||||
|
template["category"] = input("Category [general]: ").strip() or "general"
|
||||||
|
template["context_level"] = input("Context level [moderate]: ").strip() or "moderate"
|
||||||
|
|
||||||
|
print("Initial commands (comma-separated, e.g., /status,/debug): ")
|
||||||
|
commands = input("> ").strip()
|
||||||
|
template["initial_commands"] = [c.strip() for c in commands.split(",") if c.strip()]
|
||||||
|
|
||||||
|
print("Checklist items (one per line, empty line to finish):")
|
||||||
|
checklist = []
|
||||||
|
while True:
|
||||||
|
item = input(" - ").strip()
|
||||||
|
if not item:
|
||||||
|
break
|
||||||
|
checklist.append(item)
|
||||||
|
template["checklist"] = checklist
|
||||||
|
|
||||||
|
template["prompt_template"] = input("Prompt template: ").strip()
|
||||||
|
|
||||||
|
save_template(name, template)
|
||||||
|
print(f"\n✓ Template '{name}' created successfully")
|
||||||
|
else:
|
||||||
|
# Create minimal template
|
||||||
|
template = {
|
||||||
|
"name": name,
|
||||||
|
"description": "Custom template",
|
||||||
|
"category": "custom",
|
||||||
|
"context_level": "moderate",
|
||||||
|
"initial_commands": [],
|
||||||
|
"checklist": [],
|
||||||
|
"prompt_template": "",
|
||||||
|
"created": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
save_template(name, template)
|
||||||
|
print(f"✓ Created empty template: {name}")
|
||||||
|
print(f" Edit: {TEMPLATES_DIR / f'{name}.json'}")
|
||||||
|
|
||||||
|
|
||||||
|
def use_template(name: str):
|
||||||
|
"""Display template for use."""
|
||||||
|
template = load_template(name)
|
||||||
|
|
||||||
|
if not template:
|
||||||
|
print(f"Template '{name}' not found.")
|
||||||
|
list_templates()
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"\n📋 Template: {name}\n")
|
||||||
|
print(f"Description: {template.get('description', 'No description')}")
|
||||||
|
print(f"Category: {template.get('category', 'general')}")
|
||||||
|
print(f"Context: {template.get('context_level', 'moderate')}")
|
||||||
|
|
||||||
|
commands = template.get("initial_commands", [])
|
||||||
|
if commands:
|
||||||
|
print(f"\nInitial commands:")
|
||||||
|
for cmd in commands:
|
||||||
|
print(f" {cmd}")
|
||||||
|
|
||||||
|
checklist = template.get("checklist", [])
|
||||||
|
if checklist:
|
||||||
|
print(f"\nChecklist:")
|
||||||
|
for i, item in enumerate(checklist, 1):
|
||||||
|
print(f" [ ] {item}")
|
||||||
|
|
||||||
|
prompt = template.get("prompt_template", "")
|
||||||
|
if prompt:
|
||||||
|
print(f"\nPrompt template:")
|
||||||
|
print(f" {prompt}")
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def delete_template(name: str):
|
||||||
|
"""Delete a template."""
|
||||||
|
template_file = TEMPLATES_DIR / f"{name}.json"
|
||||||
|
|
||||||
|
if not template_file.exists():
|
||||||
|
print(f"Template '{name}' not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
template_file.unlink()
|
||||||
|
print(f"✓ Deleted template: {name}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Manage session templates")
|
||||||
|
parser.add_argument("--list", "-l", action="store_true", help="List templates")
|
||||||
|
parser.add_argument("--create", "-c", type=str, help="Create a template")
|
||||||
|
parser.add_argument("--use", "-u", type=str, help="Use a template")
|
||||||
|
parser.add_argument("--delete", "-d", type=str, help="Delete a template")
|
||||||
|
parser.add_argument("--non-interactive", "-n", action="store_true",
|
||||||
|
help="Non-interactive mode for --create")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.create:
|
||||||
|
create_template(args.create, interactive=not args.non_interactive)
|
||||||
|
elif args.use:
|
||||||
|
use_template(args.use)
|
||||||
|
elif args.delete:
|
||||||
|
delete_template(args.delete)
|
||||||
|
else:
|
||||||
|
list_templates()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -106,6 +106,20 @@ else
|
|||||||
fail "agent-info.py syntax error"
|
fail "agent-info.py syntax error"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Test 13: config-diff.py
|
||||||
|
if python3 -m py_compile "${AUTOMATION_DIR}/config-diff.py" 2>/dev/null; then
|
||||||
|
pass "config-diff.py syntax valid"
|
||||||
|
else
|
||||||
|
fail "config-diff.py syntax error"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test 14: session-template.py
|
||||||
|
if python3 -m py_compile "${AUTOMATION_DIR}/session-template.py" 2>/dev/null; then
|
||||||
|
pass "session-template.py syntax valid"
|
||||||
|
else
|
||||||
|
fail "session-template.py syntax error"
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== Skill Scripts ==="
|
echo "=== Skill Scripts ==="
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ Slash commands for quick actions. User-invoked (type `/command` to trigger).
|
|||||||
| `/workflow` | `/workflows`, `/wf` | List and describe workflows |
|
| `/workflow` | `/workflows`, `/wf` | List and describe workflows |
|
||||||
| `/skill-info` | `/skill`, `/skills-info` | Show skill information |
|
| `/skill-info` | `/skill`, `/skills-info` | Show skill information |
|
||||||
| `/agent-info` | `/agent`, `/agents` | Show agent information |
|
| `/agent-info` | `/agent`, `/agents` | Show agent information |
|
||||||
|
| `/diff` | `/config-diff`, `/compare` | Compare config with backup |
|
||||||
|
| `/template` | `/templates`, `/session-template` | Manage session templates |
|
||||||
| `/maintain` | `/maintenance`, `/admin` | Configuration maintenance |
|
| `/maintain` | `/maintenance`, `/admin` | Configuration maintenance |
|
||||||
| `/programmer` | | Code development tasks |
|
| `/programmer` | | Code development tasks |
|
||||||
| `/gcal` | `/calendar`, `/cal` | Google Calendar access |
|
| `/gcal` | `/calendar`, `/cal` | Google Calendar access |
|
||||||
|
|||||||
34
commands/diff.md
Normal file
34
commands/diff.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: diff
|
||||||
|
description: Compare configuration with backup
|
||||||
|
aliases: [config-diff, compare]
|
||||||
|
invokes: command:diff
|
||||||
|
---
|
||||||
|
|
||||||
|
# Diff Command
|
||||||
|
|
||||||
|
Compare current configuration with a backup to see what changed.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/diff # Compare with latest backup
|
||||||
|
/diff --backup <file> # Compare with specific backup
|
||||||
|
/diff --list # List available backups
|
||||||
|
/diff --json # Output as JSON
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 ~/.claude/automation/config-diff.py [options]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Shows changes grouped by type:
|
||||||
|
- **Added** - New files in current config
|
||||||
|
- **Removed** - Files that existed in backup but not now
|
||||||
|
- **Changed** - Modified files with details
|
||||||
|
|
||||||
|
For JSON files, shows which keys were added/removed/changed.
|
||||||
44
commands/template.md
Normal file
44
commands/template.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
name: template
|
||||||
|
description: Manage session templates for common workflows
|
||||||
|
aliases: [templates, session-template]
|
||||||
|
invokes: command:template
|
||||||
|
---
|
||||||
|
|
||||||
|
# Template Command
|
||||||
|
|
||||||
|
Create and use session templates for repeatable workflows.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
/template # List all templates
|
||||||
|
/template --use <name> # Display template for use
|
||||||
|
/template --create <name> # Create new template
|
||||||
|
/template --delete <name> # Delete a template
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 ~/.claude/automation/session-template.py [options]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Built-in Templates
|
||||||
|
|
||||||
|
| Name | Category | Description |
|
||||||
|
|------|----------|-------------|
|
||||||
|
| `daily-standup` | routine | Morning status check and planning |
|
||||||
|
| `code-review` | development | Review code changes in a project |
|
||||||
|
| `troubleshoot` | debugging | Debug an issue systematically |
|
||||||
|
| `deploy` | operations | Deploy application to environment |
|
||||||
|
|
||||||
|
## Template Contents
|
||||||
|
|
||||||
|
Each template includes:
|
||||||
|
- **Description** - What the template is for
|
||||||
|
- **Category** - Grouping for organization
|
||||||
|
- **Context level** - How much context to gather
|
||||||
|
- **Initial commands** - Commands to run at start
|
||||||
|
- **Checklist** - Steps to follow
|
||||||
|
- **Prompt template** - Suggested starting prompt
|
||||||
@@ -199,6 +199,16 @@
|
|||||||
"description": "Show agent information",
|
"description": "Show agent information",
|
||||||
"aliases": ["/agent", "/agents"],
|
"aliases": ["/agent", "/agents"],
|
||||||
"invokes": "command:agent-info"
|
"invokes": "command:agent-info"
|
||||||
|
},
|
||||||
|
"/diff": {
|
||||||
|
"description": "Compare config with backup",
|
||||||
|
"aliases": ["/config-diff", "/compare"],
|
||||||
|
"invokes": "command:diff"
|
||||||
|
},
|
||||||
|
"/template": {
|
||||||
|
"description": "Manage session templates",
|
||||||
|
"aliases": ["/templates", "/session-template"],
|
||||||
|
"invokes": "command:template"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"agents": {
|
"agents": {
|
||||||
@@ -336,7 +346,9 @@
|
|||||||
"upgrade": "~/.claude/automation/upgrade.sh",
|
"upgrade": "~/.claude/automation/upgrade.sh",
|
||||||
"workflow-info": "~/.claude/automation/workflow-info.py",
|
"workflow-info": "~/.claude/automation/workflow-info.py",
|
||||||
"skill-info": "~/.claude/automation/skill-info.py",
|
"skill-info": "~/.claude/automation/skill-info.py",
|
||||||
"agent-info": "~/.claude/automation/agent-info.py"
|
"agent-info": "~/.claude/automation/agent-info.py",
|
||||||
|
"config-diff": "~/.claude/automation/config-diff.py",
|
||||||
|
"session-template": "~/.claude/automation/session-template.py"
|
||||||
},
|
},
|
||||||
"completions": {
|
"completions": {
|
||||||
"bash": "~/.claude/automation/completions.bash",
|
"bash": "~/.claude/automation/completions.bash",
|
||||||
|
|||||||
16
state/personal-assistant/templates/daily-standup.json
Normal file
16
state/personal-assistant/templates/daily-standup.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "daily-standup",
|
||||||
|
"description": "Morning status check and planning",
|
||||||
|
"category": "routine",
|
||||||
|
"context_level": "moderate",
|
||||||
|
"initial_commands": [
|
||||||
|
"/status"
|
||||||
|
],
|
||||||
|
"checklist": [
|
||||||
|
"Check system health",
|
||||||
|
"Review pending items",
|
||||||
|
"Check calendar",
|
||||||
|
"Plan priorities"
|
||||||
|
],
|
||||||
|
"prompt_template": "Good morning! Let's start with a quick status check and plan the day."
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user