cd /news/ai-agents/show-hn-pentimento-status-and-lineag… · home › topics › ai-agents › article
[ARTICLE · art-166963] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Show HN: Pentimento – status and lineage for your coding agent's plan files

A developer released Pentimento, an open-source command-line tool installable via `pip install pentimento` that derives status, intent and lineage for plan files left behind by Claude Code, Cursor and Antigravity, whose default plans directory is `~/.claude/plans` and is overridden with the `AGENT_PLANS_DIR` environment variable. Pentimento adds a `superseded` status that no derivation produces or overwrites, plus a `pentimento hook` to keep plan state current as an agent writes and a `prior-plans` skill that has the agent search every project's plans before proposing a change. The tool is built with Claude Code and targets the case where hundreds of randomly named plan files no longer reveal which finished, which still matter, or which plan replaced which.

read12 min views7 publishedNov 10, 2026
Show HN: Pentimento – status and lineage for your coding agent's plan files
Image: Michielbdejong (auto-discovered)

Status, intent, and lineage for Claude Code, Cursor, and Antigravity plan files.

Claude Code, Cursor, and Antigravity leave plan files behind. After a few hundred of them you cannot tell which finished, which you still care about, or which plan replaced which — the filenames are random and the files say nothing about their own state. pentimento derives that state and gives you a CLI to list, filter, and render the lot as a lineage tree.

Three kinds of plan are worth finding again:

  • The long one: an infrastructure migration designed over months grows subplans, absorbs decisions made in discussion, and leaves behind the branches you rejected — the part you want back a quarter later. A superseded plan is a decision record, not garbage, which is why superseded is the one status no derivation produces or overwrites;tree <id> brings the whole thread back, tags or no tags.
  • The finished one that is not over: execution ends with findings you will not act on today. Tag it and set an intent, and list --tag orlist --starred brings it back when you are ready to pick the thread up again.
  • The one that already answered your question: a plan from another project holds the reasoning behind a decision your current change would undo — why a CI matrix was cut, why one service avoids a library. A memory exists only if an agent chose to write one, and project docs only if you did; a plan exists whenever a decision was planned. list --title andlist --grep search every project's plans at once, and theprior-plans skill has the agent run that search itself before it proposes a change.

In harness-engineering terms, plans are the state layer: what one session leaves for the next. pentimento hook keeps that state current as the agent writes it, and the prior-plans skill has the agent read it back.

A pentimento is the earlier composition showing through a repainted canvas; that is what a plans directory is.

Built with Claude Code.

pip install pentimento
pip install git+https://github.com/kjiwa/pentimento.git

Tab completion for bash, zsh, and fish is in docs/reference.md#shell-completion.

Point AGENT_PLANS_DIR at your plans directory (it defaults to ~/.claude/plans), then:

pentimento list          # see the corpus; derived fields show without backfill
pentimento backfill      # persist derived fields so they outlive the transcripts
pentimento set some-plan-id --intent active
pentimento list --starred

To park a finished plan you mean to return to, tag it and set an intent, then find it by tag later:

pentimento set some-plan-id --add-tag auth --intent someday
pentimento list --tag auth

To pull one thread of subplans back out by lineage, rather than by tag:

pentimento tree some-plan-id
pentimento tree some-plan-id --ancestors

To find how an earlier decision was made, search titles (or titles and bodies) across every project, then reopen the plan; to see what you finished recently, filter by date:

pentimento list --title 'github actions|\bGHA\b'
pentimento list --grep 'concurrency group' --status complete
pentimento show some-plan-id --full
pentimento list --status complete --since 1w

docs/workflows.md walks each of these end to end.

Sources and their default directories are covered in docs/integrations.md; Cursor's and Antigravity's rules and caveats are in the Cursor and Antigravity sections.

The samples below are regenerated by sh demo/capture.sh.

