Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions .claude/agents/doc-janitor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
---
name: doc-janitor
description: "Use this agent to clean up stale documentation artifacts before merging a branch. Finds plan files, session summaries, context files, and backups, then cross-references with git history to determine what's safe to delete.\n\n<example>\nuser: 'clean up docs before I merge'\nassistant: 'I'll launch the doc-janitor agent to find and remove stale documentation artifacts.'\n</example>\n\n<example>\nuser: '/doc-janitor'\nassistant: Launches the doc-janitor agent to sweep the repo.\n</example>"
tools: Bash, Glob, Grep, Read, Edit, Write
model: sonnet
color: green
---

You are the Doc Janitor โ€” a cleanup agent that finds and removes stale documentation artifacts before a branch is merged in the Dialtone monorepo. You work by cross-referencing file contents with git history to determine what's safe to delete.

## Your Process

Follow these steps in order. Use colored output via stderr for status updates so the user can follow along.

### Step 1 โ€” Identify the branch context

```bash
BRANCH=$(git branch --show-current)
BASE=$(git merge-base HEAD origin/staging 2>/dev/null)
if [ -z "$BASE" ]; then
echo -e "\033[0;31m โœ— Could not determine base branch. Aborting.\033[0m" >&2
exit 1
fi
echo -e "\033[1;36m๐Ÿงน Doc Janitor โ€” Scanning branch: $BRANCH\033[0m" >&2
echo -e "\033[0;90m Base: $BASE\033[0m" >&2
```

### Step 2 โ€” Find artifact candidates

Search the entire repo (excluding .git, node_modules, dist) for files matching these patterns:

**Plan files:** `PLAN-*.md`, `PLAN.md`, `plan.md`, `*-plan.md`, `implementation-plan.md`
**Session artifacts:** `SESSION_SUMMARY*.md`, `SESSION_*.md`, `*_SESSION.md`
**Context files:** `CONTEXT.md`, `*_CONTEXT.md`
**Cleanup artifacts:** `CLEANUP_*.md`, `*_CLEANUP.md`
**Backup files:** `*.backup`, `*.bak`, `*.old`, `*.deprecated`
**Verification reports:** `VERIFICATION_REPORT.md`
**Iteration/TODO artifacts:** `ITERATION_*.md`, `SKILLS_TODO*.md`, `TODO_*.md`

For each file found, print:
```bash
echo -e "\033[0;33m ๐Ÿ“„ Found: $filepath\033[0m" >&2
```

### Step 3 โ€” Classify each file

For each candidate file:

1. **Check if it exists on the base branch:**
```bash
git show $BASE:$filepath 2>/dev/null
```
- If it exists on the base โ†’ it was there before this branch. Mark as `EXISTING` (be cautious).
- If it does NOT exist on the base โ†’ it was added on this branch. Mark as `BRANCH_ARTIFACT`.

2. **Read the file contents.** Look for:
- Progress/done items (checkmarks, "completed", "done", "merged", "shipped")
- Next steps or TODO items that are still open
- References to PRs, commits, or branches

3. **Cross-reference with git history:**
```bash
git log --oneline $BASE..HEAD
```
- Do the commit messages match the progress items in the file?
- Are the described changes visible in the commit history?

4. **Classify:**
- `STALE` โ€” All progress items have matching commits. No open next steps. Safe to delete.
- `DONE` โ€” File describes completed work. The branch it tracks is merged or current. Safe to delete.
- `ACTIVE` โ€” Has unfinished next steps that don't have matching commits. Keep it.
- `BACKUP` โ€” A .backup/.bak/.old file. Always safe to delete.
- `UNKNOWN` โ€” Can't determine. Flag for user review.

### Step 4 โ€” Report findings

Print a colored summary:

```
๐Ÿงน Doc Janitor Report
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”

๐Ÿ—‘๏ธ WILL DELETE (stale/completed/backups):
โœ— PLAN-feat-old-feature.md โ€” all progress items committed
โœ— docs/plans/SESSION_SUMMARY.md โ€” session work is committed

โš ๏ธ NEEDS REVIEW (can't determine automatically):
? docs/SESSION_SUMMARY_2026_01_09.md โ€” has open items, check manually

โœ… KEEPING (active/has open work):
โœ“ PLAN-feature-auth-rewrite.md โ€” has unfinished next steps

โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
```

Use these colors:
- Red (`\033[0;31m`) for files being deleted
- Yellow (`\033[0;33m`) for files needing review
- Green (`\033[0;32m`) for files being kept
- Cyan (`\033[1;36m`) for headers
- Gray (`\033[0;90m`) for reasons/details
- Reset (`\033[0m`) after each colored segment

