ContextDock Integration (link bundles and lists to project knowledge)
ContextDock is a separate web app where you collect markdown notes into bundles and lists; linking one into an Omniscio project takes a snapshot of it, and every new session in that project starts already knowing the content. This page covers turning the integration on, linking and refreshing what you link, and managing the linked rows.
What it is
ContextDock is a separate web app where you collect markdown notes — research, customer transcripts, product specs, anything — into named bundles (hand-curated collections of docs) and lists (tag-driven views over your docs). Omniscio's ContextDock integration lets you link any bundle or list into an Omniscio project so every Claude session in that project automatically sees the contents alongside its own work.
Concretely: linking a bundle or list takes a one-time snapshot of the rendered markdown and saves it as a regular file at <project>/.claude/docs/contextdock-<id>.md. From that point on, Omniscio inlines the file's contents into the first user message of every new Claude session in that project. The agent treats it the same as any other doc you've dropped in .claude/docs/ (see project-docs-auto-injection.md) — it's just a regular .md file with a sentinel header marking Omniscio ownership.
There are three user-visible surfaces:
- Settings → ContextDock — masked input where you paste an API key, a "Validate & Save" button, and (on success) a green-check status with an identity label derived from your key (your account
emailif present, otherwise the key'skeyName, otherwise the rawuserId). A Background refresh (every 5 minutes) toggle (default on) sits below the key card — when on, Omniscio keeps the picker's bundles/lists/list-detail SQLite cache warm against upstream every 5 minutes so the Add Project Doc → ContextDock tab opens instantly with a fresh roster; flip off if you'd rather refresh only when you open a tab or click 🔄. Footer showsContextDock CLI v0.2.0 (vendored 2026-05-07)so you know exactly which CLI version Omniscio is running. - Add Project Doc dialog → ContextDock tab — fourth tab next to Upload / Paste / Google Doc. Picker is split into three inner sub-tabs — Bundles (📦), Lists (🏷️), and Library — with a per-tab client-side search filter (Bundles/Lists filter their own rows; the Library tab carries its own search box). Bundles and Lists each show a count badge. The Library sub-tab is a full-text search over your entire ContextDock doc library that links one individual doc at a time at a compression level you pick — reachable even when you have no bundles or lists (see Adding an individual doc from the Library tab below). Bundle rows carry a single Add button (tooltip: "Add this bundle to project docs") that links the whole bundle in one click using the bundle's author-set compression preference (falls back to Key Points when the bundle doesn't specify one). List rows carry two buttons: a primary Add index (tooltip: "Adds a titles-only index of this list. Use Browse to add individual files with their content.") that writes a synthesized titles-only table of contents for the list — just the list name, optional description, and a numbered list of every doc's title + id — and a secondary Browse that drills into the list's per-doc picker so you can add real doc content. The picker reads from a SQLite cache populated at startup, so opening the dialog renders both sub-tabs instantly; a small
Updated Xm agocaption plus a 🔄 refresh icon sits in the top-right of the picker body. - ProjectDocsSection rows — every linked bundle, list, or per-doc item appears in your project's docs list with the ContextDock logo on the left (so you can tell at a glance the doc came from ContextDock rather than from a plain
.claude/docs/paste), the full display name (wraps to multiple lines if long — never truncated), and a "from ContextDock · 14,237 tokens · refreshed 5m ago" sub-line. Left-click the row (or focus + Enter / Space) to preview the snapshot's rendered markdown in Omniscio's FilePeek viewer — same viewer used everywhere else in Omniscio for.mdfiles. Middle-click the row to unlink it instantly with a Ctrl+Z undo, matching the inbox-row + tag-chip middle-click contract used elsewhere in Omniscio. Right-click the row opens a context menu with the row actions (refresh, open in browser, change compression for per-doc rows, unlink) — the row itself is icon-free so the title can use the full available width. Long-press the row (~0.4s) then drag to reorder it within the Auto Context list — there's no separate grip handle, a quick click still opens the preview, and ContextDock rows reorder by the same whole-row gesture as plain docs. Per-doc (kind: doc) rows additionally show an inline compression chip in the sub-line — a sliders icon plus the current level (e.g. Key Points) — that you click to cycle Original → Key Points → Summary in place (the right-click Change compression submenu still works too). The Change compression group only appears for per-doc items (the ones added via a list's drill-in —kind: doc); whole-bundle and whole-list rows don't show it because bundles carry an author-set compression preference and lists are committed at the index level.
Opting in
The integration is opt-in at two levels:
- Feature toggle (2026-05-23): ContextDock is registered in
UNRELEASED_FEATURES(settingKey: contextdockEnabled, off by default), so it is revealed through Settings → Lab → Built-in Apps → ContextDock. (2026-08-14 declutter, decision 3A: the Settings nav row is hidden by default and the old Features-hub toggle is gone — the Lab toggle is the only reveal.) WhencontextdockEnabledis off, the ContextDock-related sidebar surfaces are hidden, and every ContextDock IPC handler (key validation/state, bundle/list refresh, picker fetches, link, refresh) rejects with{success: false, error: "ContextDock is disabled..."}. Existing installs that already have a saved ContextDock API key or any linked bundle/list/doc rows get migrated ON automatically on first launch via opt-in-toggle-migration.ts. - API-key setup: the ContextDock tab is always present in the Add Project Doc dialog (once the feature toggle is on); until you paste an API key in Settings, the tab body shows an empty state ("ContextDock isn't connected yet. Set up ContextDock in Settings → Tools & Maintenance → ContextDock.") with an Open Settings button that deep-links into Settings → ContextDock.
Where to find it
ContextDock shows up in exactly three places, and it has no sidebar row or panel of its own. The key lives in the ContextDock card in Settings, where you paste it and validate it. The picker lives in the ContextDock tab of the Add Project Doc dialog, which you open from any project. And once something is linked, it appears as a row in that project's docs section.
Settings search keywords "contextdock", "bundles", "lists", "external docs", "knowledge base" all land on the feature toggle; the contextdock-api-key and contextdock-bg-refresh settings entries stay searchable even while the nav row is hidden (decision 3A).
How it behaves
One-time setup
- Mint an API key in your ContextDock workspace (
contextdock.web.app→ workspace settings → API keys → New key with read scope). - In Omniscio: gear icon → ContextDock in the Settings sidebar. Paste the key into the input.
- Click Validate & Save. Omniscio runs
whoamiagainst your key, and on success shows a green check mark + an identity label. The label is your accountemailwhen present, otherwise the key'skeyName(e.g. "Personal"), otherwise the rawuserId— whichever is the most informative thing the API returned. - Done. The key is encrypted via Windows DPAPI (
safeStorage) and stored asenc:<ciphertext>in Omniscio'sconfig.json.
If the network is down during validation, Omniscio offers a "Save without validating" escape hatch — the draft key is written and you can validate later by clicking the row.
Linking a bundle or list to a project
- Open the project. Click Add Project Doc (the
+ Docbutton in the project's docs section). - Pick the ContextDock tab (fourth tab, always present — but only shows bundles/lists once an API key is saved; without one, the tab body is the empty state with an Open Settings link).
- The picker renders bundles and lists instantly from the local SQLite cache (populated by the startup preload — see "Cache + startup preload" below). Pick a sub-tab — Bundles (📦), Lists (🏷️), or Library. The Bundles and Lists tabs each have their own search box that filters only that sub-tab's rows; the Library tab has its own full-library search (see Adding an individual doc from the Library tab). The top-right of the picker shows
Updated Xm agoplus a 🔄 refresh button — clicking it forces a foreground re-fetch from contextdock.web.app and shows a success toast on done. - Bundles: each row carries a single Add button. Clicking it links the whole bundle in one shot at the bundle's author-set
preferLevel(Original / Key Points / Summary), falling back to Key Points when the bundle metadata didn't specify one. The picker shows the per-level token estimate next to the description so you know roughly how much context you're attaching before you click. There is no per-row compression picker — bundles are author-curated, so the bundle owner has already chosen the level you should use. - Lists: each row carries two buttons. The metadata line below the list name shows the doc count (or
List indexwhen the upstreamdocCountis absent) and, when the cache has akeyPointstoken estimate for this list, a· <N>k tokenschip — same format the bundle rows use — so you can see the index size before clicking. The primary Add index writes a synthesized titles-only table of contents for the list as a single doc — samecontextdock-<listId>.mdfile shape as a bundle, withkind: liston the sentinel header. What lands on disk is just the list name, optional description, and a numbered list of every doc's title + id (plus its original-version token count when known). No doc content is included. The synthesized body explicitly tells the agent that "the document content is NOT in this snapshot" and instructs it to ask the user to add specific docs via Browse if it needs them — because there is no MCP wiring, the agent cannot pull individual docs at runtime. This makes "Add index" a true context-switching aid: the agent learns what's available in the list without burning the ~125K-token injection cap on doc bodies. The secondary Browse drills into the list's per-doc picker (see "Drill into a list" below) so you can pick a subset of the list's docs and individually choose the compression level for each — that's how doc content actually enters context. Use Add index when you want a navigable manifest; use Browse when you want the doc bodies themselves. - On success, Omniscio fetches the rendered markdown, prepends a sentinel header, and atomically writes it to
<project>/.claude/docs/contextdock-<id>.md(whole bundle or whole list) — or onecontextdock-<docId>.mdper individually-picked doc when you commit from a list's drill-in. The dialog closes and the new row(s) appear in the project's docs section.
The bundle or list is now part of the project. Omniscio inlines the contents into the first user message of every new Claude session you spawn in this project (see project-docs-auto-injection.md for the injection mechanics). Because injection happens once, on the first message, already-running and paused sessions won't pick up a newly-linked bundle — they're already past their first message. Spawn a new session (or the next scheduled/recipe run) to pick up freshly-linked content.
Bundles vs lists. A bundle is a hand-picked, author-curated set of docs you've assembled in ContextDock — including a preferLevel for the right compression. A list is a saved tag query — you pick one or more tag IDs and a match mode (any / all), and the list resolves to whatever docs currently match. The snapshot Omniscio writes to disk is the rendered markdown at link time for both kinds; refreshing a list re-runs the tag query against the current state of your workspace.
Pinning bundles
You can pin a bundle so it always sorts to the top of the Bundles sidebar pane. Two controls set the pin state:
- Hover-reveal pin icon — hover over any bundle row in the sidebar to reveal a small pin icon on the right edge. Click it to toggle pin on/off. When pinned, the icon stays visible in accent color; when unpinned, it only appears on hover. The icon pulses briefly while the update is in flight.
- Pinned toggle in the edit dialog — open a bundle's edit dialog (only in edit mode, not create). A Pinned toggle sits below the Shared toggle, with the description "Pinned bundles appear at the top of the sidebar."
Pinned bundles sort before unpinned bundles; within each group, bundles sort alphabetically by name. The pin state is stored as isPinned on the BundleSummary type and persisted in the is_pinned column of the contextdock_bundles_cache table.
Drill into a list (per-doc selection)
A ContextDock list resolves to a tag-matched set of docs. Click anywhere on a list row in the picker to enter the per-doc selection surface — it replaces the picker body in place (the Add Project Doc dialog stays open):
- Doc list — every doc the list currently resolves to is rendered as a row with a checkbox, the doc title, and a per-row native
<select>dropdown (Original / Key Points / Summary). The select is indented below the title so it visually reads as a sub-control of the doc, not a peer of the checkbox. Each option label embeds the per-level token count when the CLI provided one (e.g.Summary — 400 tokens); options with no count render label-only and still pick the right level. - Per-row select change sets the row's level AND selects it. This makes "I want this doc at Summary" a single interaction. Clicking the checkbox directly toggles selection without mutating the stored level — selection ≠ level.
- Bulk action toolbar —
Select AllandDeselect Allfor selection, plus a single Apply level to selected docs<select>that applies the chosen level to every currently-selected row. The dropdown is a one-shot picker — it stays at its placeholder (Apply level to selected…), fires the bulk-apply on change, then resets back to the placeholder so re-applying the same level is a fresh choice each time rather than a stuck "current selection". The bulk select is disabled while nothing is selected. - Running totals — the top bar shows
<count> of <total> selected · ~<tokens> tokenssummed across the selected rows at their per-row levels. Updates live as you change selects and toggle checkboxes. - Add (N) — clicking the primary button commits the selection. Omniscio fires one
contextdock:link-docIPC per selected doc, in sequence; each successful one writes its owncontextdock-<docId>.mdfile. The toast roll-up showsAdded Non full success, orAdded N of M — F failed (<first-error-code>)when some rows fail. Failed rows stay selected so you can retry; succeeded rows are deselected and removed from the active set. ← Back to ContextDockalways returns to the picker without committing. Selection state is dropped on back-out (intentional — the drill-in is a per-list scratchpad).- Deselect All clears selection but PRESERVES the per-row level overrides so you can re-select a row and pick up where you left off. Selection is your intent for "what to commit now"; level is a property of the row.
Each successful drill-in commit is rolled up into a single drill_in_commit telemetry event with { docCount, levelMix: { original, keyPoints, summary }, errorCount } — no doc IDs, no titles, no error messages. The per-doc bundle_linked / fetch_failed rows are still emitted by link-doc for individual successes and failures.
Adding an individual doc from the Library tab
The Library sub-tab links a single doc from your whole ContextDock library — without going through a bundle or a list. It's the fastest path when you know the doc you want and don't care which bundle/list it lives in, and it's the only picker path that works when you have zero bundles and zero lists.
- Open Add Project Doc → ContextDock → Library. The tab lazily loads your doc roster on first open (so the picker still opens instantly on the default Bundles tab).
- The empty search box shows your full library roster; type to search ("Search your library…") runs a debounced full-text search and shows matching hits with a snippet. Roster rows carry per-level token counts (shown in the level dropdown); search hits don't — the token count appears on the row once it's added.
- Each result has a per-row compression
<select>(Original / Key Points / Summary — default Key Points) and an Add button. Pick the level, click Add, and Omniscio links just that doc viacontextdock:link-doc— the same per-doc channel the list drill-in uses — writing onecontextdock-<docId>.mdfile. A success toast showsAdded <title> (<N> tokens); if the add pushes.claude/docs/over the ~125K-token cap you also get a clip warning. - The new doc appears as a per-doc row (
kind: doc) in the project's Auto Context list, with the inline compression chip you can use to change its level later.
Library-linked docs are identical on disk to docs added via a list's Browse drill-in (kind: doc sentinel) — the only difference is the entry point: Browse is scoped to one list's tag-matched docs, while Library searches your entire library.
Previewing a linked snapshot
To see what Omniscio actually wrote to disk for a linked bundle or list, left-click the row in the project's docs section. Omniscio opens the file in its standard FilePeek markdown viewer — the same one used for plain .md docs elsewhere — and renders the snapshot's body. The sentinel header at the top of the file is an HTML comment block, so the viewer renders it as invisible; you see only the bundle's / list's actual markdown content. Press Escape (or click the X) to close.
Keyboard access: tabbing through the docs section lands on each row in turn; pressing Enter or Space on a focused row opens the same viewer.
This is purely a viewer — it has no edit / save controls. If you want to change a linked snapshot, either refresh it from upstream (right-click → Refresh) or unlink and re-link.
Refreshing
Two kinds of refresh, one for each surface:
- The picker's library list (bundles + lists shown in Add Project Doc → ContextDock). The 🔄 button next to the
Updated Xm agocaption forces a foreground re-fetch from contextdock.web.app, rewrites the SQLite cache, and updates the on-screen list. Use this when you've just created or renamed a bundle/list at contextdock.web.app and want it to appear in the picker without waiting for the next startup preload. - A linked snapshot (a
contextdock-<id>.mdfile already in a project's docs). The snapshot does not auto-update. If you edit the bundle on contextdock.web.app — or if a list's tag-matching docs change underneath you — Omniscio's local copy stays at the version you originally linked. To pull a new snapshot, right-click the row → Refresh. Omniscio re-fetches the bundle or re-runs the list's tag query and rewrites the file atomically.
In-flight snapshot refreshes for the same (projectId, contextdockId) pair are deduplicated — clicking the refresh button twice while the first request is mid-flight is a no-op.
Changing compression on a per-doc item
When you drilled into a list and committed a subset of its docs, each landed as its own per-doc row in the project's docs section (contextdock-doc-<docId>-<level>.md on disk, with kind: doc on the sentinel header). After the fact, you can change which compression level (Original / Key Points / Summary) any of those rows is fetched at without leaving the project. Two equivalent controls do the same swap:
- Inline compression pill (quick). Every per-doc row shows a small pill in its sub-line — the current level label, a compact token count, and a down-arrow (e.g. Key Points · 1.2k ▾). Click the pill to open a short dropdown and pick a level (Original / Key Points / Summary). While the swap is in flight the pill shows a spinner and is disabled, and clicking it never opens the row's preview (the click is captured by the pill).
- Right-click menu (discoverable). Right-click the row → Change compression → pick a level — the explicit menu, handy when you want to jump straight to a specific level rather than cycling.
The submenu lives between Open in browser and Unlink in the per-doc row's right-click menu, rendered as three radio-style items (one per level). The currently-active level shows a filled dot (●) and is disabled — clicking your current level is a no-op, not a refetch.
Picking a different level atomically swaps the snapshot:
- Omniscio fetches the doc at the new level from contextdock.web.app.
- On success, the new file (
contextdock-doc-<docId>-<newLevel>.md) is written to.claude/docs/. - The old file at the old level is unlinked.
If the fetch fails (network down, key revoked, doc deleted upstream), nothing on disk changes — the old file stays exactly where it was at the old level. Specifically:
not_found(doc deleted upstream between link-time and change-time) → row gets the same yellow ⚠ stale marker that refresh-fails use; the local snapshot at the old level is untouched and still works.- All other error codes (
auth,auth_revoked,rate_limit,budget,network,vendor_broken) → toast with the same wording the refresh path uses; old file untouched.
The "Change compression" menu is only shown for per-doc rows (sentinel.kind === 'doc'). Whole-bundle and whole-list rows don't expose it — bundles carry an author-set compression preference (the picker uses it at link time), and lists are committed at the index level rather than a per-doc level. If you want to change the level of a whole bundle or list, unlink it and re-link.
In-flight change-level operations on the same per-doc row are deduplicated by (projectId, filename, newLevel). Picking the same target level twice while the first request is mid-flight is a no-op.
Open-in-browser
Right-click the row → Open in browser opens the right URL for the kind: https://contextdock.web.app/bundles/<bundleId> for a bundle, https://contextdock.web.app/lists/<listId> for a list. Quick jump back to ContextDock to edit it.
Unlinking
Two paths, same result — delete the local snapshot from .claude/docs/. Omniscio does not delete the bundle or list on contextdock.web.app — only the local snapshot. The next session prompt will not include it.
- Fast path — middle-click the row. No confirmation. The file is moved to system trash via the existing
FILES_DELETEIPC, the row disappears from the docs section, and a toast showsUnlinked "<name>"with an Undo action plus theCtrl+Zshortcut hint. Hit Undo (or press Ctrl+Z) to re-link the bundle/list/doc — the snapshot is re-fetched fresh from contextdock.web.app via the samecontextdock:linkorcontextdock:link-docIPC the picker uses, so you get the latest content, not the trashed copy. Redo via Ctrl+Y re-deletes. This is the recommended path for routine cleanup. - Discoverable path — right-click the row → Unlink. Opens a confirmation dialog. On confirm, deletes the file. No undo toast (the confirm step is the safety net). Use this when you want a hard-stop confirmation or you don't remember the middle-click shortcut.
Why re-link rather than restore from trash? FILES_DELETE uses Electron's shell.trashItem() which has no programmatic restore. But the Omniscio sentinel header at the top of every contextdock-<id>.md file carries everything the link IPC needs — kind, contextdockId, displayName, preferLevel — so re-linking from the sentinel snapshot is equivalent and always succeeds when the upstream bundle/list/doc still exists. The trade-off: if the upstream bundle was deleted on contextdock.web.app between unlink and undo, the undo will fail with a not_found error and the local snapshot is gone for good. Right-click → Unlink is the safer path when you suspect that.
Related
This page is split into three parts. Part 2 covers the limits the integration deliberately holds to, the full error-and-recovery map, the injection caps, and what to gather before filing a bug; part 3 covers the internals, where a linked snapshot is written on disk, the sentinel header that marks it as Omniscio-managed, how the picker cache is kept warm, and the full IPC surface.
The first-message injection that puts a linked snapshot in front of the agent is the same mechanism plain project docs use, described on the project docs auto-injection page. The separate, still-in-development local-first rebuild of the ContextDock library is on the ContextDock (Native) page.
Last verified 2026-09-23