Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c739f4c490 | ||
|
|
5a2bef35c1 | ||
|
|
b1ff71a7fd |
@@ -17,7 +17,7 @@ on:
|
||||
|
||||
jobs:
|
||||
tag:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -44,15 +44,37 @@ changes. CI tags every change as a `vX.Y.Z` release (see
|
||||
|
||||
## Wired harnesses
|
||||
|
||||
Phase 1 (current) wires **Claude Code** only:
|
||||
| Harness | Target path | Notes |
|
||||
|---|---|---|
|
||||
| **Claude Code (global)** | `~/.claude/skills/<name>` | Visible in every Claude Code session on the host. |
|
||||
| **Claude Code (per-repo)** | `<repo>/.claude/skills/<name>` | Created when `install` is invoked inside a git repo (not the skills repo itself). Gitignored globally via `**/.claude/skills/`. |
|
||||
| **Crush** | `~/.config/crush/skills/<name>` | Charmbracelet Crush — successor to `opencode-ai/opencode` (archived). |
|
||||
| **Antigravity** | `~/.gemini/antigravity/skills/<name>` | Google Antigravity (VS Code extension). Our `SKILL.md` frontmatter (`name` + `description`) is already in the format Antigravity expects, so symlinks suffice — no manifest translation. |
|
||||
|
||||
- Global: `~/.claude/skills/<name>` → `~/.local/share/skills/<name>`
|
||||
- Per-repo: `<repo>/.claude/skills/<name>` (when invoked from a git
|
||||
repo) → same
|
||||
### Not wired (and why)
|
||||
|
||||
Phase 2 will add: Crush, opencode, antigravity, gitea-resident agents
|
||||
(cobalt-dingo, agentsquad). See `mathias/infra` issue `infra#62`
|
||||
addendum for the roadmap.
|
||||
- **opencode** (`opencode-ai/opencode`): archive notice; succeeded by Crush.
|
||||
Its only "skill"-like surface — Custom Commands at `~/.config/opencode/commands/` —
|
||||
is for user-facing prompt templates, not system-level instructions.
|
||||
Misaligned with how we use skills. Skipped.
|
||||
- **gitea-resident agents** (cobalt-dingo, agentsquad): they consume
|
||||
skills via their containing project's `.claude/skills` directory
|
||||
(populated by the per-repo wirer when run from the host), or via the
|
||||
brain MCP. No special target needed.
|
||||
|
||||
### Per-host env-var overrides
|
||||
|
||||
Each target path can be overridden by setting the matching env var before
|
||||
running `install.sh`:
|
||||
|
||||
| Env var | Default |
|
||||
|---|---|
|
||||
| `SKILLS_REPO_URL` | `https://gitea.d-ma.be/mathias/skills.git` |
|
||||
| `SKILLS_REF` | `main` (set to e.g. `v0.1.0` to pin a release) |
|
||||
| `SKILLS_CHECKOUT_DIR` | `$HOME/.local/share/skills` |
|
||||
| `CLAUDE_SKILLS_DIR` | `$HOME/.claude/skills` |
|
||||
| `CRUSH_SKILLS_DIR` | `$HOME/.config/crush/skills` |
|
||||
| `ANTIGRAVITY_SKILLS_DIR` | `$HOME/.gemini/antigravity/skills` |
|
||||
|
||||
## Versioning
|
||||
|
||||
|
||||
+48
-3
@@ -12,6 +12,11 @@ vars:
|
||||
REPO_URL: 'https://gitea.d-ma.be/mathias/skills.git'
|
||||
CHECKOUT_DIR: '{{.HOME}}/.local/share/skills'
|
||||
CLAUDE_GLOBAL_DIR: '{{.HOME}}/.claude/skills'
|
||||
CRUSH_DIR: '{{.HOME}}/.config/crush/skills'
|
||||
ANTIGRAVITY_DIR: '{{.HOME}}/.gemini/antigravity/skills'
|
||||
# Anything at the repo root that is NOT a skill directory. Both
|
||||
# Taskfile + install.sh share this exclusion regex.
|
||||
NON_SKILL_ENTRIES: '^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$'
|
||||
|
||||
tasks:
|
||||
|
||||
@@ -26,17 +31,19 @@ tasks:
|
||||
silent: true
|
||||
|
||||
install:
|
||||
desc: 'Wire skills into every harness detected on this host (Claude Code today, more in phase 2).'
|
||||
desc: 'Wire skills into every supported harness (Claude Code, Crush, Antigravity).'
|
||||
cmds:
|
||||
- task: install:claude:global
|
||||
- task: install:claude:repo
|
||||
- task: install:crush
|
||||
- task: install:antigravity
|
||||
|
||||
install:claude:global:
|
||||
desc: 'Per-skill symlinks under ~/.claude/skills/<name> — visible in every Claude Code project on this host.'
|
||||
cmds:
|
||||
- mkdir -p {{.CLAUDE_GLOBAL_DIR}}
|
||||
- |
|
||||
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$"); do
|
||||
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
||||
target="{{.TASKFILE_DIR}}/${skill}"
|
||||
link="{{.CLAUDE_GLOBAL_DIR}}/${skill}"
|
||||
if [ -L "$link" ] || [ -e "$link" ]; then
|
||||
@@ -63,7 +70,7 @@ tasks:
|
||||
fi
|
||||
target_dir="$repo/.claude/skills"
|
||||
mkdir -p "$target_dir"
|
||||
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "^(Taskfile.yml|install.sh|README.md|SKILLS_INDEX.md|.gitea|.git$)$"); do
|
||||
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
||||
target="{{.TASKFILE_DIR}}/${skill}"
|
||||
link="${target_dir}/${skill}"
|
||||
if [ -L "$link" ] || [ -e "$link" ]; then
|
||||
@@ -77,6 +84,44 @@ tasks:
|
||||
echo "linked $link → $target"
|
||||
done
|
||||
|
||||
install:crush:
|
||||
desc: 'Per-skill symlinks under ~/.config/crush/skills/<name> — visible in every Crush session on this host.'
|
||||
cmds:
|
||||
- mkdir -p {{.CRUSH_DIR}}
|
||||
- |
|
||||
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
||||
target="{{.TASKFILE_DIR}}/${skill}"
|
||||
link="{{.CRUSH_DIR}}/${skill}"
|
||||
if [ -L "$link" ] || [ -e "$link" ]; then
|
||||
current=$(readlink "$link" 2>/dev/null || true)
|
||||
if [ "$current" = "$target" ]; then
|
||||
continue
|
||||
fi
|
||||
rm -rf "$link"
|
||||
fi
|
||||
ln -s "$target" "$link"
|
||||
echo "linked $link → $target"
|
||||
done
|
||||
|
||||
install:antigravity:
|
||||
desc: 'Per-skill symlinks under ~/.gemini/antigravity/skills/<name>. SKILL.md frontmatter (name + description) is already antigravity-compatible.'
|
||||
cmds:
|
||||
- mkdir -p {{.ANTIGRAVITY_DIR}}
|
||||
- |
|
||||
for skill in $(ls -1 "{{.TASKFILE_DIR}}" | grep -Ev "{{.NON_SKILL_ENTRIES}}"); do
|
||||
target="{{.TASKFILE_DIR}}/${skill}"
|
||||
link="{{.ANTIGRAVITY_DIR}}/${skill}"
|
||||
if [ -L "$link" ] || [ -e "$link" ]; then
|
||||
current=$(readlink "$link" 2>/dev/null || true)
|
||||
if [ "$current" = "$target" ]; then
|
||||
continue
|
||||
fi
|
||||
rm -rf "$link"
|
||||
fi
|
||||
ln -s "$target" "$link"
|
||||
echo "linked $link → $target"
|
||||
done
|
||||
|
||||
update:
|
||||
desc: 'git pull the canonical checkout then re-run install.'
|
||||
cmds:
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
---
|
||||
name: dream
|
||||
description: >
|
||||
Run a "dream" — a reflective memory consolidation pass over an agent's memory
|
||||
directory. Use this skill whenever the user says "dream", "run a dream", "consolidate
|
||||
my memory files", "clean up my MEMORY.md", or asks Claude to do a memory maintenance
|
||||
pass, prune stale notes, or reorganize topic files. Also trigger when the user wants
|
||||
to rebuild a memory index, merge duplicate facts, or convert relative dates in notes
|
||||
to absolute ones. This is an agentic, multi-phase workflow — always use this skill
|
||||
rather than improvising the steps.
|
||||
---
|
||||
|
||||
# Dream — Memory Consolidation Skill
|
||||
|
||||
You are performing a **dream**: a reflective, agentic pass over a memory directory.
|
||||
Your goal is to synthesize recent signal into durable, well-organized memory so that
|
||||
future sessions can orient quickly.
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight
|
||||
|
||||
Before starting, confirm:
|
||||
1. **Where is the memory directory?** Ask the user if not obvious from context.
|
||||
Common locations: `~/memory/`, `~/.agent/memory/`, `./memory/`, a path in an env var like `$MEMORY_DIR`.
|
||||
2. **Are there transcripts or daily logs to scan?** Ask if not obvious.
|
||||
3. **Any topics to skip or treat as sensitive?**
|
||||
|
||||
Once confirmed, proceed through the four phases in order. Narrate each phase briefly as you go.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Orient
|
||||
|
||||
**Goal**: Get a map of what exists before touching anything.
|
||||
|
||||
```bash
|
||||
ls -la <memory_dir>/
|
||||
cat <memory_dir>/MEMORY.md
|
||||
```
|
||||
|
||||
For each file listed (excluding MEMORY.md):
|
||||
- Read or skim it (first 40–60 lines is usually enough unless it's small).
|
||||
- Note: topic, approximate recency, any obvious staleness or duplication.
|
||||
|
||||
Build a mental inventory:
|
||||
- Files present, rough line counts
|
||||
- Topics covered
|
||||
- Any files that look abandoned, mislabeled, or overlapping
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Gather Recent Signal
|
||||
|
||||
**Goal**: Find new facts, corrections, and drift since the last dream.
|
||||
|
||||
Check in this order:
|
||||
|
||||
1. **Daily logs** — read recent entries (last 7–14 days).
|
||||
Look for: new decisions, changed preferences, completed projects, new relationships/tools.
|
||||
|
||||
2. **Drifted facts** — scan existing topic files for statements that may now be false.
|
||||
Examples: "currently evaluating X" (did they pick one?), "planning to do Y" (done or dropped?), relative dates like "last week" or "recently".
|
||||
|
||||
3. **Transcripts** — only grep narrowly if there's a specific gap.
|
||||
Avoid bulk-reading transcripts; it's slow and noisy. Use targeted patterns:
|
||||
```bash
|
||||
grep -r "decided\|switched to\|no longer\|now using\|moved to" <transcripts_dir>/ | tail -40
|
||||
```
|
||||
|
||||
Collect a list of **updates to make**: new facts, corrections, removals.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 — Consolidate
|
||||
|
||||
**Goal**: Apply the updates. Leave memory files cleaner and more accurate than you found them.
|
||||
|
||||
For each topic file:
|
||||
|
||||
- **Merge duplicates**: if the same fact appears in two files, keep it in the more specific one and remove from the general one.
|
||||
- **Convert relative dates**: replace "last week", "recently", "a few months ago" with an absolute date (use the current date as reference; estimate if necessary and note the uncertainty).
|
||||
- **Delete contradicted facts**: if a new fact supersedes an old one, remove the old one outright — don't leave both.
|
||||
- **Tighten language**: convert vague hedges ("probably uses", "might be") to definite statements where the evidence supports it, or remove if genuinely unknown.
|
||||
- **Add new facts** from Phase 2 to the appropriate topic file. Create a new topic file if no good home exists.
|
||||
|
||||
After editing files, do a final pass:
|
||||
```bash
|
||||
grep -n "last week\|recently\|a few months\|soon\|currently planning" <memory_dir>/*.md
|
||||
```
|
||||
Clean up any remaining relative time references.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4 — Prune and Index
|
||||
|
||||
**Goal**: Rebuild MEMORY.md as a clean, navigable index under 200 lines.
|
||||
|
||||
**MEMORY.md structure**:
|
||||
```markdown
|
||||
# Memory Index
|
||||
_Last updated: YYYY-MM-DD_
|
||||
|
||||
## Overview
|
||||
One short paragraph: who this agent is, primary context, most important standing facts.
|
||||
|
||||
## Topic Files
|
||||
| File | Contents | Last updated |
|
||||
|------|----------|-------------|
|
||||
| person.md | Identity, preferences, background | YYYY-MM-DD |
|
||||
| projects.md | Active and recent projects | YYYY-MM-DD |
|
||||
| tools.md | Stack, infra, dev environment | YYYY-MM-DD |
|
||||
| ... | ... | ... |
|
||||
|
||||
## Quick Facts
|
||||
- Bullet list of the 10–15 most frequently-needed facts (role, location, key tools, etc.)
|
||||
|
||||
## Recent Changes
|
||||
- Bullet list of what changed in this dream (so the next session knows what's fresh)
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Remove any pointers to files that no longer exist.
|
||||
- Add pointers for any new files created in Phase 3.
|
||||
- Keep Quick Facts ≤ 15 bullets — this is for speed, not completeness.
|
||||
- Recent Changes section replaces itself each dream (don't accumulate).
|
||||
- Total MEMORY.md length: **200 lines max**.
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
After completing all four phases, return a **dream summary** to the user:
|
||||
|
||||
```
|
||||
## Dream complete — YYYY-MM-DD
|
||||
|
||||
### What changed
|
||||
- [file]: [what was updated]
|
||||
- MEMORY.md: rebuilt index, N topic files indexed
|
||||
|
||||
### Facts added
|
||||
- ...
|
||||
|
||||
### Facts removed / corrected
|
||||
- ...
|
||||
|
||||
### Files created
|
||||
- ...
|
||||
|
||||
### Files deleted or merged
|
||||
- ...
|
||||
|
||||
### Still uncertain / needs follow-up
|
||||
- ...
|
||||
```
|
||||
|
||||
Keep it concise — a few bullets per section, not exhaustive diffs. The goal is for the user to quickly confirm the dream went well and catch any mistakes.
|
||||
|
||||
---
|
||||
|
||||
## Tips and Edge Cases
|
||||
|
||||
- **No MEMORY.md exists yet**: create one from scratch using the structure above. Treat all existing files as "first time indexed".
|
||||
- **Memory dir is empty**: create MEMORY.md and a starter `scratch.md` noting the date and that the memory system is new.
|
||||
- **Conflicting facts with no clear resolution**: note both in the file with a date stamp and flag in "Still uncertain" section of the summary.
|
||||
- **Large transcript dumps**: resist reading them in full. Grep is your friend. If you must read, read the last N lines only.
|
||||
- **Files with sensitive content**: if the user flagged topics to skip, skip them entirely — don't even open them.
|
||||
+26
@@ -17,6 +17,8 @@ REPO_URL="${SKILLS_REPO_URL:-https://gitea.d-ma.be/mathias/skills.git}"
|
||||
REF="${SKILLS_REF:-main}"
|
||||
CHECKOUT_DIR="${SKILLS_CHECKOUT_DIR:-$HOME/.local/share/skills}"
|
||||
CLAUDE_GLOBAL_DIR="${CLAUDE_SKILLS_DIR:-$HOME/.claude/skills}"
|
||||
CRUSH_DIR="${CRUSH_SKILLS_DIR:-$HOME/.config/crush/skills}"
|
||||
ANTIGRAVITY_DIR="${ANTIGRAVITY_SKILLS_DIR:-$HOME/.gemini/antigravity/skills}"
|
||||
|
||||
log() { printf '[skills] %s\n' "$*"; }
|
||||
|
||||
@@ -69,6 +71,28 @@ wire_claude_global() {
|
||||
done < <(list_skills)
|
||||
}
|
||||
|
||||
wire_crush() {
|
||||
# Crush reads skills from ~/.config/crush/skills/<name>/SKILL.md.
|
||||
# Pre-creating the dir is cheap even when Crush isn't installed.
|
||||
mkdir -p "$CRUSH_DIR"
|
||||
while IFS= read -r skill; do
|
||||
[ -n "$skill" ] || continue
|
||||
link_skill "$CRUSH_DIR" "$skill"
|
||||
done < <(list_skills)
|
||||
}
|
||||
|
||||
wire_antigravity() {
|
||||
# Antigravity (Google's VS Code extension) reads global skills from
|
||||
# ~/.gemini/antigravity/skills/<name>/SKILL.md. Our SKILL.md files
|
||||
# already carry the required `name` + `description` YAML frontmatter,
|
||||
# so symlinks are sufficient — no manifest translation step.
|
||||
mkdir -p "$ANTIGRAVITY_DIR"
|
||||
while IFS= read -r skill; do
|
||||
[ -n "$skill" ] || continue
|
||||
link_skill "$ANTIGRAVITY_DIR" "$skill"
|
||||
done < <(list_skills)
|
||||
}
|
||||
|
||||
wire_claude_repo() {
|
||||
# Only wire per-repo when invoked from inside a git repo and it isn't
|
||||
# the skills repo itself.
|
||||
@@ -92,6 +116,8 @@ main() {
|
||||
ensure_checkout
|
||||
wire_claude_global
|
||||
wire_claude_repo
|
||||
wire_crush
|
||||
wire_antigravity
|
||||
log "done — $(list_skills | wc -l | tr -d ' ') skill(s) wired at ref=$REF"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user