Skip to content

Repository files navigation

arc

A CLI for tracking the arc of AI-assisted projects end-to-end. Each project lives as an Obsidian note with YAML frontmatter; arc drives the full lifecycle from GitHub issue to merged PR using your coding agent of choice (Claude, Cursor, Codex) for planning, implementation, code review, and informal chatting.

Why

When you're working on multiple projects in parallel, context gets scattered — across GitHub issues, Obsidian notes, web clippings, and your codebase. You lose track of what's running where, which branch is which, and the current stage of ongoing projects.

arc is opinionated. It bakes in the development patterns and preferences I've landed on after months of working with agents. I built it to solve some very specific problems in my workflow, some of which may or may not be relevant to you. That said, the problems it solves are general. If you're feeling the friction of context switching, parallel development, tracking project status, and documenting agent sessions across long-running AI-assisted projects, then arc might be useful to you as-is. Or at least as a starting point for building your own version. The customization guide is written specifically to help your coding agent of choice adapt arc to your repo, your tools, and your workflow.

The specific problems arc solves:

  • Context management: Pulls together GitHub issues, Obsidian notes, and repo state into a single project view. Plans, clippings, and related notes are automatically linked and fed to agents as progressively disclosed context. Notes and plans get updated after every conversation.
  • Parallel development: Each project gets its own isolated git clone (not a worktree — a lightweight clone), its own feature branch, and optionally its own dev server, all sharing a common local database (my personal preference, ymmv).
  • At-a-glance status: The dashboard shows all active projects — development and non-development — with their current stage, branch, PR status, and running tmux sessions.
  • Automate the predictable parts: Code review, CI monitoring, branch management, and PR creation are tedious but mechanical. arc handles them so you can focus on more interesting things without dropping the ball.

Workflow

1. arc sync / arc new     →  Pull GitHub issues or create a project from scratch
2. arc plan               →  Collect context from Obsidian + GitHub, break into stages
3. arc sandbox / arc dev  →  Create isolated clone, optionally launch dev server
4. arc implement          →  Coding agent implements (interactive or background)
5. arc review             →  AI code review
6. arc diff-review        →  Manual (human) diff review in VS Code (Local PR Review extension)
7. arc address-review     →  AI addresses your review comments
8. arc approve            →  Push, open PR, continuous CI monitor with auto-fix
9. arc done / arc archive →  Mark complete, clean up sessions/sandbox

At any point during a project's lifecycle, you can drop in for human-in-the-loop work:

  • arc chat <slug> — start a conversation with full project context loaded. Good for brainstorming, asking questions about the codebase, or thinking through a tricky decision without launching a full implementation session.
  • arc note <slug> — jump straight to the project's Obsidian note. Review plans, check session history, or update the objective.
  • arc editor <slug> — open the project sandbox in VS Code (or Cursor with --cursor) with the changed files pre-loaded, so you can review a diff or make a quick manual edit.

These aren't part of the linear workflow — they're escape hatches for when you want to check in, steer, or just read what's happened.

Highlights

  • Not everything needs an agent — arc makes it easy to jump between Obsidian, VS Code/Cursor, and the terminal. arc note, arc editor, and the dashboard are quick-nav commands, not AI wrappers.
  • Separate implementation and review — use one model to implement, then another to review. You can configure the model/provider on every command. Or skip AI review and review the diff yourself. The steps are decoupled.
  • Obsidian as the knowledge layer — project context, plans, clippings, and related notes all live in your vault. The vault organizer automatically tags and cross-links new notes to active projects.
  • Project-aware sessions — the ARC_PROJECT_SLUG env var is set before every Claude launch, so your Claude Code status line and hooks can show which project you're working on.
  • Lightweight clones, not worktrees — each sandbox is a complete git clone via --reference, so you get full isolation with minimal disk cost. Dev servers can run in parallel on different ports sharing a common local database. You can easily pull from the main branch, and don't have to worry about worktrees.
  • CI monitor with auto-fix — after opening a PR, arc polls CI status and can automatically invoke Claude to fix failures and push.

Quick start

