npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

agent-carnet

v0.1.4

Published

File-based markdown notebook CLI for AI agents — the leather notebook in your jacket pocket.

Readme

Agent Carnet (pronounced /ˌeɪdʒənt kɑːrˈneɪ/, like "agent kar-NAY") is a tiny CLI — agent-carnet — that gives AI coding agents (Claude Code, Codex, Cursor) a shared notebook on disk. Each note is a markdown file under .carnet/<category>/<slug>.md.

💡 Why Agent Carnet

Notes are just markdown files. Most agent-memory tools hide notes inside a vector DB or proprietary store, so you cannot grep, git diff, or hand-edit them. Agent Carnet keeps every note as a plain .md file under .carnet/. The agent writes through the CLI; you read and review the exact same files with the tools you already use. Anything that can shell out — Claude Code, Codex, Cursor, your own scripts — works against the same notebook. No SDK, no MCP, no daemon.

Stale notes disappear on their own. A note-store that only grows is a note-store that rots. Every carnet has a 30-day lifespan that resets each time it is read or written, so useful notes survive and the rest drift to .trash/ automatically (with a 7-day grace period before deletion). Pin anything you cannot afford to lose with keep: true. The notebook stays alive without manual cleanup.

🚀 Quick start

# Initialize a notebook in the current directory
npx agent-carnet init

# Save a note (stdin or --body)
echo "Notes about iconv-esm interop." | npx agent-carnet save deps/iconv-issue \
  --summary "iconv-esm compatibility fix" \
  --agent claude-code \
  --tags compat,esm

# Look around
npx agent-carnet list
npx agent-carnet find iconv
npx agent-carnet read deps/iconv-issue

Or install once:

npm install -g agent-carnet

Install the Claude Code skill

Agent Carnet ships a bundled skill at skills/agent-carnet/ (SKILL.md plus a small references/ set) so any Claude Code, Codex, or Cursor session knows when to reach for the CLI. Install it with npx skills, the open agent-skills installer:

# Project install (default) — drops the skill into <cwd>/.claude/skills/agent-carnet/
npx skills add yamadashy/agent-carnet

# Global install — drops it into ~/.claude/skills/agent-carnet/ instead
npx skills add yamadashy/agent-carnet -g

npx skills handles the install / uninstall / list lifecycle uniformly across agents, so Agent Carnet itself doesn't need to know about Claude Code's filesystem layout.

🔄 Lifespan

Every carnet flows through this lifecycle. Useful notes get reset by use, idle ones decay to .trash/, and stale ones eventually disappear:

stateDiagram-v2
    direction LR

    [*] --> Live: save
    Live --> Live: read / used / save --update<br/>(resets lifespan)
    Live --> Trash: expired (30d idle)
    Trash --> Live: restore
    Trash --> [*]: delete (7d)

expired (30d idle) means last_used + lifespan < today — auto-prune sweeps the carnet to .trash/ once nothing has touched it for the lifespan window (default 30 days). delete (7d) is the hard delete that runs after the trash TTL. The table further down spells out which fields each command on the self-loop actually bumps (read / used / save --update differ on which of updated, last_used, use_count they touch).

The expiry date is computed from two frontmatter fields:

expiry = last_used + lifespan
  • last_used — last time the carnet was read or applied (YYYY-MM-DD, UTC, CLI-managed). Falls back to updated for legacy carnets that pre-date the field.
  • lifespan — duration string (30d, 90d, 1y, never). Defaults to AGENT_CARNET_DEFAULT_LIFESPAN (30d) when omitted.

When expiry <= today, the carnet is considered stale and auto-prune moves it to .trash/ on the next CLI invocation.

Four CLI-managed dates / counters

| Field | Bumped by | Purpose | |---|---|---| | created | save (first time only) | Birth date. Immutable. | | updated | save, save --update | Last content modification. Independent of usage. | | last_used | save, read, used | Last interaction. Drives expiry. | | use_count | used (only) | Explicit-use counter. A reference signal of importance. |

Refresh-on-use: two strengths of "use"

