# CHANGELOG

Notable changes to Zinn and its integrations. Each CLI release names the core version it includes, and integrations release on their own versions. **Breaking** changes include the action needed when upgrading.

## 0.44.0 - 2026-11-07

[CLI 0.44.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.44.0) | [Core 0.33.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.33.0)

### 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](/docs/data) and [Settings](/docs/settings#control-local-telemetry).
- `task blame <task-key>` and core's `task.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](/docs/cli#task-blame) and [core](/docs/core#task-blame).
- 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 any `ZINN_CLIENT` declaration. `whoami`, activity text and JSON expose the client, and `activity list --client <name>` filters by program or part. Library callers can supply `Actor.client` and read chains with `parseClientChain`. See [Client chains](/docs/cli#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](/docs/core).
- `activity.getDeletedTaskIdentities({ taskIds, taskKeys })` returns the ID, key, title and `deleted` status of deleted tasks matching the given IDs or keys, read from task and project deletions at once. See [Core](/docs/core).
- A command that fails with a `storage` or `internal` error writes a log under `logs/` in the Zinn directory, with the stack, the cause chain, the run, the store and the arguments as typed. `error.details.logPath` in the `--json` error and the last stderr line name the file, and the newest 10 logs are kept. See [Scripting](/docs/scripting).
- `zinn --help` lists 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](/docs/cli).
- `task search` now accepts `--column <name>` and filters by an exact column name, ignoring case, together with the text query, labels, deadlines and archive filters. See [CLI](/docs/cli).

### Changed

- **Breaking CLI:** `task list --starting-after` takes the cursor printed by the continuation command instead of a task ID. A script that passes a task ID must switch to that cursor.
  - Continuation survives changes to the last listed task. `task.getPage` and `activity.getAll` return `nextCursor` when more results remain. See [Core](/docs/core) and [CLI](/docs/cli).
- 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 `Done` takes over the green, and a board with a single column colors it gray. Task rows, relation rows and `project column list` show the same colors. `--json` task, relation and column results add `is_first` and `is_last` for the column's board placement. See [CLI](/docs/cli) and [Scripting](/docs/scripting).
- `activity list` reads only the history and tasks a page needs instead of every event. On a database with 50,000 events, `--project` and `--project-id` pages list in under a third of the time and global pages in under half. Project and task selections in `activity.getAll` gain the same way.
- `activity list` without `--project` or `--task` checks reused task keys and deleted tasks across the whole database. `--limit`, `--starting-after` and 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.
- `--limit` on `task list`, `task search` and `activity list` accepts any integer from `1` through `9007199254740991`, and the core `limit` option accepts up to `Number.MAX_SAFE_INTEGER`. Values above `11871` are no longer refused. Omitting `--limit` still returns every match. See [CLI](/docs/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 dangling `backups` link fails only `zinn backup`, `zinn migrate`, `zinn restore` or an automatic upgrade, with an error that names the folder, and other commands keep working. `backup.restore` accepts an optional `BackupStorage` snapshot to retain a previously selected destination. See [Core](/docs/core) and [Data and backups](/docs/data).
- 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](/docs/data).

### Security

