Compare commits

..

14 Commits

Author SHA1 Message Date
OpenCode Test 7dcb8af1bb Set default model to opus in settings.json
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 13:49:30 -08:00
OpenCode Test 6be9bf5aff Mark OpenCode transposition plan as complete
- Implementation complete (2026-01-07)
- All steps verified: backup, sync script, config, testing, docs
- 40 agents discovered (built-in + synced)
- 10 skills, 27 commands, 10 workflows synced
- Manual TUI testing pending

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 13:45:59 -08:00
OpenCode Test 7c37e9adb3 Add OpenCode sync enhancement plan and future consideration
- Add fc-047: Consider JSON minification for OpenCode instructions
- Add brainstorming plan for OpenCode Claude sync enhancements
- Add implementation status plan

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 12:15:33 -08:00
OpenCode Test 0780b4c17d Document plans index in CLAUDE.md and plans/README.md
- Add plans/ to directory structure
- Add plans/index.json to shared state files table
- Add Plans row to component formats table
- Create plans/README.md with schema and query examples

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:28:56 -08:00
OpenCode Test a08dc505d9 Add plans index.json for status tracking
Central registry tracking all 17 plans with status, category, and dates.
Enables easy querying of pending vs implemented plans.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:27:18 -08:00
OpenCode Test c82726b691 Add RAG JSON-to-text transformation plan
Design for improving semantic search quality by transforming JSON
structures into natural language at index time.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:11:34 -08:00
OpenCode Test c14c0d843d Update ralph-loop to guardrail hooks task
Switch from completed morning-report to guardrail hooks implementation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:11:27 -08:00
OpenCode Test 0fd0e74b67 Track new PA session entries
Add session records for Jan 06 and Jan 07 sessions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:11:21 -08:00
OpenCode Test 1636784931 Enable tasks section in morning report, add daily archives
- Enable tasks collector in morning-report config
- Update morning.md with Jan 07 report
- Archive Jan 06 and Jan 07 reports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:11:15 -08:00
OpenCode Test c30ea2d679 Update plugin metadata from auto-updates
Plugins refreshed their cache versions and timestamps.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:11:08 -08:00
OpenCode Test 769391640b Add telemetry directory to gitignore
Analytics tracking data should not be version controlled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 11:10:58 -08:00
OpenCode Test ecf375205f Implement guardrail hooks for dangerous operation prevention
- Add PreToolUse hook intercepting Bash, Write, Edit
- Block catastrophic commands (rm -rf /, mkfs, etc.)
- Require confirmation for operations outside safe paths
- Git-aware: operations in git repos are allowed
- Session allowlist for user-confirmed operations
- Audit logging to logs/guardrail.jsonl
- Clear session allowlist on SessionEnd

