@plinth-music/cli 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,38 @@
2
2
 
3
3
  Notable changes to `@plinth-music/cli`. Grouped by what a user notices, not by PR.
4
4
 
5
+ ## 0.16.0 - 2026-09-02
6
+
7
+ Three code PRs since `0.15.0` (`v0.15.0..5c60627`, #157, #159, #161; #158 and #160 are
8
+ docs). A minor because the mirror gains a whole new surface: memory rendered as one file
9
+ per fact.
10
+
11
+ - **Memory can now arrive as individual facts, and the mirror renders them.** Once a
12
+ workspace's memory has been moved to per-fact rows (a migration Plinth runs, not
13
+ something you do), `workspace-memory.md` and `user-memory.md` become generated indexes,
14
+ one line per fact, and every fact gets its own file: shared facts under
15
+ `memory/<slug>.md`, your own under `memory/private/<slug>.md`, each carrying the fact's
16
+ id, description, type, status and source in its frontmatter. The agent reads the index at
17
+ session start and opens a fact's file only when it needs the body. Nothing changes for a
18
+ workspace that has not been migrated: its memory files sync exactly as before.
19
+ - ⚠️ **A hand edit to a generated memory file is refused, kept, and reported.** The daemon
20
+ will not push it; the edit is stashed beside the file, `plinth status` names it, and the
21
+ message says where the write goes instead: the memory fact tools the agent already has.
22
+ Nothing is lost silently. Until a store is generated, the files are yours to edit as
23
+ today.
24
+ - **`plinth status` reports three new things about your master context files**
25
+ (`_artist.md` and the editable block of the workspace `CLAUDE.md`): a single list item or
26
+ paragraph past 2,000 characters, an entry under an "upcoming" label whose latest date has
27
+ passed, and a "Current state" heading with no as-of date or one older than 30 days.
28
+ Reports only; nothing is edited for you. `/plinth:close` points at the three while the
29
+ file is open.
30
+ - **Archived tasks no longer count as open in the per-artist `CLAUDE.md`.** An archived
31
+ task was being listed and counted as open work.
32
+ - Dead-link reporting and `plinth backlinks` resolve `[[memory/<slug>]]` and
33
+ `[[memory/private/<slug>]]`.
34
+
35
+ The desktop app pins `0.15.0` exact and picks none of this up until its own release repins.
36
+
5
37
  ## 0.15.0 - 2026-08-31
6
38
 