- `@zinn-dev/core` and `@zinn-dev/cli` now depend on `zod` 4.6.7, outside the affected range of [GHSA-mrhx-c7xc-m3w5](https://github.com/advisories/GHSA-mrhx-c7xc-m3w5).

## OpenCode 0.4.0 - 2026-11-06

[@zinn-dev/opencode 0.4.0](https://www.npmjs.com/package/@zinn-dev/opencode/v/0.4.0)

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

## OpenCode 0.3.1 - 2026-11-01

[@zinn-dev/opencode 0.3.1](https://www.npmjs.com/package/@zinn-dev/opencode/v/0.3.1)

### Fixed

- The inspector ships a compiled TUI module, so task rows render in a registry install instead of stopping at `ZINN [loading…]`.

## 0.43.0 - 2026-10-31

[CLI 0.43.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.43.0) | [Core 0.32.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.32.0)

### Added

- Tasks can have deadlines. `task create` and `task edit` accept `--due <timestamp>`, and `--due none` clears it. `task list` and `task search` select deadlines with `--due-since` and `--due-until`, and `task list --sort due` puts the earliest first. See [CLI](/docs/cli).
  - Timestamps are integer epoch milliseconds or a complete timestamp with `Z` or a numeric offset. Task JSON includes `due_at`, and activity records deadline changes. Core adds `dueSince`, `dueUntil` and `timestampSchema`.
- `task list` and `activity list` continue a long result with `--starting-after <id>`. When `--limit` cuts the results short, stderr prints the command that continues them. Core adds `task.getPage`, which returns `{ tasks, hasMore }`.
- `task list --sort activity` lists the most recently active tasks first, counting comments and relations. `task list` also accepts `--limit`.
- `activity list` selects history with `--changed`, `--action`, `--since` and `--until`.
  - `--changed <field>` matches a change to `column`, `priority`, `labels`, `title`, `description` or `due_at`. `column`, `priority` and `labels` also take a value, as in `--changed column=Review`.
  - `--action <action>` matches recorded actions such as `move` or `comment_create`. `--since` and `--until` bound the recorded time, inclusively.
  - Repeat a flag to match any of its values. Core accepts the same filters as `changed`, `actions`, `since` and `until`.
- `project column delete` deletes an empty column after confirmation. A project can have no columns, and creating a task in it fails until a column is added. Core adds `column.delete` and `column.getByName`.
- `project list` shows each project's active and archived task counts. JSON entries include `taskCounts`, and core adds `project.getAllWithTaskCounts`.
- Parser and routing failures report `error.details.reason`, such as `unknown_option` or `missing_argument`. `zinn capabilities --json` lists every value. See [Scripting](/docs/scripting).
- Core exports `isSqliteErrorCode` and `isSqliteErrorFamily`, which match SQLite result codes and narrow the error to `SqliteError`. See [Core](/docs/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 create` and `label create` create a database when the store has none. Other commands fail with `not_found`, `details.entity: "database"` and `details.databasePath`, exit with code `1` and write nothing.
  - Core operations other than `project.create` and `label.create` throw a `CoreError` with kind `missing` and entity `database`. See [Core](/docs/core).
- **Breaking relations:** a task pair holds one relation across all types and directions. `task relation create` names the conflicting links and asks before replacing them, and `--yes` replaces them without asking, including with `--json`. `task create` refuses two relation types for the same target.
  - Core callers pass `replace` to `relation.create`, with the IDs from `relation.getConflicts` or `true`, or receive a conflict. See [Core](/docs/core).
- **Breaking core API:** `activity.getAll` returns `{ events, hasMore }` instead of an array and accepts `startingAfter`. Read the records from `events`. `activity list --json` still prints an array.
- **Breaking limits:** `--limit` on `task list`, `task search` and `activity list` accepts an integer from `1` through `11871`. Scripts that passed a larger value must lower it. `task list` and `activity list` can continue with `--starting-after`.
- Full task objects in every `--json` result include `created_at`, `updated_at` and `due_at`. Consumers validating results against a previous release's schemas must refresh `zinn capabilities --json`. See [Scripting](/docs/scripting).
- Deleting a project records a `relation_delete` event on each surviving task that loses a cross-project relation, including archived tasks, without changing its `updated_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.help` and `error.details` keep their complete values.

## OpenCode 0.3.0 - 2026-10-31

[@zinn-dev/opencode 0.3.0](https://www.npmjs.com/package/@zinn-dev/opencode/v/0.3.0)

### Changed

- Installing the inspector no longer pulls `@opencode/plugin` and its server and GUI dependencies. OpenCode provides the plugin API at runtime, so an install only needs the OpenTUI and solid-js peers.

## OpenCode 0.2.0 - 2026-10-31

[@zinn-dev/opencode 0.2.0](https://www.npmjs.com/package/@zinn-dev/opencode/v/0.2.0)

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

## OpenCode 0.1.0 - 2026-10-28

[@zinn-dev/opencode 0.1.0](https://www.npmjs.com/package/@zinn-dev/opencode/v/0.1.0)

### 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](/integrations/opencode/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.
- `/zinn` finds a loaded active task. The command palette also offers **Zinn: task keys in this chat** and **Zinn: pick a task from the board**.
- The `rows` option set to `2` adds column, priority and labels to each task in the sidepanel. The `current` option set to `"glyph"` marks tasks with recorded activity from this conversation, and the mark animates while the conversation is busy.

## 0.42.0 - 2026-10-24

[CLI 0.42.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.42.0) | [Core 0.31.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.31.0)

### Added

- Hooks are now available. Configure post-change hooks in global settings. They receive activity records on stdin after the change commits. See [Hooks](/docs/hooks).
- `zinn whoami` reports the actor identity a change would record in the current environment and directory.
- `task order` and `task delete` accept multiple task keys and apply each batch atomically. Core exposes `task.orderMany` and `task.deleteMany`.
- `activity list` selects history with `--project`, `--project-id`, `--task` or `--task-id`. Use `--show-deleted` with a project or task key to include history for deleted identities.
- Read descriptions with `--description-file` and comments with `--text-file`. Use `-` to read stdin.
- Use `show`, `ls`, `rm` and `new` as aliases for `view`, `list`, `delete` and `create`.
- `label view` shows a label's full description.

### Changed

- **Breaking core API:** `task.edit`, `task.label.add`, `task.label.remove` and `task.order` return `{ task, changed }`.
  - `project.edit`, `column.edit`, `column.order`, `label.edit` and `comment.edit` return the corresponding entity field and `changed`. Read the record from that field and use `changed` to 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 refresh `zinn capabilities --json`. See [Scripting](/docs/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 view` includes creation and update times.
- **Breaking storage:** a folder chosen through `ZINN_DIR` or `storage.directory` must already exist. Create it with `mkdir -p <path>` first.
  - A missing folder, a path that is not a folder, or an empty `ZINN_DIR` fails with `error.code` `storage` and `error.details.reason` `invalid_storage`. The default `~/.zinn` folder is still created on first use. See [Settings](/docs/settings).

### 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 --json` describes the manifest exactly, including the fixed order of `invocation.canonicalArgv`.

## 0.41.0 - 2026-10-18

[CLI 0.41.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.41.0) | [Core 0.30.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.30.0)

### Added

- `zinn capabilities` describes every installed command: its inputs, effects, confirmations, and result schemas. With `--json` it emits the full contract, including reusable JSON Schemas.
- `zinn info` reports 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 code `0`.
- `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_MODEL` and `ZINN_EFFORT`. Without these, `CLAUDE_CODE_SESSION_ID`, `CODEX_SESSION_ID`/`CODEX_THREAD_ID` or `OPENCODE_SESSION_ID` fills 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 view` reports the declared identity.
- The CLI package ships the Zinn workflow skill at `skills/zinn/SKILL.md`. Install it using `npx 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 `info` operation, `defaultProject` resolution, 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 optional `details` and `diagnostics` alongside `message` and `help`. Scripts must match on `code` and read failure facts from `details` instead of message text, and notices, including lock waits, print only after a `--json` command completes.
- **Breaking core API:** `Actor.name` is nullable, `null` only for unnamed agents, and `Activity` gains the six nullable `actor_*` keys. Expected failures throw `CoreError` or `InputValidationError`, which carry a failure `kind` and typed facts, instead of plain `Error`, and SQLite failures throw `SqliteError`. Consumers must handle `null` actor 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 create` and `task edit` are case-insensitive, and stored values and JSON use lowercase. Relations in `task relation list` and `task view` read oldest first.
- Refusals of likely mistakes name the fix: an unrecognized `--status` or `--column` on `task edit` or `task move` suggests `zinn task move` with the same task keys, and a dash-leading option value suggests the command joined with `=`, as in `--description='- item'`.

### Fixed

- `zinn restore` attaches the backup by plain path, so backup paths containing `?`, `#` or other special URI characters, including non-ASCII names, restore correctly.

## 0.40.0 - 2026-10-15

[CLI 0.40.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.40.0) | [Core 0.29.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.29.0)

### 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](/docs/installation).
- Confirmation prompts use stderr. Enter declines, and `y` or `Y` confirms.
- **Breaking core API:** `readReleaseMode` is removed from the public exports. Pass your client's `release` identity directly to `configureDatabase` with `source` for development or `published` for prepared release artifacts. Core determines its own identity independently.

## 0.39.0 - 2026-10-11

[CLI 0.39.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.39.0) | [Core 0.28.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.28.0)

### Added

- Configure `defaultProject` in `settings.json` or `zinn.local.json` to use a project when its key is omitted. Use `--all-projects` to list or search tasks across projects.
- Read task activity with `zinn task view <task-key> --history`.
- Validate settings files with the published [JSON Schema](/schemas/settings/v1.json).

### Changed

- **Breaking confirmations:** `project delete`, `task delete` and `restore` require `--yes` when stdin or stdout is redirected. Unanswered confirmations exit with code **1** before changing data. Scripts must pass `--yes` and type the project key when deleting a project. Piped answers do not confirm an operation.
- **Breaking settings isolation:** `ZINN_DIR` reads settings only from the selected store's `settings.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.

## 0.38.0 - 2026-10-10

[CLI 0.38.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.38.0) | [Core 0.27.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.27.0)

### Added

- Read recorded changes with `zinn activity list` and `zinn 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 `--append` or `--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.

## 0.37.0 Eridu - 2026-10-08

[CLI 0.37.0](https://www.npmjs.com/package/@zinn-dev/cli/v/0.37.0) | [Core 0.26.0](https://www.npmjs.com/package/@zinn-dev/core/v/0.26.0)

### 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 with `zinn reindex`, and apply supported schema upgrades with `zinn migrate`.
- Configure `storage.directory` in global `settings.json` or per-codebase `zinn.local.json`. Note that `ZINN_DIR` overrides 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-read` for best-effort read-only access and cannot be upgraded with `zinn migrate`. Save a snapshot with `zinn backup` before upgrading. See [database upgrades](/docs/data#upgrade-a-database).
- **Breaking JSON:** `zinn project view --json` replaces `tasks` with `columns` containing `name` and `taskCount`, plus `archivedTaskCount`. Use `zinn task list <project-key> --json` to read tasks. Move, archive and unarchive return an object for one task or an array for several, with a `changed` flag 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.
