Skill: sssamuelll/dev-chronicle (version 1.0.1)

--- name: dev-chronicle description: Generate narrative chronicles of developer work from git history, session transcripts, and memory files. Use when the user asks "what did I do today/this week", wants a work summary, daily/weekly chronicle, standup notes, or portfolio narrative. Also triggers on "chronicle", "dev diary", "work story", "recap", or "standup". --- # DevChronicle — Narrative Engineering Journal DevChronicle generates prose chronicles of developer work — not dashboards, not metrics, not bullet lists. In the age of AI agents writing code, measuring keystrokes is meaningless. What matters is what you *decided*, what you *killed*, and where you're *going*. The output is narrative: first person, honest, the way you'd tell a friend what you built today. ## Setup On first use, check for `{baseDir}/config.json`. If it doesn't exist, create it by asking the user: ```json { "projectDirs": ["~/Projects"], "projectDepth": 3, "memoryDir": null, "sessionsDir": null } ``` - `projectDirs`: directories to scan for git repos (array, supports `~`) - `projectDepth`: how deep to search for `.git` folders (default: 3) - `memoryDir`: path to OpenClaw memory files, or `null` to auto-detect (`<workspace>/memory`) - `sessionsDir`: path to session transcripts, or `null` to auto-detect (`~/.openclaw/agents/main/sessions`) ## Gathering Data Run the gather script to collect raw data for a period: ```bash bash {baseDir}/scripts/gather.sh [YYYY-MM-DD] [days] ``` Examples: - `bash {baseDir}/scripts/gather.sh` — today only - `bash {baseDir}/scripts/gather.sh 2026-02-19 7` — week ending Feb 19 The script reads `{baseDir}/config.json` for paths. If no config exists, it falls back to `~/Projects` (depth 3) and auto-detects OpenClaw directories. After gathering, read the output and generate a chronicle. ### Data Sources (priority order) 1. **Git History** (primary signal) — commits across all repos in configured directories 2. **Memory Files** — `memory/YYYY-MM-DD.md` files contain decisions, context, things worth remembering 3. **Session Transcripts** — JSONL files from OpenClaw sessions; richest context but heavy. Scan metadata line first, only read relevant sessions. 4. **External Tools** (optional) — Trello, Notion, calendar, etc. Enrichment, not primary. ## Generating the Chronicle ### Voice **Critical**: Read `{baseDir}/references/voice-profile.md` before generating any chronicle. The voice IS the product. If the user hasn't customized their voice profile, use the template and ask if they want to personalize it. A chronicle without voice is just a changelog. Core rules (regardless of voice profile): - **Decisions > tasks.** What got rejected matters as much as what shipped. - **No corporate speak.** No "leveraged", "synergized", "deliverables", "open threads", "action items". - **Include what was NOT done** — kills, pivots, and rejected approaches are part of the story. - **Emotional beats matter** — the satisfaction, frustration, surprise. These are human signals. - **Be personal.** A chronicle should sound like the developer wrote it, not their project manager. If it reads like a status report, rewrite it. - **Structure is a suggestion, not a cage.** If the day had one big theme, write one section. If it was chaos, let it be chaotic. Don't force headers. ### Formats **Daily Chronicle** (default — aim for ~500-800 words, not a novel) ```markdown # Chronicle — [Date] [Opening: set the scene in 1-2 punchy sentences] ## [Theme 1] [Narrative: what happened, why, what got killed or rejected, how it felt] ## [Theme 2] [...] [Weave metrics naturally: "12 commits later..." not a stats block at the end] [End with what's unfinished — but as narrative, not a TODO list] ``` Rules: - **Daily = tight.** One screen of text. Save the epic for weekly. - **No "Metrics" section.** If commit count matters, weave it in. "67 commits across two days" belongs in a sentence, not a table. - **No "Open Threads" or "Next Steps".** If something's unfinished, say it where it fits: "El Press Kit sigue esperando que Angélica suba el PDF." Done. - **Numbers without story are noise.** "5 deploys" means nothing. "Deployed 5 times because the server kept OOM-killing on a 914MB box" means something. **Weekly Chronicle** — roll up daily themes into arcs. This one CAN be long. Emphasize direction and pivots over individual tasks. **Standup** — telegraphic: yesterday / today / blockers. Three bullets max each. **Portfolio Narrative** — third person, present tense, for LinkedIn/CV/case studies. Punchy and honest, not marketing-speak. ## Direction/Execution Ratio When enough data exists (weekly+), calculate and mention: - **Spec lines vs code lines** — are you building or planning? - **Commits vs decisions** — activity vs impact - **Kills** — what got cut and why (kills show taste) - **Pivots** — direction changes and their reasoning This is not a KPI. It's a mirror.