Prerequisites

  • Python 3.11+
  • uv (script runner — arc uses uv inline scripts, no virtualenv needed)
  • claude CLI (Claude Code)
  • codex CLI — npm install -g @openai/codex (for code review; optional if using --tool claude)
  • gh CLI (authenticated)
  • tmux, git
  • Obsidian — all project state lives in your vault as Markdown notes
  • VS Code or Cursorarc editor opens sandboxes in your editor (use --cursor for Cursor)
  • macOS (uses osascript for notifications; Linux users can swap for notify-send)

Setup

git clone https://github.com/ehutt/arc.git
cd arc
arc init   # interactive config setup — or copy config.example.toml to config.toml

Make arc.py your CLI entry point:

# Option A: alias
echo 'alias arc="uv run --script ~/path/to/arc/arc.py"' >> ~/.zshrc

# Option B: symlink
ln -s ~/path/to/arc/arc.py ~/.local/bin/arc

Project slug autocompletion is built in via Typer — tab-complete works for all commands that take a <slug> argument.

Commands

Command Description
arc Dashboard: status, stage progress, branches, tmux sessions
Project Management
arc sync Pull GitHub issues and sync PR status from the configured repo (reconciles sandbox branches first)
arc reconcile Reconcile vault state against sandbox git reality: branch, last_activity (latest commit), planned→active promotion, orphan-sandbox report. --dry-run to preview. Scheduled nightly via launchd.
arc note <slug> Open a project's Obsidian note
arc new <title> Create a new project note without a GitHub issue
arc done <slug> [stage] Mark a stage (or whole project) as done — auto-promotes next stages to ready; closing the whole project stops sessions and removes its sandbox. Use --skip to skip instead.
arc archive <slug> Shelve a project without completing it — kills sessions and always preserves the sandbox for later. Use when pausing or abandoning work.
Planning
arc plan <slug> Interactive planning session (Claude by default; --codex for Codex)
arc stage <slug> List stages; --add "name" to add, --depends-on 1,2 for deps, --plan <id> to plan a stage (--codex for Codex)
Development
arc sandbox <slug> Create an isolated git clone with a feature branch
arc implement <slug> Implementation session; --bg for autonomous tmux mode, --codex to use Codex instead of Claude
arc editor <slug> Open sandbox in VS Code (or Cursor with --cursor) with changed files
arc dev <slug> Launch dev server in a background tmux session
Review & Ship
arc review <slug> AI code review (--tool codex or --tool claude, --model to override). Add --thorough for a multi-lens review, or --debate for an adversarial Codex-reviews / Claude-fixes loop.
arc pr-review <number> Review someone else's PR by number: checks the PR out into a pr-<n> sandbox (created on demand), creates a lightweight pr-review project, and runs an approval-biased read-only review (--thorough / --debate supported). Writes a local report ending in RECOMMENDATION: APPROVE or REQUEST-CHANGES; never posts to GitHub unless --comment.
arc diff-review <slug> Open VS Code with a Local PR Review session pre-seeded against the sandbox (base→branch diff with inline-comment gutter). Installs the extension on first run.
arc address-review <slug> Hand the unresolved comments from the diff-review session to Claude/Codex to fix or reply, then mark addressed threads resolved.
arc approve <slug> Push branch, create PR, and launch CI monitor
Knowledge
arc search <query> Natural-language vault search. Lifecycle/folder filters parsed from the query itself (e.g. "evergreen notes on RAG" or "skip old blog drafts"). Clickable Obsidian links in output.
arc lint Read-only vault health report: notes missing lifecycle frontmatter, project status contradictions, folders without index.md, dead wikilinks.
arc migrate-lifecycle One-shot: tag every vault note with lifecycle: + source_type: frontmatter. Idempotent. --dry-run to preview.
Utilities
arc init Interactive setup — create config.toml from prompts
arc chat <slug> Informal chat with project context (Claude by default; --codex for Codex)
arc organize Run the vault organizer (tag & link notes)
arc cleanup Remove clean, commit-free sandboxes for done projects, then move done/archived project folders to the vault archive (--dry-run to preview). Archived-project sandboxes are preserved.

Knowledge layer

Every note in the vault carries a lifecycle and source_type in its frontmatter. These drive how organize.py treats the note (what it can edit vs. leave alone) and power arc search.