PLAN shows the short id, such as auth-rollout for api-auth-rollout. --columns (or PENTIMENTO_COLUMNS) chooses which columns show and in what order; see Columns. list and tree share their filters, listed in the synopsis.

PLAN                  STATUS       INTENT     PROJECT   SOURCE  TITLE                           TAGS             CREATED     UPDATED
style-guide           superseded   abandoned  platform  claude  Write a docs style guide                         2026-07-31       6w
auth-redesign         complete     abandoned  platform  claude  Redesign the auth API           [auth, +1]       2026-08-05       5w
dunning-copy          complete     someday    billing   claude  Rewrite dunning email copy      [billing]        2026-08-20       3w
auth-rollout          partial      active     platform  claude  Roll out the new auth API       [auth, +1]       2026-08-25       2w
auth-cleanup          not-started  queued     platform  claude  Remove the old auth API         [auth, +1]       2026-08-30       2w
auth-docs             not-started  unset      platform  claude  Document the new auth API                        2026-09-02       1w
invoice-retry         unknown      unset      billing   claude  Retry failed invoice charges    [billing]        2026-09-04       1w
relevance-tuning      not-started  active     billing   claude  Tune search relevance           [search]         2026-09-09       5d
onboarding-checklist  not-started  unset                claude  Write the onboarding checklist                   2026-09-13       1d

9 plans

Below the table threshold, list stacks each plan into a record; see the layout rule.

Write a docs style guide
  style-guide  superseded  abandoned  platform  claude  2026-07-31  6w
Redesign the auth API
  auth-redesign  complete  abandoned  platform  claude  [auth, security]
  2026-08-05  5w
Rewrite dunning email copy
  dunning-copy  complete  someday  billing  claude  [billing]  2026-08-20  3w
Roll out the new auth API
  auth-rollout  partial  active  platform  claude  [auth, security]  2026-08-25
  2w
Remove the old auth API
  auth-cleanup  not-started  queued  platform  claude  [auth, security]
  2026-08-30  2w
Document the new auth API
  auth-docs  not-started  unset  platform  claude  2026-09-02  1w
Retry failed invoice charges
  invoice-retry  unknown  unset  billing  claude  [billing]  2026-09-04  1w
Tune search relevance
  relevance-tuning  not-started  active  billing  claude  [search]  2026-09-09
  5d
Write the onboarding checklist
  onboarding-checklist  not-started  unset  claude  2026-09-13  1d

9 plans

Plans nested under their parents, grouped by project. Plain tree renders the whole corpus this way; tree <id> roots it at one plan instead — that plan plus everything beneath it (see tree <id>). --ancestors also walks up to <id>'s topmost ancestor, spine only, for pulling a single thread out of a larger forest.

(no project)
`-- Write the onboarding checklist
      onboarding-checklist  not-started  unset  2026-09-13  1d

billing
|-- Rewrite dunning email copy
|     dunning-copy  complete  someday  [billing]  2026-08-20  3w
|-- Retry failed invoice charges
|     invoice-retry  unknown  unset  [billing]  2026-09-04  1w  (parent elided: no-such-plan)
`-- Tune search relevance
      relevance-tuning  not-started  active  [search]  2026-09-09  5d

platform
|-- Write a docs style guide
|     style-guide  superseded  abandoned  2026-07-31  6w
`-- Redesign the auth API
      auth-redesign  complete  abandoned  [auth, security]  2026-08-05  5w
    `-- Roll out the new auth API
          auth-rollout  partial  active  [auth, security]  2026-08-25  2w
        |-- Remove the old auth API
        |     auth-cleanup  not-started  queued  [auth, security]  2026-08-30  2w
        `-- Document the new auth API
              auth-docs  not-started  unset  2026-09-02  1w

9 plans
platform
`-- Redesign the auth API
      auth-redesign  complete  abandoned  [auth, security]  2026-08-05  5w
    `-- Roll out the new auth API
          auth-rollout  partial  active  [auth, security]  2026-08-25  2w
        |-- Remove the old auth API
        |     auth-cleanup  not-started  queued  [auth, security]  2026-08-30  2w
        `-- Document the new auth API
              auth-docs  not-started  unset  2026-09-02  1w