A carnet's life is extended whenever it is used. The CLI distinguishes two strengths so the same notebook can express "kept alive because it gets read" and "matters enough to be cited as load-bearing":

flowchart TB
    A[carnet accessed?] -->|find / list| B[no signal<br/>lifespan unchanged]
    A -->|read| C[weak signal<br/>last_used = today]
    A -->|used| D[strong signal<br/>last_used = today<br/>use_count + 1]

    style B stroke-dasharray: 4 2
    style C stroke-width:2px
    style D stroke-width:3px
  • Weak signal — read. Reading the body resets the lifespan. The agent bothered to pull the carnet into context, which is enough to keep it alive.
  • Strong signal — used. After applying a carnet's content (fixing a bug with the recorded fix, citing a debunked hypothesis, reusing a vocabulary entry), call agent-carnet used <path>. This bumps last_used and increments use_count — a durable importance metric you can sort by (agent-carnet list --sort use_count) or that downstream tooling can read.

| Action | updated | last_used | use_count | |---|---|---|---| | save <path> (create) | today | today | 0 | | save <path> --update | today | today | unchanged | | read <path> | — | today | — | | read <path> --no-touch | — | — | — | | used <path> | — | today | +1 | | find <keyword> | — | — | — | | list, move, rm | — | — | — |

Useful notes survive because they get read. Important notes accumulate use_count and rise to the top of importance-sorted views. Notes nobody touches drift toward expiry on their own — no manual triage required.

Live, trash, and hard delete

The lifecycle diagram above has three states on disk:

  1. live (.carnet/<category>/<slug>.md) — the working notebook.
  2. trash (.carnet/.trash/<category>/<slug>.md) — soft-deleted carnets, kept for AGENT_CARNET_TRASH_TTL (default 7d). Original sub-path is preserved, so restoring is mv .carnet/.trash/foo/bar.md .carnet/foo/bar.md.
  3. hard delete — anything in .trash/ whose mtime is older than the trash TTL is unlinked permanently. This is the only step that loses data.

Auto-prune

Every CLI invocation (except --help / --version) sweeps the notebook:

  1. Walks .carnet/ and moves carnets where expiry <= today to .trash/.
  2. Hard-deletes anything in .trash/ whose mtime is older than the trash TTL.

Opt out per call with --no-auto-prune, or globally with AGENT_CARNET_AUTO_PRUNE=false and run agent-carnet prune --auto from CI instead. The latter is the recommended pattern for shared, git-tracked notebooks: each developer's local CLI should not silently delete other people's carnets.

Exemptions

A carnet never expires when:

  • keep: true is set (explicit pin), or
  • lifespan: never is set, or
  • both updated and last_used are missing (treated as "freshly imported, not yet measured" — the safety valve that prevents a one-shot agent-carnet list from sweeping unmigrated notes into .trash/).

🍳 Cookbook

Agent Carnet is just a folder of markdown files; useful patterns emerge from how you tag and link them, not from special folders or commands. The example below stays inside the existing CLI surface — only the tags: field carries the convention, so the carnet remains a portable markdown file you can also open in Obsidian, VS Code, or any editor.

Vocabulary alignment

Multiple agents (and humans) routinely invent different names for the same concept — Claude Code calls something "staging adapter", Codex writes "proxy layer", a human's note uses "forward middleware". By the time anyone notices, three identifiers have leaked into the codebase.

Use one carnet per term, tagged with vocab. The tags: value declares membership; the optional meta.vocab.* subtree carries structured data downstream tools can act on (resolve an alias to its canonical, list rejected names, etc.) while the body explains the why in narrative form:

---
summary: "staging adapter — the thin proxy in front of POST /v1/stage"
agent: claude-code
tags: [vocab]
related:
  - .carnet/vocab/payload-envelope.md
  - src/staging/adapter.ts
meta:
  vocab:
    canonical: staging adapter
    aliases:
      - proxy layer
      - forward middleware
      - request shim
---

# staging adapter