path: README.md
content: # DevChronicle

Narrative engineering journal for OpenClaw. Turns your git history, agent memory, and session transcripts into prose chronicles of what you actually did — and more importantly, what you decided.

## Why

In the age of AI agents writing code, measuring lines-of-code or time-in-editor is meaningless. DevChronicle measures what matters: **decisions, kills, and direction**.

The output is narrative, not a dashboard. First person, honest, like telling a friend what you built today.

## Install

```bash
clawhub install dev-chronicle
```

Or copy the skill folder to `~/.openclaw/skills/dev-chronicle/`.

## Usage

Ask your agent:
- "What did I do today?"
- "Generate a chronicle for this week"
- "Standup notes"
- "Write a portfolio narrative for [project]"

## Setup

On first use, the skill creates `config.json` in the skill directory:

```json
{
  "projectDirs": ["~/Projects"],
  "projectDepth": 3,
  "memoryDir": null,
  "sessionsDir": null
}
```

Edit `projectDirs` to point to where your code lives. Memory and session directories auto-detect for OpenClaw installations.

## Voice Profile

The secret sauce. Edit `references/voice-profile.md` to match how YOU talk about your work. The template gets you started, but a chronicle without your voice is just a changelog.

## Formats

- **Daily Chronicle** — narrative recap with themes, decisions, and metrics
- **Weekly Chronicle** — arcs and progress over individual tasks
- **Standup** — telegraphic: yesterday / today / blockers
- **Portfolio Narrative** — third person, for LinkedIn/CV/case studies

## Requirements

- `git` (for commit history)
- `python3` (for session transcript scanning)
- `bash` (macOS or Linux)

## License

MIT


path: config.json
content: {
  "projectDirs": ["~/Projects"],
  "projectDepth": 3,
  "memoryDir": null,
  "sessionsDir": null
}


path: references/voice-profile.md
content: # Voice Profile — Template

This file defines how your chronicles sound. Customize it to match YOUR voice.

A chronicle without voice is just a changelog. The voice is the product.

## How to Customize

Replace the sections below with your own patterns. Think about:
- How do you talk about your work to a friend?
- What language(s) do you mix?
- What words do you never use?
- What's your version of "hell yeah, that worked"?

---

## Language
- Primary: English
- Mix in other languages naturally if you're multilingual
- Technical terms stay in their native language (nobody translates "deploy")

## Tone
- Casual but competent — like explaining to a peer, not a manager
- Honest about what didn't work
- Short sentences between longer narrative ones
- First person for daily/weekly; third person OK for portfolio narratives

## Words You Use
- (Add your verbal signatures here — phrases you actually say)

## Words You Never Use
- "Leverage", "synergy", "deliverables", "stakeholder", "action items", "open threads"
- "I think perhaps we could consider..."
- Anything that sounds like a corporate email
- Section headers like "Metrics", "Next Steps", "Key Takeaways" — weave that stuff into the narrative

## What Matters to You
- Decisions over tasks
- Why something was killed, not just what was built
- The human side — frustration, satisfaction, surprise
- Craft — you care about how things feel, not just that they work

## Example (customize this)

"Today was all about the credits system. The old form had static 'Client' and 'Role' fields that felt amateur — in film, nobody says 'client', you say 'Artist' or 'Production'. So we built a dynamic pair system, like movie credits.

First version was a rigid grid. Looked like a spreadsheet with nice fonts. Switched to flex-wrap and now the credits flow naturally — year, artist, production, direction, all sizing themselves based on content.

The heavy thing today: I analyzed that project I'd been spec'ing for months. 20K lines of docs, barely any code. The uncomfortable truth is that what it wanted to be already exists. But from that autopsy came something better."


path: scripts/gather.sh
content: #!/usr/bin/env bash
# DevChronicle — Data Gatherer
# Usage: gather.sh [date] [days]
# Example: gather.sh 2026-02-19 1    (single day)
#          gather.sh 2026-02-19 7    (week ending on date)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
CONFIG="$SKILL_DIR/config.json"

DATE="${1:-$(date +%Y-%m-%d)}"
DAYS="${2:-1}"

