diff --git a/README.md b/README.md index 9cb4ac4..91bc261 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ This is my personal **.skills** repository for Codex, Cursor, OpenClaw and agent |---|---|---| | `technical-deslop` | Ship clean diffs fast: remove AI noise and keep behavior unchanged. | `npx skills add vincentkoc/dotskills --skill technical-deslop -y` | | `technical-documentation` | Produce dev‑ready docs: clear, structured build/review for brownfield + evergreen. | `npx skills add vincentkoc/dotskills --skill technical-documentation -y` | +| `session-done` | Capture atomic `/done` session teardowns with structured decisions, follow-ups, reflection, and PKM handoff. | `npx skills add vincentkoc/dotskills --skill session-done -y` | | `technical-integrations` | Design integrations that land: vendor‑agnostic API/RFC/SDK plans with rollout safety. | `npx skills add vincentkoc/dotskills --skill technical-integrations -y` | | `technical-skill-finder` | Turn real agent pain into new skills: mine logs, rank wins, draft next steps. | `npx skills add vincentkoc/dotskills --skill technical-skill-finder -y` | @@ -45,6 +46,7 @@ Install one skill: ```bash npx skills add vincentkoc/dotskills --skill technical-deslop -y npx skills add vincentkoc/dotskills --skill technical-documentation -y +npx skills add vincentkoc/dotskills --skill session-done -y npx skills add vincentkoc/dotskills --skill technical-integrations -y npx skills add vincentkoc/dotskills --skill technical-skill-finder -y ``` diff --git a/catalog.yaml b/catalog.yaml index 8cbc332..c784d44 100644 --- a/catalog.yaml +++ b/catalog.yaml @@ -41,6 +41,13 @@ skills: tags: [docs, writing, review, evergreen, brownfield] version: 0.1.0 + - id: session-done + name: Session Done + path: skills/session-done + source: local + tags: [handoff, teardown, memory, workflow] + version: 0.1.0 + - id: technical-integrations name: Technical Integrations path: skills/technical-integrations diff --git a/private-skills/session-done/SKILL.md b/private-skills/session-done/SKILL.md new file mode 100644 index 0000000..d62a844 --- /dev/null +++ b/private-skills/session-done/SKILL.md @@ -0,0 +1,59 @@ +--- +name: session-done +description: Run a post-session teardown with `/done` to capture what was discussed, key decisions, questions, follow-ups, and outcomes into a session markdown artifact. +license: Proprietary +metadata: + internal: true + version: "0.1.0" +--- + +# Session Done + +## Purpose + +Capture and persist every finished agent session as a compact, atomic knowledge unit so work can be resumed quickly, reflected on, and indexed in your knowledge base. + +## When to use + +- You finish a chat/session and want a structured teardown before switching context. +- You need a canonical `.md` handoff containing session ID, branch, decisions, blockers, and follow-up state. +- You want to feed outcomes into Obsidian/PKM and optional flashcard extraction. + +## Workflow + +1. Trigger with `/done` at the end of a session. +2. Run `scripts/session-done` with collected session notes: + - `--session-id` (optional, defaults to `CLAUDE_SESSION_ID` or timestamp) + - `--branch` (optional, defaults to current `git` branch) + - `--summary`, `--decisions`, `--questions`, `--follow-ups`, `--state`, `--reflection`, `--flashcards` +3. Confirm output path (default: `./.session-notes` under repo root, or `DONE_NOTES_DIR` override). +4. If `DONE_OBSIDIAN_VAULT` is set, the script copies the file into that directory for Obsidian/PKM ingestion. +5. If a shared memory file exists, append the reflection block to `done-shared-memory.md`. +6. End session with one action item and one risk item included in the `## Next actions` section. + +## Inputs + +- `--summary`: high-level recap of what the agent worked on. +- `--decisions`: finalized choices and rationale. +- `--questions`: unresolved or open questions from the session. +- `--follow-ups`: concrete follow-up tasks or blockers. +- `--state`: current state of work and repository status. +- `--reflection`: explicit self-reflection on what to improve. +- `--flashcards`: optional question/answer bullets for spaced repetition. +- Optional env vars: + - `DONE_NOTES_DIR`: target folder for generated `.md`. + - `DONE_OBSIDIAN_VAULT`: optional Obsidian/PKM target folder. + - `DONE_MEMORY_FILE`: optional shared memory file (default: `done-shared-memory.md` in notes dir). + +## Outputs + +- A Markdown teardown file with: + - filename containing `session` + session id + branch + timestamp. + - sectioned recap, decisions, questions, follow-ups, reflection, and flashcards. + - machine-readable metadata block for future re-ingestion. +- Optional copied/added artifact in your Obsidian/PKM folder. +- Optional updated shared-memory file. + +## Notes + +The script is intentionally minimal and deterministic. Keep the `/done` payload concise and complete, because this artifact is designed to be the source of truth for the next session spin-up. diff --git a/private-skills/session-done/scripts/session-done b/private-skills/session-done/scripts/session-done new file mode 100755 index 0000000..8540843 --- /dev/null +++ b/private-skills/session-done/scripts/session-done @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: session-done [options] + +Required: at least one content section. + +Options: + --session-id VALUE Session identifier (defaults to CLAUDE_SESSION_ID or timestamp) + --branch VALUE Git branch name (defaults to current branch or unknown) + --summary VALUE Session summary + --decisions VALUE Key decisions + --questions VALUE Open questions + --follow-ups VALUE Follow-up actions + --state VALUE Current state snapshot + --reflection VALUE Self-reflection notes + --flashcards VALUE Flashcards in "Q: ... | A: ..." per line + --notes-dir VALUE Directory for output notes (default: ./.session-notes) + --obsidian-dir VALUE Additional copy target (e.g. Obsidian vault notes folder) + --memory-file VALUE Shared memory file inside notes dir (default: done-shared-memory.md) + --help Show this help + +Environment variables: + DONE_NOTES_DIR Default notes dir + DONE_OBSIDIAN_VAULT Optional Obsidian/PKM notes folder + DONE_MEMORY_FILE Default shared memory filename +EOF +} + +notes_dir="${DONE_NOTES_DIR:-.session-notes}" +obsidian_dir="${DONE_OBSIDIAN_VAULT:-}" +memory_file="${DONE_MEMORY_FILE:-done-shared-memory.md}" +session_id="${CLAUDE_SESSION_ID:-$(date +%s)}" +branch="$(git branch --show-current 2>/dev/null || true)" +summary="" +decisions="" +questions="" +follow_ups="" +state="" +reflection="" +flashcards="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --session-id) + session_id="${2:?missing --session-id value}" + shift 2 + ;; + --branch) + branch="${2:?missing --branch value}" + shift 2 + ;; + --summary) + summary="${2:?missing --summary value}" + shift 2 + ;; + --decisions) + decisions="${2:?missing --decisions value}" + shift 2 + ;; + --questions) + questions="${2:?missing --questions value}" + shift 2 + ;; + --follow-ups) + follow_ups="${2:?missing --follow-ups value}" + shift 2 + ;; + --state) + state="${2:?missing --state value}" + shift 2 + ;; + --reflection) + reflection="${2:?missing --reflection value}" + shift 2 + ;; + --flashcards) + flashcards="${2:?missing --flashcards value}" + shift 2 + ;; + --notes-dir) + notes_dir="${2:?missing --notes-dir value}" + shift 2 + ;; + --obsidian-dir) + obsidian_dir="${2:?missing --obsidian-dir value}" + shift 2 + ;; + --memory-file) + memory_file="${2:?missing --memory-file value}" + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage + exit 2 + ;; + esac +done + +if [[ -z "${summary}" && -z "${decisions}" && -z "${questions}" && -z "${follow_ups}" && -z "${state}" && -z "${reflection}" && -z "${flashcards}" ]]; then + echo "No content provided. Add at least one of --summary/--decisions/--questions/--follow-ups/--state/--reflection/--flashcards." >&2 + exit 2 +fi + +if [[ -z "${branch}" ]]; then + branch="unknown" +fi + +timestamp="$(date -u +%Y-%m-%dT%H-%M-%SZ)" +safe_session_id="$(echo "${session_id}" | tr -cd '[:alnum:]._-' | head -c 48)" +if [[ -z "${safe_session_id}" ]]; then + safe_session_id="session" +fi +safe_branch="$(echo "${branch}" | tr -cd '[:alnum:]._-' | head -c 64)" +if [[ -z "${safe_branch}" ]]; then + safe_branch="branch" +fi +safe_ts="${timestamp//:/-}" + +mkdir -p "${notes_dir}" +note_file="${notes_dir}/session-${safe_branch}-${safe_session_id}-${safe_ts}.md" + +cat > "${note_file}" <> "${memory_path}" + echo "Updated shared memory: ${memory_path}" +fi diff --git a/skills/session-done/SKILL.md b/skills/session-done/SKILL.md new file mode 100644 index 0000000..79b6b6e --- /dev/null +++ b/skills/session-done/SKILL.md @@ -0,0 +1,55 @@ +--- +name: session-done +description: Capture each completed agent session as a structured `/done` teardown with branch/session metadata, key decisions, questions, follow-ups, reflection, and PKM-friendly handoff artifacts. +license: AGPL-3.0-only +metadata: + source: "https://github.com/vincentkoc/dotskills" +--- + +# Session Done + +## Purpose + +Create an atomic handoff for every finished agent session so context can be resumed without ambiguity and work can be resumed in small, traceable units. + +## When to use + +- A session has ended and you want a durable teardown before context switch. +- You need a canonical `.md` record with session id, branch, decisions, questions, and follow-ups. +- You want optional Obsidian/PKM sync plus shared-memory updates. +- You want self-reflection plus flashcard-ready prompts captured alongside outcomes. + +## Workflow + +1. End with `/done` and collect values for at least one section. +2. Run `scripts/session-done` with section flags: + - `--summary` + - `--decisions` + - `--questions` + - `--follow-ups` + - `--state` + - `--reflection` + - `--flashcards` +3. Use `--session-id` for the current Claude session and `--branch` (defaults to git branch). +4. Review the generated `.md` file in `DONE_NOTES_DIR` (or `.session-notes`). +5. If configured, confirm sync to Obsidian/PKM and shared-memory append. + +## Inputs + +- `--summary`: what was discussed or changed. +- `--decisions`: final choices and rationale. +- `--questions`: unresolved questions. +- `--follow-ups`: pending items. +- `--state`: work state / status. +- `--reflection`: what to improve next session. +- `--flashcards`: optional spaced-repetition prompts. +- Optional env vars: + - `DONE_NOTES_DIR`: output directory (default `./.session-notes`). + - `DONE_OBSIDIAN_VAULT`: optional PKM/Obsidian target directory. + - `DONE_MEMORY_FILE`: optional shared-memory filename in notes dir. + +## Outputs + +- One markdown file named with session id + branch + timestamp. +- Structured sections for summary, decisions, questions, follow-ups, state, reflection, and flashcards. +- Optional Obsidian/PKM copy and optional shared-memory append. diff --git a/skills/session-done/agents/openai.yaml b/skills/session-done/agents/openai.yaml new file mode 100644 index 0000000..a46831e --- /dev/null +++ b/skills/session-done/agents/openai.yaml @@ -0,0 +1,10 @@ +interface: + display_name: "Session Done" + short_description: "Capture atomic post-session teardown notes with session metadata and structured handoff outputs." + icon_small: "./assets/icon.jpg" + icon_large: "./assets/icon.jpg" + brand_color: "#111827" + default_prompt: "Capture session teardown in structured markdown for continuation, shared memory, and PKM workflows." + +policy: + allow_implicit_invocation: true diff --git a/skills/session-done/assets/icon.jpg b/skills/session-done/assets/icon.jpg new file mode 100644 index 0000000..8c8b22a Binary files /dev/null and b/skills/session-done/assets/icon.jpg differ diff --git a/skills/session-done/scripts/session-done b/skills/session-done/scripts/session-done new file mode 100755 index 0000000..8540843 --- /dev/null +++ b/skills/session-done/scripts/session-done @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Usage: session-done [options] + +Required: at least one content section. + +Options: + --session-id VALUE Session identifier (defaults to CLAUDE_SESSION_ID or timestamp) + --branch VALUE Git branch name (defaults to current branch or unknown) + --summary VALUE Session summary + --decisions VALUE Key decisions + --questions VALUE Open questions + --follow-ups VALUE Follow-up actions + --state VALUE Current state snapshot + --reflection VALUE Self-reflection notes + --flashcards VALUE Flashcards in "Q: ... | A: ..." per line + --notes-dir VALUE Directory for output notes (default: ./.session-notes) + --obsidian-dir VALUE Additional copy target (e.g. Obsidian vault notes folder) + --memory-file VALUE Shared memory file inside notes dir (default: done-shared-memory.md) + --help Show this help + +Environment variables: + DONE_NOTES_DIR Default notes dir + DONE_OBSIDIAN_VAULT Optional Obsidian/PKM notes folder + DONE_MEMORY_FILE Default shared memory filename +EOF +} + +notes_dir="${DONE_NOTES_DIR:-.session-notes}" +obsidian_dir="${DONE_OBSIDIAN_VAULT:-}" +memory_file="${DONE_MEMORY_FILE:-done-shared-memory.md}" +session_id="${CLAUDE_SESSION_ID:-$(date +%s)}" +branch="$(git branch --show-current 2>/dev/null || true)" +summary="" +decisions="" +questions="" +follow_ups="" +state="" +reflection="" +flashcards="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --session-id) + session_id="${2:?missing --session-id value}" + shift 2 + ;; + --branch) + branch="${2:?missing --branch value}" + shift 2 + ;; + --summary) + summary="${2:?missing --summary value}" + shift 2 + ;; + --decisions) + decisions="${2:?missing --decisions value}" + shift 2 + ;; + --questions) + questions="${2:?missing --questions value}" + shift 2 + ;; + --follow-ups) + follow_ups="${2:?missing --follow-ups value}" + shift 2 + ;; + --state) + state="${2:?missing --state value}" + shift 2 + ;; + --reflection) + reflection="${2:?missing --reflection value}" + shift 2 + ;; + --flashcards) + flashcards="${2:?missing --flashcards value}" + shift 2 + ;; + --notes-dir) + notes_dir="${2:?missing --notes-dir value}" + shift 2 + ;; + --obsidian-dir) + obsidian_dir="${2:?missing --obsidian-dir value}" + shift 2 + ;; + --memory-file) + memory_file="${2:?missing --memory-file value}" + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage + exit 2 + ;; + esac +done + +if [[ -z "${summary}" && -z "${decisions}" && -z "${questions}" && -z "${follow_ups}" && -z "${state}" && -z "${reflection}" && -z "${flashcards}" ]]; then + echo "No content provided. Add at least one of --summary/--decisions/--questions/--follow-ups/--state/--reflection/--flashcards." >&2 + exit 2 +fi + +if [[ -z "${branch}" ]]; then + branch="unknown" +fi + +timestamp="$(date -u +%Y-%m-%dT%H-%M-%SZ)" +safe_session_id="$(echo "${session_id}" | tr -cd '[:alnum:]._-' | head -c 48)" +if [[ -z "${safe_session_id}" ]]; then + safe_session_id="session" +fi +safe_branch="$(echo "${branch}" | tr -cd '[:alnum:]._-' | head -c 64)" +if [[ -z "${safe_branch}" ]]; then + safe_branch="branch" +fi +safe_ts="${timestamp//:/-}" + +mkdir -p "${notes_dir}" +note_file="${notes_dir}/session-${safe_branch}-${safe_session_id}-${safe_ts}.md" + +cat > "${note_file}" <> "${memory_path}" + echo "Updated shared memory: ${memory_path}" +fi