Lifecycle classes:

  • live — active project indexes, current plans. arc auto-refreshes last_activity, last_command, open_prs, and active_stage on every chat / plan / implement / review / approve / sandbox / dev / sync.
  • log — session-log files (notes.md under Projects). Append-only; never touched by the organizer.
  • frozen — archived project indexes, finished blog drafts. No edits.
  • evergreen — authored notes worth re-verifying occasionally (Research, Agents).
  • reference — clippings and web-sourced material. Tagged and linked, but bodies are never modified.

Source types: project-meta, session-log, web-clipping, authored. The source_type is permanent — a note clipped from the web keeps source_type: web-clipping even if it later moves out of Clippings/.

Classification is deterministic by folder + frontmatter; any note with an explicit lifecycle: you've set yourself is always preserved.

Status lifecycle

Project statuses (derived from stages): needs-planplannedactivedonearchived

Stage statuses: pendingreadyimplementedreviewedpr-opendone

Project status is derived from stages automatically, with one exception: a project is only marked done via an explicit arc done <slug> call (not by completing all stages). All stages being done means the current plan is finished — the project stays active until you close it. When the last stage completes, arc prints a hint to run arc done when you're ready.

Stages auto-promote from pending to ready when their dependencies are met. Arc owns all status transitions — agents don't update frontmatter.

Project folder structure

Projects/my-feature/
  index.md              # frontmatter + objective + tasks (lean)
  notes.md              # project-level session log
  stages/
    1-api-endpoints/
      plan.md           # stage plan
      notes.md          # stage session log
    2-ui-components/
      plan.md
      notes.md

Session notes, plans, and CI events all go to the appropriate notes.md — never into index.md. Each stage gets its own folder with isolated context.

Existing projects are auto-migrated on load: ## Notes sections are extracted from index.md into notes.md, old statuses are mapped to the new system, and stage folders are created.

Configuration

Run arc init for interactive setup, or copy config.example.toml to config.toml and edit it. The file is gitignored — your personal config stays local.

[core]
obsidian_vault = "~/path/to/your/obsidian-vault"
projects_folder = "Projects"
sandbox_root = "~/Projects/my-repo-clones"
branch_prefix = "your-username"

[github]
user = "your-github-username"
repo = "org/repo-name"

[agent]
test_cmd = "pytest tests/"
lint_cmd = "make lint"

[organize]
skip_folders = ["Templates", ".obsidian", "Assets"]
max_notes_per_run = 20
model = "claude-sonnet-4-20250514"

Config reference

Key Purpose Example
core.obsidian_vault Path to your Obsidian vault (~ expanded) "~/Documents/Notes"
core.projects_folder Folder inside the vault for project notes "Projects"
core.sandbox_root Where isolated git clones are created "~/Projects/my-repo-clones"
core.branch_prefix Prefix for feature branches (prefix/slug) "jdoe"
github.user Your GitHub username (for filtering assigned issues) "jdoe"
github.repo Target repo in org/name format "my-org/my-app"
agent.test_cmd Test command run in sandboxes by agents "pytest tests/ -x"
agent.lint_cmd Lint command run in sandboxes by agents "ruff check ."
organize.skip_folders Vault folders the organizer ignores ["Templates", "Assets"]
organize.max_notes_per_run Caps API calls per organizer run 20
organize.model Claude model for the vault organizer "claude-sonnet-4-20250514"

Customization guide

Pointing arc at a different repo

  1. config.toml: Set github.repo to the target repo (e.g. "my-org/my-app") and github.user to the user's GitHub username.
  2. config.toml: Set sandbox_root to wherever clones should live (e.g. "~/Projects/my-app-clones").
  3. arc.py Config.bare_repo property: Returns self.sandbox_root / ".phoenix-bare". Rename .phoenix-bare to something appropriate (e.g. ".my-app-bare"). This is a local bare git reference used for fast git clone --reference.
  4. arc.py _load_env_keys() function: Loads API keys from a .env file at cfg.sandbox_root.parent / "phoenix" / ".env". Change "phoenix" to the directory name of your main repo checkout, or remove this function if you manage env vars differently.

