{"_id":"@asterzephyr/session-bridge","name":"@asterzephyr/session-bridge","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@asterzephyr/session-bridge","version":"0.1.0","description":"Move local coding-agent sessions between Claude Code and Codex CLI.","type":"module","bin":{"session-bridge":"dist/session-bridge.js"},"license":"MIT","engines":{"node":">=22"},"repository":{"type":"git","url":"git+https://github.com/AsterZephyr/session-bridge.git","directory":"packages/cli"},"homepage":"https://github.com/AsterZephyr/session-bridge#readme","bugs":{"url":"https://github.com/AsterZephyr/session-bridge/issues"},"keywords":["claude-code","codex","codex-cli","session","migration","cli","developer-tools"],"publishConfig":{"access":"public"},"dependencies":{"@hono/node-server":"^1.18.0","chalk":"^5.4.0","commander":"^14.0.0","hono":"^4.8.0","open":"^10.2.0"},"devDependencies":{"@session-bridge/core":"0.1.0"},"scripts":{"build":"tsc -p tsconfig.json --noEmit && tsup && node scripts/copy-ui.mjs","dev":"tsx bin/session-bridge.ts","test":"vitest run --passWithNoTests","typecheck":"tsc -p tsconfig.json --noEmit"},"_id":"@asterzephyr/session-bridge@0.1.0","_integrity":"sha512-K9rgpn2oEeYtjIGGU6oVGMp3Iu7gqJt/bLc+0DDB4ARKb+GAdD7wF+JOXdlm8vqDqbOItIxTX1FWm70Fvc4k6Q==","_resolved":"/private/var/folders/wb/0z38tn196n1_zl569q2nr0p00000gn/T/tmp.xZWz7HQsto/package/asterzephyr-session-bridge-0.1.0.tgz","_from":"file:package/asterzephyr-session-bridge-0.1.0.tgz","_nodeVersion":"25.2.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-K9rgpn2oEeYtjIGGU6oVGMp3Iu7gqJt/bLc+0DDB4ARKb+GAdD7wF+JOXdlm8vqDqbOItIxTX1FWm70Fvc4k6Q==","shasum":"3ada96c77f2239a7f5adb55b30f67ac3f4081769","tarball":"https://registry.npmjs.org/@asterzephyr/session-bridge/-/session-bridge-0.1.0.tgz","fileCount":19,"unpackedSize":829984,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCDVL2GAfp93Jww11udNWxQShKksbw0k4byIk5WmITbmQIgb7pgO5CZrOi/AfiyY2xeI+MoAtvM1FS8DmrHxP1pCGY="}]},"_npmUser":{"name":"asterzephyr","email":"hxz2046084122@outlook.com"},"directories":{},"maintainers":[{"name":"asterzephyr","email":"hxz2046084122@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/session-bridge_0.1.0_1783927522712_0.7999445346946792"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-13T07:25:22.542Z","0.1.0":"2026-07-13T07:25:22.893Z","modified":"2026-07-13T07:25:23.186Z"},"maintainers":[{"name":"asterzephyr","email":"hxz2046084122@outlook.com"}],"description":"Move local coding-agent sessions between Claude Code and Codex CLI.","homepage":"https://github.com/AsterZephyr/session-bridge#readme","keywords":["claude-code","codex","codex-cli","session","migration","cli","developer-tools"],"repository":{"type":"git","url":"git+https://github.com/AsterZephyr/session-bridge.git","directory":"packages/cli"},"bugs":{"url":"https://github.com/AsterZephyr/session-bridge/issues"},"license":"MIT","readme":"# session-bridge\n\nHand off local coding-agent sessions between Claude Code and Codex CLI. Each handoff creates a new session on the target side, preserving conversation history along a shared timeline.\n\nsession-bridge does not modify either tool's native sessions in place. It treats a session handoff as a generation: the source session becomes the parent, the new target session becomes the child, and the full chain is recorded for traceability.\n\n## Why Generation Handoffs\n\nMost agent tools store sessions in proprietary formats with no official export path. When you need to switch tools mid-task, you lose the conversation that led to your current state: decisions, constraints, context about what was tried.\n\nsession-bridge solves this by creating a new session on the target side that contains the transferable content from the source. Both agents can then be used in alternation on the same project, with each handoff recorded as a generation in a shared timeline. This is not the same as modifying a single native thread in place: each generation is a new, independent session file that the target agent discovers normally.\n\nThe term \"bidirectional\" means you can hand off in either direction (Claude to Codex, Codex to Claude), not that both tools share a single session. A round-trip creates two new sessions: one on each side.\n\n![Dashboard showing a handoff timeline with source/target pairs and generation markers](docs/assets/dashboard.png)\n\n## Capabilities\n\n- Parse and convert user/assistant messages and tool use/result blocks between Claude Code JSONL and Codex rollout JSONL\n- Claude to Codex: delegates to the official [`codex app-server`](https://learn.chatgpt.com/docs/app-server) RPC (`externalAgentConfig/detect` + `externalAgentConfig/import`), then polls the import ledger and verifies the thread via `thread/read`\n- Codex to Claude: writes minimal recoverable Claude Code JSONL atomically, re-parses the output to verify session ID and message count\n- Idempotent: same source path + same SHA-256 digest returns the previous result without re-importing\n- Generation lineage: same source path with changed content creates a new target session and records it as the next generation on the same timeline\n- Handoff capsule: goal, decisions, constraints, changedFiles, validation, openQuestions, nextAction, note; persisted in `~/.session-bridge/state.json`\n- AdapterRegistry with built-in claude/codex adapters; third-party adapters can be registered programmatically\n- Lifecycle hooks (opt-in): Claude Code `SessionEnd` and Codex [`Stop`](https://learn.chatgpt.com/docs/hooks) hooks queue events for manual or automatic processing\n- Web UI for browsing timelines, inspecting transfers, and triggering imports\n- Doctor command for diagnosing runtime dependencies and hook state\n\n## Limitations\n\n- No in-place modification of native sessions. Each handoff creates a new target session.\n- No incremental append to an existing target session.\n- No conflict resolution or merge between diverged sessions.\n- No provider-side prompt cache restoration. First resume after import may behave differently.\n- Codex `Stop` hook fires per turn, not per session exit. In auto mode this triggers a handoff attempt on every turn.\n- Large session budget: 64 MiB cumulative transferable-message limit per import (fail-closed, no partial import).\n- Content not transferred: thinking-block signatures, encrypted content, system/developer messages, permission records, MCP instructions, sidechain/meta messages, file-history snapshots.\n- Windows: manual `import` works, but `hooks install` explicitly rejects Windows (`process.platform === \"win32\"`) with a message to use manual imports instead.\n- The tool is pre-release. It is not published to npm and is not production-stable.\n\n## Quick Start\n\n### Prerequisites\n\n- Node.js 22 or later\n- pnpm 10+\n- Claude Code installed (`~/.claude/projects/` directory exists)\n- Codex CLI installed (`~/.codex/sessions/` directory exists, `codex app-server --stdio` responds)\n\n### Install from Source\n\n```sh\ngit clone https://github.com/AsterZephyr/session-bridge.git\ncd session-bridge\npnpm install\npnpm build\n```\n\nThe CLI binary is at `packages/cli/dist/session-bridge.js`. During development:\n\n```sh\npnpm dev -- <command> [flags]\n```\n\nAfter the package is published to npm (not yet available):\n\n```sh\nnpm i -g @asterzephyr/session-bridge\nsession-bridge doctor\n```\n\n### Back Up First\n\nsession-bridge writes new files and calls Codex import RPC. It does not delete or overwrite existing sessions. Back up before first use:\n\n```sh\ncp -a ~/.claude ~/.claude.bak\ncp -a ~/.codex ~/.codex.bak\ncp -a ~/.session-bridge ~/.session-bridge.bak\n```\n\n## CLI Commands\n\n### import\n\nImport one session by specifying its source agent and session ID or file path.\n\n```sh\nsession-bridge import --from claude --session <session-id-or-path>\nsession-bridge import --from codex --session <session-id-or-path>\nsession-bridge import --from codex --session ~/.codex/sessions/2026/07/13/rollout-xxx.jsonl --dry-run\nsession-bridge import --from claude --session abc123 --capsule ./handoff.json --note \"auth refactor done\"\n```\n\n| Flag | Required | Description |\n|------|----------|-------------|\n| `--from <agent>` | yes | Source agent: `claude` or `codex` |\n| `--session <id-or-path>` | yes | Source session ID or JSONL file path |\n| `--to <agent>` | no | Target adapter (default: the other built-in adapter) |\n| `--capsule <path>` | no | JSON file with handoff decisions and constraints |\n| `--note <text>` | no | Short note stored with this generation |\n| `--dry-run` | no | Validate and preview without writing |\n| `--json` | no | Machine-readable JSON output |\n\n### sync\n\nFind the latest native (non-bridged) session for the current project and import it.\n\n```sh\nsession-bridge sync --to codex\nsession-bridge sync --to claude\nsession-bridge sync --to both --project ~/my-project\nsession-bridge sync --to codex --dry-run --json\n```\n\n| Flag | Required | Description |\n|------|----------|-------------|\n| `--to <agent>` | yes | Target: `claude`, `codex`, or `both` |\n| `--project <path>` | no | Project working directory (default: cwd) |\n| `--note <text>` | no | Short note for this handoff |\n| `--dry-run` | no | Validate without writing |\n| `--json` | no | Machine-readable output |\n\nWhen `--to both`: finds the latest Claude session and imports to Codex, then finds the latest Codex session and imports to Claude.\n\n### list\n\nShow transferable sessions for the current project. Sessions already imported are marked `bridged` based on the state ledger and a content digest comparison.\n\n```sh\nsession-bridge list\nsession-bridge list --all-projects\nsession-bridge list --project ~/my-project --json\n```\n\n### status\n\nCheck which adapters are available and how many sessions exist.\n\n```sh\nsession-bridge status\nsession-bridge status --json\n```\n\n### doctor\n\nDiagnose the runtime environment: Node version, `claude`/`codex` commands, session directories, hook installation, hook launcher path validity, failed queue state, and pending hook events.\n\n```sh\nsession-bridge doctor\nsession-bridge doctor --json\n```\n\nExits with code 1 if any check fails.\n\n### hooks\n\nInstall, manage, and process lifecycle hooks.\n\n```sh\nsession-bridge hooks install             # prompt mode (default)\nsession-bridge hooks install --mode auto # auto mode\nsession-bridge hooks uninstall\nsession-bridge hooks status\nsession-bridge hooks pending\nsession-bridge hooks failed              # list failed events available for retry\nsession-bridge hooks run                 # process oldest pending event (FIFO)\nsession-bridge hooks run --all           # process all pending events\nsession-bridge hooks run --failed        # retry failed events instead of pending\nsession-bridge hooks run --failed --all  # retry all failed events\n```\n\n### ui\n\nLaunch a local web dashboard.\n\n```sh\nsession-bridge ui\nsession-bridge ui --port 9000 --no-open\n```\n\nOpens at `http://127.0.0.1:<port>/#token=<random>`. See the Security section.\n\n## Hooks\n\nsession-bridge can install lifecycle hooks into both Claude Code and Codex so that session handoffs are queued when a session ends.\n\n**Claude Code**: registers a `SessionEnd` hook in `~/.claude/settings.json`.\n**Codex**: registers a `Stop` hook in `~/.codex/hooks.json`. Codex requires user approval of hooks in its `/hooks` view before they run.\n\nThere are two modes:\n\n- **prompt** (default): the hook writes minimal event metadata to `~/.session-bridge/hook-events/` and exits. You process events later with `session-bridge hooks pending` and `session-bridge hooks run`.\n- **auto**: the hook enqueues the event and immediately spawns a detached background worker (`session-bridge hook process --event <path>`) that runs the import.\n\nImportant caveats:\n\n- Codex `Stop` is a turn-scope event, not a session-exit event. It fires after every assistant turn. In auto mode, this means a handoff attempt runs after every Codex turn. In prompt mode, events accumulate and you choose which to process.\n- Install creates a `.session-bridge.bak` backup of the settings file before modifying it.\n- The hook command uses absolute paths for both the Node runtime and the CLI script (shell-quoted), so it does not depend on `session-bridge` being in PATH. The `doctor` command verifies that the installed launcher paths are still accessible.\n- Uninstall removes only session-bridge-owned hooks (identified by the `sessionBridge: {owner, version, agent}` group marker), preserving any other hooks in the file. Third-party wrapper commands are never claimed or removed based on command suffix matching.\n- For legacy installations that pre-date the group marker, uninstall also matches by the exact full command string of the current launcher (Node binary + CLI script + arguments). It never matches by command suffix alone, so a wrapper that happens to end with the same script name is not removed.\n- session-bridge does not forge Codex hook trust. After installation, open `/hooks` in Codex and approve the hook.\n- The event queue automatically prunes processed events after 7 days and failed events after 30 days. Pending events are never removed automatically.\n\n## Web UI\n\nThe `session-bridge ui` command starts a local HTTP server with a single-page dashboard.\n\n**Dashboard**: displays sessions grouped by project along a handoff timeline. Each entry shows source/target pair, generation number, sync state (pending/synced/warning/orphan), and an import action (shown only for pending state). The server's cwd is auto-selected as the initial project if it has sessions.\n\n**Timeline**: drill-down view for a single session showing the message sequence with role indicators, timestamps, tool-call collapse, and generation markers. Target sessions hide the already-transferred prefix and display a generation boundary marker, capsule contents, transfer report, and verification warnings. An accessible modal prompts for an optional handoff note before triggering an import.\n\n**Settings**: read-only view of adapter status, paths, sync rules, and hook configuration.\n\nThe UI is dark-only, uses Geist Mono for paths/IDs and Geist for labels, built with React 19, Tailwind CSS 4, and Vite 7. Verified at 1200px and 375px viewports.\n\n## How It Works\n\n```mermaid\nflowchart TD\n    subgraph \"Claude Code to Codex\"\n        CC[Claude Code JSONL] -->|parse| TS[TransferSession]\n        TS -->|RPC| AS[\"codex app-server --stdio\"]\n        AS -->|detect + import| CX[New Codex thread]\n        CX -->|poll ledger| V1[Verify thread/read]\n    end\n\n    subgraph \"Codex to Claude Code\"\n        CXS[Codex JSONL] -->|parse| TS2[TransferSession]\n        TS2 -->|atomic write| CCT[\"~/.claude/projects/{path-hash}/{uuid}.jsonl\"]\n        CCT -->|re-parse| V2[Verify sessionId + count]\n    end\n\n    subgraph \"State\"\n        V1 --> ST[\"~/.session-bridge/state.json\"]\n        V2 --> ST\n        ST -->|\"path + SHA-256 = idempotent\"| DUP[Return existing result]\n        ST -->|\"path + new digest\"| GEN[New generation on same timeline]\n    end\n```\n\n### Content Fidelity\n\n| Transferred | Not transferred |\n|-------------|-----------------|\n| User messages (full text) | System/developer messages |\n| Assistant replies (text + tool_use) | Thinking-block signatures |\n| Tool call inputs and outputs | Encrypted content |\n| Timestamps | File-history snapshots |\n| | Permission records, MCP instructions |\n| | Sidechain/meta messages |\n\nProvider-side prompt cache is not restored. The target session builds its own cache from the first resumed turn onward. The tool does not write or modify any provider-specific metadata fields.\n\n### Idempotency\n\nThe state ledger at `~/.session-bridge/state.json` keys each import by `{source-agent}:{resolved-path}:{sha256}`.\n\n- Same path, same content: returns the previous result immediately.\n- Same path, different content (Claude to Codex): creates a new generation on the same timeline. The Codex app-server import mechanism creates a new thread.\n- Same path, different content (Codex to Claude): creates a new target session and records it as the next generation.\n\nConcurrent imports are serialized via an exclusive file lock (`state.json.lock`) with post-lock state re-check. Lock acquisition polls at 100ms intervals; the lock times out after 90 seconds.\n\n## Security and Privacy\n\nsession-bridge makes no network requests. The only external process interaction is spawning `codex app-server --stdio` as a child process over stdin/stdout pipes. Network behavior of that subprocess is outside this tool's control.\n\n**File safety**:\n- Atomic writes for overwritable internal files (state ledger, settings, hook events): `O_EXCL` temp, `fsync`, then `rename` over the existing path.\n- Atomic writes for new Claude target sessions: `O_EXCL` temp, `fsync`, then `link` to create the target path atomically. If the target already exists, `link` returns `EEXIST` and the import fails without overwriting. The temp file is removed after a successful link.\n- Directories created at mode `0o700`, files at `0o600`.\n- Source path validated via `lstat`: must be a regular file (symlinks rejected). Destination directory validated via `lstat` to block symlinked directories. Hook event paths checked for root containment (`ensureInside`).\n- Source stability checked via stat before and after read/hash; throws if size, mtime, dev, or ino changed.\n\n**Web UI server**:\n- Binds to `127.0.0.1` only.\n- 256-bit random token generated on each startup; passed to the browser via URL fragment, stored in sessionStorage, never sent as a query parameter.\n- `X-Session-Bridge-Token` header required on all API requests. Timing-safe comparison.\n- Host header validated against `127.0.0.1:<port>`.\n- Origin header validated on POST requests.\n- CSP: `default-src 'self'`, `frame-ancestors 'none'`. Additional headers: `X-Frame-Options: DENY`, `X-Content-Type-Options: nosniff`, `Referrer-Policy: no-referrer`, `Cache-Control: no-store`.\n\n## State and Backup\n\nAll persistent state lives in `~/.session-bridge/`:\n\n| Path | Purpose |\n|------|---------|\n| `state.json` | Import ledger: timeline generations, capsules, digests |\n| `state.json.lock` | Cross-process exclusive lock |\n| `hook-events/` | Queued lifecycle hook events (one JSON file per event) |\n\nsession-bridge never overwrites or deletes existing Claude or Codex session files that were not created by the current import. Each import writes a new file. If the state ledger write fails after a target session has been created, the behavior depends on the adapter's `importSafety` declaration: a `\"rollback\"` adapter's `rollbackImport` method removes the just-created target file only if its path and digest still match what was just written, so no orphan accumulates; a `\"reconcile\"` adapter does not delete the target but relies on its idempotent reconciliation to avoid duplicates on retry (the target may already exist from a prior attempt). User-owned sessions are never touched in either case.\n\nThe state ledger is an atomically rewritten cumulative file; each new import adds to the existing entries. The ledger is bounded at 10 MiB; imports that would exceed this limit are rejected before creating a target session (with a 256 KiB reserve for the result entry).\n\nTo reset: delete `~/.session-bridge/`. This discards all lineage tracking and deduplication state. Subsequent imports may produce duplicate target sessions for sources that were previously imported. Target session files already written remain in place and are valid sessions in their respective tools.\n\n## Architecture\n\n```\nsession-bridge/\n├── packages/\n│   ├── core/           @session-bridge/core (private, zero UI deps)\n│   │   ├── adapters/   AdapterRegistry, claude/codex parsers and writers\n│   │   ├── bridge/     ImportService, StateStore (dedup + lineage)\n│   │   ├── codex/      JSON-RPC client for codex app-server\n│   │   ├── hooks/      Hook config (install/uninstall), event queue\n│   │   ├── io/         Streaming JSONL reader, atomic file writer\n│   │   ├── timeline/   Types, HandoffCapsule, generation lineage\n│   │   └── config.ts   BridgePaths resolution\n│   ├── cli/            @asterzephyr/session-bridge (npm bin)\n│   │   ├── bin/        Commander entrypoint\n│   │   ├── commands/   import, sync, list, status, doctor, hooks, ui\n│   │   └── server/     Hono HTTP server (UI backend)\n│   └── ui/             @session-bridge/ui (private, React dashboard)\n│       ├── views/      Dashboard, Timeline, Settings\n│       └── api.ts      Fetch wrapper with token auth\n├── testdata/           Sanitized session samples for tests\n├── scripts/            Package install smoke test\n└── .github/workflows/  CI (Node 22/24) + release (npm trusted publishing)\n```\n\nThe CLI is bundled with tsup, which inlines `@session-bridge/core`. Built UI assets are copied into `packages/cli/dist/ui`. The result is a single npm package.\n\n## Development\n\n```sh\npnpm install\npnpm check            # typecheck + test + build + package smoke test\npnpm dev -- list      # run CLI via tsx\n```\n\nIndividual packages:\n\n```sh\npnpm --filter @session-bridge/core test\npnpm --filter @session-bridge/core typecheck\npnpm --filter @session-bridge/ui build\npnpm --filter @asterzephyr/session-bridge build\n```\n\n### Verification\n\n`pnpm check` runs the full validation gate:\n\n1. TypeScript type checking across all packages\n2. 21 tests: 16 core adapter/import tests + 4 UI timeline model tests + 1 CLI API test\n3. Production build (tsup bundle + Vite build)\n4. Package install smoke test: `npm install` the tarball, verify README.md, LICENSE, bundled UI `index.html`, and binary version output\n\nCI runs on Node 22 and 24.\n\n### Environment Variables\n\n| Variable | Default | Purpose |\n|----------|---------|---------|\n| `CLAUDE_CONFIG_DIR` | `~/.claude` | Claude Code config root |\n| `CODEX_HOME` | `~/.codex` | Codex data root |\n| `SESSION_BRIDGE_HOME` | `~/.session-bridge` | State and hook-events location |\n| `SESSION_BRIDGE_UI_DIR` | (auto-detected) | Override bundled UI asset path |\n\n## Extending: Writing an Adapter\n\nThe `AdapterRegistry` accepts adapters implementing the `SessionAdapter` interface:\n\n```typescript\ninterface SessionAdapter {\n  id: AgentId;\n  displayName: string;\n  capabilities: AdapterCapabilities;\n  discover(cwd?: string, limits?: DiscoveryLimits): Promise<SessionMeta[]>;\n  parse(path: string, options?: SessionParseOptions): Promise<TransferSession>;\n  importSession?(request: AdapterImportRequest): Promise<AdapterImportResult>;\n  rollbackImport?(result: AdapterImportResult): Promise<void>;\n  resumeCommand(sessionId: string): string[];\n}\n\ninterface AdapterCapabilities {\n  discover: boolean;\n  parse: boolean;\n  importFrom: AgentId[];\n  resume: boolean;\n  lifecycleHook: \"stable\" | \"experimental\" | \"none\";\n  importSafety: \"rollback\" | \"reconcile\" | \"none\";\n}\n```\n\n**Import safety contract**: adapters that implement `importSession` must declare `importSafety` as either `\"rollback\"` or `\"reconcile\"`. `\"rollback\"` requires implementing `rollbackImport`; the bridge calls it when a post-import state write fails to remove the just-created target. `\"reconcile\"` means the adapter handles partial-failure recovery internally via idempotent reconciliation (the target may already exist; nothing is deleted). Read-only adapters that do not implement `importSession` must declare `\"none\"`. The registry enforces this constraint at registration time.\n\n**Result size limits**: `AdapterImportResult` is serialized into the state ledger. The normalized result must not exceed 64 KiB; the state ledger reserves 256 KiB for the entry (result + lineage metadata). Imports that would exceed these limits are rejected.\n\nTo add support for a new agent (Gemini CLI, Cursor, Amp):\n\n1. Implement `SessionAdapter` in `packages/core/src/adapters/`.\n2. The `parse` method reads the tool's session format and returns a `TransferSession`.\n3. The `importSession` method writes or triggers import on the target side.\n4. Declare `importSafety` in capabilities: `\"rollback\"` (must implement `rollbackImport`) or `\"reconcile\"` (adapter handles recovery via idempotent reconciliation). Only read-only adapters without `importSession` may declare `\"none\"`.\n5. Register your adapter with the registry; extend the CLI to accept the new agent name.\n\nBuilt-in adapters: `claude` (lifecycleHook: `\"stable\"`) and `codex` (lifecycleHook: `\"experimental\"`).\n\n## Release\n\nThe release workflow (`.github/workflows/release.yml`) triggers on `v*` tags and publishes to npm using [trusted publishing](https://docs.npmjs.com/trusted-publishers/). It consists of two jobs:\n\n1. **package** (no `id-token`): checks out code, installs dependencies, runs `pnpm check` (typecheck + test + build + package smoke test), then builds and uploads the release tarball as an artifact.\n2. **publish** (has `id-token: write`, no checkout or dependency install): downloads the verified tarball, verifies that the runner's npm version is >= 11.5.1 (required for trusted publishing), and publishes it.\n\nThe tag must match the version in `packages/cli/package.json`. Node 24 is used for both jobs.\n\nBefore `npm publish`, the `package` job runs `packages/cli/scripts/package-docs.mjs prepare` to stage the root README and LICENSE into the package directory. The local `test-package` script calls the same `prepare` step then cleans up in a `finally` block.\n\n### Bootstrap (first publish)\n\nnpm trusted publishing requires the package to already exist on the registry. Since `@asterzephyr/session-bridge` has not been published yet, a maintainer must bootstrap the initial version manually:\n\n```sh\n# 1. Validate everything passes\npnpm check\n\n# 2. Stage docs into the package directory\nmkdir -p artifacts\nnode packages/cli/scripts/package-docs.mjs prepare\n\n# 3. Pack (even if this fails, you MUST run cleanup in step 4)\nnpm pack --ignore-scripts --pack-destination artifacts ./packages/cli\n\n# 4. Remove staged docs (required even if pack failed)\nnode packages/cli/scripts/package-docs.mjs cleanup\n\n# 5. Publish the tarball\nnpm login\nnpm publish ./artifacts/asterzephyr-session-bridge-0.1.0.tgz --ignore-scripts --access public\n```\n\nAfter the initial publish, configure the trusted publisher mapping on npmjs.com to point at the `AsterZephyr/session-bridge` repository's `release.yml` workflow and grant it publish permission. Subsequent releases triggered by `v*` tags use trusted publishing with no manual credentials. The workflow is not functional until the bootstrap publish and trusted publisher configuration are both complete.\n\n## Roadmap\n\nPlanned directions, no committed timeline:\n\n**v0.2**\n- Hook coalescing: debounce Codex per-turn Stop events so auto mode does not attempt a handoff after every turn\n- Generation diff: show what changed between consecutive generations of the same timeline\n- Capsule editor: interactive CLI and UI for editing handoff capsule fields before confirming an import\n- Capsule export: Markdown or JSON export of a timeline's full capsule chain\n\n**v0.3**\n- Adapter SDK: schema-based compatibility checking for third-party adapters\n- Format contract and canary compatibility tests: validate adapter parse/import against versioned fixture snapshots to catch format drift\n- Windows hooks support and full path separator testing\n- Optional encrypted local metadata (encrypt capsule content at rest)\n- Generation compaction: squash intermediate generations into a single summary generation\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md","_rev":"1-7968976ef5398ae1463793c9c57f354d"}