## Definition
The thin proxy that fronts the production gateway and reshapes incoming
requests into the `payload-envelope` format. Nothing more.

## Why this name
"proxy" is overloaded; "middleware" collides with the Express concept.
"staging adapter" leaves no doubt about which layer is meant.

The agent-side flow is small. Before naming a new concept, the agent checks whether someone already named it:

agent-carnet find <candidate> --in tags

If a term wins out, the canonical version is saved once, and every subsequent agent (Claude Code, Codex, Cursor, ...) can find it the same way:

echo "..." | agent-carnet save vocab/staging-adapter \
  --summary "staging adapter — the thin proxy in front of POST /v1/stage" \
  --agent claude-code \
  --tags vocab

Refresh-on-use does the rest: synonyms that keep getting cited stay alive, ones that nobody invokes drift to .trash/ automatically. The vocab tag is purely a project-level convention — the file is just markdown, and Agent Carnet itself does not know or care that it represents a term.

Hypothesis ledger

Long debugging sessions keep producing dead-ends — "we tried X and it didn't work because Y" — and the next session (or the next agent) cheerfully retries the same thing. Vector search and CLAUDE.md skim well for "what worked"; they're worse at "what was already tried and ruled out". A small carnet per hypothesis fixes that with no extra machinery: tag it hypothesis, write the test and the verdict in the body, let meta.hypothesis.* carry the structured status:

---
summary: "iconv-lite v0.7 esm import path — types broken upstream"
agent: claude-code
tags: [hypothesis]
related:
  - https://github.com/pillarjs/iconv-lite/issues/363
meta:
  hypothesis:
    status: debunked
    last_tested: 2026-04-30
---

## Hypothesis
Switching to esm imports should let us run `iconv-lite` on Node 22
(v0.7 advertises ESM support).

## Tests
1. `npm install [email protected]` → type error (`Cannot find module declaration`).
2. Set `tsconfig.moduleResolution` to `bundler` → same error.
3. Inspected v0.7.1 source → broken `package.json#exports` types.