Customizing the dev server command

The arc dev command launches a dev server in tmux. By default it runs make dev in the sandbox. To customize:

Set dev.env_file in config.toml to source a canonical environment file before every launch. When configured, arc dev refuses to start if the file does not exist. Keep this file limited to non-secret application configuration. At runtime, arc dev retrieves its API keys directly from macOS Keychain so their values are not written into the launch script.

[dev]
env_file = "~/Projects/phoenix/.env"
  1. Change the launch command: In arc.py, find the dev_script construction (search for exec make). Replace the last line with your app's dev server command (e.g. exec npm run dev, exec cargo run, etc.).
  2. Change the default port: The default port is 6006. Change to whatever your dev server uses.
  3. Phoenix-specific env vars: The PHOENIX_CLOUD_VARS list and PHOENIX_WORKING_DIR are specific to the Phoenix app. Remove or replace these with env vars relevant to your app.

Customizing environment variable injection

arc injects env vars into agent subprocesses in two ways:

  1. MODEL_API_KEYS and DEV_API_KEYS lists: API keys carried into agent subprocesses and Phoenix dev-server processes. arc dev resolves DEV_API_KEYS from Keychain at launch time without serializing their values into its script. Add or remove keys as needed for your setup.

  2. _load_env_keys() function: Loads each key from the macOS Keychain first (security find-generic-password -a $USER -s <KEY> -w), falling back to a .env file at cfg.sandbox_root.parent / "phoenix" / ".env" for any key not in the Keychain. Store keys with security add-generic-password -a "$USER" -s <KEY> -w. Agent sessions use this loader; arc dev sources its configured dev.env_file and independently resolves DEV_API_KEYS from Keychain inside the server launch process.

Subscription billing guard: ANTHROPIC_API_KEY is always stripped from Claude Code launches (interactive and background) via _clean_env("claude") — its presence would silently switch Claude Code from subscription (OAuth) billing to pay-per-token API billing. Codex sessions and non-Claude subprocesses still receive it for code under test. Agents inside a Claude session that need the key can fetch it on demand from the Keychain.

  1. _clean_env() function: Strips conda/virtualenv paths from PATH before launching agents. This prevents Claude and Codex from seeing the wrong Python environment. If you don't use conda, this is harmless. If you use a different environment manager, you may need to adjust the path filters.

Choosing Claude vs Codex

The interactive agent commands default to Claude but can use Codex with a --codex flag:

arc plan <slug> --codex
arc stage <slug> --plan <id> --codex
arc chat <slug> --codex
arc implement <slug> --codex          # interactive
arc implement <slug> --bg --codex     # autonomous (writes AGENTS.md, runs `codex exec`)

Codex has no --system-prompt flag for interactive sessions, so arc concatenates the system prompt and initial message into one combined prompt. For --bg, autonomous implementation instructions are written to AGENTS.md (Codex convention) instead of CLAUDE.md.

arc review and arc address-review use a separate --tool {claude,codex} flag (Codex is the default for those — see below).

Customizing the code review

arc review defaults to Codex but supports both tools:

arc review <slug>                        # codex (default)
arc review <slug> --tool claude          # claude instead
arc review <slug> --model gpt-5.3-codex # codex with specific model
arc review <slug> --tool claude -m claude-sonnet-4-20250514

To change the default, edit DEFAULT_REVIEW_TOOL and DEFAULT_REVIEW_MODEL near the top of _run_review() in arc.py.

Thorough (multi-lens) review

arc review <slug> --thorough    # or -T

Instead of one pass, --thorough splits the review into independent lens agents that each look at the diff from a different angle. Lens agents run in parallel (headless, approvals bypassed since the sandbox is already an isolated clone), then an interactive synthesis agent merges the findings and walks you through them one-by-one, asking before applying each fix.

Lenses:

  • behavior — correctness + regression risk (logic paths, boundary conditions, error handling, broken invariants). Runs tests and lint.
  • tests — test quality and coverage (meaningful assertions, missing edge cases, flaky patterns)
  • interface — public-surface ergonomics (naming, signatures, defaults, docs, discoverability)
  • security — auto-added when the diff contains signals like auth, token, crypto, pickle, shell=True, etc.

