Skip to main content

pentimento

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

check PyPI Python versions

demo

Claude Code and Cursor 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 or list --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 and list --grep search every project's plans at once, and the prior-plans skill has the agent run that search itself before it proposes a change.

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

Built with Claude Code.

Install

pip install pentimento
# or from source:
pip install git+https://github.com/kjiwa/pentimento.git

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

Quick start

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

pentimento backfill      # derive status/intent/created/parent/project once
pentimento list          # see the corpus
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 caveats are in docs/troubleshooting.md.

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

list

PLAN shows the short id: the shortest trailing hyphen-segment run that's unique across the corpus, so is-it-possible-to-abundant-rabbit displays as abundant-rabbit. The table adapts to terminal width, dropping columns before truncating any; --columns (or PENTIMENTO_COLUMNS) overrides which columns show and in what order — see docs/reference.md#columns. list and tree share their filters: --status, --intent, --project, --source, --starred, --tag, --finding (plans with a check finding, or with one given CODE), the regex matches --title and --grep, and the date range --since/--until, which tests modified (the UPDATED column) unless --date created says otherwise.

STATUS       INTENT     PROJECT   SOURCE  PLAN                  TITLE                           UPDATED
superseded   abandoned  platform  claude  style-guide           Write a docs style guide             6w
complete     abandoned  platform  claude  auth-redesign         Redesign the auth API                5w
complete     someday    billing   claude  dunning-copy          Rewrite dunning email copy           3w
partial      active     platform  claude  auth-rollout          Roll out the new auth API            2w
not-started  queued     platform  claude  auth-cleanup          Remove the old auth API              2w
unknown      unset      billing   claude  invoice-retry         Retry failed invoice charges         1w
not-started  active     billing   claude  relevance-tuning      Tune search relevance                5d
unknown      unset                claude  onboarding-checklist  Write the onboarding checklist       1d

8 plans

tree

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, resolved against the whole corpus, so --project is unnecessary. --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  unknown  unset  2026-09-13  1d

billing
├─ Rewrite dunning email copy
│    dunning-copy  complete  someday  [billing]  2026-08-20  3w
├─ Retry failed invoice charges (parent elided: no-such-plan)
│    invoice-retry  unknown  unset  [billing]  2026-09-04  1w
└─ 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

8 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

3 of 8 plans

show

# Roll out the new auth API

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

✓ 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

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
status-behind-progress  onboarding-checklist  status 'unknown' but '## Progress' derives 'not-started'

8 plans checked, 4 findings
dangling-parent: pentimento set <id> --parent <id>, or --clear-parent
status-behind-history: pentimento history <id>, then pentimento set <id> --status <value>
status-behind-progress: pentimento backfill, or pentimento show <id>, then pentimento set <id> --status <value>
underivable-status: add a checklist to '## Progress', or pentimento show <id>, then pentimento set <id> --status <value>
narrow with: pentimento list --finding <code>

history

history shows which sessions touched a plan's file: the session whose id matches the plan's own id authored it; any later session that read, edited, or delegated work on it worked it. An empty result prints no session history for <id>; searched: <directory> — that means no matching transcript was found there, never a claim the plan wasn't worked.

WHEN              WHAT      SESSION                                TOUCHES
2026-08-30 12:30  authored  api-auth-cleanup                             1
2026-09-11 12:30  worked    implement-api-auth-cleanup-eager-wolf        1

Frontmatter

---
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 Every backfill run (including the per-write pentimento hook) recomputes it from ## Progress checkboxes. The hook caps the result at partial; only a full backfill sweep advances it to complete. set --status overrides it directly and pins it (see pinned); it is the only way to set superseded, which no derivation produces or overwrites.
pinned operator Never derived. set --status sets it to true automatically; set --unpin clears it. While set, backfill (with or without --rederive) leaves status untouched and check reports only pin-behind-progress for it.
intent operator Gap-filled to unset by backfill the first time it sees the plan, then left alone. Only set --intent changes it after that.
tags operator Never derived. set --add-tag/--remove-tag/--clear-tags; filter with list/tree --tag, which ANDs repeated tags.
parent derived, or operator backfill fills it in from a session-prompt or body reference (an <id>.md literal or a trailing codename) to an earlier same-project, same-source plan. --rederive recomputes it from scratch, including removing one that no longer resolves. set --parent/--clear-parent set or clear it directly; set --parent rejects a value that would create a cycle. Read the chain back with tree <id>/tree <id> --ancestors.
project derived, or operator backfill derives it from a session's cwd. set --project/--clear-project set or clear it directly; --project . resolves to the current directory's name.
created derived once A local date, set once and then immutable except through backfill --recreate.
modified derived, not stored Not a frontmatter field: max(session end time, file mtime). Neither backfill nor set 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 body-only lineage and no project at all.

Commands

Command Does
list Flat table of plans, one line each.
tree [<id>] Plans nested under their parents, grouped by project; <id> roots the tree at one plan's thread instead, --ancestors walking up to its topmost ancestor.
show <id> One plan's title, frontmatter, and rendered body.
set <id> Rewrite one plan's frontmatter in place.
backfill Derive and write missing frontmatter across the corpus.
hook Run as a Claude Code PostToolUse hook, reading the payload on stdin.
index Write INDEX.md into the plans directory.
check Validate lineage, vocabulary, and status; exits 1 on any finding.
history <id> Every session that touched one plan, oldest first.
completion <shell> Print a bash/zsh/fish tab-completion script.

Full flags for every command, plus the environment variables, are in docs/reference.md. Run pentimento <command> --help for the same information from the CLI itself.

Scope

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, session-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.

Prior art

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.

Requirements and limitations

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

pentimento enriches modified and lineage by reading Claude Code's session transcripts (~/.claude/projects/*.jsonl), an undocumented, private format. If that 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.

Development

python3 -m unittest discover
uvx ruff check
uvx ruff format --check

CI (.github/workflows/check.yml) runs all three on Ubuntu and macOS.

Docs

  • docs/reference.md — every command's full flags, and the environment variables.
  • docs/integrations.md — wiring backfill and pentimento hook into Claude Code and Cursor, 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 through check, scripting with --format json.
  • docs/troubleshooting.md — every empty field and check finding, explained.

Release files for pentimento 0.1.14

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pentimento 0.1.14
File Size Uploaded
pentimento-0.1.14.tar.gz 771.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pentimento 0.1.14
File Interpreter ABI Platform
pentimento-0.1.14-py3-none-any.whl Python 3 none any Details

Total release size: 837.0 kB

Release files / pentimento-0.1.14.tar.gz

Download URL pentimento-0.1.14.tar.gz
Size 771.0 kB
Tags Source
SHA-256 checksum
How to use checksums
65b37d380dfd6408be4bca252079359d36456a5c0f6a0256306101c60cf8d36b
BLAKE2b-256 checksum
How to use checksums
36d9606ddb0205ef3b95b8e3976d475e93e3774a528862f0e6e7aaac9b0ccf67
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release files / pentimento-0.1.14-py3-none-any.whl

Download URL pentimento-0.1.14-py3-none-any.whl
Size 66.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7bdf5154ddb3657fa2675b1f762cb8e2f8f2b9448e4668315e0958fb061ed027
BLAKE2b-256 checksum
How to use checksums
32a2dba7d5c44bd5cc9f0d6949f98f9232863f6f4aa88d992e69bcbb25831e9f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 24, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.17

2 release files

0.1.16

2 release files

0.1.15

2 release files

This release

0.1.14 This release

2 release files

0.1.13

2 release files

0.1.12

2 release files

0.1.11

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page