# Cross-platform date math (macOS + Linux)
date_add() {
  local base="$1" offset="$2"
  if date -v+0d &>/dev/null; then
    # macOS
    date -v"${offset}d" -j -f "%Y-%m-%d" "$base" +%Y-%m-%d
  else
    # Linux
    date -d "$base ${offset} days" +%Y-%m-%d
  fi
}

START_DATE=$(date_add "$DATE" "-$DAYS")
# Include end date in git log
END_NEXT=$(date_add "$DATE" "+1")

# Read config (or defaults)
if [ -f "$CONFIG" ]; then
  PROJECT_DIRS=$(python3 -c "
import json, os
c = json.load(open('$CONFIG'))
for d in c.get('projectDirs', ['~/Projects']):
    print(os.path.expanduser(d))
" 2>/dev/null || echo "$HOME/Projects")
  PROJECT_DEPTH=$(python3 -c "import json; print(json.load(open('$CONFIG')).get('projectDepth', 3))" 2>/dev/null || echo 3)
  MEMORY_DIR=$(python3 -c "
import json, os
c = json.load(open('$CONFIG'))
d = c.get('memoryDir')
print(os.path.expanduser(d) if d else '')
" 2>/dev/null || echo "")
  SESSIONS_DIR=$(python3 -c "
import json, os
c = json.load(open('$CONFIG'))
d = c.get('sessionsDir')
print(os.path.expanduser(d) if d else '')
" 2>/dev/null || echo "")
else
  PROJECT_DIRS="$HOME/Projects"
  PROJECT_DEPTH=3
  MEMORY_DIR=""
  SESSIONS_DIR=""
fi

# Auto-detect OpenClaw dirs if not configured
if [ -z "$MEMORY_DIR" ]; then
  for candidate in "$HOME/.openclaw/workspace/memory" "$HOME/.claude/workspace/memory"; do
    [ -d "$candidate" ] && MEMORY_DIR="$candidate" && break
  done
fi
if [ -z "$SESSIONS_DIR" ]; then
  for candidate in "$HOME/.openclaw/agents/main/sessions" "$HOME/.claude/agents/main/sessions"; do
    [ -d "$candidate" ] && SESSIONS_DIR="$candidate" && break
  done
fi

echo "=== DEVCHRONICLE DATA GATHER ==="
echo "Period: $START_DATE → $DATE"
echo ""

# ── Git History ──
echo "## GIT COMMITS"
echo ""
while IFS= read -r proj_dir; do
  [ -d "$proj_dir" ] || continue
  find "$proj_dir" -maxdepth "$PROJECT_DEPTH" -name ".git" -type d 2>/dev/null | sort | while read -r gitdir; do
    repo=$(dirname "$gitdir")
    name=$(basename "$repo")
    log=$(git -C "$repo" log --after="$START_DATE" --before="$END_NEXT" --format="%h | %s | %ai" --all 2>/dev/null || true)
    if [ -n "$log" ]; then
      count=$(echo "$log" | wc -l | tr -d ' ')
      echo "### $name ($count commits)"
      echo "$log"
      echo ""
    fi
  done
done <<< "$PROJECT_DIRS"

# ── Memory Files ──
if [ -n "$MEMORY_DIR" ] && [ -d "$MEMORY_DIR" ]; then
  echo "## MEMORY FILES"
  echo ""
  current="$START_DATE"
  while [[ "$current" < "$END_NEXT" ]]; do
    memfile="$MEMORY_DIR/$current.md"
    if [ -f "$memfile" ]; then
      echo "### $current"
      cat "$memfile"
      echo ""
    fi
    current=$(date_add "$current" "+1")
  done
fi

# ── Session Metadata ──
if [ -n "$SESSIONS_DIR" ] && [ -d "$SESSIONS_DIR" ]; then
  echo "## SESSIONS"
  echo ""
  python3 -c "
import os, json, glob, sys
sdir = '$SESSIONS_DIR'
start, end = '$START_DATE', '$DATE'
found = 0
for f in sorted(glob.glob(os.path.join(sdir, '*.jsonl'))):
    if f.endswith('.lock'): continue
    try:
        with open(f) as fh:
            d = json.loads(fh.readline())
        ts = d.get('timestamp','')[:10]
        if ts >= start and ts <= end:
            lines = sum(1 for _ in open(f))
            sid = os.path.basename(f).replace('.jsonl','')
            print(f'- {sid[:12]}... | {ts} | {lines} lines')
            found += 1
    except: pass
if not found:
    print('(no sessions found for this period)')
" 2>/dev/null || echo "(python3 not available — skipping sessions)"
fi

echo ""
echo "=== END GATHER ==="


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)