Output:

  • <vault>/Projects/<slug>/review/<lens>.md — each lens's findings
  • <vault>/Projects/<slug>/review/summary.md — merged summary written by the synthesis agent. Compound findings (same location flagged by ≥2 lenses) are surfaced first.
  • <vault>/Projects/<slug>/review/logs/<lens>.log — raw codex output per lens, for debugging.

Customizing lens prompts: each lens is a markdown file in lenses/ next to arc.py. Edit those to adjust methodology, severity bars, or output format — arc loads them at runtime.

Flow:

arc review <slug> --thorough
  → spawn N lens agents in parallel (codex exec, bypassed approvals)
  → show progress: [✓] behavior (42s) | [·] tests (running) | ...
  → when all done, spawn synthesis agent (interactive codex)
  → synthesis writes summary.md, then:
      For each finding: "Apply this fix? [y/n/e/q]"
      → y applies + commits one-line per fix
      → n skips
      → e expands reasoning
      → q quits the walkthrough

Reviewing other people's PRs

arc pr-review 14054                 # basic read-only review (codex by default)
arc pr-review 14054 --tool claude   # claude instead
arc pr-review 14054 --thorough      # multi-lens
arc pr-review 14054 --debate        # adversarial: Codex attacks, Claude defends
arc pr-review 14054 --comment       # explicitly opt in to posting the report as a PR comment

Unlike arc review (your own work, fixes get committed), pr-review is built for reviewing PRs you don't own:

  • Approval-biased posture: the agent is instructed to default to approval and gate only on serious issues — correctness bugs, data loss, security, breaking API changes, test regressions. Style observations are phrased as optional suggestions.
  • Strictly read-only: never modifies the PR's code, never pushes, and never posts comments/reviews/approvals to GitHub. The only exception is an explicit --comment, which posts the finished report as a single PR comment.
  • Self-managing sandbox: fetches PR metadata via gh, clones a pr-<n> sandbox from the bare reference repo on first use (reused on re-runs), and gh pr checkouts the branch. The diff is taken against the PR's base branch (git diff origin/<base>...HEAD).
  • --debate flips the roles: Codex reviews adversarially, but Claude acts as the author's defender — it CONFIRMs or REBUTs each finding with evidence instead of fixing code. What survives both agents is what you gate on.
  • Reports live under Projects/pr-<n>/review/ in the vault, and each PR gets a lightweight pr-review-type project so the dashboard shows what you have in flight.
  • Auto-retirement: the nightly arc reconcile checks each open pr-review project against GitHub; once the PR merges, the project is marked done, its tmux sessions are killed, and the pr-<n> sandbox is deleted (only clean clones named pr-* under sandbox_root — a dirty clone is left in place with a warning). The weekly cleanup then archives the project folder.

Adversarial debate review

arc review <slug> --debate            # up to 5 rounds (default)
arc review <slug> --debate --rounds 3