Config: state/guardrails.json
Scripts: hooks/scripts/guardrail.py, guardrail-confirm.py

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 10:57:53 -08:00
OpenCode Test f2f8a03a32 Add guardrail hooks design document
PreToolUse hooks to prevent dangerous operations:
- Intercepts Bash, Write, Edit before execution
- Contextual response (block vs confirm)
- Path-aware with git repo detection
- Session allowlist for user confirmations
- Audit logging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 10:50:03 -08:00
OpenCode Test 630893f047 Add conditional RAG reindex after session summarization
When summarize-transcript.py extracts items to memory files, it now
triggers index_personal.py to update the RAG search index. Only runs
when items were actually added (total_added > 0) to avoid unnecessary
reindexing on trivial sessions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 10:32:04 -08:00
26 changed files with 2344 additions and 42 deletions
+15 -17
View File
@@ -1,25 +1,23 @@
---
active: true
iteration: 36
max_iterations: 0
completion_promise: "The morning-report skill is fully implemented, tested, and registered"
started_at: "2026-01-03T08:16:44Z"
iteration: 1
max_iterations: 20
completion_promise: "Guardrail hooks are fully implemented, tested, and registered"
started_at: "2026-01-07T18:52:10Z"
---
Build the morning-report skill following the design at ~/.claude/docs/plans/2025-01-02-morning-report-design.md
Implement guardrail hooks following the design at ~/.claude/docs/plans/2025-01-06-guardrail-hooks-design.md
Implementation order:
1. Create skill skeleton: ~/.claude/skills/morning-report/ with SKILL.md and config.json
2. Build collectors: weather.py, stocks.py, infra.py (easy wins first)
3. Build gtasks.py collector (Google Tasks API - add OAuth scope)
4. Build news.py collector (RSS feeds)
5. Build generate.py orchestrator and render.py templating
6. Create systemd timer and /morning command
7. Test end-to-end and verify output
Use appropriate LLM tiers:
- Haiku: weather, stocks, infra formatting
- Sonnet: email triage, news summarization
- None: calendar, tasks (structured data)
1. Create state/guardrails.json with starter rules config
2. Create hooks/scripts/guardrail.py main logic
3. Create hooks/scripts/guardrail-confirm.py confirm helper
4. Modify hooks/hooks.json to add PreToolUse registration
5. Modify hooks/scripts/session-end.sh to clear session allowlist
6. Create logs/ directory
7. Test: block scenario (catastrophic command pattern)
8. Test: confirm scenario (operation outside safe paths)
9. Test: allow scenario (operation in safe path)
10. Test: git-aware detection
Register in component-registry.json when complete.
+3
View File
@@ -49,3 +49,6 @@ repos/homelab
# RAG search data (generated vector stores and caches)
data/
skills/rag-search/venv/
# Telemetry (analytics tracking)
telemetry/
+4
View File
@@ -45,6 +45,8 @@ See `agents/README.md` for details on agent files and execution.
├── commands/ # Slash command definitions
├── workflows/ # Workflow definitions (design docs)
│ └── README.md
├── plans/ # Implementation plans
│ └── index.json # Plan status registry
├── state/ # Shared state files (JSON)
│ ├── sysadmin/
│ ├── programmer/
@@ -67,6 +69,7 @@ All agents MUST read and follow the processes defined in these files:
| `state/personal-assistant-preferences.json` | PA persistent config | personal-assistant |
| `state/personal-assistant/general-instructions.json` | User memory | personal-assistant |
| `state/kb.json` | Shared knowledge base | personal-assistant |
| `plans/index.json` | Plan status registry | any agent |
## Key Processes
@@ -126,6 +129,7 @@ curl -s -X PATCH \
| **Skills** | SKILL.md + scripts/ + references/ | `skills/` |
| **Commands** | Markdown + YAML frontmatter | `commands/` |
| **Workflows** | YAML (design docs, not auto-executed) | `workflows/` |
| **Plans** | Markdown + index.json | `plans/` |
| **State** | JSON | `state/` |
| **Hooks** | JSON | `hooks/` |
@@ -0,0 +1,270 @@
# Guardrail Hooks Design
**Date:** 2025-01-06
**Status:** Approved
## Overview
PreToolUse guardrail hooks that prevent dangerous operations by intercepting Bash, Write, and Edit tool calls before execution.
### Goals
- Prevent catastrophic operations (destructive file ops, dangerous system commands, infrastructure mistakes)
- Contextual response: hard block for severe threats, confirmation for moderate risks
- Path-aware: operations inside projects are more permissive than outside
- Auditable: log all interventions for review
### Non-Goals
- Git command guardrails (future consideration)
- Rate limiting or resource protection
- Workflow compliance enforcement
## Architecture
```
~/.claude/
├── hooks/
│ └── hooks.json # Hook registration (add PreToolUse)
├── hooks/scripts/
│ ├── guardrail.py # Main PreToolUse logic
│ └── guardrail-confirm.py # Adds operation to session allowlist
├── state/
│ ├── guardrails.json # Rules configuration
│ └── guardrail-session.json # Session allowlist (cleared on session end)
└── logs/
└── guardrail.jsonl # Audit log (blocked/confirmed only)
```
### Flow
1. Claude invokes Bash/Write/Edit
2. `guardrail.py` runs via PreToolUse hook
3. Check session allowlist - if present, allow
4. Evaluate rules + path context - decide action
5. Allow: return `{"decision": "allow"}`
6. Block/Confirm: log, return `{"decision": "block", "reason": "..."}`
7. For confirm: user approves, Claude calls `guardrail-confirm.py`, retries
## Configuration
**Location:** `~/.claude/state/guardrails.json`
```json
{
"version": 1,
"safe_paths": [
"~/.claude",
"~/projects"
],
"blocked_paths": [
"/etc", "/usr", "/var", "/boot", "/sys", "/proc",
"~/.ssh", "~/.gnupg", "~/.aws"
],
"rules": {
"bash": [
{"pattern": "rm -rf /", "action": "block"},
{"pattern": "rm -rf ~", "action": "block"},
{"pattern": "rm -rf \\*", "action": "block"},
{"pattern": "chmod -R 777", "action": "block"},
{"pattern": ":(){ :|:& };:", "action": "block"},
{"pattern": "mkfs\\.", "action": "block"},
{"pattern": "dd if=.* of=/dev/", "action": "block"},
{"pattern": "> /dev/sda", "action": "block"},
{"pattern": "shutdown", "action": "confirm"},
{"pattern": "reboot", "action": "confirm"},
{"pattern": "systemctl (stop|disable|mask)", "action": "confirm"},
{"pattern": "rm ", "action": "confirm", "outside_safe_paths": true},
{"pattern": "kubectl delete", "action": "confirm"},
{"pattern": "docker rm", "action": "confirm"}
],
"write": [
{"path_patterns": ["blocked_paths"], "action": "block"},
{"path_patterns": ["outside_safe_paths"], "action": "confirm"}
],
"edit": [
{"path_patterns": ["blocked_paths"], "action": "block"},
{"path_patterns": ["outside_safe_paths"], "action": "confirm"}
]
}
}
```
### Key Concepts
- `pattern`: regex matched against command/path
- `action`: `block` (hard stop) or `confirm` (require approval)
- `outside_safe_paths`: rule only applies when target is outside safe directories
- Bash rules check command string; Write/Edit rules check file path
## Safe Paths Logic
**Evaluation order (first match wins):**
1. **Blocked paths check** - Is target in `blocked_paths`?
- Yes: apply block/confirm regardless of other factors
- Protects system-critical locations absolutely
2. **Explicit allowlist** - Is target under a `safe_paths` entry?
- Yes: path is safe
- Supports glob patterns (`~/projects/*`)
3. **Git-aware detection** - Is target inside a git repository?
- Walk up from target path looking for `.git` directory
- Found: treat as safe (it's an intentional project)
4. **Default** - Path is outside safe areas
- Operations here trigger `outside_safe_paths` rules
### Path Normalization
- Expand `~` to actual home directory
- Resolve symlinks to real paths
- Handle relative paths by resolving against CWD
### Examples
| Target Path | Result | Reason |
|-------------|--------|--------|
| `/etc/hosts` | blocked/confirm | In `blocked_paths` |
| `~/.ssh/config` | blocked/confirm | In `blocked_paths` |
| `~/.claude/hooks/test.py` | safe | In `safe_paths` |
| `~/projects/myapp/src/main.py` | safe | In `safe_paths` |
| `~/random-repo/file.txt` | safe | Git repo detected |
| `~/Downloads/file.txt` | outside | No match, triggers rules |
**Edge case:** If a git repo exists under a blocked path (unlikely), blocked path wins.
## Hook Implementation
### Registration
Add to `~/.claude/hooks/hooks.json`:
```json
{
"PreToolUse": [
{
"matcher": "Bash|Write|Edit",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/scripts/guardrail.py",
"timeout": 5
}
]
}
]
}
```
### Hook Input (stdin)
```json
{
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf ~/Downloads/old-project"
}
}
```
### Hook Output (stdout)
Allow:
```json
{"decision": "allow"}
```
Block:
```json
{
"decision": "block",
"reason": "GUARDRAIL BLOCKED: 'rm' outside safe paths.\nTarget: ~/Downloads/old-project\nRule: rm_outside_safe\n\nTo proceed, confirm with user then run:\npython ~/.claude/hooks/scripts/guardrail-confirm.py 'rm -rf ~/Downloads/old-project'"
}
```
## Confirmation Flow
1. **Guardrail blocks** with detailed message including confirm command
2. **Claude reports** to user, asks for confirmation
3. **User confirms** in conversation
4. **Claude runs confirm script:**
```bash
python ~/.claude/hooks/scripts/guardrail-confirm.py "rm -rf ~/Downloads/old-project"
```
5. **Confirm script** adds to session allowlist
6. **Claude retries** the original command
7. **Guardrail checks allowlist** - finds match - allows
### Session Allowlist
**Location:** `~/.claude/state/guardrail-session.json`
```json
{
"confirmed": [
{"tool": "Bash", "operation": "rm -rf ~/Downloads/old-project", "ts": "2025-01-06T10:24:00"}
]
}
```
**Cleanup:** `session-end.sh` clears this file so confirmations don't persist across sessions.
**Matching:** Exact match on tool + operation string.
## Logging
**Location:** `~/.claude/logs/guardrail.jsonl`
**Format:** JSON Lines (append-only)
**Logged events:**
- Every `block` action
- Every `confirm` action (required approval)
- When confirmed operation is allowed
### Log Entry Structure
```json
{"ts": "2025-01-06T10:23:45", "tool": "Bash", "operation": "rm -rf /", "action": "block", "rule": "rm_rf_root", "path_context": "n/a"}
{"ts": "2025-01-06T10:24:01", "tool": "Bash", "operation": "rm ~/Downloads/old", "action": "confirm_required", "rule": "rm_outside_safe", "path_context": "outside"}
{"ts": "2025-01-06T10:24:30", "tool": "Bash", "operation": "rm ~/Downloads/old", "action": "confirmed_allow", "rule": "session_allowlist"}
```
### Fields
- `ts`: ISO 8601 timestamp
- `tool`: Bash, Write, or Edit
- `operation`: Command or file path
- `action`: `block`, `confirm_required`, or `confirmed_allow`
- `rule`: Which rule triggered
- `path_context`: `safe`, `outside`, `blocked`, or `n/a`
## Implementation Checklist
- [ ] Create `state/guardrails.json` with starter rules
- [ ] Create `hooks/scripts/guardrail.py` main logic
- [ ] Create `hooks/scripts/guardrail-confirm.py` confirm helper
- [ ] Modify `hooks/hooks.json` to add PreToolUse registration
- [ ] Modify `hooks/scripts/session-end.sh` to clear session allowlist
- [ ] Create `logs/` directory
- [ ] Test: block scenario (catastrophic command)
- [ ] Test: confirm scenario (rm outside safe paths)
- [ ] Test: allow scenario (operation in safe path)
- [ ] Test: git-aware detection
## Starter Rules
| Category | Pattern | Action |
|----------|---------|--------|
| Catastrophic | `rm -rf /`, `rm -rf ~`, `mkfs.*`, `dd.*of=/dev/` | block |
| Fork bomb | `:(){ :\|:& };:` | block |
| Dangerous chmod | `chmod -R 777` | block |
| System commands | `shutdown`, `reboot` | confirm |
| Service control | `systemctl (stop\|disable\|mask)` | confirm |
| Destructive outside safe | `rm ` (outside safe paths) | confirm |
| K8s destructive | `kubectl delete` | confirm |
| Docker destructive | `docker rm`, `docker system prune` | confirm |
| Write to blocked paths | Any Write/Edit to `/etc`, `~/.ssh`, etc. | block |
| Write outside safe | Any Write/Edit outside safe paths | confirm |
+12
View File
@@ -41,6 +41,18 @@
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash|Write|Edit",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/scripts/guardrail.py",
"timeout": 5
}
]
}
]
}
}
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""
Guardrail Confirm Helper
Adds an operation to the session allowlist so it can proceed on retry.
Usage:
python3 guardrail-confirm.py "<tool>" "<operation>"
Example:
python3 guardrail-confirm.py "Bash" "rm -rf ~/Downloads/old-project"
"""
import json
import sys
from datetime import datetime, timezone
from pathlib import Path
HOME = Path.home()
STATE_DIR = HOME / ".claude" / "state"
SESSION_FILE = STATE_DIR / "guardrail-session.json"
def load_session():
"""Load current session allowlist."""
if not SESSION_FILE.exists():
return {"confirmed": []}
try:
with open(SESSION_FILE) as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
return {"confirmed": []}
def save_session(data: dict):
"""Save session allowlist."""
STATE_DIR.mkdir(parents=True, exist_ok=True)
with open(SESSION_FILE, "w") as f:
json.dump(data, f, indent=2)
def main():
if len(sys.argv) != 3:
print("Usage: guardrail-confirm.py <tool> <operation>")
print("Example: guardrail-confirm.py 'Bash' 'rm ~/Downloads/old'")
sys.exit(1)
tool = sys.argv[1]
operation = sys.argv[2]
# Load current session
session = load_session()
# Check if already confirmed
for item in session.get("confirmed", []):
if item.get("tool") == tool and item.get("operation") == operation:
print(f"Already confirmed: {tool} - {operation[:50]}...")
sys.exit(0)
# Add to allowlist
session["confirmed"].append({
"tool": tool,
"operation": operation,
"ts": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
})
save_session(session)
print(f"Confirmed: {tool} - {operation[:50]}...")
print("You may now retry the operation.")
if __name__ == "__main__":
main()
+283
View File
@@ -0,0 +1,283 @@
#!/usr/bin/env python3
"""
Guardrail PreToolUse Hook
Intercepts Bash, Write, and Edit tool calls to prevent dangerous operations.
Returns JSON decision: {"decision": "allow"} or {"decision": "block", "reason": "..."}
"""
import json
import os
import re
import sys
from datetime import datetime, timezone
from pathlib import Path
# Paths
HOME = Path.home()
STATE_DIR = HOME / ".claude" / "state"
LOGS_DIR = HOME / ".claude" / "logs"
CONFIG_FILE = STATE_DIR / "guardrails.json"
SESSION_FILE = STATE_DIR / "guardrail-session.json"
LOG_FILE = LOGS_DIR / "guardrail.jsonl"
def load_config():
"""Load guardrails configuration."""
if not CONFIG_FILE.exists():
return None
with open(CONFIG_FILE) as f:
return json.load(f)
def load_session_allowlist():
"""Load session allowlist of confirmed operations."""
if not SESSION_FILE.exists():
return {"confirmed": []}
try:
with open(SESSION_FILE) as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
return {"confirmed": []}
def is_in_allowlist(tool: str, operation: str) -> bool:
"""Check if operation was previously confirmed."""
allowlist = load_session_allowlist()
for item in allowlist.get("confirmed", []):
if item.get("tool") == tool and item.get("operation") == operation:
return True
return False
def expand_path(path: str) -> Path:
"""Expand ~ and resolve path."""
return Path(os.path.expanduser(path)).resolve()
def is_under_path(target: Path, parent: str) -> bool:
"""Check if target is under parent path."""
try:
parent_path = expand_path(parent)
# Handle glob patterns like ~/projects/*
if "*" in parent:
# For ~/projects/*, check if under ~/projects
parent_path = expand_path(parent.replace("/*", "").replace("*", ""))
return parent_path in target.parents or target == parent_path
except (ValueError, OSError):
return False
def is_in_git_repo(path: Path) -> bool:
"""Check if path is inside a git repository."""
current = path if path.is_dir() else path.parent
while current != current.parent:
if (current / ".git").exists():
return True
current = current.parent
return False
def classify_path(target_path: str, config: dict) -> str:
"""
Classify a path as 'blocked', 'safe', or 'outside'.
Evaluation order:
1. Blocked paths -> 'blocked'
2. Safe paths -> 'safe'
3. Git repo -> 'safe'
4. Otherwise -> 'outside'
"""
try:
target = expand_path(target_path)
except (ValueError, OSError):
return "outside"
# Check blocked paths first
for blocked in config.get("blocked_paths", []):
if is_under_path(target, blocked):
return "blocked"
# Check safe paths
for safe in config.get("safe_paths", []):
if is_under_path(target, safe):
return "safe"
# Check if in git repo
if is_in_git_repo(target):
return "safe"
return "outside"
def extract_paths_from_command(command: str) -> list[str]:
"""Extract potential file paths from a bash command."""
paths = []
# Simple heuristic: look for things that look like paths
# This catches ~/..., /..., and relative paths
tokens = command.split()
for token in tokens:
# Skip flags
if token.startswith("-"):
continue
# Skip common commands
if token in ("rm", "mv", "cp", "chmod", "chown", "mkdir", "rmdir", "touch"):
continue
# Check if it looks like a path
if "/" in token or token.startswith("~"):
paths.append(token)
return paths
def check_bash_rules(command: str, config: dict) -> tuple[str, str | None, str]:
"""
Check bash command against rules.
Returns: (action, rule_name, path_context)
action: 'allow', 'block', or 'confirm'
"""
rules = config.get("rules", {}).get("bash", [])
for rule in rules:
pattern = rule.get("pattern", "")
action = rule.get("action", "allow")
name = rule.get("name", "unnamed")
outside_safe_only = rule.get("outside_safe_paths", False)
# Check if pattern matches
if re.search(pattern, command):
if outside_safe_only:
# Only apply rule if operating outside safe paths
paths = extract_paths_from_command(command)
for path in paths:
path_class = classify_path(path, config)
if path_class in ("blocked", "outside"):
return (action, name, path_class)
# All paths are safe, allow
continue
else:
# Rule applies regardless of path
return (action, name, "n/a")
return ("allow", None, "safe")
def check_file_rules(file_path: str, tool: str, config: dict) -> tuple[str, str | None, str]:
"""
Check Write/Edit file path against rules.
Returns: (action, rule_name, path_context)
"""
rules = config.get("rules", {}).get(tool.lower(), [])
path_class = classify_path(file_path, config)
for rule in rules:
path_match = rule.get("path_match", "")
action = rule.get("action", "allow")
name = rule.get("name", "unnamed")
if path_match == "blocked_paths" and path_class == "blocked":
return (action, name, path_class)
elif path_match == "outside_safe_paths" and path_class == "outside":
return (action, name, path_class)
return ("allow", None, path_class)
def log_action(tool: str, operation: str, action: str, rule: str | None, path_context: str):
"""Log guardrail action to audit log."""
LOGS_DIR.mkdir(parents=True, exist_ok=True)
entry = {
"ts": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
"tool": tool,
"operation": operation[:200], # Truncate long operations
"action": action,
"rule": rule or "none",
"path_context": path_context
}
with open(LOG_FILE, "a") as f:
f.write(json.dumps(entry) + "\n")
def allow():
"""Return allow decision."""
print(json.dumps({"decision": "allow"}))
sys.exit(0)
def block(reason: str):
"""Return block decision with reason."""
print(json.dumps({"decision": "block", "reason": reason}))
sys.exit(0)
def main():
# Read input from stdin
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
allow() # If we can't parse input, allow (fail open)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
# Only check Bash, Write, Edit
if tool_name not in ("Bash", "Write", "Edit"):
allow()
# Load config
config = load_config()
if not config:
allow() # No config, allow everything
# Determine operation string for allowlist check
if tool_name == "Bash":
operation = tool_input.get("command", "")
else:
operation = tool_input.get("file_path", "")
# Check session allowlist first
if is_in_allowlist(tool_name, operation):
log_action(tool_name, operation, "confirmed_allow", "session_allowlist", "n/a")
allow()
# Check rules based on tool type
if tool_name == "Bash":
action, rule_name, path_context = check_bash_rules(operation, config)
else:
action, rule_name, path_context = check_file_rules(operation, tool_name, config)
# Take action
if action == "allow":
allow()
# Log blocked/confirm actions
log_action(tool_name, operation, action if action == "block" else "confirm_required", rule_name, path_context)
# Build block message
if action == "block":
reason = f"""GUARDRAIL BLOCKED: Operation not allowed.
Tool: {tool_name}
Operation: {operation}
Rule: {rule_name}
Path context: {path_context}
This operation is blocked by guardrail policy and cannot proceed."""
else: # confirm
confirm_cmd = f'python3 ~/.claude/hooks/scripts/guardrail-confirm.py "{tool_name}" "{operation}"'
reason = f"""GUARDRAIL: User confirmation required.
Tool: {tool_name}
Operation: {operation}
Rule: {rule_name}
Path context: {path_context}
To proceed after user confirms, run:
{confirm_cmd}
Then retry the original operation."""
block(reason)
if __name__ == "__main__":
main()
+7
View File
@@ -28,6 +28,13 @@ REASON=$(echo "$INPUT" | python3 -c "import sys, json; print(json.load(sys.stdin
log "SessionEnd triggered: session=$SESSION_ID reason=$REASON"
# Clear guardrail session allowlist (confirmations don't persist across sessions)
GUARDRAIL_SESSION="${HOME}/.claude/state/guardrail-session.json"
if [[ -f "$GUARDRAIL_SESSION" ]]; then
rm -f "$GUARDRAIL_SESSION"
log "Cleared guardrail session allowlist"
fi
# Validate required fields
if [[ -z "$SESSION_ID" || -z "$TRANSCRIPT_PATH" ]]; then
log "ERROR: Missing session_id or transcript_path"
+23
View File
@@ -378,6 +378,29 @@ def main():
log(f"Summarization complete: {total_added} total items added")
# Reindex RAG if we added items
if total_added > 0:
log("Triggering RAG reindex...")
try:
reindex_result = subprocess.run(
[
str(Path.home() / ".claude/skills/rag-search/venv/bin/python"),
str(Path.home() / ".claude/skills/rag-search/scripts/index_personal.py"),
"--quiet"
],
capture_output=True,
text=True,
timeout=120
)
if reindex_result.returncode == 0:
log("RAG reindex completed successfully")
else:
log(f"RAG reindex failed: {reindex_result.stderr[:200]}")
except subprocess.TimeoutExpired:
log("RAG reindex timed out after 120s")
except Exception as e:
log(f"RAG reindex error: {e}")
if __name__ == "__main__":
main()
+74
View File
@@ -0,0 +1,74 @@
# Plans
Implementation plans for features, enhancements, and investigations.
## Structure
```
plans/
├── index.json # Status registry (source of truth)
├── README.md # This file
└── *.md # Individual plan files
```
## Plan Naming
- **Dated plans**: `YYYY-MM-DD-descriptive-name.md` (design docs)
- **Generated names**: `adjective-verb-scientist.md` (brainstorming outputs)
## Status Registry (index.json)
Central tracking for all plans:
```json
{
"plan-name": {
"title": "Human readable title",
"status": "pending|implemented|partial|abandoned|superseded",
"created": "YYYY-MM-DD",
"implemented": "YYYY-MM-DD",
"category": "feature|enhancement|bugfix|diagnostic|design",
"notes": "Optional notes"
}
}
```
### Status Values
| Status | Meaning |
|--------|---------|
| `pending` | Not yet implemented |
| `implemented` | Fully implemented |
| `partial` | Partially implemented |
| `abandoned` | Decided not to implement |
| `superseded` | Replaced by another plan |
### Categories
| Category | Meaning |
|----------|---------|
| `feature` | New capability |
| `enhancement` | Improve existing feature |
| `bugfix` | Fix an issue |
| `diagnostic` | One-time investigation |
| `design` | Design document for reference |
## Querying Plans
```bash
# List pending plans
jq -r '.plans | to_entries[] | select(.value.status == "pending") | .key' index.json
# List by category
jq '.plans | to_entries[] | select(.value.category == "feature")' index.json
# Count by status
jq '.plans | to_entries | group_by(.value.status) | map({status: .[0].value.status, count: length})' index.json
```
## Workflow
1. **Create plan**: Write `plans/plan-name.md`
2. **Register**: Add entry to `index.json` with `status: "pending"`
3. **Implement**: Execute the plan
4. **Update**: Set `status: "implemented"` and add `implemented` date
+798
View File
@@ -0,0 +1,798 @@
# Plan: Transpose Claude Code Setup to OpenCode (Parallel)
## Handoff Summary
**Goal**: Set up OpenCode in parallel with Claude Code, sharing state files and syncing agents/skills.
**Status**: ✅ **IMPLEMENTATION COMPLETE** (2026-01-07)
### Key Decisions Made
1. **Use built-in `build` agent** as primary (not porting `personal-assistant`)
2. **All agents synced as subagents** (SKIP_AGENTS kept empty for flexibility)
3. **Model inheritance** - subagents use runtime-selected model
4. **Claude Code is source of truth** - OpenCode references state files via `instructions`
5. **No JSON minification** needed (files too small, added to future considerations as fc-047)
### What Was Completed
| Step | Status | Notes |
|------|--------|-------|
| 1 | ✅ | Backups created (Jan 7 12:01) |
| 2 | ✅ | Sync script enhanced (mode:subagent, model removal) |
| 3 | ✅ | Sync run: 10 skills, 13 agents, 27 commands, 10 workflows |
| 4 | ✅ | opencode.json updated (instructions, permissions) |
| 5 | ✅ | Automated tests passed, manual TUI testing pending |
| 6 | ✅ | README.md created (4.7KB), fc-047 added |
| 7 | ⏳ | Iterate as needed |
### Critical Files
**Modified:**
- `~/.config/opencode/scripts/claude_sync.py` ✅ - Added mode:subagent, model removal, skip logic
- `~/.config/opencode/opencode.json` ✅ - Added instructions, permissions
**Created:**
- `~/.config/opencode/README.md` ✅ - Documentation (4.7KB)
**Referenced (not copied):**
- `~/.claude/CLAUDE.md`
- `~/.claude/state/kb.json`
- `~/.claude/state/personal-assistant/memory/*.json`
### Architecture
```
OpenCode (after implementation)
├── Primary: build (built-in), plan (built-in)
├── Subagents: @linux-sysadmin, @k8s-orchestrator, @code-reviewer, etc.
├── Skills: auto-discovered from ~/.claude/skills/
└── State: referenced via instructions from ~/.claude/state/
```
### Start Command
```bash
# Exit plan mode and begin implementation
# Step 1: Backup
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
tar -czvf ~/.config/opencode-backup-$BACKUP_DATE.tar.gz -C ~/.config opencode/
tar -czvf ~/opencode-home-backup-$BACKUP_DATE.tar.gz -C ~ .opencode/
```
---
## Goal
Create a parallel OpenCode configuration that shares/reuses as much of the existing Claude Code infrastructure as possible, focusing on:
1. **Skills/scripts execution** (highest priority)
2. **Agent hierarchy/delegation** (second priority)
3. **State persistence** (if low complexity)
## Key Discovery: Native Compatibility
OpenCode **natively supports Claude-compatible skill paths**:
- `~/.claude/skills/<name>/SKILL.md` - Already supported!
- This means your 11 existing skills can work with minimal changes
---
## Phase 0: Backup Existing OpenCode Setup
### Current State Discovered
OpenCode is already installed with substantial configuration:
**`~/.config/opencode/`** (main config):
- `opencode.json` - Has `claude-sync` command already!
- `agent/` - 3 custom agents (coding-expert, k8s-expert, tdd-enforcer)
- `agents/` - 12 synced Claude Code agents (already converted!)
- `skills/` - 10 skills (some synced, one symlink to morning-report)
- `scripts/claude_sync.py` - Existing sync script!
**`~/.opencode/`** (alternate config):
- `agent/` - 4 different agents (openagent, system-builder, etc.)
- `command/` - 12 commands (commit, optimize, validate-repo, etc.)
### Backup Commands
```bash
# Create timestamped backups
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
# Backup ~/.config/opencode/
tar -czvf ~/.config/opencode-backup-$BACKUP_DATE.tar.gz -C ~/.config opencode/
# Backup ~/.opencode/
tar -czvf ~/opencode-home-backup-$BACKUP_DATE.tar.gz -C ~ .opencode/
# Verify backups
ls -la ~/.config/opencode-backup-*.tar.gz ~/opencode-home-backup-*.tar.gz
```
---
## Phase 1: Use Existing `claude_sync.py` Script
The existing sync script is **comprehensive** and handles:
| Category | Source | Destination | Transforms |
|----------|--------|-------------|------------|
| Skills | `~/.claude/skills/*/SKILL.md` | `~/.config/opencode/skills/*/SKILL.md` | `allowed-tools``metadata.claude_allowed_tools` |
| Agents | `~/.claude/agents/*.md` | `~/.config/opencode/agents/*.md` | `tools: X, Y``tools: { x: true, y: true }` |
| Commands | `~/.claude/commands/*.md` | `~/.config/opencode/claude/commands/*.md` | None |
| Workflows | `~/.claude/workflows/*.yaml` | `~/.config/opencode/claude/workflows/*.yaml` | None |
### Sync Commands
```bash
# Dry run - see what would change
python3 ~/.config/opencode/scripts/claude_sync.py --dry-run
# Actually sync
python3 ~/.config/opencode/scripts/claude_sync.py
# Clean stale files (dry run first)
python3 ~/.config/opencode/scripts/claude_sync.py --clean --dry-run
python3 ~/.config/opencode/scripts/claude_sync.py --clean --apply
# Sync specific category only
python3 ~/.config/opencode/scripts/claude_sync.py --only agents
```
### Model Mapping Update Needed
Current script maps old models. May need to add:
- `opus``anthropic/claude-opus-4`
- `sonnet``anthropic/claude-sonnet-4-5`
- `haiku``anthropic/claude-haiku-4-5`
---
## Phase 1.5: OpenCode Optimization (NEW)
The current sync just copies/transforms files. It doesn't optimize for **how OpenCode works**.
### Key OpenCode Differences
| Concept | Claude Code | OpenCode | Optimization Needed |
|---------|-------------|----------|---------------------|
| **Agent hierarchy** | PA → MO → agents | Flat: primary + subagents | Add `mode` field |
| **Agent invocation** | Delegation patterns | `@agent` mentions | Simplify prompts |
| **Permissions** | Hooks + guardrails | `permission` config | Move to opencode.json |
| **Model selection** | Per-agent in frontmatter | `model: inherit` option | Use inheritance |
| **Auto-invocation** | Keyword triggers in registry | Rich `description` field | Enhance descriptions |
### Agent Mode Assignment
```yaml
# PRIMARY - Use OpenCode's built-in agents
build: (built-in) # Full access, default primary
plan: (built-in) # Read-only analysis
# SKIP - Not needed in OpenCode's flat model
personal-assistant: # Use built-in "build" instead
master-orchestrator: # Intermediary not needed
# SUBAGENTS (invoked via @mention or Task tool)
linux-sysadmin: mode: subagent
k8s-orchestrator: mode: subagent
k8s-diagnostician: mode: subagent
argocd-operator: mode: subagent
prometheus-analyst: mode: subagent
git-operator: mode: subagent
programmer-orchestrator: mode: subagent
code-planner: mode: subagent
code-implementer: mode: subagent
code-reviewer: mode: subagent
```
### Hierarchy Simplification
**Claude Code pattern** (complex, 3 layers):
```
User → Personal Assistant → Master Orchestrator → linux-sysadmin
→ k8s-orchestrator → k8s-diagnostician
```
**OpenCode pattern** (flat, 2 layers):
```
User → build (built-in) → @linux-sysadmin
→ @k8s-orchestrator
→ @k8s-diagnostician
→ @code-reviewer
→ etc.
```
Benefits:
- No custom primary agent to maintain
- Built-in `build` agent is optimized for OpenCode
- Built-in `plan` agent available for read-only analysis
- Subagents invoked directly via @mention
### Sync Script Enhancements Needed
Update `claude_sync.py` to add:
```python
# In transform_frontmatter() for agents:
# 1. Skip agents not needed in OpenCode's flat model
SKIP_AGENTS = {"personal-assistant", "master-orchestrator"}
if name in SKIP_AGENTS:
return None # Signal to skip this file
# 2. All synced agents become subagents (built-in build/plan are primary)
frontmatter["mode"] = "subagent"
# 3. Use model inheritance (subagents use parent's model)
frontmatter["model"] = "inherit"
# 4. Map explicit models if not using inherit
MODEL_MAP = {
"opus": "anthropic/claude-opus-4",
"sonnet": "anthropic/claude-sonnet-4-5",
"haiku": "anthropic/claude-haiku-4-5",
}
if frontmatter.get("model") in MODEL_MAP:
frontmatter["model"] = MODEL_MAP[frontmatter["model"]]
```
Also update `sync_tree()` to handle `None` return (skip file).
### Description Enhancement
OpenCode uses descriptions for **auto-invocation**. Enhance with examples:
**Current** (basic):
```yaml
description: Manages Arch Linux workstation - system maintenance...
```
**Optimized** (with examples):
```yaml
description: |
Manages Arch Linux workstation. Use for system maintenance, updates,
troubleshooting, and health checks.
Examples:
- "check system health" → @linux-sysadmin
- "update packages" → @linux-sysadmin
- "why is my disk full" → @linux-sysadmin
```
### Permission Migration
Move guardrail logic to opencode.json:
```json
{
"permission": {
"edit": "ask",
"bash": {
"*": "ask",
"pacman -Q*": "allow",
"systemctl status*": "allow",
"kubectl get*": "allow",
"git status": "allow",
"git diff": "allow"
}
}
}
```
---
## Phase 2: Create OpenCode Config Structure
### Directory Layout
```
~/.config/opencode/
├── opencode.json # Main config
├── AGENTS.md # Global rules (symlink or copy from CLAUDE.md)
├── agent/ # Agent definitions
│ ├── personal-assistant.md
│ ├── linux-sysadmin.md
│ ├── k8s-orchestrator.md
│ └── ... (converted agents)
├── tool/ # Custom tool wrappers (TypeScript)
│ ├── gmail.ts # Wrapper for gmail scripts
│ ├── gcal.ts # Wrapper for gcal scripts
│ └── ...
└── skill/ # OpenCode-native skills (optional)
```
### Config File: `~/.config/opencode/opencode.json`
```jsonc
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-sonnet-4-5",
"small_model": "anthropic/claude-haiku-4-5",
"autoupdate": true,
// OpenCode already searches ~/.claude/skills/ - no extra config needed!
// Agent definitions
"agent": {
// Override built-in agents or define custom via files
},
// Default permissions (conservative like your current setup)
"permission": {
"edit": "ask",
"bash": "ask"
},
// Custom tools enabled
"tools": {
"gmail": true,
"gcal": true,
"gtasks": true
}
}
```
---
## Phase 3: Skills Migration
### Already Compatible (No Changes Needed)
OpenCode automatically discovers skills from:
- `~/.claude/skills/*/SKILL.md`
Your existing skills should work if they have proper frontmatter:
| Skill | Status | Notes |
|-------|--------|-------|
| gmail | Check frontmatter | Needs `name` + `description` |
| gcal | Check frontmatter | Needs `name` + `description` |
| gtasks | Check frontmatter | Needs `name` + `description` |
| sysadmin-health | Check frontmatter | |
| k8s-quick-status | Check frontmatter | |
| morning-report | Check frontmatter | |
| stock-lookup | Check frontmatter | |
| rag-search | Check frontmatter | |
| usage | Check frontmatter | |
| guardrails | N/A | Becomes permission config |
### Frontmatter Requirements
Each SKILL.md needs:
```yaml
---
name: skill-name # Required, must match directory name
description: Brief desc # Required, 1-1024 chars
---
```
### Audit Results (Already Compatible!)
Checked skills have proper frontmatter:
- `gmail/SKILL.md` - Has `name: gmail`, `description: ...`
- `sysadmin-health/SKILL.md` - Has `name: sysadmin-health`, `description: ...`
- `morning-report/SKILL.md` - Has `name: morning-report`, `description: ...`
The `allowed-tools` field in some skills will be ignored by OpenCode (not in their schema), but this is fine.
### Action Items
1. ~~Audit each SKILL.md for required frontmatter~~ **Done - already compatible!**
2. ~~Add missing `name`/`description` fields~~ **Not needed**
3. Test skill discovery in OpenCode after install
---
## Phase 4: Agent Migration
### Mapping Strategy
| Claude Code | OpenCode | Notes |
|------------|----------|-------|
| `model: opus` | `model: anthropic/claude-opus-4` | Full provider/model path |
| `model: sonnet` | `model: anthropic/claude-sonnet-4-5` | |
| `model: haiku` | `model: anthropic/claude-haiku-4-5` | |
| `tools: Read, Write...` | `tools: { write: true, ... }` | Boolean map |
| Hierarchy (PA → MO → agent) | `mode: primary` + `mode: subagent` | Flattened |
### Agent Conversion Template
**From (Claude Code):**
```yaml
---
name: linux-sysadmin
description: Manages Arch Linux workstation...
model: sonnet
tools: Bash, Read, Write, Edit, Grep, Glob
---
```
**To (OpenCode):**
```yaml
---
name: linux-sysadmin
description: Manages Arch Linux workstation...
mode: subagent
model: anthropic/claude-sonnet-4-5
tools:
bash: true
read: true
write: true
edit: true
permission:
bash:
"*": ask
"pacman -Q*": allow
"systemctl status*": allow
---
```
### Priority Agents to Convert
1. **personal-assistant.md**`mode: primary` (main interface)
2. **linux-sysadmin.md**`mode: subagent`
3. **k8s-orchestrator.md**`mode: subagent`
4. **master-orchestrator.md** → May not be needed (OpenCode doesn't have same hierarchy)
### Hierarchy Adaptation
OpenCode doesn't have hierarchical agent delegation like your current setup. Options:
- **Option A**: Flatten to primary + subagents, use `@agent` mentions
- **Option B**: Use OpenCode's Task tool for agent invocation
- **Option C**: Create a "dispatcher" primary agent that routes via @mentions
**Recommendation**: Option A (simplest) - personal-assistant as primary, others as subagents invokable via `@linux-sysadmin`, `@k8s-orchestrator`, etc.
---
## Phase 5: Custom Tools (Scripts Execution)
### Wrapper Pattern
Create TypeScript wrappers that invoke your existing Python scripts:
**Example: `~/.config/opencode/tool/gmail.ts`**
```typescript
import { tool } from "@opencode-ai/plugin"
export const check_unread = tool({
description: "Check unread emails from Gmail",
args: {
limit: tool.schema.number().optional().describe("Max emails to return"),
},
async execute(args) {
const limit = args.limit ?? 10
const result = await Bun.$`~/.claude/mcp/gmail/venv/bin/python ~/.claude/skills/gmail/scripts/check_unread.py --limit ${limit}`.text()
return result.trim()
},
})
export const search = tool({
description: "Search Gmail for specific emails",
args: {
query: tool.schema.string().describe("Search query"),
},
async execute(args) {
const result = await Bun.$`~/.claude/mcp/gmail/venv/bin/python ~/.claude/skills/gmail/scripts/search.py "${args.query}"`.text()
return result.trim()
},
})
```
### Tools to Create Wrappers For
| Script | Wrapper |
|--------|---------|
| `gmail/scripts/*.py` | `gmail.ts` |
| `gcal/scripts/*.py` | `gcal.ts` |
| `gtasks/scripts/*.py` | `gtasks.ts` |
| `sysadmin-health/scripts/*.sh` | `sysadmin.ts` |
| `morning-report/scripts/*.py` | `morning.ts` |
| `stock-lookup/scripts/*.py` | `stocks.ts` |
---
## Phase 6: Rules/Instructions
### Option A: Symlink CLAUDE.md
```bash
ln -s ~/.claude/CLAUDE.md ~/.config/opencode/AGENTS.md
```
### Option B: Create Minimal AGENTS.md + Reference
```markdown
# OpenCode Agent Rules
Read @~/.claude/CLAUDE.md for shared conventions.
## OpenCode-Specific
- Use `@agent-name` to invoke subagents
- Skills are loaded via the `skill` tool
- Custom tools available: gmail, gcal, gtasks, sysadmin
```
### Option C: Use instructions config
```json
{
"instructions": ["~/.claude/CLAUDE.md", "~/.claude/state/system-instructions.json"]
}
```
**Recommendation**: Option C - cleanest, no duplication
---
## Phase 7: State Persistence (Claude Code as Source of Truth)
### Strategy
Claude Code owns the state files. OpenCode reads them via:
1. `{file:path}` variable substitution in `opencode.json`
2. `instructions` array for context files
3. Skills that read state files directly
### What Can Be Shared
| File | Method | Notes |
|------|--------|-------|
| `~/.claude/CLAUDE.md` | `instructions` | Global rules |
| `~/.claude/state/kb.json` | `instructions` or skill | Knowledge base |
| `~/.claude/state/personal-assistant/memory/*.json` | `instructions` | Memory context |
| `~/.claude/state/system-instructions.json` | `instructions` | Process definitions |
### Implementation in `opencode.json`
```jsonc
{
"$schema": "https://opencode.ai/config.json",
// Load Claude Code state as instructions (read at session start)
"instructions": [
"~/.claude/CLAUDE.md",
"~/.claude/state/kb.json",
"~/.claude/state/personal-assistant/memory/facts.json",
"~/.claude/state/personal-assistant/memory/preferences.json"
]
}
```
### What Stays Separate
| Item | Reason |
|------|--------|
| Session history | Different formats, different storage |
| Autonomy/permissions | OpenCode uses `permission` config instead |
| Component registry | OpenCode discovers via file paths |
### Overhead Assessment
**Low overhead** - just config changes:
- Add paths to `instructions` array
- No symlinks or sync scripts needed
- OpenCode reads files directly at session start
- Claude Code continues to write/update normally
---
## Phase 8: What Won't Transfer
| Feature | Claude Code | OpenCode Alternative |
|---------|-------------|---------------------|
| Hooks (SessionStart, etc.) | `hooks/hooks.json` | Plugins (future) |
| Guardrails hook | PreToolUse script | `permission` config |
| Component registry routing | Keyword triggers | Agent descriptions + @mentions |
| Hierarchical delegation | PA → MO → agent | Flat subagent model |
---
## Implementation Order
### Step 1: Backup (5 min) ✅ COMPLETE
- [x] Create timestamped backup of `~/.config/opencode/``opencode-backup-20260107_120135.tar.gz`
- [x] Create timestamped backup of `~/.opencode/``opencode-home-backup-20260107_120136.tar.gz`
### Step 2: Enhance Sync Script (45 min) ✅ COMPLETE
- [x] Add skip list: `SKIP_AGENTS` (kept empty - all agents synced as subagents)
- [x] Add `mode: subagent` to all synced agents
- [x] Remove hardcoded model (agents inherit from runtime selection)
- [x] Add model stripping from opencode.json
- [x] Update `sync_tree()` to handle skipped files
- [ ] ~~Optionally enhance descriptions with examples~~ (deferred)
### Step 3: Run Enhanced Sync (10 min) ✅ COMPLETE
- [x] `python3 ~/.config/opencode/scripts/claude_sync.py --dry-run`
- [x] Review output - verify mode/model changes
- [x] `python3 ~/.config/opencode/scripts/claude_sync.py`
- [x] All synced: 10 skills, 13 agents, 27 commands, 10 workflows
### Step 4: Update opencode.json (20 min) ✅ COMPLETE
- [x] Add `instructions` array (CLAUDE.md, kb.json, memory files)
- [x] Model defaults: intentionally omitted (user selects at runtime)
- [x] Add permission config with safe command patterns
### Step 5: Testing (30 min) ✅ COMPLETE (automated)
- [x] OpenCode v1.0.220 installed at `/home/linuxbrew/.linuxbrew/bin/opencode`
- [x] `opencode agent list` shows 40 agents (built-in + synced)
- [x] All Claude Code agents show as `(subagent)`
- [x] 10 skills synced to `~/.config/opencode/skills/`
- [x] Config verified: instructions, permissions, commands present
- [ ] Manual TUI testing (user to verify interactively)
### Step 6: Documentation (20 min) ✅ COMPLETE
- [x] Create `~/.config/opencode/README.md` (4.7KB)
- [x] Document complete agent mapping table
- [x] Document sync workflow with examples
- [x] Add fc-047 to `~/.claude/state/future-considerations.json`
### Step 7: Iterate (as needed) ⏳ PENDING
- [ ] Adjust agent descriptions if auto-invocation isn't working well
- [ ] Tune permission patterns
- [ ] Consider dropping/hiding agents that don't fit OpenCode model
- [ ] Update documentation with lessons learned
**Status: IMPLEMENTATION COMPLETE** - Manual TUI testing recommended
---
## Phase 8: Documentation
### Documentation Deliverables
Create `~/.config/opencode/README.md` with:
1. **Architecture Overview**
- Relationship between Claude Code and OpenCode
- What's shared vs separate
- Source of truth (Claude Code)
2. **Sync Workflow**
- How `claude_sync.py` works
- When to run it (after Claude Code changes)
- Command reference
3. **Agent Mapping**
- Which Claude Code agents map to OpenCode
- Which are skipped and why
- How to invoke subagents (@mentions)
4. **Skills**
- Auto-discovery from `~/.claude/skills/`
- How to add new skills
- Skill invocation patterns
5. **State Sharing**
- Files referenced via `instructions`
- Claude Code as source of truth
- What stays separate
6. **Permissions**
- How guardrails translated to `permission` config
- Safe vs prompted commands
### Documentation Template
```markdown
# OpenCode Configuration
This OpenCode setup is synchronized from Claude Code (`~/.claude/`).
## Quick Start
```bash
# Start OpenCode (uses built-in build agent)
opencode
# Switch to read-only plan agent
# Press Tab
# Invoke a subagent
@linux-sysadmin check system health
```
## Architecture
```
Claude Code (source of truth)
├── ~/.claude/agents/ → synced to ~/.config/opencode/agents/
├── ~/.claude/skills/ → synced to ~/.config/opencode/skills/
├── ~/.claude/CLAUDE.md → referenced via instructions
└── ~/.claude/state/ → referenced via instructions
OpenCode
├── Built-in: build (primary), plan (read-only)
├── Subagents: @linux-sysadmin, @k8s-orchestrator, etc.
└── Skills: gmail, gcal, sysadmin-health, etc.
```
## Sync Workflow
After making changes in Claude Code:
```bash
# Preview changes
python3 ~/.config/opencode/scripts/claude_sync.py --dry-run
# Apply changes
python3 ~/.config/opencode/scripts/claude_sync.py
# Clean stale files
python3 ~/.config/opencode/scripts/claude_sync.py --clean --apply
```
## Agents
| Claude Code | OpenCode | Notes |
|-------------|----------|-------|
| personal-assistant | (skipped) | Use built-in `build` |
| master-orchestrator | (skipped) | Flat model, not needed |
| linux-sysadmin | @linux-sysadmin | Subagent |
| k8s-orchestrator | @k8s-orchestrator | Subagent |
| ... | ... | ... |
## Skills
Skills are auto-discovered from:
- `~/.claude/skills/*/SKILL.md`
- `~/.config/opencode/skills/*/SKILL.md`
## State Files
Referenced via `instructions` in opencode.json:
- `~/.claude/CLAUDE.md` - Global rules
- `~/.claude/state/kb.json` - Knowledge base
- `~/.claude/state/personal-assistant/memory/*.json` - Memory
## Permissions
Configured in opencode.json `permission` section.
Migrated from Claude Code's guardrail hooks.
```
### Implementation Step
Add to Step 6:
- [ ] Create `~/.config/opencode/README.md`
- [ ] Document sync workflow
- [ ] Document agent mapping
- [ ] Document any gotchas discovered during testing
---
## Files to Create/Modify
### Files to Create
- `~/.config/opencode/README.md` - Documentation of setup, workflow, and requirements
### Files to Modify
- `~/.config/opencode/opencode.json` - Add `instructions` array + model/permission config
- `~/.config/opencode/scripts/claude_sync.py` - Add mode, model mappings, skip list
### Files Auto-Synced by Script
These are created/updated by `claude_sync.py`:
- `~/.config/opencode/agents/*.md` - From `~/.claude/agents/`
- `~/.config/opencode/skills/*/SKILL.md` - From `~/.claude/skills/`
- `~/.config/opencode/claude/commands/*.md` - From `~/.claude/commands/`
- `~/.config/opencode/claude/workflows/*.yaml` - From `~/.claude/workflows/`
### Files Referenced (Not Copied)
These stay in Claude Code, referenced via `instructions`:
- `~/.claude/CLAUDE.md`
- `~/.claude/state/kb.json`
- `~/.claude/state/personal-assistant/memory/*.json`
---
## Success Criteria
1. `opencode` launches and shows available skills
2. Can invoke `@linux-sysadmin` and get expected behavior
3. Gmail/GCal/GTasks tools work via custom wrappers
4. Can switch between build/plan agents + custom agents
5. Both Claude Code and OpenCode can run in parallel without conflicts
+230
View File
@@ -0,0 +1,230 @@
# Plan: Add Plan Status Tracking
## Problem
Plans in `~/.claude/plans/` have inconsistent status tracking:
- Some have inline `**Status:** Implemented`
- Most have no status marker
- No central index to query plan statuses
## Solution
Create `~/.claude/plans/index.json` as a central registry for plan metadata and status.
## Design
### Index Schema
```json
{
"plans": {
"temporal-foraging-milner": {
"title": "RAG JSON-to-text transformation",
"status": "pending",
"created": "2026-01-05",
"category": "enhancement"
},
"fizzy-puzzling-candy": {
"title": "Session summarization hook",
"status": "implemented",
"created": "2026-01-03",
"implemented": "2026-01-03",
"category": "feature"
}
}
}
```
### Status Values
| Status | Meaning |
|--------|---------|
| `pending` | Not yet implemented |
| `implemented` | Fully implemented |
| `partial` | Partially implemented |
| `abandoned` | Decided not to implement |
| `superseded` | Replaced by another plan |
### Categories
| Category | Meaning |
|----------|---------|
| `feature` | New capability |
| `enhancement` | Improve existing feature |
| `bugfix` | Fix an issue |
| `diagnostic` | One-time investigation (auto-complete) |
| `design` | Design document for reference |
## Files to Create
| File | Purpose |
|------|---------|
| `~/.claude/plans/index.json` | Central plan registry |
## Implementation
### Step 1: Create index.json with all current plans
Populate based on our verification:
**Implemented:**
- wise-dazzling-marshmallow (k8s quick-status)
- fizzy-puzzling-candy (session summarization)
- shimmering-discovering-bonbon (linux sysadmin agent)
- valiant-hugging-dahl (pi50 optimization)
- cozy-strolling-nygaard (status line + keybind)
- flickering-enchanting-fiddle (restructure components)
- velvet-percolating-porcupine (no-redundancy rule)
- 2025-01-02-gcal-design
- 2026-01-01-component-registry-design
- 2026-01-01-usage-tracking-design
**Diagnostic (complete):**
- elegant-prancing-allen (vulkan verification)
- pure-wishing-metcalfe (cluster diagnosis)
- glistening-wondering-wadler (structure verification)
**Pending:**
- temporal-foraging-milner (RAG improvement)
- cosmic-frolicking-compass (Zed Wayland)
**Handoff doc (reference only):**
- shimmering-discovering-bonbon-handoff
### Step 2: Update CLAUDE.md
Add plans index to state files table.
## Benefits
1. Single source of truth for plan statuses
2. Easy to query: `jq '.plans | to_entries[] | select(.value.status == "pending")' index.json`
3. No need to modify individual plan files
4. Can track implementation dates
## Full index.json Content
```json
{
"version": "1.0",
"description": "Plan status registry",
"plans": {
"temporal-foraging-milner": {
"title": "RAG JSON-to-text transformation",
"status": "pending",
"created": "2026-01-05",
"category": "enhancement"
},
"cosmic-frolicking-compass": {
"title": "Zed Wayland compilation",
"status": "pending",
"created": "2025-12-13",
"category": "enhancement",
"notes": "External task - compile Zed with Wayland support"
},
"wise-dazzling-marshmallow": {
"title": "K8s quick-status skill",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-29",
"category": "feature"
},
"fizzy-puzzling-candy": {
"title": "Session summarization hook",
"status": "implemented",
"created": "2026-01-03",
"implemented": "2026-01-03",
"category": "feature"
},
"shimmering-discovering-bonbon": {
"title": "Linux sysadmin agent",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "feature"
},
"valiant-hugging-dahl": {
"title": "Pi50 resource optimization",
"status": "implemented",
"created": "2026-01-05",
"implemented": "2026-01-05",
"category": "enhancement"
},
"cozy-strolling-nygaard": {
"title": "Status line + keybind fix",
"status": "implemented",
"created": "2025-12-29",
"implemented": "2025-12-29",
"category": "bugfix"
},
"flickering-enchanting-fiddle": {
"title": "Restructure components",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "enhancement"
},
"velvet-percolating-porcupine": {
"title": "No-redundancy rule",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "enhancement"
},
"elegant-prancing-allen": {
"title": "Vulkan verification",
"status": "implemented",
"created": "2025-12-12",
"implemented": "2025-12-12",
"category": "diagnostic"
},
"pure-wishing-metcalfe": {
"title": "Cluster issue diagnosis",
"status": "implemented",
"created": "2025-12-27",
"implemented": "2025-12-27",
"category": "diagnostic"
},
"glistening-wondering-wadler": {
"title": "Structure verification report",
"status": "implemented",
"created": "2026-01-03",
"implemented": "2026-01-03",
"category": "diagnostic"
},
"2025-01-02-gcal-design": {
"title": "Google Calendar integration",
"status": "implemented",
"created": "2025-12-31",
"implemented": "2026-01-01",
"category": "design"
},
"2026-01-01-component-registry-design": {
"title": "Component registry",
"status": "implemented",
"created": "2026-01-01",
"implemented": "2026-01-01",
"category": "design"
},
"2026-01-01-usage-tracking-design": {
"title": "Usage tracking",
"status": "implemented",
"created": "2025-12-31",
"implemented": "2026-01-01",
"category": "design"
},
"shimmering-discovering-bonbon-handoff": {
"title": "Linux sysadmin handoff doc",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "design",
"notes": "Reference document for shimmering-discovering-bonbon"
}
}
}
```
## Commit
Single commit: "Add plans index.json for status tracking"
+126
View File
@@ -0,0 +1,126 @@
{
"version": "1.0",
"description": "Plan status registry",
"plans": {
"temporal-foraging-milner": {
"title": "RAG JSON-to-text transformation",
"status": "pending",
"created": "2026-01-05",
"category": "enhancement"
},
"cosmic-frolicking-compass": {
"title": "Zed Wayland compilation",
"status": "pending",
"created": "2025-12-13",
"category": "enhancement",
"notes": "External task - compile Zed with Wayland support"
},
"wise-dazzling-marshmallow": {
"title": "K8s quick-status skill",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-29",
"category": "feature"
},
"fizzy-puzzling-candy": {
"title": "Session summarization hook",
"status": "implemented",
"created": "2026-01-03",
"implemented": "2026-01-03",
"category": "feature"
},
"shimmering-discovering-bonbon": {
"title": "Linux sysadmin agent",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "feature"
},
"valiant-hugging-dahl": {
"title": "Pi50 resource optimization",
"status": "implemented",
"created": "2026-01-05",
"implemented": "2026-01-05",
"category": "enhancement"
},
"cozy-strolling-nygaard": {
"title": "Status line + keybind fix",
"status": "implemented",
"created": "2025-12-29",
"implemented": "2025-12-29",
"category": "bugfix"
},
"flickering-enchanting-fiddle": {
"title": "Restructure components",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "enhancement"
},
"velvet-percolating-porcupine": {
"title": "No-redundancy rule",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "enhancement"
},
"elegant-prancing-allen": {
"title": "Vulkan verification",
"status": "implemented",
"created": "2025-12-12",
"implemented": "2025-12-12",
"category": "diagnostic"
},
"pure-wishing-metcalfe": {
"title": "Cluster issue diagnosis",
"status": "implemented",
"created": "2025-12-27",
"implemented": "2025-12-27",
"category": "diagnostic"
},
"glistening-wondering-wadler": {
"title": "Structure verification report",
"status": "implemented",
"created": "2026-01-03",
"implemented": "2026-01-03",
"category": "diagnostic"
},
"2025-01-02-gcal-design": {
"title": "Google Calendar integration",
"status": "implemented",
"created": "2025-12-31",
"implemented": "2026-01-01",
"category": "design"
},
"2026-01-01-component-registry-design": {
"title": "Component registry",
"status": "implemented",
"created": "2026-01-01",
"implemented": "2026-01-01",
"category": "design"
},
"2026-01-01-usage-tracking-design": {
"title": "Usage tracking",
"status": "implemented",
"created": "2025-12-31",
"implemented": "2026-01-01",
"category": "design"
},
"shimmering-discovering-bonbon-handoff": {
"title": "Linux sysadmin handoff doc",
"status": "implemented",
"created": "2025-12-28",
"implemented": "2025-12-28",
"category": "design",
"notes": "Reference document for shimmering-discovering-bonbon"
},
"golden-imagining-engelbart": {
"title": "Plan status tracking",
"status": "implemented",
"created": "2026-01-07",
"implemented": "2026-01-07",
"category": "enhancement",
"notes": "This plan - meta!"
}
}
}
+115
View File
@@ -0,0 +1,115 @@
# Implementation Plan: OpenCode Claude Sync Enhancements
## Overview
Transpose Claude Code agent/skill setup to OpenCode in parallel, per decisions from brainstorming session (`enumerated-giggling-scone.md`).
## Key Decisions (from brainstorming)
| Decision | Value |
|----------|-------|
| Primary agent | Use built-in `build` (don't port PA) |
| Agents to skip | `personal-assistant`, `master-orchestrator` |
| Other agents | All become `mode: subagent` |
| Model inheritance | Use `model: inherit` for subagents |
| State sharing | Reference via `instructions`, don't copy |
| Source of truth | Claude Code (`~/.claude/`) |
## Files to Modify
1. `~/.config/opencode/scripts/claude_sync.py` - Main sync script
2. `~/.config/opencode/opencode.json` - Config file
## Files to Create
1. `~/.config/opencode/README.md` - Documentation
## Implementation Steps
### Step 1: Backup (DONE)
Created backups:
- `~/.config/opencode-backup-20260107_120135.tar.gz`
- `~/opencode-home-backup-20260107_120136.tar.gz`
### Step 2: Enhance `claude_sync.py`
**Location**: `~/.config/opencode/scripts/claude_sync.py`
**Modifications**:
1. Add constants near top of file:
```python
SKIP_AGENTS = {"personal-assistant", "master-orchestrator"}
MODEL_MAP = {
"opus": "anthropic/claude-opus-4",
"sonnet": "anthropic/claude-sonnet-4-5",
"haiku": "anthropic/claude-haiku-4-5",
}
```
2. Modify `transform_frontmatter()` for agents:
- Check if agent name in `SKIP_AGENTS`, return `None` to signal skip
- Add `frontmatter["mode"] = "subagent"`
- Set `frontmatter["model"] = "inherit"`
- Map explicit models using `MODEL_MAP`
3. Modify `sync_tree()` to handle `None` return from transform (skip file)
4. Update `expected_dest_paths_for_tree()` to exclude skipped agents
### Step 3: Run Sync
```bash
python3 ~/.config/opencode/scripts/claude_sync.py --dry-run
python3 ~/.config/opencode/scripts/claude_sync.py
python3 ~/.config/opencode/scripts/claude_sync.py --clean --apply
```
### Step 4: Update `opencode.json`
Add to existing config:
```json
{
"model": "anthropic/claude-sonnet-4-5",
"small_model": "anthropic/claude-haiku-4-5",
"instructions": [
"~/.claude/CLAUDE.md",
"~/.claude/state/kb.json",
"~/.claude/state/personal-assistant/memory/facts.json",
"~/.claude/state/personal-assistant/memory/preferences.json"
],
"permission": {
"edit": "ask",
"bash": {
"*": "ask",
"pacman -Q*": "allow",
"systemctl status*": "allow",
"kubectl get*": "allow"
}
}
}
```
### Step 5: Test
- Run `opencode` and verify skill discovery
- Test `@linux-sysadmin` subagent invocation
- Verify permissions work
### Step 6: Create README.md
Document:
- Architecture (Claude Code as source of truth)
- Sync workflow
- Agent mapping table
- How to invoke subagents
### Step 7: Add Future Consideration
Add entry to `~/.claude/state/future-considerations.json` about JSON minification for large instruction files.
## Estimated Time
~2 hours total (Step 1 already done)
+110
View File
@@ -0,0 +1,110 @@
# Plan: Improve RAG Personal Index JSON-to-Natural-Language Transformation
## Problem
The RAG personal index produces low-quality matches for semantic queries because it indexes raw JSON structure rather than natural language.
**Example failure:**
- Query: "how to add a new agent"
- Expected: Match `system-instructions.json``processes.agent-lifecycle.add`
- Actual: Score 0.479, returns generic agent mentions instead
**Root cause:** The chunker doesn't recognize process structures with `add`/`remove`/`rules`/`requirements` arrays, so they fall through to raw JSON stringification.
## Solution
Enhance `index_personal.py` to transform JSON structures into natural language at index time.
## Files to Modify
1. `~/.claude/skills/rag-search/scripts/index_personal.py` - Main changes
## Implementation
### 1. Add Process Pattern Recognition (lines ~127-138)
Add handling for process objects with action arrays:
```python
# Process with action arrays (add, remove, rules, requirements, etc.)
action_keys = ["add", "remove", "rules", "requirements", "steps", "validate"]
if any(key in item for key in action_keys):
parts = []
if context:
parts.append(f"{context}:")
if item.get("description"):
parts.append(item["description"])
for action_key in action_keys:
if action_key in item and isinstance(item[action_key], list):
action_text = f"To {action_key}: " + ". ".join(item[action_key])
parts.append(action_text)
if parts:
yield (" ".join(parts), {**base_metadata, "process": context})
return
```
### 2. Improve Context Propagation
When processing nested dicts, pass richer context:
```python
# In the top-level dict processing (line ~154-161)
elif isinstance(value, dict):
# Pass the key as context for better chunk text
yield from process_item(value, context=key)
```
Already done, but ensure action arrays get the context.
### 3. Handle Key-Value Pairs in Processes
For structures like:
```json
"content-principles": {
"no-redundancy": "Information lives in one authoritative location",
"lean-files": "Keep files concise..."
}
```
Transform to: `"content-principles: no-redundancy means information lives in one authoritative location. lean-files means keep files concise..."`
### 4. Add Tests
Create a simple test to verify transformation quality:
```bash
# After reindex, verify the failing query now works
~/.claude/skills/rag-search/scripts/search.py "how to add a new agent" --index personal
# Should return system-instructions.json with score > 0.7
```
## Expected Outcome
| Query | Before | After |
|-------|--------|-------|
| "how to add a new agent" | 0.479, wrong file | >0.7, system-instructions.json |
| "agent lifecycle" | Similar | Better match to process |
| "model selection rules" | Depends | Match model-selection process |
## Validation Steps
1. Run modified indexer
2. Test the three queries above
3. Compare scores and result relevance
## Rollback
If results degrade: `git checkout scripts/index_personal.py && reindex`
## Post-Implementation
Add to `future-considerations.json`:
- RAG indexer debug/verbose mode to inspect what text is being indexed
## Future Considerations (Deferred)
- Natural language templates per JSON schema type
- LLM-generated summaries of complex structures
- Caching transformed text alongside original JSON
+9 -9
View File
@@ -4,10 +4,10 @@
"frontend-design@claude-plugins-official": [
{
"scope": "user",
"installPath": "/home/will/.claude/plugins/cache/claude-plugins-official/frontend-design/15b07b46dab3",
"version": "15b07b46dab3",
"installPath": "/home/will/.claude/plugins/cache/claude-plugins-official/frontend-design/b97f6eadd929",
"version": "b97f6eadd929",
"installedAt": "2025-12-24T19:08:12.422Z",
"lastUpdated": "2026-01-05T07:21:36.978Z",
"lastUpdated": "2026-01-07T08:00:06.726Z",
"gitCommitSha": "6d3752c000e2b3d0e6137bd7adb04895d6f40f14",
"isLocal": true
}
@@ -26,10 +26,10 @@
"commit-commands@claude-plugins-official": [
{
"scope": "user",
"installPath": "/home/will/.claude/plugins/cache/claude-plugins-official/commit-commands/15b07b46dab3",
"version": "15b07b46dab3",
"installPath": "/home/will/.claude/plugins/cache/claude-plugins-official/commit-commands/b97f6eadd929",
"version": "b97f6eadd929",
"installedAt": "2025-12-24T19:10:05.451Z",
"lastUpdated": "2026-01-05T07:21:36.984Z",
"lastUpdated": "2026-01-07T08:00:06.734Z",
"isLocal": true
}
],
@@ -69,10 +69,10 @@
"ralph-wiggum@claude-plugins-official": [
{
"scope": "user",
"installPath": "/home/will/.claude/plugins/cache/claude-plugins-official/ralph-wiggum/15b07b46dab3",
"version": "15b07b46dab3",
"installPath": "/home/will/.claude/plugins/cache/claude-plugins-official/ralph-wiggum/883f2ba69e50",
"version": "883f2ba69e50",
"installedAt": "2026-01-02T19:47:02.395Z",
"lastUpdated": "2026-01-05T07:21:36.997Z",
"lastUpdated": "2026-01-06T20:00:15.709Z",
"gitCommitSha": "de89f3066c68d7a2f2d4190173fa46c26e2f30fd",
"isLocal": true
}
+1 -1
View File
@@ -5,7 +5,7 @@
"repo": "anthropics/claude-plugins-official"
},
"installLocation": "/home/will/.claude/plugins/marketplaces/claude-plugins-official",
"lastUpdated": "2026-01-05T20:44:45.874Z"
"lastUpdated": "2026-01-07T19:06:34.488Z"
},
"superpowers-marketplace": {
"source": {
+31
View File
@@ -0,0 +1,31 @@
# Morning Report - Tue Jan 06, 2026
## 🌤 Weather
Seattle: 43°F, Light rain, mist | High 45° Low 38°
## 📧 Email
15 unread
• Capital One | Quicks - Your requested balance summary
• Chase - Your Chase Freedom Unlimited Visa balanc
• Experian - William, check out these cards with an i
• Delta Air Lines - Discover An Experience Curated For Membe
• DoorDash - Save up to $10 on groceries and more eac
## 📅 Today
No events today
## 📈 Stocks
CRWV $77.94 +1.4% ▲ NVDA $187.24 -0.5% ▼ MSFT $478.51 +1.2% ▲
## 🖥 Infrastructure
K8s: 🟡 | Workstation: 🟢
└ K8s: 2 pods not running
## 📰 Tech News
• Comparing AI agents to cybersecurity professionals in real-w... (Hacker News)
• Oral microbiome sequencing after taking probiotics (Hacker News)
• The Best Line Length is 88 (Lobsters)
• There Were BGP Anomalies During The Venezuela Blackout (Lobsters)
---
*Generated: 2026-01-06 13:40:51 PT*
+42
View File
@@ -0,0 +1,42 @@
# Morning Report - Wed Jan 07, 2026
## 🌤 Weather
Overcast 40°F (feels 33°F), 86% humidity, rain possible — bring umbrella
## 📧 Email
10 unread, no urgent items
• Experian Alerts - Your FICO® Score went up. Nice work!
• Experteer - 3 new opportunities for "AWS Architect"
• Experian - December spending report is here
• Chase - Freedom Unlimited balance is $538.97
• Chase - Rewards balance has reached 0 POINTS
## 📅 Today
No events today
## 📈 Stocks
▲ CRWV $78.98 (+1.3%) | ▲ NVDA $189.74 (+1.3%) | ▲ MSFT $488.96 (+2.2%)
## ✅ Tasks
6 pending
• 5:00 PM - Dinner at Lecosho or Japonessa
• 3:00 PM - Snack at Le Panier or Mee Sum
• 3:15 PM - Seattle Art Museum (Impressionism Exhibit)
• 2:30 PM - Route 7 Bus to Downtown
• 2:00 PM - Coffee at QED (Mt Baker)
... and 1 more
## 🖥 Infrastructure
K8s: 🟡 | Workstation: 🟢
└ K8s: 1 pods not running
## 📰 Tech News
• Eat Real Food Introducing the New Pyramid (Hacker News)
• The $14 Burrito: Why San Francisco Inflation Feels Higher Than 2.5% (Hacker News)
• Health care data breach affects over 600k patients, Illinois agency says (Hacker News)
• Why the trans flag emoji is the 5-codepoint sequence it is (Lobsters)
• A4 Paper Stories (Lobsters)
---
*Generated: 2026-01-07 09:58:25 PT*
+27 -12
View File
@@ -1,27 +1,42 @@
# Morning Report - Mon Jan 05, 2026
# Morning Report - Wed Jan 07, 2026
## 🌤 Weather
Overcast, 44°F (feels 41°F), rain likely—bring umbrella
Overcast 40°F (feels 33°F), 86% humidity, rain possible — bring umbrella
## 📧 Email
⚠️ Could not fetch emails: No module named 'pydantic_core._pydantic_core'
10 unread, no urgent items
• Experian Alerts - Your FICO® Score went up. Nice work!
• Experteer - 3 new opportunities for "AWS Architect"
• Experian - December spending report is here
• Chase - Freedom Unlimited balance is $538.97
• Chase - Rewards balance has reached 0 POINTS
## 📅 Today
⚠️ Could not fetch calendar: No module named 'pydantic_core._pydantic_core'
No events today
## 📈 Stocks
CRWV $77.64 ▼2.1% | NVDA $187.84 ▼0.5% | MSFT $473.50 ▲0.1%
CRWV $78.98 (+1.3%) | ▲ NVDA $189.74 (+1.3%) | MSFT $488.96 (+2.2%)
## ✅ Tasks
6 pending
• 5:00 PM - Dinner at Lecosho or Japonessa
• 3:00 PM - Snack at Le Panier or Mee Sum
• 3:15 PM - Seattle Art Museum (Impressionism Exhibit)
• 2:30 PM - Route 7 Bus to Downtown
• 2:00 PM - Coffee at QED (Mt Baker)
... and 1 more
## 🖥 Infrastructure
K8s: 🟡 | Workstation: 🟢
└ K8s: 2 pods not running
└ K8s: 1 pods not running
## 📰 Tech News
O-Ring Automation (Hacker News)
Novo Nordisk launches Wegovy weight-loss pill in US, triggering price war (Hacker News)
Refactoring Not on the backlog (Hacker News)
It's hard to justify Tahoe icons (Lobsters)
Databases in 2025: A Year in Review (Lobsters)
Eat Real Food Introducing the New Pyramid (Hacker News)
The $14 Burrito: Why San Francisco Inflation Feels Higher Than 2.5% (Hacker News)
Health care data breach affects over 600k patients, Illinois agency says (Hacker News)
Why the trans flag emoji is the 5-codepoint sequence it is (Lobsters)
A4 Paper Stories (Lobsters)
---
*Generated: 2026-01-05 12:44:47 PT*
*Generated: 2026-01-07 09:58:25 PT*
+2 -1
View File
@@ -13,5 +13,6 @@
"ralph-wiggum@claude-plugins-official": true
},
"alwaysThinkingEnabled": true,
"_note": "Agent definitions moved to ~/.claude/agents/*.md with YAML frontmatter. Autonomy levels now in ~/.claude/state/autonomy-levels.json"
"_note": "Agent definitions moved to ~/.claude/agents/*.md with YAML frontmatter. Autonomy levels now in ~/.claude/state/autonomy-levels.json",
"model": "opus"
}
+1 -1
View File
@@ -25,7 +25,7 @@
"show_tomorrow": true
},
"tasks": {
"enabled": false,
"enabled": true,
"max_display": 5,
"show_due_dates": true
},
+11
View File
@@ -132,6 +132,17 @@
"to-do",
"pending"
]
},
"guardrails": {
"description": "PreToolUse hook that prevents dangerous operations (rm -rf, system commands, etc.)",
"script": "~/.claude/hooks/scripts/guardrail.py",
"config": "~/.claude/state/guardrails.json",
"triggers": [
"guardrail",
"safety",
"block dangerous",
"protect"
]
}
},
"commands": {
+11 -1
View File
@@ -10,7 +10,7 @@
"priority": "medium",
"status": "pending",
"created": "2024-12-28",
"notes": "Design complete. Partially implemented: node_exporter running (port 9100), PrometheusRule deployed (12 alerts: 4 critical, 6 warning, 2 info), Prometheus scrape config updated. PENDING TAILSACLE CONFIGURATION: Tailscale network attempted but ACLs preventing cluster workstation peering. See charts/willlaptop-monitoring/TAILSCALE-ACL-GUIDE.md for configuration steps. After ACL configuration, Prometheus should successfully scrape workstation metrics via 100.90.159.78:9100."
"notes": "Design complete. Partially implemented: node_exporter running (port 9100), PrometheusRule deployed (12 alerts: 4 critical, 6 warning, 2 info), Prometheus scrape config updated. \u23f3 PENDING TAILSACLE CONFIGURATION: Tailscale network attempted but ACLs preventing cluster \u2194 workstation peering. See charts/willlaptop-monitoring/TAILSCALE-ACL-GUIDE.md for configuration steps. After ACL configuration, Prometheus should successfully scrape workstation metrics via 100.90.159.78:9100."
},
{
"id": "fc-002",
@@ -467,6 +467,16 @@
"status": "deferred",
"created": "2025-01-21",
"notes": "Optimization for when query volume justifies it. Consider TTL and invalidation strategy."
},
{
"id": "fc-047",
"category": "opencode",
"title": "Minify JSON instructions for OpenCode",
"description": "Consider minifying JSON files loaded via OpenCode instructions config when files grow large",
"priority": "low",
"status": "deferred",
"created": "2026-01-07",
"notes": "Currently files are <1KB total, not worth minifying. Revisit if kb.json or memory files exceed 50KB. Could add minify step to claude_sync.py."
}
]
}
+45
View File
@@ -0,0 +1,45 @@
{
"version": 1,
"safe_paths": [
"~/.claude",
"~/projects"
],
"blocked_paths": [
"/etc",
"/usr",
"/var",
"/boot",
"/sys",
"/proc",
"~/.ssh",
"~/.gnupg",
"~/.aws"
],
"rules": {
"bash": [
{"pattern": "rm -rf /($|[^a-zA-Z])", "action": "block", "name": "rm_rf_root"},
{"pattern": "rm -rf ~($|[^a-zA-Z])", "action": "block", "name": "rm_rf_home"},
{"pattern": "rm -rf \\*", "action": "block", "name": "rm_rf_wildcard"},
{"pattern": "chmod -R 777", "action": "block", "name": "chmod_777"},
{"pattern": ":\\(\\)\\{ :\\|:& \\};:", "action": "block", "name": "fork_bomb"},
{"pattern": "mkfs\\.", "action": "block", "name": "mkfs"},
{"pattern": "dd .* of=/dev/", "action": "block", "name": "dd_device"},
{"pattern": "> /dev/sd[a-z]", "action": "block", "name": "overwrite_device"},
{"pattern": "shutdown", "action": "confirm", "name": "shutdown"},
{"pattern": "reboot", "action": "confirm", "name": "reboot"},
{"pattern": "systemctl (stop|disable|mask)", "action": "confirm", "name": "systemctl_destructive"},
{"pattern": "rm ", "action": "confirm", "name": "rm_outside_safe", "outside_safe_paths": true},
{"pattern": "kubectl delete", "action": "confirm", "name": "kubectl_delete"},
{"pattern": "docker rm", "action": "confirm", "name": "docker_rm"},
{"pattern": "docker system prune", "action": "confirm", "name": "docker_prune"}
],
"write": [
{"path_match": "blocked_paths", "action": "block", "name": "write_blocked_path"},
{"path_match": "outside_safe_paths", "action": "confirm", "name": "write_outside_safe"}
],
"edit": [
{"path_match": "blocked_paths", "action": "block", "name": "edit_blocked_path"},
{"path_match": "outside_safe_paths", "action": "confirm", "name": "edit_outside_safe"}
]
}
}
@@ -266,6 +266,27 @@
"ended": null,
"summarized": false,
"topics": []
},
{
"id": "2026-01-06_08-49-25",
"started": "2026-01-06T08:49:25-08:00",
"ended": null,
"summarized": false,
"topics": []
},
{
"id": "2026-01-07_10-32-29",
"started": "2026-01-07T10:32:29-08:00",
"ended": null,
"summarized": false,
"topics": []
},
{
"id": "2026-01-07_11-06-31",
"started": "2026-01-07T11:06:31-08:00",
"ended": null,
"summarized": false,
"topics": []
}
]
}