7
39
  One code PR since `0.14.0` (`v0.14.0..f293dd0`, #153). A minor because sync gains a
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Plinth capstone sync daemon and CLI. Mirrors your Plinth workspace to `~/Plinth/<workspace>/` and keeps it bidirectionally synced. AI-native: pair with Claude Code or any LLM agent for context-aware artist management.
4
4
 
5
- **Status: early access.** Two-way sync runs as a daemon across documents, projects, tasks, artists, meetings, threads, a user-writable `files/` subtree, the three memory files and `voice.md` (your voice profile, round-tripped to the server and injected read-only into every session). See `V1_SPEC` §5 M3 in the plinth monorepo for the full milestone plan, and [`CHANGELOG.md`](CHANGELOG.md) for what landed when.
5
+ **Status: early access.** Two-way sync runs as a daemon across documents, projects, tasks, artists, meetings, threads, a user-writable `files/` subtree, the three memory files and `voice.md` (your voice profile, round-tripped to the server and injected read-only into every session). See `V1_SPEC` §5 M3 in the plinth monorepo for the full milestone plan, and [`CHANGELOG.md`](CHANGELOG.md) for what landed when. Since 0.16.0 a workspace whose memory has been migrated to per-fact rows receives its memory as generated indexes plus one file per fact under `memory/` (shared) and `memory/private/` (yours); those files are read-only on disk and a hand edit is refused, stashed and reported rather than lost.
6
6
 
7
7
  ## Install
8
8
 
@@ -25,7 +25,7 @@ plinth start # run the daemon: continuous pull + watch + pu
25
25
  - `plinth login --workspace <slug> [--reset-sync]` — issue a personal access token in the browser, paste it back, store it in the OS keychain (macOS Keychain / Linux Secret Service / Windows Credential Manager). Logging in again to the same workspace is a token rotation and **keeps your sync cursors**, so the next sync picks up where the last one left off; it clears them only when the token belongs to a different workspace or user. `--reset-sync` discards them deliberately, making the next sync a full re-pull — use it after rebuilding a mirror from scratch, which is otherwise unreachable now that rotation preserves them.
26
26
  - `plinth sync [--workspace <slug>]` — one-shot pull into `~/Plinth/<workspace>/`. Also generates the workspace-root `CLAUDE.md` (a thin TOC + entity model + skill pointers), an empty `CLAUDE.local.md` stub on first sync, and the two session-boundary commands (below), and silently refreshes `CLAUDE.md` on a schema-version change. Defaults to the active workspace if `--workspace` is omitted. **Every mirrored task, project, document, artist, meeting and thread file carries its row `id:` in frontmatter**, so an agent holding a file can address the MCP write for that row directly instead of going through `search_entities` and risking a duplicate on an empty result; files already on disk when you upgrade get it from a one-time backfill, since a cursor feed never re-sends an unchanged row. The key is server-authoritative and stripped before the wire, so it moves no hash and pushes nothing. It also **scaffolds `files/` and the six entity folders**, so a new workspace arrives with somewhere to put things instead of an empty directory, and the folder map in the generated `CLAUDE.md` reads the disk rather than asserting what ought to be there.
27
27
  - `plinth start` — run the long-running sync daemon: pull the latest, then watch the mirror for local edits and push them. It also projects each mapped artist's Google calendar into `views/calendar/` in the mirror — a file per artist plus an `upcoming.md` — on its own 15-minute timer (`PLINTH_CALENDAR_INTERVAL_MS`), with one run at boot. That is deliberately not the 15-second pull cadence: one tick makes a Google call per mapped calendar, which would breach the server route's rate limit inside a minute. A refresh that fails keeps the last known bodies and rewrites only the header, so a stale view says it is stale rather than reading as an empty diary. Logs to `~/.plinth/daemon.log`. **One daemon per workspace:** it holds an exclusive kernel lock on `~/.plinth/daemon-<workspace>.lock` for its life (macOS only; other platforms are refused and told why), so a second `plinth start`, or a standalone `plinth sync` while it runs, exits 1 naming the holder's pid. **Never delete the lock file** — the lock lives on the inode, and removing the path is what lets a second daemon in beside a live one. It also writes `views/conflicts.md` on every sync: every local edit the cloud won and the daemon set aside in `~/.plinth/_conflicts/`, each with a `cp` command that restores it; the zero case is written too, so an absent file means an old daemon, not a clean one.
28
- - `plinth status` — local sync state: daemon liveness, last sync and per-type counts, any destructive batches being held, writes awaiting retry or permanently rejected, **every local write that has not reached Plinth** (scanned from the mirror against the last acknowledged state, with the remedy that clears each — start the daemon, or open and save a file that was created while it was down), the state of `voice.md`, and any conflict stashes in `~/.plinth/_conflicts/<workspace>/` — local copies that were set aside when the cloud version won. Warns when a source install's `dist/` is behind its checkout. Four further rows. **Commands** warns when the customisable block in a generated command still names machinery that command retired — the block is preserved verbatim forever and renders after the generated body, so a hand-written step that runs a retired ritual wins, and the generator may never edit it. **Its verdict is derived from the command registry, not asserted**: a token that still resolves to a live subcommand is described as still working and left to you, because the one token it lists is `memory-gate`, which the grounding block hands every session. **Links** names `[[pointers]]` in `workspace-memory.md` and `artists/*/memory.md` that resolve to nothing — a memory fact is a hook plus a pointer to its source, so a dead pointer can mean the hook is the only surviving copy of the fact. It reuses `plinth backlinks`'s resolution ladder, reports prose links only (never one inside a code fence), never reports an ambiguous target, and strips anchors before resolving. **Injection** reports the session-start block's size against its 20,000-byte budget and **where those bytes go** — seven categories that sum to the block by construction, marking which are yours to change — plus how many facts of each index ride whole, how many ride as their opening line only, and which file to shorten. **Too long** names each memory file past the size it is useful at: a rule file (`voice.md`, `workspace-rules.md`) at **6,000 bytes**, a fact store (`artists/<slug>/memory.md`) at **12,000**. The two bars are not one bar: what a long rule file spends is the model's instruction-following budget, which reference facts do not touch. A file under its bar renders nothing at all.
28
+ - `plinth status` — local sync state: daemon liveness, last sync and per-type counts, any destructive batches being held, writes awaiting retry or permanently rejected, **every local write that has not reached Plinth** (scanned from the mirror against the last acknowledged state, with the remedy that clears each — start the daemon, or open and save a file that was created while it was down), the state of `voice.md`, and any conflict stashes in `~/.plinth/_conflicts/<workspace>/` — local copies that were set aside when the cloud version won. Warns when a source install's `dist/` is behind its checkout. Four further rows. **Commands** warns when the customisable block in a generated command still names machinery that command retired — the block is preserved verbatim forever and renders after the generated body, so a hand-written step that runs a retired ritual wins, and the generator may never edit it. **Its verdict is derived from the command registry, not asserted**: a token that still resolves to a live subcommand is described as still working and left to you, because the one token it lists is `memory-gate`, which the grounding block hands every session. **Links** names `[[pointers]]` in `workspace-memory.md` and `artists/*/memory.md` that resolve to nothing — a memory fact is a hook plus a pointer to its source, so a dead pointer can mean the hook is the only surviving copy of the fact. It reuses `plinth backlinks`'s resolution ladder, reports prose links only (never one inside a code fence), never reports an ambiguous target, and strips anchors before resolving. **Injection** reports the session-start block's size against its 20,000-byte budget and **where those bytes go** — seven categories that sum to the block by construction, marking which are yours to change — plus how many facts of each index ride whole, how many ride as their opening line only, and which file to shorten. **Too long** names each memory file past the size it is useful at: a rule file (`voice.md`, `workspace-rules.md`) at **6,000 bytes**, a fact store (`artists/<slug>/memory.md`) at **12,000**. The two bars are not one bar: what a long rule file spends is the model's instruction-following budget, which reference facts do not touch. A file under its bar renders nothing at all. Since 0.16.0 it also reports master-context walls (one block past 2,000 characters), played entries still under an "upcoming" label, and a "Current state" heading with no as-of or a stale one.
29
29
  - `plinth confirm` — review and release destructive batches the daemon has quarantined (apply, or `--discard`).
30
30
  - `plinth refresh-context [--workspace <slug>]` — force-regenerate the workspace-root `CLAUDE.md` **and the two generated commands** from the current Plinth schema + workspace. The user-customisable block between the `<!-- BEGIN: user-customisable -->` / `<!-- END -->` markers is always preserved verbatim in every generated file; `CLAUDE.local.md` is never touched.
31
31
  - `plinth grounding` — print the session-start grounding block (current date, **who the member is**, entity resolution, write confirmation, workspace rules, workspace memory, user memory). It opens by naming the member: display name, email, role, workspace and machine timezone, and that tasks the agent creates are assigned to them unless told otherwise. That renders from a principal **persisted at login**, not a call at render time — this command runs on every session start, so a round trip would tax all of them and fail offline. An existing login self-heals on the next sync, so no re-login is needed, and a server that does not yet carry `role` renders a shorter true line rather than a dangling clause. Invoked by the generated Claude Code SessionStart hook. **Bounded at 20,000 bytes total.** Only the two memory indexes give: a generated safety primitive is never cut, and if the generated block alone exceeded the budget the indexes collapse to a pointer and the block goes over rather than losing a rule. An index that does not fit whole keeps the **most recently banked** facts, by the date each line carries rather than by position — `workspace-memory.md` supersedes in place, so an updated line keeps its original slot and position does not track age — and renders them in the file's own order, with a note saying how many of how many are shown. **A fact that does not fit whole is named by its title** — its own first sentence, a boundary the member already wrote, with its `[[pointer]]` kept — so the agent knows the fact exists and where to read it. ⚠️ **Naming is preferred over carrying, and that trades depth for breadth:** the naming pass runs first and packs, and whole facts compete for what is left, so on a full index no fact may ride whole at all. A title pays in proportion to how well the hook was written; a fact whose first sentence runs long compresses barely at all, which is why *shorten each fact's first sentence* buys room where *trim the file* does not. `voice.md` is a **pointer, not a payload**: it is read on demand when drafting, never injected, which is what took a live workspace's block from 43,578 bytes to 19,911.
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha": "71f22efc85823f5d401c67709224d315e7504974",
2
+ "sha": "4d6d17c915cb0b2fd774da4ae8876a450a658eb0",
3
3
  "dirty": false,
4
- "built_at": "2026-08-31T19:26:38.895Z"
4
+ "built_at": "2026-09-02T18:56:56.610Z"
5
5
  }