Skip to main content

pentimento

Status, intent, and lineage over agent plan files.

check PyPI Python versions

demo

Coding agents 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.

Two 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.

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

docs/workflows.md walks all three loops 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.

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  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 corpus and exits 1 on any finding. It takes no plan id — it always checks the whole corpus. The table's PLAN column uses the same short id as list/tree; --format json|tsv emits the full id in its plan_id field. See docs/troubleshooting.md for what each CODE means and how to fix it.

CODE                   PLAN           MESSAGE
dangling-parent        invoice-retry  parent 'no-such-plan' does not resolve to a plan
status-behind-history  auth-cleanup   status 'not-started' but 1 later session worked this plan; see `pentime…
8 plans checked, 2 findings

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> — that means no matching transcript was found on this machine, 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 — the only way to set superseded, which no derivation ever produces or overwrites — and pins it (see pinned).
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.
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 and vocabulary; 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

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. claude-log-viewer is a local web app for Claude Code projects and session logs, not plan files. dela lists markdown todos from the CLI, with no status derived from checkboxes and no notion of supersession or lineage. 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 triage-nudge pattern, check in CI.
  • docs/workflows.md — triage, picking a plan back up, supersession, lineage trees, scripting with --format json.
  • docs/troubleshooting.md — every empty field and check finding, explained.

Release files for pentimento 0.1.11

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.11
File Size Uploaded
pentimento-0.1.11.tar.gz 864.8 kB Details

Built distribution (wheel)

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

Total release size: 925.8 kB

Release files / pentimento-0.1.11.tar.gz

Download URL pentimento-0.1.11.tar.gz
Size 864.8 kB
Tags Source
SHA-256 checksum
How to use checksums
344e6c3385d5f2f09831e6bbc7b37bfdb2d2c3e38c4c929765ac9bd12e1cfbb1
BLAKE2b-256 checksum
How to use checksums
e86242a708d0fb1638640c99226a05a206666b908c142c8c8ad2796b6fca3664
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 23, 2026.

Transparency log

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

Download URL pentimento-0.1.11-py3-none-any.whl
Size 61.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a422156bd9aa92f6f0d3a1309df7aeff691b5a54ffb83aa07670348dd8a3a003
BLAKE2b-256 checksum
How to use checksums
a9fc96db678b3af85edc8d3fc91fd15c0f0756a6de753e9a1f4a7106ec869922
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 23, 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

0.1.14

2 release files

0.1.13

2 release files

0.1.12

2 release files

This release

0.1.11 This release

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