Added
- Telemetry: Zinn now records local usage by default. Each run appends a line to a record file in
telemetry/in the Zinn directory. Records never leave the machine. See Data and backups and Settings. task blame <task-key>and core’stask.blame(taskKey)attribute current fields, label membership, parent, relations and description lines to recorded activity, including actor and session identity. Unchanged lines keep their attribution through exact-line comparisons. Incomplete history is shown as unrecorded. See CLI and core.- Activity records include
actor_client, which is intended as a record of the chain of programs making a change. The CLI records its version and anyZINN_CLIENTdeclaration.whoami, activity text and JSON expose the client, andactivity list --client <name>filters by program or part. Library callers can supplyActor.clientand read chains withparseClientChain. See Client chains.- Schema version 4 adds the nullable field. Existing events keep a null client. A published release upgrades the database to schema 4 on first use, after saving a backup. An older Zinn can read the upgraded database but refuses changes to it.
activity.getCurrentTaskIdentities({ taskIds, taskKeys })returns the ID, key, title and status of existing active and archived tasks matching the given IDs or keys, without reading history. See Core.activity.getDeletedTaskIdentities({ taskIds, taskKeys })returns the ID, key, title anddeletedstatus of deleted tasks matching the given IDs or keys, read from task and project deletions at once. See Core.- A command that fails with a
storageorinternalerror writes a log underlogs/in the Zinn directory, with the stack, the cause chain, the run, the store and the arguments as typed.error.details.logPathin the--jsonerror and the last stderr line name the file, and the newest 10 logs are kept. See Scripting. zinn --helplists what Zinn can do and the command or flag for each: deadlines, several tasks per command, text from a file or stdin, search, continuing long lists and activity. See CLI.task searchnow accepts--column <name>and filters by an exact column name, ignoring case, together with the text query, labels, deadlines and archive filters. See CLI.
Changed
- Breaking CLI:
task list --starting-aftertakes the cursor printed by the continuation command instead of a task ID. A script that passes a task ID must switch to that cursor. - Column names are now colored by board position instead of by name in CLI. A board’s first column is gray and its last column is green, whatever they are called, so renaming a column keeps its color. A column appended after
Donetakes over the green, and a board with a single column colors it gray. Task rows, relation rows andproject column listshow the same colors.--jsontask, relation and column results addis_firstandis_lastfor the column’s board placement. See CLI and Scripting. activity listreads only the history and tasks a page needs instead of every event. On a database with 50,000 events,--projectand--project-idpages list in under a third of the time and global pages in under half. Project and task selections inactivity.getAllgain the same way.activity listwithout--projector--taskchecks reused task keys and deleted tasks across the whole database.--limit,--starting-afterand filters no longer change whether a row names a deleted task or adds its task ID. Each row shows as it does in the full list.--limitontask list,task searchandactivity listaccepts any integer from1through9007199254740991, and the corelimitoption accepts up toNumber.MAX_SAFE_INTEGER. Values above11871are no longer refused. Omitting--limitstill returns every match. See CLI.
Fixed
- Breaking core API: database paths and backup folders use the same filesystem resolution for migration results, backups, storage inspection and diagnostics.
getStorage()returns a fresh snapshot from the cached configuration on each call and resolves the database destination. Keep the snapshot for an operation instead of relying on object identity between calls.getBackupStorage()resolves the backup folder for an operation that writes a backup, so a danglingbackupslink fails onlyzinn backup,zinn migrate,zinn restoreor an automatic upgrade, with an error that names the folder, and other commands keep working.backup.restoreaccepts an optionalBackupStoragesnapshot to retain a previously selected destination. See Core and Data and backups.
- Backups on macOS and Linux sync their directory entries before reporting success. A sync failure stops an upgrade or restore before it changes the database. See Data and backups.
Security
@zinn-dev/coreand@zinn-dev/clinow depend onzod4.6.7, outside the affected range of GHSA-mrhx-c7xc-m3w5.
Added
- Task details show a short excerpt of the latest mention in the conversation.
- Select a related task to open it in the same dialog, then use back to return. Long relation lists scroll in place, and the dialog closes from its close control or Esc.
Fixed
- The inspector ships a compiled TUI module, so task rows render in a registry install instead of stopping at
ZINN [loading…].
Added
- Tasks can have deadlines.
task createandtask editaccept--due <timestamp>, and--due noneclears it.task listandtask searchselect deadlines with--due-sinceand--due-until, andtask list --sort dueputs the earliest first. See CLI.- Timestamps are integer epoch milliseconds or a complete timestamp with
Zor a numeric offset. Task JSON includesdue_at, and activity records deadline changes. Core addsdueSince,dueUntilandtimestampSchema.
- Timestamps are integer epoch milliseconds or a complete timestamp with
task listandactivity listcontinue a long result with--starting-after <id>. When--limitcuts the results short, stderr prints the command that continues them. Core addstask.getPage, which returns{ tasks, hasMore }.task list --sort activitylists the most recently active tasks first, counting comments and relations.task listalso accepts--limit.activity listselects history with--changed,--action,--sinceand--until.--changed <field>matches a change tocolumn,priority,labels,title,descriptionordue_at.column,priorityandlabelsalso take a value, as in--changed column=Review.--action <action>matches recorded actions such asmoveorcomment_create.--sinceand--untilbound the recorded time, inclusively.- Repeat a flag to match any of its values. Core accepts the same filters as
changed,actions,sinceanduntil.
project column deletedeletes an empty column after confirmation. A project can have no columns, and creating a task in it fails until a column is added. Core addscolumn.deleteandcolumn.getByName.project listshows each project’s active and archived task counts. JSON entries includetaskCounts, and core addsproject.getAllWithTaskCounts.- Parser and routing failures report
error.details.reason, such asunknown_optionormissing_argument.zinn capabilities --jsonlists every value. See Scripting. - Core exports
isSqliteErrorCodeandisSqliteErrorFamily, which match SQLite result codes and narrow the error toSqliteError. See Core.
Changed
- A published release upgrades databases at schema version 1 or 2 to version 3 on first use, after saving a backup.
- Breaking store initialization: only
project createandlabel createcreate a database when the store has none. Other commands fail withnot_found,details.entity: "database"anddetails.databasePath, exit with code1and write nothing.- Core operations other than
project.createandlabel.createthrow aCoreErrorwith kindmissingand entitydatabase. See Core.
- Core operations other than
- Breaking relations: a task pair holds one relation across all types and directions.
task relation createnames the conflicting links and asks before replacing them, and--yesreplaces them without asking, including with--json.task createrefuses two relation types for the same target.- Core callers pass
replacetorelation.create, with the IDs fromrelation.getConflictsortrue, or receive a conflict. See Core.
- Core callers pass
- Breaking core API:
activity.getAllreturns{ events, hasMore }instead of an array and acceptsstartingAfter. Read the records fromevents.activity list --jsonstill prints an array. - Breaking limits:
--limitontask list,task searchandactivity listaccepts an integer from1through11871. Scripts that passed a larger value must lower it.task listandactivity listcan continue with--starting-after. - Full task objects in every
--jsonresult includecreated_at,updated_atanddue_at. Consumers validating results against a previous release’s schemas must refreshzinn capabilities --json. See Scripting. - Deleting a project records a
relation_deleteevent on each surviving task that loses a cross-project relation, including archived tasks, without changing itsupdated_at. - Namespace help, such as
zinn task --help, lists each command on its own line with a short summary. - Table columns stay aligned with wide characters, emoji and combining marks. Description previews and comment echoes never split a character.
- Parser and routing error messages are capped at 1,024 UTF-16 units.
error.helpanderror.detailskeep their complete values.
Changed
- Installing the inspector no longer pulls
@opencode/pluginand its server and GUI dependencies. OpenCode provides the plugin API at runtime, so an install only needs the OpenTUI and solid-js peers.
Changed
- Visuals adjusted so that the inspector sidepanel has one rail from the header to the footer. Each task’s state glyph sits in the rail, Done tasks highlight it.
- Tasks created during the session show
+next to the key.
Added
- The OpenCode sidepanel lists the Zinn tasks mentioned in the current conversation, read through the local Zinn CLI. Selecting a task opens its details. Requires OpenCode 2.2.0 and Zinn CLI 0.41.0 or newer. See OpenCode task inspector.
- Task details show status, priority, labels, relations, comment count and the first paragraph of the description. Opened from a mention, they also count the task’s mentions in this conversation.
/zinnfinds a loaded active task. The command palette also offers Zinn: task keys in this chat and Zinn: pick a task from the board.- The
rowsoption set to2adds column, priority and labels to each task in the sidepanel. Thecurrentoption set to"glyph"marks tasks with recorded activity from this conversation, and the mark animates while the conversation is busy.
Added
- Hooks are now available. Configure post-change hooks in global settings. They receive activity records on stdin after the change commits. See Hooks.
zinn whoamireports the actor identity a change would record in the current environment and directory.task orderandtask deleteaccept multiple task keys and apply each batch atomically. Core exposestask.orderManyandtask.deleteMany.activity listselects history with--project,--project-id,--taskor--task-id. Use--show-deletedwith a project or task key to include history for deleted identities.- Read descriptions with
--description-fileand comments with--text-file. Use-to read stdin. - Use
show,ls,rmandnewas aliases forview,list,deleteandcreate. label viewshows a label’s full description.
Changed
- Breaking core API:
task.edit,task.label.add,task.label.removeandtask.orderreturn{ task, changed }.project.edit,column.edit,column.order,label.editandcomment.editreturn the corresponding entity field andchanged. Read the record from that field and usechangedto distinguish saved changes from no-op requests.
- JSON results include stored entity IDs, and additional mutation results report
changed. Consumers validating results against a previous release’s schemas must refreshzinn capabilities --json. See Scripting. - Breaking text input: descriptions and comment text are limited to 1 MiB of UTF-8 input. Keep each input and the resulting text of a partial edit within that limit.
- Oversized input fails without writing changes.
- Descriptions and comment text normalize CRLF pairs to LF, including append, find and replacement inputs. Other whitespace, including lone carriage returns is preserved.
task viewincludes creation and update times.- Breaking storage: a folder chosen through
ZINN_DIRorstorage.directorymust already exist. Create it withmkdir -p <path>first.- A missing folder, a path that is not a folder, or an empty
ZINN_DIRfails witherror.codestorageanderror.details.reasoninvalid_storage. The default~/.zinnfolder is still created on first use. See Settings.
- A missing folder, a path that is not a folder, or an empty
Fixed
- Invalid text-source flag combinations fail before reading files or stdin.
- Upgrades from schema version 1 preserve SQLite execution errors on older Bun versions and roll back the failed migration.
- The manifest schema in
zinn capabilities --jsondescribes the manifest exactly, including the fixed order ofinvocation.canonicalArgv.
Added
zinn capabilitiesdescribes every installed command: its inputs, effects, confirmations, and result schemas. With--jsonit emits the full contract, including reusable JSON Schemas.zinn inforeports the selected store, the settings files, the configured default project, the database schema state, and the CLI, core and runtime versions. Unusable settings and database problems are reported there, and the command still exits with code0.zinn task comment view <comment-key>shows a comment’s full text, keeping its line breaks.- Activity records the declared actor identity. Declarations use
ZINN_SESSION_ID,ZINN_THREAD_ID,ZINN_AGENT,ZINN_MODELandZINN_EFFORT. Without these,CLAUDE_CODE_SESSION_ID,CODEX_SESSION_ID/CODEX_THREAD_IDorOPENCODE_SESSION_IDfills the session or thread, and OpenCode’s ID is recorded as the thread. zinn activity list --session <id>and--thread <id>match complete stored IDs exactly, and rows without the identity do not match.activity viewreports the declared identity.- The CLI package ships the Zinn workflow skill at
skills/zinn/SKILL.md. Install it usingnpx skills add yethranayeh/zinn --skill zinn -g -y. The skill covers picking up work, keeping task context, recording follow-ups, and handing off for review. - Core exposes the
infooperation,defaultProjectresolution, and validation schemas for settings, activity records, and label, priority, and relation values.
Changed
- Breaking JSON: handled failures carry required
error.code(invalid_input,not_found,conflict,storage,internal) and optionaldetailsanddiagnosticsalongsidemessageandhelp. Scripts must match oncodeand read failure facts fromdetailsinstead of message text, and notices, including lock waits, print only after a--jsoncommand completes. - Breaking core API:
Actor.nameis nullable,nullonly for unnamed agents, andActivitygains the six nullableactor_*keys. Expected failures throwCoreErrororInputValidationError, which carry a failurekindand typed facts, instead of plainError, and SQLite failures throwSqliteError. Consumers must handlenullactor names and catch these error classes. - A published release upgrades databases at schema version 1 to version 2 on first use, after saving a backup.
- Priority values on
task createandtask editare case-insensitive, and stored values and JSON use lowercase. Relations intask relation listandtask viewread oldest first. - Refusals of likely mistakes name the fix: an unrecognized
--statusor--columnontask editortask movesuggestszinn task movewith the same task keys, and a dash-leading option value suggests the command joined with=, as in--description='- item'.
Fixed
zinn restoreattaches the backup by plain path, so backup paths containing?,#or other special URI characters, including non-ASCII names, restore correctly.
Changed
- Install the CLI with
npm i -g @zinn-dev/cli. The CLI and core ship as ESM JavaScript with TypeScript declarations and run on Node.js 24.15.0 or newer. Bun remains supported. See installation. - Confirmation prompts use stderr. Enter declines, and
yorYconfirms. - Breaking core API:
readReleaseModeis removed from the public exports. Pass your client’sreleaseidentity directly toconfigureDatabasewithsourcefor development orpublishedfor prepared release artifacts. Core determines its own identity independently.
Added
- Configure
defaultProjectinsettings.jsonorzinn.local.jsonto use a project when its key is omitted. Use--all-projectsto list or search tasks across projects. - Read task activity with
zinn task view <task-key> --history. - Validate settings files with the published JSON Schema.
Changed
- Breaking confirmations:
project delete,task deleteandrestorerequire--yeswhen stdin or stdout is redirected. Unanswered confirmations exit with code 1 before changing data. Scripts must pass--yesand type the project key when deleting a project. Piped answers do not confirm an operation. - Breaking settings isolation:
ZINN_DIRreads settings only from the selected store’ssettings.json. Put settings needed by that store there. Global and per-codebase files are ignored.
Fixed
- Activity history marks deleted entities and distinguishes task IDs when a task key is reused.
- Migrations and restores refuse to commit if foreign keys reference missing tables, as well as missing rows.
Added
- Read recorded changes with
zinn activity listandzinn activity view. - Assign a column and relations when creating a task, and filter task lists by column.
- Append text or replace matching text when editing descriptions and comments, using
--appendor--replacing. - Core exposes activity reads and structured text edits.
Changed
- Breaking arguments: extra positional words, repeated single-use options and conflicting options fail with usage help. Remove extra arguments and pass each single use option once.
- Breaking validation: task titles and project, column and label names are trimmed and must be nonblank single lines without control characters. Use descriptions for multiline text.
Added
- Move, archive or unarchive several tasks in one command. Each operation applies to all supplied tasks atomically.
- Restore a database with
zinn restore, repair its search index withzinn reindex, and apply supported schema upgrades withzinn migrate. - Configure
storage.directoryin globalsettings.jsonor per-codebasezinn.local.json. Note thatZINN_DIRoverrides both. - Core exposes database configuration, migration and recovery operations, plus bulk task operations.
Changed
- Breaking database compatibility: databases now record a schema version. Published releases upgrade supported older versions after saving a backup. Unmarked databases created before version tracking require
--allow-legacy-readfor best-effort read-only access and cannot be upgraded withzinn migrate. Save a snapshot withzinn backupbefore upgrading. See database upgrades. - Breaking JSON:
zinn project view --jsonreplacestaskswithcolumnscontainingnameandtaskCount, plusarchivedTaskCount. Usezinn task list <project-key> --jsonto read tasks. Move, archive and unarchive return an object for one task or an array for several, with achangedflag on each result.
Fixed
- Backups use unique filenames and become available only after the snapshot is complete.
- SQLite writes use stronger commit durability, and statements created inside a transaction cannot outlive it.