4 of 9 plans

id: api-auth-rollout
path: ~/.claude/plans/api-auth-rollout.md
status: partial   intent: active   tags: [auth, security]
parent: api-auth-redesign   project: platform
created: 2026-08-25   source: claude   modified: 2026-08-25 12:30

Progress

[x] Ship behind a feature flag
[ ] Flip the flag for all tenants

Context

Tenants opt in via the auth_v2 flag in tenant_settings. Watch error rates before flipping the
remaining cohort. See the rollout runbook.

Cohort    Status
internal  complete
beta      in progress

check validates the whole corpus and exits 1 on any finding; it takes no plan id. PLAN uses the short id, and a line under the summary gives the fix for each CODE. --format json|tsv emits the full id in id and the fix in hint. To work through findings, see Working through check; docs/troubleshooting.md explains each CODE.

CODE                   PLAN           MESSAGE
dangling-parent        invoice-retry  parent 'no-such-plan' does not resolve to a plan
underivable-status     invoice-retry  '## Progress' has no checkboxes or recognized phrase
status-behind-history  auth-cleanup   status 'not-started' but 1 later session worked this plan
unadopted-tag          auth-docs      no tags, but its thread carries [auth, security]

9 plans checked, 4 findings
dangling-parent: pentimento set <id> --parent <id>, or --clear-parent
status-behind-history: tick the plan's '## Progress', or pentimento history <id>, then pentimento set <id>
  --status <value> (pins)
unadopted-tag: pentimento set <id> --add-tag <tag>
underivable-status: add a checklist to '## Progress', or pentimento show <id>, then pentimento set <id>
  --status <value>
narrow with: pentimento list --finding <code>

history shows which sessions touched a plan's file. The session whose id matches the plan's own id, or failing that the session whose first touch wrote the plan, is authored; any later session that edited, wrote, or delegated work on it is worked; one that only read it is read. Only Claude Code transcripts feed history. For an empty result, see history is empty.

WHEN              WHAT      SESSION                                TOUCHES
2026-08-30 12:30  authored  auth-cleanup                                 1
2026-09-11 12:30  worked    implement-api-auth-cleanup-eager-wolf        1
2026-09-13 12:30  read      review-api-auth-cleanup-calm-fox             1
---
pentimento:
  status: not-started | partial | complete | superseded | unknown
  pinned: true                  # omitted unless set
  intent: active | queued | someday | abandoned | unset
  tags: [auth, security]        # omitted if untagged
  parent: some-other-plan-id    # omitted for roots
  project: platform             # omitted if undetermined
  created: 2026-09-08
---

The vocabulary lives in one place: pentimento/vocabulary.py.

Field Set by How
status derived Views derive it live from status signals, first match wins; backfill (including the per-writepentimento hook , whichcaps it at partial ) persists it:## Progress , then a Cursor plan'stodos: , then checkboxes elsewhere in the body.set --status overrides it directly and pins it (seepinned ); it is the only way to setsuperseded , which no derivation produces or overwrites.
pinned operator Never derived. set --status sets it totrue automatically;set --unpin clears it. While set,backfill (with or without--rederive ) leavesstatus untouched; seecheck findings for whatcheck still reports.
intent operator Shown as unset until set;backfill writesunset the first time it sees the plan, then leaves it alone. Onlyset --intent changes it after that.
tags operator Never derived or written; check suggests them (unadopted-tag ).set --add-tag /--remove-tag /--clear-tags ; filter withlist /tree --tag , which ANDs repeated tags.
parent derived, or operator Views derive it from a plan reference and backfill persists it (seeparent is empty ).--rederive recomputes it from scratch: it replaces a parent set withset --parent by the derived one, or removes it when no reference resolves.set --parent /--clear-parent set or clear it directly;set --parent rejects a value that would create a cycle. Read the chain back withtree <id> /tree <id> --ancestors .
project derived, or operator Views derive it from a session's cwd andbackfill persists it, which keeps it after Claude Code prunes the transcript (a Cursor or Antigravity plan's: seeCursor andAntigravity ).set --project /--clear-project set or clear it directly;--project . resolves to the current directory's name.
created derived once A local date, derived by views and persisted once by backfill , then immutable except throughbackfill --recreate .
modified derived, not stored Not a frontmatter field: max(session end time, file mtime) . Neitherbackfill norset bumps it when the write only touches frontmatter bookkeeping.

