pentimento
Status, intent, and lineage for Claude Code and Cursor plan files.
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
supersededis 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 --tagorlist --starredbrings 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 --titleandlist --grepsearch every project's plans at once, and theprior-plansskill 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,
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 unknown 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 unknown unset claude 2026-09-13 1d
9 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 (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 unknown 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
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
[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
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'
unadopted-tag auth-docs no tags, but its thread carries [auth, security]
9 plans checked, 5 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>
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
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. 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
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, which caps it at partial) recomputes it from ## Progress checkboxes. 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; see check findings for what check still reports. |
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 or written; check suggests them (unadopted-tag). 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 plan reference (see parent is empty). --rederive recomputes it from scratch: it replaces a parent set with set --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 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 |
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 Claude Code 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.
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 (AGENT_SESSIONS_DIR, default ~/.claude/projects), 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
Setup, checks, and CI are in CONTRIBUTING.md.
Docs
- docs/reference.md — every command's full flags, and the environment variables.
- docs/integrations.md
— wiring
backfillandpentimento hookinto Claude Code and Cursor, a slash command, a skill that searches past plans before a new one, a triage-nudge pattern,checkin 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
checkfinding, explained.
Release files for pentimento 0.1.18
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pentimento-0.1.18.tar.gz | 877.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pentimento-0.1.18-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 948.7 kB
Release files / pentimento-0.1.18.tar.gz
| Download URL | pentimento-0.1.18.tar.gz |
|---|---|
| Size | 877.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
055c670851799cc4b2b2cfcbb85f71b8b210abaa49750994f309dab30214c530
|
|
BLAKE2b-256 checksum How to use checksums |
306e771ab31c2f48419bded3721a90092730cbb59fd1354c4b5d12cedcea2d2e
|
| 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 25, 2026.
Transparency logRelease files / pentimento-0.1.18-py3-none-any.whl
| Download URL | pentimento-0.1.18-py3-none-any.whl |
|---|---|
| Size | 71.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
03ea0ec42fe1ad4331dcbe30c186f0fc6f59dd60123d965e9813473ac15ecb04
|
|
BLAKE2b-256 checksum How to use checksums |
6cbe3b19cae4ca7073c214f3e52df30d8fd52ebe4e41823f37bd018948b348c6
|
| 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 25, 2026.
Transparency log