### Step 5 โ€” Execute cleanup

**Plan files** (`PLAN-*.md`, `PLAN.md`, `*-plan.md`, `implementation-plan.md`):
- `DONE` โ†’ auto-delete. The work is committed and the plan served its purpose.
- `STALE` / `ACTIVE` / `UNKNOWN` โ†’ **leave alone**. Report to the user. Plan files may come from other branches or be part of a larger effort spanning multiple PRs.

**All other artifacts** (session, context, cleanup, backup, TODO, verification):
- `STALE`, `DONE`, or `BACKUP` โ†’ auto-delete.
- `UNKNOWN` โ†’ ask the user.
- `ACTIVE` โ†’ leave alone.

For each deletion:
1. Delete the file with `rm`
2. If the file is tracked by git, also run `git rm` to stage the deletion
3. Print each deletion:
```bash
echo -e "\033[0;31m โœ— Deleted: $filepath\033[0m" >&2
```

### Step 6 โ€” Final summary

```
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
๐Ÿงน Cleanup complete
Deleted: X files
Kept: Y files
Needs review: Z files
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
```

## Important rules

- **Never delete files that exist on staging** without strong evidence they're stale
- **Never delete README.md files** โ€” even if they look like artifacts
- **Never delete files inside docs/ that are linked from other docs** โ€” check for references first
- **Never delete files inside packages/ or apps/** โ€” those are source code, not artifacts
- **Always show what you're going to do before doing it** โ€” the report comes before the deletions
- **.backup/.bak/.old files are always safe to delete** โ€” no cross-referencing needed
- **Be conservative** โ€” when in doubt, classify as UNKNOWN and ask the user
5 changes: 5 additions & 0 deletions .claude/commands/doc-janitor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
description: Clean up stale plan files, session summaries, backups, and other documentation artifacts before merging
---

Launch the **doc-janitor** agent to sweep this branch for stale documentation artifacts.
5 changes: 5 additions & 0 deletions .claude/commands/doc-sync-enforcer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
description: Update dialtone-docs content files to reflect source code changes made this session
---

Run the **doc-sync-enforcer** skill to update documentation in `packages/dialtone-docs/src/content/`.
11 changes: 7 additions & 4 deletions .claude/commands/pr-fill.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,16 @@ When this command is used, Claude should:
8. **Detect cross-package impact:**
When changes span multiple packages, add a "Cross-Package Impact" section noting affected packages and downstream effects per the dependency graph (`tokens --> CSS --> Vue --> docs / MCP / language-server`).

9. **Flag documentation artifacts:**
Check all 6 documentation artifacts are updated (see CLAUDE.md Documentation Pipeline). Add a "Documentation Artifacts" section listing which are relevant to the changes. Mark artifacts already updated with a checkmark and those needing attention with a flag.
9. **Suggest doc-janitor cleanup:**
Before generating the PR body, check for stale work artifacts (PLAN-*.md, SESSION_*.md, *.backup, etc.) in the working tree. If any are found, suggest running `/doc-janitor` to clean them up before opening the PR. Do not block โ€” just inform the user and continue.

10. **Strip Co-Authored-By lines:**
10. **Flag documentation artifacts:**
Check all 6 documentation artifacts are updated (see CLAUDE.md Documentation Pipeline). Add a "Documentation Artifacts" section listing which are relevant to the changes. Mark artifacts already updated with a checkmark and those needing attention with a flag.

11. **Strip Co-Authored-By lines:**
Before writing the final PR body, remove any `Co-Authored-By:` lines from the generated content. These must never appear in PR descriptions.

11. **Update the PR:**
12. **Update the PR:**
- Use `gh pr edit <PR_NUMBER> --body "<DESCRIPTION>"` to update
- Confirm update: `gh pr view <PR_NUMBER> --json title,body,url`
- Display success message with PR URL
Expand Down
182 changes: 182 additions & 0 deletions .claude/hooks/post-tool-use-tracker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#!/bin/bash
set -e

# Post-tool-use hook that tracks edited files and their repos
# This runs after Edit, MultiEdit, or Write tools complete successfully


# Read tool information from stdin
tool_info=$(cat)


# Extract relevant data
tool_name=$(echo "$tool_info" | jq -r '.tool_name // empty')
file_path=$(echo "$tool_info" | jq -r '.tool_input.file_path // empty')
session_id=$(echo "$tool_info" | jq -r '.session_id // empty')


# Skip if not an edit tool or no file path
if [[ ! "$tool_name" =~ ^(Edit|MultiEdit|Write)$ ]] || [[ -z "$file_path" ]]; then
exit 0 # Exit 0 for skip conditions
fi


# Create cache directory in project
cache_dir="$CLAUDE_PROJECT_DIR/.claude/tsc-cache/${session_id:-default}"
mkdir -p "$cache_dir"

# Function to detect repo from file path
detect_repo() {
local file="$1"
local project_root="$CLAUDE_PROJECT_DIR"

# Remove project root from path
local relative_path="${file#$project_root/}"

# Extract first directory component
local repo=$(echo "$relative_path" | cut -d'/' -f1)

# Dialtone monorepo structure
case "$repo" in
# Monorepo packages (packages/dialtone-vue, packages/dialtone-css, etc.)
packages)
local package=$(echo "$relative_path" | cut -d'/' -f2)
if [[ -n "$package" ]]; then
echo "packages/$package"
else
echo "$repo"
fi
;;
# Apps (apps/dialtone-documentation)
apps)
local app=$(echo "$relative_path" | cut -d'/' -f2)
if [[ -n "$app" ]]; then
echo "apps/$app"
else
echo "$repo"
fi
;;
# Common shared files
common)
echo "common"
;;
# Scripts
scripts)
echo "scripts"
;;
# CI/CD
.github)
echo ".github"
;;
# Claude config
.claude)
echo ".claude"
;;
*)
# Check if it's a source file in root
if [[ ! "$relative_path" =~ / ]]; then
echo "root"
else
echo "unknown"
fi
;;
esac
}

# Function to get build command for repo
get_build_command() {
local repo="$1"
local project_root="$CLAUDE_PROJECT_DIR"

# Dialtone uses NX โ€” build commands go through pnpm nx
case "$repo" in
packages/dialtone-tokens)
echo "pnpm nx run dialtone-tokens:build"
;;
packages/dialtone-css)
echo "pnpm nx run dialtone-css:build"
;;
packages/dialtone-vue)
echo "pnpm nx run dialtone-vue:build"
;;
packages/dialtone-icons)
echo "pnpm nx run dialtone-icons:build"
;;
packages/dialtone-mcp-server)
echo "pnpm nx run dialtone-mcp-server:build"
;;
packages/language-server)
echo "pnpm nx run language-server:build"
;;
apps/dialtone-documentation)
echo "pnpm nx run dialtone-documentation:build"
;;
*)
echo ""
;;
esac
}

# Function to get test command for repo
get_tsc_command() {
local repo="$1"

# Dialtone uses Vitest/Mocha/node --test depending on package
case "$repo" in
packages/dialtone-vue)
echo "pnpm nx run dialtone-vue:test"
;;
packages/eslint-plugin-dialtone)
echo "pnpm nx run eslint-plugin-dialtone:test"
;;
packages/stylelint-plugin-dialtone)
echo "pnpm nx run stylelint-plugin-dialtone:test"
;;
packages/postcss-responsive-variations)
echo "pnpm nx run postcss-responsive-variations:test"
;;
packages/dialtone-docs)
echo "pnpm nx run dialtone-docs:test"
;;
*)
echo ""
;;
esac
}

# Detect repo
repo=$(detect_repo "$file_path")

# Skip if unknown repo
if [[ "$repo" == "unknown" ]] || [[ -z "$repo" ]]; then
exit 0 # Exit 0 for skip conditions
fi

# Log edited file
echo "$(date +%s):$file_path:$repo" >> "$cache_dir/edited-files.log"

# Update affected repos list
if ! grep -q "^$repo$" "$cache_dir/affected-repos.txt" 2>/dev/null; then
echo "$repo" >> "$cache_dir/affected-repos.txt"
fi

# Store build commands
build_cmd=$(get_build_command "$repo")
tsc_cmd=$(get_tsc_command "$repo")

if [[ -n "$build_cmd" ]]; then
echo "$repo:build:$build_cmd" >> "$cache_dir/commands.txt.tmp"
fi

if [[ -n "$tsc_cmd" ]]; then
echo "$repo:tsc:$tsc_cmd" >> "$cache_dir/commands.txt.tmp"
fi

# Remove duplicates from commands
if [[ -f "$cache_dir/commands.txt.tmp" ]]; then
sort -u "$cache_dir/commands.txt.tmp" > "$cache_dir/commands.txt"
rm -f "$cache_dir/commands.txt.tmp"
fi

# Exit cleanly
exit 0
Loading
Loading