Lineage and source discovery are covered in full in docs/integrations.md and docs/troubleshooting.md. Cursor plans get project and prompt lineage from Cursor's agent transcripts, matched by name; a stop hook keeps their status current. The matching rule is in the integrations guide. Antigravity plans read their own conversation's transcript, so they need no matching, and a PostToolUse hook keeps their status current (setup).

Command Does
list flat table of plans
tree [<id>] lineage tree, grouped by project
show <id> H1, frontmatter, and the rendered body
set <id>... rewrite frontmatter in place
backfill gap-fill intent/created/project/parent; advance status
hook run as a PostToolUse hook; reads the payload on stdin
index write INDEX.md into the plans directory
check validate lineage, vocabulary, status, and tags; exits 1 on any finding
history <id> session-touch history for a plan
completion <shell> print a shell integration script

Full flags for every command, plus the environment variables, are in docs/reference.md.

pentimento is built for one operator's corpus on one machine. Shared, concurrent, or multi-author planning is out of scope and not a gap this tool intends to close — project, prompt lineage, and modified are all derived from local Claude Code transcripts, and deriving them across authors would need a different source, a sync, and an identity model.

Keeping the plans directory in git does get you review and history, and part of the derived state travels with the files: status, operator-set frontmatter, and body-referenced parent survive a checkout anywhere; project, prompt-derived parent, and session history do not. See docs/workflows.md for the details, and pentimento index for an INDEX.md worth committing.

ccplan and planc track Claude Code plans by a status you set by hand, ccplan in a sidecar file and planc in frontmatter through a TUI. claude-plan-viewer browses and searches them in a web UI. planning-with-files keeps an agent's plan on disk while it works and recovers it after /clear or compaction; it manages the files it creates, not a directory of finished ones. None of them records which plan replaced which. pentimento reads a corpus it did not author and derives status, project, and parent across it. The longer comparison is in Managing Claude Code plan files.

Stdlib-only Python 3.9+, zero runtime dependencies, no PyYAML.

pentimento enriches modified and lineage by reading Claude Code's session transcripts (AGENT_SESSIONS_DIR, default ~/.claude/projects) and Cursor's agent transcripts (CURSOR_SESSIONS_DIR, default ~/.cursor/projects), and Antigravity's conversation transcripts (next to each plan under ANTIGRAVITY_BRAIN_DIR, default ~/.gemini/antigravity/brain), all undocumented, private formats. If a format changes, or the transcripts are absent, this enrichment degrades to file mtimes and plain body/preamble references — it does not break, and the frontmatter itself stays plain, hand-editable markdown either way.

Setup, checks, and CI are in CONTRIBUTING.md.

  • docs/reference.md — every command's full flags, and the environment variables.
  • docs/integrations.md — wiringbackfill andpentimento hook into Claude Code, Cursor, and Antigravity, a slash command, a skill that searches past plans before a new one, a triage-nudge pattern,check in CI.
  • docs/workflows.md — triage, picking a plan back up, reusing a past decision, supersession, lineage trees, working throughcheck , scripting with--format json .
  • docs/troubleshooting.md — every empty field andcheck finding, explained.
── more in #ai-agents 4 stories · sorted by recency
── more on @pentimento 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-pentimento-s…] indexed:0 read:12min 2026-11-10 · —