An implement/review loop with the two agents in adversarial roles, using file-based handoff (no MCP session plumbing — each agent reads the other's artifact):

round N:
  Codex (exec, read-write) reviews `git diff main` with an explicit
  "assume it's broken" stance → writes findings + VERDICT to
  <vault>/Projects/<slug>/review/debate/round-N.md
  → VERDICT: APPROVED  → done
  → VERDICT: REVISE    → Claude (headless) fixes each finding and commits,
    or appends a REBUTTAL with evidence under the finding
  next round: Codex re-checks fixes and rebuttals, re-raises what's unresolved

The loop terminates on APPROVED, a missing findings file, or --rounds. The outcome is appended to the project's session notes. Round files are kept for audit.

Ad-hoc cross-agent consultation: Codex is also registered as an MCP server in Claude Code (claude mcp add --scope user codex -- codex mcp-server), so any Claude session can invoke the codex tool directly — useful for second opinions mid-session without a full debate loop.

Nightly reconcile

Status drift was the biggest real-world failure mode: statuses only moved when arc commands ran, so work done directly in a sandbox (or via a plain claude session) never reached the vault. arc reconcile treats git as ground truth: it updates branch from the sandbox's actual HEAD (fixing PR sync matching), advances last_activity to the latest commit when it beats the stored wall-clock timestamp, promotes planned projects with feature-branch commits to active, and lists orphan sandboxes no project tracks. arc sync also runs it before PR matching.

A launchd plist runs it nightly at 07:30:

launchctl load ~/Library/LaunchAgents/com.elizabethhutton.arc-reconcile.plist

Logs: logs/launchd-reconcile.log.

Session-end hooks: both Claude Code (~/.claude/settings.json → SessionEnd) and Codex (~/.codex/hooks.json → SessionEnd) run a shared hook script (~/.claude/hooks/arc-session-end.sh) when an interactive session ends. The script resolves the project from ARC_PROJECT_SLUG (arc-launched sessions) or from the session's cwd being inside sandbox_root (sessions launched with plain claude/codex), then fires arc reconcile <slug> in the background. Sessions outside a sandbox are a silent no-op, and teardown never blocks — the reconcile is fire-and-forget with output to logs/hook-reconcile.log. The design keeps status git-derived: hooks trigger arc; agents still never write frontmatter.

System prompts injected by arc

arc injects system prompts into Claude and Codex at several points. You can customize these to match your team's conventions, coding standards, or review criteria. Each prompt includes project context (title, paths to notes/plans) and task-specific instructions. Agents are told to write session notes to the appropriate notes.md and are explicitly told not to update status in frontmatter.

Command Where in arc.py What the prompt does
arc plan plan() function Instructs Claude to write a plan; points to project note and vault
arc implement (interactive) _implement_interactive_simple() and _implement_interactive_staged() Gives Claude the project context, sandbox path, and git commit instructions. For staged projects, auto-includes plan.md and notes.md from all dependent stages under a "Prior Stages" section.
arc implement --bg _implement_bg() — writes a CLAUDE.md (or AGENTS.md with --codex) to the sandbox Autonomous implementation instructions with paths to project note and notes file
arc implement --bg _implement_bg() — inline prompt One-line prompt passed to claude (or codex exec with --codex) in the orchestrator script
arc review _run_review() Code quality review prompt: diff against main, check correctness, run tests/lint, fix issues
arc review --thorough _run_thorough_review() + lenses/*.md Multi-lens pipeline: parallel lens agents (behavior, tests, interface, optional security) write to <project>/review/<lens>.md, then an interactive synthesis agent merges findings to summary.md and walks you through fixes. Prompts live in lenses/ as markdown.
arc chat chat() function Lightweight context prompt with paths to project note and notes file
arc approve (CI fix) CI monitor script in approve() Instructs Claude to read CI failures, fix them, and push

Claude Code integration

Global project index: arc writes a compact index of all non-archived projects (slug, status, active stage, branch, sandbox, last activity) to ~/.claude/projects-index.md on every dashboard run and pipeline command. Reference it from your global agent context file (@~/.claude/projects-index.md in ~/.claude/CLAUDE.md, or symlink that file to ~/.codex/AGENTS.md) so any agent launched outside arc knows what projects exist and where their notes live.

arc sets the ARC_PROJECT_SLUG environment variable before launching Claude sessions. This lets you customize Claude Code's behavior per-project using hooks and the status line.

Status line: If you want your Claude Code status bar to show the active project, create ~/.claude/statusline.sh:

#!/bin/bash
input=$(cat)
model=$(echo "$input" | jq -r '.model.display_name')
cwd=$(echo "$input" | jq -r '.cwd')
dir="${cwd##*/}"
pct=$(echo "$input" | jq -r '.context_window.used_percentage // 0' | cut -d. -f1)

# Use arc project slug if available, otherwise directory name
project="${ARC_PROJECT_SLUG:-$dir}"

echo "[$model] $project | ${pct}% context"

Then in ~/.claude/settings.json:

{
  "statusLine": {
    "type": "command",
    "command": "~/.claude/statusline.sh"
  }
}

Tab title hook: To set your terminal tab/window title to the project name, create a hook script (e.g. ~/.claude/hooks/set-tab-title.sh):