## Verdict
Pin to `v0.6.3`. The whole v0.7 series is broken upstream (Issue #363).
Wait for v0.8 before retrying.

Before exploring a new theory, the agent checks whether anyone has been here before:

agent-carnet find <symptom> --in all
agent-carnet find <library> --in tags    # narrow to hypothesis: notes

If a hypothesis is debunked, the body explains why and the agent (or human) moves on without burning the same evidence again. Refresh-on-use turns staleness into signal: a debunked hypothesis nobody has needed to consult in 30 days drops to .trash/, which is the right behavior — by then either the library has moved or the problem isn't recurring. The hypotheses that do keep getting cited are the load-bearing "do not touch" entries.

📖 Reference

Lookup material — every command and flag, the on-disk file format, the directory layout, and the runtime knobs.

Commands

| Command | What it does | |---|---| | init [--gitignore] | Create .carnet/ in the current directory. --gitignore adds an entry. | | save <category>/<slug> --summary <s> --agent <a> [--tags] [--related] [--body or stdin] [--lifespan] [--keep] [--update] | Create or update a carnet. | | list [category] [--recent N] [--tags a,b] [--expiring 7d] [--sort last_used\|updated\|created\|name\|use_count] | List carnets, grouped by category. | | find <keyword> [--in summary\|tags\|body\|all] [--category] [--limit N] | Pure-JS search. Default scope is summary. Does not refresh last_used. | | read <category>/<slug> [--no-touch] [--no-frontmatter] | Print a carnet. Bumps last_used to today (weak use signal); pass --no-touch to peek without leaving fingerprints. | | used <category>/<slug> | Mark a carnet as applied — bumps last_used and increments use_count. The strong use signal; call after the note actually shaped your work. | | move <from> <to> [--update] | Move a carnet between categories. Trailing / on <to> keeps the source filename. | | rm <category>/<slug> [--yes] [--hard] | Delete one carnet. Soft-delete to .trash/ by default; --hard unlinks immediately. | | prune [--dry-run] [--auto] [--interactive] [--include-trash] | Move expired carnets to .trash/. --interactive prompts per carnet (y/N/q). |

Global flags: --json, --no-color, --no-auto-prune, --quiet, --help, --version.

Per-subcommand help: agent-carnet <command> -h (e.g. agent-carnet save -h) prints the focused help for that command — required arguments, all options, and examples — without touching the filesystem.

Skill installation lives outside the CLI — see Install the Claude Code skill above for the npx skills flow.

Frontmatter schema

---
summary: "iconv-esm compatibility fix"   # required (one line)
agent: claude-code                       # required (claude-code, codex, cursor, human, ...)
created: 2026-05-04                      # CLI-managed, immutable after first save
updated: 2026-05-04                      # CLI-managed, last content modification (save / save --update)
last_used: 2026-05-04                    # CLI-managed, last read/applied (save / read / used) — drives expiry
use_count: 7                             # CLI-managed, count of explicit `used` calls (importance signal)
tags: [compat, esm]                      # optional
related:                                 # optional (paths or other carnets)
  - src/core/file/encoding.ts
lifespan: 90d                            # optional (override default 30d)
keep: true                               # optional (pin against auto-prune)
meta:                                    # optional, free-form extension namespace
  <extension>:
    <key>: <value>
---

Notes:

  • created is immutable after the first save. Every other CLI-managed date is recorded separately so you can tell "when was this note last edited" (updated) apart from "when was it last applied" (last_used) and "how often has it been applied" (use_count).
  • Expiry is driven by last_used, not updated. Editing the body is independent of using it. See Lifespan above.
  • lifespan accepts duration strings (30d, 90d, 1y) and the literal never.
  • meta: is a deliberate extension point for tools and conventions that need structured data beyond what tags: and related: express. The CLI does not interpret meta: itself — it preserves the full subtree on every read/write so downstream consumers (an Obsidian plugin, a sibling agent, your own script) can read and act on it. Namespace keys under the convention name (meta.vocab.*, meta.hypothesis.*) so different extensions don't collide.

Storage layout

<cwd>/.carnet/
├── <category>/
│   └── <slug>.md
├── <category>/<sub>/
│   └── <slug>.md
└── .trash/                  # safety net for auto-pruned carnets
    └── <category>/
        └── <slug>.md

Phase 1 stores carnets only under the current working directory. A global ~/.carnet/ and --scope flag may come later.

Configuration

Phase 1 has no config file. Behavior is controlled by environment variables:

| Variable | Default | Purpose | |---|---|---| | AGENT_CARNET_AUTO_PRUNE | true | Run lifespan/trash sweep on every CLI invocation. | | AGENT_CARNET_DEFAULT_LIFESPAN | 30d | Default per-carnet expiry. | | AGENT_CARNET_TRASH_TTL | 7d | How long .trash/ keeps soft-deleted carnets before hard delete. |

🆚 How it differs from built-in agent memory

| | Vendor-managed agent memory | Agent Carnet | |---|---|---| | Agents that can use it | One vendor's tool only | Any (the interface is bash) | | Storage | Opaque, server- or vendor-managed | Plain markdown files on your disk | | File-direct edits | Not possible | Encouraged — open in any editor | | Lifespan enforcement | LLM-judged or none | CLI-enforced (auto-prune to .trash/) | | Frontmatter validation | n/a | CLI-enforced (summary/agent required) |

Agent Carnet is intentionally less ambitious than vendor memories: it does not try to summarize, embed, or reason about your notes. It is just a tidy, auto-expiring file shelf you can share between agents.

🛠️ Development

npm install
npm run lint     # biome + oxlint + tsgo + secretlint
npm run test     # vitest
npm run build    # tsdown bundle

Source layout mirrors pdfvision:

src/
├── bin/agent-carnet.ts   # thin entry point
├── cli/                  # argv parsing, help, version, stdin
├── core/                 # pure-ish logic (no process.exit, no console.log)
├── output/               # human + JSON formatters
└── types/                # shared types

📜 License

MIT (c) 2026 Kazuki Yamada