#!/bin/bash
label="${ARC_PROJECT_SLUG:-$(basename "${CLAUDE_PROJECT_DIR:-unknown}")}"
printf '\033]1;CC: %s\033\\' "$label"
printf '\033]2;Claude Code — %s\033\\' "$label"

Then in ~/.claude/settings.json:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/set-tab-title.sh"
          }
        ]
      }
    ]
  }
}

These are optional — arc works without them. They just make it easier to track which project you're in when running multiple Claude sessions.

Customizing the vault organizer

organize.py runs independently as a uv inline script. It uses Claude to tag notes and link them to projects. Authored notes get inline wikilinks; reference notes (clippings) keep pristine bodies but receive suggested links in an appended ## Related footer, so they still join the Obsidian graph.

  1. Scheduling: Set up a launchd plist (macOS) or cron job (Linux) to run uv run --script organize.py periodically.
  2. API key: The organizer looks for ANTHROPIC_API_KEY in the environment, then falls back to macOS Keychain (security find-generic-password -a vault-organize -s ANTHROPIC_API_KEY). Set whichever is convenient.
  3. Model: Configured via organize.model in config.toml.
  4. System prompt: The SYSTEM_PROMPT in organize.py describes the vault owner as "an AI engineer focused on LLM evaluation." Change this to match the user's domain for better tagging.

Weekly project cleanup

cleanup.py runs independently as a uv inline script. It scans your Projects folder, finds any project with a done or archived status, and moves the entire project folder into Projects/Archived/. Before archiving a done project, it removes the project's sandbox only when the worktree is clean and its branch has no commits ahead of origin/main (or local main). Sandboxes for archived projects are always preserved so they can be resumed later.

Manual run:

arc cleanup --dry-run   # preview what would move
arc cleanup             # execute

Scheduling (macOS): A launchd plist fires every Saturday at 09:00.

# Install (one-time):
launchctl load ~/Library/LaunchAgents/com.elizabethhutton.arc-cleanup.plist

# To change the day or time, edit the plist (Weekday: 0=Sun … 6=Sat), then reload:
launchctl unload ~/Library/LaunchAgents/com.elizabethhutton.arc-cleanup.plist
launchctl load   ~/Library/LaunchAgents/com.elizabethhutton.arc-cleanup.plist

Configuration (config.toml):

[cleanup]
statuses = ["done", "archived"]   # which statuses trigger archival
archived_folder = "Archived"      # subfolder within projects_folder

Logs are written to logs/cleanup-YYYY-MM-DD.log and logs/launchd-cleanup.log.

Architecture

arc.py (~2400 lines) — main CLI built with Typer. All project state lives in Obsidian YAML frontmatter — no database.

organize.py — standalone vault organizer. Scans for new/changed notes, classifies them with Claude, adds tags and wikilinks.

config.toml — user configuration (gitignored, see config.example.toml).

Data model

  • Config — parsed from config.toml: vault path, sandbox root, GitHub repo, branch prefix, test/lint commands
  • Project — parsed from {vault}/Projects/{slug}/index.md frontmatter: slug, title, status, branch, sandbox path, stages, linked issues/PRs. Project status is derived from stage statuses (except done, which requires explicit arc done).
  • Stage — subtask with dependency graph: id, name, status, depends_on, github_issues, github_prs. Each stage gets a folder under stages/ with plan.md and notes.md.

tmux sessions

Session Created by
arc-{slug} arc implement --bg (autonomous implementation)
arc-{slug}-ci arc approve (CI monitor loop)
arc-{slug}-dev arc dev (dev server)

Agent pipeline

arc implement  →  Claude writes code  →  you review the diff
arc review     →  AI code review (codex or claude)
arc diff-review / arc address-review  →  manual diff review + AI fixup
arc approve    →  PR + CI monitor  →  auto-fix CI failures  →  merge

In --bg mode, implementation runs in a tmux session with macOS notifications on completion. Review is a separate step you run when ready.

License

MIT

About

A CLI to track the arc of AI-assisted projects. Uses Obsidian as the memory layer so every chat, implementation, and review starts with full context. Works with your coding agent of choice (claude, cursor, codex), optimized for parallel work in a monorepo.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages