When Claude Code finishes a unit of work, a journal entry lands in Ocul-PM — and the next conversation receives that entry and the remaining plan as its starting context and carries on. Changes are reviewed as a local diff; the pile becomes standups. Local-first. No account, no server, no telemetry.
beta Windows x64 Linux AppImage Linux .deb
Built for people who build with AI every day · On Claude Code? Two lines in /plugin is another way in
Try it in the project you actually work in. If this round trip works, the app has done its job.
Claude Code in your terminal, nothing new to learn. Adding the project plants AGENTS.md;
two lines in /plugin add the hooks and the recording tools.
When the work is done the agent writes its entry, and Today's “First record” card confirms the first entry of that conversation — judged by conversation id, not by entry count. A record is not a verification, and the card says so.
Open a new conversation the next day and the hook loads the remaining plan and the last three entries into the starting context. The “Resume” card shows what went into which conversation, and one button hands it to the terminal. You don't explain yesterday again.
Verified on the terminal Claude Code + oculpm plugin path. Codex · Cursor · Gemini CLI do steps 1 · 2 from the rules file; the step-3 hook exists for Claude Code only, so far.
Code has never been faster — and the first ten minutes of this morning's conversation went to explaining yesterday. Speed bought with memory — that is the trade of the agent era, and Ocul-PM is where it stops.
A record that writes itself as your agents work. A proof that shows every change where it happened. And a console with the agent itself inside it.
The moment an agent finishes, the entry is already written — what changed and why, which agent ran it on which model, classified as bug, feature, refactor, error or chore, in markdown a human reads.
The files an agent claims it touched — check them as a local diff, inside the app, before you commit. Put it side by side with the journal and compare what was said with what actually changed.
The real Claude Code runs inside the app. Every step — reading files, editing them, running commands — arrives as a card, and since v2.11 the edit itself is drawn as a diff.
Terminal hyperlinks and editor URLs died silently on browser defaults the webview blocks. Outbound links share one path.
The delete + resend round trip on every committed syllable is gone. All 150 traces were counted first: no characters were lost.
A deleted project's tabs and terminal windows close before the row does. The rebuild no longer freezes the app while you wait.
Judged by the conversation id, not the entry count. Ready → in progress → first entry → ended without one → unattributed. The card says recorded ≠ reviewed.
The SessionStart hook carries the last three entries after the plan and writes a ledger line. The card says which conversation got it — and that referencing is unknown.
journal_search from the cache with relevance ranking, 132→25 ms. Link related entries in one click, clean up tags, read the weekly rollup first.
Tool calls in 15 words of our own. Reads and edits fold into one row; approvals, errors and records never do. Expand it and the raw exchange is underneath.
Commits, journals, plans and changed files under one coordinate, with a recorded rate and the changes with no entry. Nothing new stored — it reads your git.
Claude Code and Codex become one Agents row; Discussions and Docs move to Reference. ⌘ numbers are reassigned — announced once.
Every hook event carries its own conversation id. A work session now records every conversation that took part, and each entry names the one that wrote it.
Pick a color per session and the left stripe and terminal border both take it. The card reads 2/4 running — and keeps the count when collapsed.
Drag files to move them (hover a collapsed folder and it opens) and grab several at once. Renaming carries open tabs and unsaved edits along.
The socket where the app meets the process holding your shells no longer moves. The app adopts whatever is still alive at the old address instead of replacing it.
Every place the app told that process to shut down is gone. A shell with nobody attached gets one grace period — three hours.
Every conversation shows its id. Click to copy the full one and claude --resume reopens the same conversation in a terminal.
Every save, and every edit an agent makes, leaves a version behind. Pick one from the clock in the path bar to overlay or restore — kept in .oculpm/, not git.
Project-wide diagnostics grouped by file, so you stop opening files one by one. The empty state says "no problems known yet", not "no problems".
A single click reuses one italic tab, pinned by your first edit. ⇧⌘O and ⌃G jump straight to a symbol or a line.
Pick only what belongs to this project out of an export. Browsing the list is entirely offline and free.
Rules, skills and automations as one YAML file. A teammate's document shows what would change before it applies.
A fallback answer carries a badge, unreachable models are dimmed not hidden, and automations defer instead of failing.
Reloading the whole plan, journal and rules on every question is over. The list once, the bodies on demand.
"What did I do last week" gets history. "What should I name this" does not. No AI is called to decide.
A 2,500-character cut once swallowed "never write secrets". Now three lines stay resident and the rest arrives whole.
Five baked-in palettes used to be all of it. Now you build one and trade it as .json — the built-ins use the same format, so the built-ins are the examples.
There is no preview box. Change one color and the whole app changes with it. Colors you did not touch stay untouched.
Bind a theme to a project and only that window wears it. Five repos open at once stop blurring together.
It watches the folder, and when the edits go quiet it drafts what just happened. How long to wait is six tiers — 0.2s to 10min.
By hand · by agent · auto-draft · schedule · watcher · MCP — every entry carries its source. With one kind in the list, the filter row removes itself.
Actual firings over 7 days, and the rules that never caught once. A carefully written rule quietly doing nothing is invisible otherwise.
With four running, finding the one waiting on you meant clicking through windows. Now that session turns amber and "N waiting" takes you there.
Each command gets a status bar and failures are dotted down the right edge. ⌘↑/⌘↓ jump between them, and long output keeps its command pinned on top.
Click a block for "save as a journal entry" and "attach to the planner". Command, exit code, duration and the tail of the output arrive prefilled.
Move between screens, and find journals, plans, discussions and docs by title. Diff review and plan toggles are finished from the keyboard.
One window holds many projects as Chrome-style tabs. Tear one off into a window, drop it back to merge — the right-click menu does the same. Terminal sessions never drop.
Set "every Friday at 17:00, summarize this week" and the app runs it then. Even the reason it did not run is logged, and if the Mac was off it catches up exactly once.
You can start without the app — a hook bridge, 7 MCP tools and 5 skills install in two lines.
A session status icon and a popover briefing — you know a new entry landed without opening the app.
The UI is English or Korean, and the language the AI writes journals in is set separately.
Completions as you type, underlines where it is wrong. Hover types · go to definition · rename · code actions — standard LSP.
Rust · TypeScript · Python · Go. On your PATH it attaches, and when it is missing it says so — it does not fail quietly.
.oculpm/ · .github/ · .gitignore — the ones you open most often are visible.
Several files in tabs, two side by side in a split. Create, rename, drag to move; delete goes to the trash. Bring files in from Finder or with ⌘V.
Click the gutter for a breakpoint, then unfold the call stack and variables where it stopped. Rust · Python · Go.
Per item, "N times in 30 days" / "never fired", plus KB injected per session. Claude Code transcripts are counted on this machine — zero LLM, zero network.
Everything since the commit is overlaid in the editor, and you jump straight to the journal entry that changed this file.
Open from the tree, edit with syntax highlighting and save with ⌘S. 12 languages, and the theme follows the app.
If an agent edits the same file, saving asks with a banner instead of overwriting. Unsaved edits survive leaving the screen.
One button in a search result or the code map opens that file at that line, in the app.
No more leaving for Finder to look at a screenshot. Fit-to-window ↔ actual size on a checkerboard, PDFs as documents — up to 16MB.
Not the index — what is on disk right now. Case, whole word, regex, and replace one, one file or everything.
The Today activity ring reads as "N entries · N files · +N/−N lines". The tooltips that announced themselves on every hover went quiet.
Today's journal · active plans in the sidebar. An agent's entry appears within a second; checkboxes reach the .md through the app's oculpm-mcp. Copilot sees the same tools.
A verified mark is stored with the body's fingerprint. If an agent edits the entry later it shows "Changed after review · re-check" and drops out of the verified-only filter. Read it again, click again, and it binds to the current text.
This app is built with itself — every line below came out of Ocul-PM's own work journal.
/oculpm:board in Claude Code and your active plans and item states go up as one private claude.ai board; run it again to refresh it, and follow progress from your phone or another machine. The source stays in .oculpm/planner/; the app builds what gets uploaded and masks anything that looks like a secret first, and agents reading plans get the same masking. The Notion integration is gone — the leftover token is removed from the keychain after the update, and outbound connections are down to five kinds. Code search stops indexing tables, record files and large data files, and duplicate chunks of the same file (17.8% of a real database) no longer pile up.› [image] /private/tmp/…/x.png (1.9MB), seeing it meant digging through a hidden temp folder in Finder. Now hover the path and a card with the image, name and size appears; click to open it large. If a narrow terminal splits the path across two lines, either line shows the same image. The file may live outside the project, but only real images open — a renamed non-image, a folder or anything over 16MB is refused with the reason, and the image is drawn inside the app only. Applies to terminals opened after the update..claude/settings.json hooks and .mcp.json servers as-is, and unlike the CLI it never asked whether you trust the folder. It now asks "Trust this project?" first and lists the settings files that can run code (it is the same trust as language servers, so projects you trusted in v3.10 start as before). Automatic git on the Today and Changes screens no longer follows .git/config fsmonitor · filters · external diff or hooks, plugin installs can't fetch a different bundle than the one previewed, and a paired phone can no longer read .env. New windows no longer miss what already happened — a running session, a tab's busy dot, an automation's "running" state and language-server status were wrong in a freshly opened window; closing a tab or quitting now stops language servers and debuggers too. A database that won't open shows the reason and offers "Start fresh" instead of quitting silently..rs, .ts, .py or .go file now shows "Language server off · Trust and turn on" in the status bar; one click turns them on.build.rs, macros and chosen toolchain, so opening a single file from someone else's repository could run their code. Language servers now start only after you trust the project on this device (the status bar's "Trust and turn on"; revoke it in Settings). Code snippets the AI panel attaches as context are masked before they leave, .env, keys and credential files are never indexed whatever .gitignore says, and files deleted while the app was closed are swept from the index at launch. Consent for background automation is tied to the switches and instructions you saw, so later changes ask again. And the discussion editor is rebuilt — no autocomplete while you write, no text cut off in wide windows, no drafts lost on another screen, plus a centered column, a section outline, a status bar and side-by-side view that scrolls by section..oculpm/ and editor saves. A second external review pointed at the gap, and a fresh sweep found a bigger one — the rule files and .gitignore block the app syncs on every open followed links, so their targets were copied into the project (AGENTS.md → ~/.zshrc) or the block was written into your global Claude instructions (.claude → ~/.claude). Linked spots are now left alone with the reason shown, and the external editor, previews, the Changes screen, indexing and local history all resolve links to the end first. Two ways masking could switch off (a broken config file, the CHANGELOG samples in release-notes drafts) are closed too. And screen headers are slimmer — height 52 → 44 px, title 17 → 14 px, sheet corners 20 → 12 px; the editor's right-click menu and autocomplete no longer render larger than the code..env-style secret lines are always masked, including before anything goes to a background model. A symbolic link planted in a repository can no longer redirect a save outside the project. A Content Security Policy for the app window — it runs only the app's own files and loads no remote images, and every release checks zero violations on every screen, and that the policy is actually enforced, in the real Windows and Linux apps. Also: the agent adapter installs only from a version- and hash-pinned list, dangerous permission modes ask once more, the plugin preview shows the commands MCP servers will run, and connecting Notion no longer leaves a token in your browser history.{#…} inside a title stole the item id · adding to an anchored phase created a duplicate phase · naming a phase "Decisions" could delete the decisions section), and the Claude Code · Codex adapters are updated (0.81.0 · 1.13.0).gh, agent TUIs) and URLs in the editor body fell through to browser defaults the webview blocks, so a ⌘click quietly did nothing — the editor side did not even reach the log. Outbound links now share one path. Typing Korean made the terminal erase and rewrite — every committed syllable sent a delete plus the same character again, and that round trip tripped the "committed but no echo" diagnostic on normal input, filling 4 MB of log a week (all 150 collected traces were counted first: no characters were lost). Deleting a project left traces in the windows — its tab stayed as a number and failed when clicked, and the delete waited on a database rebuild, freezing the app for seconds. Also: the confirm dialog redesigned (Enter confirms), four places in Settings that crashed when opened from the Start tab, and symlinks pointing outside the project that looked like ordinary files in the code tree.journal_search answers from the SQLite cache with relevance ranking (132→25 ms), a Journal scope on the Search screen, related-entry suggestions with one-click linking, a tag dictionary and clean-up sheet, a weekly rollup layer, hotspot and velocity cards, an unreviewed queue, plan-log archiving. Plus two leaks into hidden project tabs: terminal ⌘D and the new-entry toast's Open.plan_create now refuses when an active plan with an overlapping title or id exists and returns its id, title and hash so items go there (allow_similar overrides). Also: the Today headline's detached Korean particle, the Branch screen showing an error card for non-git projects, and the Search empty state gaining an icon and title.files_touched, the branch screen's file attribution and journal diff capture (the quoted header went unrecognized, so that file's diff was dropped) all come back as the real name. A hand-edited config.toml missing one key invalidated the whole project — missing values are now filled from the defaults. Also: secret masking leaving the tail of an overlapping match, an indexing panic on non-ASCII session ids, the settings screen rewriting the config on every open, stale responses overwriting fresh screens, tray popover partial failures and sort order, double-clicking a tab's × — seventeen fixes..oculpm/index/ in the deleted spot. When the root is gone the watcher drops the events and nothing is recreated.«, just gone. macOS 27 ties each status item to the process identity captured at launch, and this app re-launched itself right after start to apply its memory tuning (idle 645MB → 38MB), so the system no longer recognised it as "that app". The bundle now injects the same tuning at process creation, so nothing re-launches and the icon stays put. Claude Code adapter 0.77.0.oculpm.ocul-pm puts today's journal and the active plans in the sidebar, and an agent's entry shows up there within a second. Ticking a plan item's checkbox changes the .md and the app's planner together — the extension never edits files itself; writes go through the app's oculpm-mcp, so locking, the update log and concurrent-edit protection stay intact. Copilot agent mode sees the journal_write·plan_update tools as they are, "Open in editor" on a journal entry lands on that entry in the VS Code sidebar, and "Open in Ocul-PM" from VS Code lands on it in the app (the entry that oculpm://open used to drop is finally used). Settings > Integration gains a "VS Code extension" row, and two market links (VS Code Marketplace · Open VSX) joined the connection list as browser hand-offs. VS Code 1.101+, read-only without the app, and the extension opens no network connection..oculpm/retro/*.md files are not deleted. About 1,900 half-step drifts in type, spacing and radius, three ways of opening settings, and 700 strings in two voices were folded into one./Applications while the request came from a file that no longer existed. An old process holding no terminals at all is now replaced silently when the app connects — one running shell is enough to leave it alone.oculpm command the terminal hands to agents launched the whole app whenever it met a word it did not know — one stale line in a config, one typo, was enough — and the window vanished when the caller killed it on timeout. A call made under that name now always ends as a command, answering an unknown word with its usage instead of a window. A --project path written before the tool name, silently dropped until now, is honored too..dmg with "is damaged and can't be opened" — not real damage, just the quarantine flag on a downloaded file — and clearing it meant typing xattr -dr com.apple.quarantine yourself. Builds are now signed with an Apple Developer ID and notarized. The app now answers when a skill fires — that answer used to be one line the skill wrote about itself; the detail view leads with "When it fires" and the ledger quotes the actual prompt that triggered it. Turning journal watching off was a 60-second lie, because the supervisor read a watcher-less project as deaf and revived it; a user pause and a death are now told apart, and the screen states the lifetime out loud. Today's "files changed" counted touches, not files, running up to 105% high, and the activity rings were a wall rather than a scale with 22 of the last 26 days pinned at the cap — both fixed, and "What's running now" is new (when it can't tell, it says nothing). A plan with unfinished items no longer closes as done. Out of sight: 5,000 files landing in target/ leave a single entry in the watch queue, closing a project no longer holds a lock for up to 4.3s, and the entry chunk is 26.5KB lighter (−9.2%).oculpm:// deep link was reachable from a single link on a web page, and "project overview" missed the same crash in this repository by one byte. Deep links, the Notion connection, "go to definition" and the overview were all copies of the same code; they now share one. Closing the setup wizard with ESC threw away everything you had typed — closing saves a draft, but ESC alone saved the empty draft from the moment the screen opened. Today's activity rings looked identical on every busy day: the extra paint a round stroke cap adds past the dash was never counted, so the inner ring drew 102% and any day past ~7,500 changed lines became a plain closed circle — each ring is now clamped for its own radius, so a saturated day reads as three concentric rings stopping together. The AI panel's first token arrives sooner now that plans and journal entries are fetched together instead of one at a time (up to eight round trips per message). Out of sight: the Notion connection's one-time nonce is a real random value, plugin installs can no longer write outside the folder by following a symlink, and the webview's "open any path" permission is gone..oculpm/, never .env*, 50 per file). Also: svg renders beside the code from the unsaved buffer, and three terminal fixes — a shell idling at its prompt read as "running", a process surviving an app update that kept returning stale answers, and that same process holding shells forever with nobody attached./rules, /plan, /journal, /skill push things in deliberately). Records used often gain relevance; unused ones fade on a 30-day half-life. Settings gained a Context tab showing the standing instructions (global and per-project), the exact list this conversation received, the last recall budget and the candidates in relevance order with a per-item "forget"..json — the five built-ins use the same format, so "duplicate and edit" is a running start. There is no preview box: change one color and the whole app becomes that color, and colors you did not change are left alone (a theme that sets five background lines is a complete theme). A theme that specifies no accent keeps the accent you chose, and on macOS "follow the system accent" paints it with the OS color. Bind a theme to a project and only the window looking at that project wears it — five repos open at once stop blurring together. An imported file gets a fresh identifier, and a name collision asks (overwrite or keep a copy). A theme can only paint a fixed list of colors, so nothing else in the file reaches the screen..oculpm/, the AGENTS.md rules, the .gitignore change) and suggests committing it. Settings → Diagnostics gained a nine-line doctor (watcher · locks · adapters · API keys · index · shell integration · hooks · MCP) that fixes problems on the spot. A tab with a running command or an answering agent asks before it closes. In a project with no index, search and the code map say "no index + build one" instead of "no results", and ⌘/ shows every shortcut. The first-screen bundle went 538KB → 268KB, and a day's summary round trips dropped from 17 to 5 on Today and 30 to 4 on the journal.git init, node_modules was being indexed and a single line of a compressed file was duplicated hundreds of times, inflating the DB to 558MB — that is cleaned up (measured 382MB), and Settings → Diagnostics gained size and "compact". Closing a terminal now really ends vim, ssh and claude, and quitting the app leaves no orphaned adapters. A verification toggle on journal details plus related-entry chips close the review loop.All of it sits in one folder inside your project — as markdown a human can read.
Delete the app and the memory stays. Because it is yours.
People who build with an AI coding agent every day and are tired of re-explaining yesterday's context to a fresh conversation. When Claude Code finishes a unit of work a journal entry is left behind, and the next conversation receives those entries and the remaining plan as its starting context — that is the one round trip above. If you only use an agent now and then, you may not need this round trip — and that's fine: the app is free and the data is markdown in your project folder, so you can stop at any time.
The Claude Code plugin's SessionStart hook carries the last three journal entries after the active plan items into the session-start context — no network, reading only the project's .oculpm/. What it delivered goes into a ledger, and Today's Resume card shows the same material and which conversation it was included in. Whether the model referenced it or was helped by it cannot be known, so the card does not claim it.
Since v2.48.0 there is a VS Code extension, oculpm.ocul-pm. The editor is the real VS Code; the extension puts today's journal and the active plans in its sidebar — an agent's entry shows up within a second, and ticking a plan item's checkbox changes the .md and the app's planner together. The extension never edits files itself: writes go through the app's oculpm-mcp, so locking, the update log and concurrent-edit protection stay intact. Copilot agent mode sees the journal_write · plan_update tools as they are, "Open in editor" on a journal entry lands on that entry in the VS Code sidebar, and "Open in Ocul-PM" from VS Code lands on it in the app. VS Code 1.101 or newer is enough; Cursor and VSCodium get it from Open VSX. Installed without the app it runs read-only, and the extension opens no network connection.
Not since v2.45.0. Builds are signed with an Apple Developer ID and notarized, so you open the .dmg, drag it into Applications and launch it. Earlier versions were unsigned, so macOS refused them with "is damaged and can't be opened" — not real damage, just the quarantine flag macOS puts on downloaded files — and you had to run xattr -dr com.apple.quarantine yourself. If you are coming from an older version, your keychain may ask once on the first launch of this one: API keys live in the macOS keychain and access to them is bound to the app's signature, which has changed. Click Always Allow and it stops asking; nothing you stored is lost.
From v2.40.0, yes. Before that, splitting the terminal four ways and running four Claude Code sessions still showed the app a single session — each hook event carries its own conversation id, and we dropped it, leaving one constant label on screen. A work session now records every conversation that took part (the menu-bar tray says "4 conversations" and lists them in the tooltip), and each journal entry's frontmatter names the conversation that wrote it (the recording tool is a child process of Claude Code, so it already knows its own conversation; where it doesn't, the line simply isn't written). The work session itself is deliberately not split: the watcher that sees file changes doesn't know who wrote them, so splitting into N sessions would duplicate file counts and commit positions arbitrarily. Keeping one container and recording who was attached is the honest answer. In the terminal screen you can give each session a color (right-click its card) that the left stripe and terminal border both take, and the card reads "2/4 running" for that tab, keeping the count on the icon even when the rail is collapsed.
A local-first AI project manager for developers who work with AI coding agents. It records what the agents did, organizes it, and lets you verify every change against a local diff before you commit it.
"Supported" hides four different promises, so we split them into tiers. ① Rules-compatible — we only plant a rules file, and journals get written as far as the model obeys it: Gemini CLI · Cursor · GitHub Copilot · Windsurf · Cline · aider · Zed · Antigravity · pi (anything that reads AGENTS.md lands here). ② Structured journaling — the app registers the MCP journaling tools for it: Claude Desktop. ③ Session observation — hooks deliver start/stop and the app flags conversations that ended without a journal. ④ In-app — the agent runs inside the app and tool approvals arrive as cards: Claude Code · Codex (which include ② and ③). Per-agent evidence and when each tier was last verified are in the wiki tier table. Official builds are macOS Apple Silicon only.
Yes. The Code screen browses the project tree, opens files and saves with ⌘S. Since v2.15.0 the editor understands the code — completion as you type, diagnostics underlined where they belong, hover types and docs, F12 / ⌘-click to jump to a definition, symbol rename that updates every file that uses it, and the code actions that come with a diagnostic. It works for Rust, TypeScript/JavaScript, Python and Go, and when a language server is missing from your machine it says so instead of failing quietly. Because a language server can run the repository's build scripts, from v3.10.0 it starts only after you trust the project on this device (one click in the status bar). It is not a VS Code fork — standard LSP is wired directly. Dotfiles (.oculpm/ · .github/ · .gitignore) show up in the tree. Search results and the code map jump straight to the line, and if an agent edits the same file, saving does not overwrite it — a banner asks which version to keep, and unsaved edits survive leaving the screen. Since v2.16.0 you get tabs and a split view, file create/rename/drag-move (delete goes to the trash), a breakpoint debugger (Rust · Python · Go) and an inline comparison that overlays what the agent changed. Since v2.19.0 ⇧⌘F adds project-wide search and replace against what is actually on disk. Since v2.20.0 images (png · jpg · gif · webp) and PDFs open in a preview instead of the editor — pictures on a checkerboard that reveals transparency, toggling fit-to-window ↔ actual size, PDFs as documents, up to 16MB regardless of the editor's 2MB limit (svg is both picture and code, so it still opens in the editor — and since v2.34.0 a live preview rendered from the unsaved buffer sits beside it). Since v2.21.0 you can drag files and folders in from Finder or paste them with ⌘V — same names are never overwritten, they get a -2 suffix, folders recurse, and symlinks are skipped. Since v2.34.0 the editor also keeps the hygiene of one you leave open all day: save-time whitespace and final-newline cleanup plus auto-save (both off by default; auto-save never touches the cursor's line), preview tabs that do not pile up while skimming, ⇧⌘O symbol and ⌃G line navigation, sticky scroll, a problems panel with project-wide diagnostics, and local history — a version per save and per agent edit, kept in .oculpm/ rather than git. Since v2.47.0, ⌘P opens files by fragments of their name (agmd → AGENTS.md), tree and tab names carry git status (modified · added · deleted) and diagnostics as colour, breadcrumbs show the symbol under the cursor, and ⌥Z word wrap turns itself on for prose files.
Yes. Since v2.10.1 ⌘J opens a terminal dock over whatever screen you are on. Dock it to the bottom (wide), the left or the right (tall) and drag the edge to size it. You can pull it out into a window of its own without dropping the shell — scrollback comes along. Since v2.22.0 sessions stand in a vertical list on the left where each card carries status, agent, elapsed time and last command — drag a session to the edge of the screen and the pane splits so you can watch two at once, and the split's handle (⠿) swaps them or pops one out as its own session. Font size is set in px in Settings → Appearance; line height and padding are a separate density setting (roomy / standard / compact).
That is exactly what v2.23.0 targets. When an agent calls for a human (terminal bell) that session turns amber and a "N waiting" banner appears above the list — click it and you land on the session that is waiting, then cycle to the next. A bell is certain because the program rang it; "quiet for 20 seconds" is a guess, and it is written as a guess. Output is also split per command: each command gets a status bar (green success, red failure), failure points are dotted along the right edge of the whole scrollback, and ⌘↑ / ⌘↓ jump between commands. Clicking a status bar offers copy, plus "save as a journal entry" and "attach to the planner" — the entry opens prefilled with the command line, exit code, duration and the last 40 lines of output. There is deliberately no re-run button; it only prefills the prompt, so nothing you have not looked at runs by itself.
Yes, since v2.24.0. Every item on the Skills & Rules screen carries an "N times in 30 days" badge, and the budget bar splits what enters each session into always-loaded, conditional (measured), irrelevant, skill blurbs, and agents & commands. It is counted deterministically from the Claude Code transcripts on this machine — the moments a rule was actually injected — so no LLM and no network is involved. Since v2.36.0 a conditional rule's badge reports how many files its globs actually match in this project rather than how many globs it has (over 30% and it is flagged "always in practice"), and rules you load and then override in CLAUDE.md are called out with the sentence that overrides them. A skill that never fired is no longer assumed to be broken: the screen separates "nothing to act on", "held back by your own instructions" and "too new to tell", and only suggests a rewrite for a genuine description problem.
Exactly five things. ① LLM requests — sent straight to the provider you chose; we have no relay server. ② Claude Code and Codex inside the app — their own provider traffic under your subscription, plus a one-time adapter download from npm (version- and hash-pinned). ③ Update checks — one GitHub release file. ④ GitHub fetches for patch notes, bundles and theme files (GitHub or the oculpm.com gallery) — only when you click. ⑤ A one-time embedding-model download the first time you enable semantic search: the model comes down, your code never goes up, and search is offline afterwards. Never leaves: journal entries (except what you ask the AI about), everything under .oculpm/, your source code (except snippets sent with a question), and API keys (OS keychain only). No usage statistics, no crash reporting — there is no analytics SDK in the app. The full list, and how to count the calls yourself, is on the privacy page.
Since v3.2.0 the verified mark is stored with a fingerprint of the body you verified. If the body changes afterwards, the entry shows "Changed after review · re-check" in the ledger row and the reading pane, and the "verified only" filter and change-review groups stop counting it as verified. Read it again and click verify to bind the mark to the current content. Entries verified before 3.2.0 have no fingerprint and are honoured as-is — nothing is retroactively unverified. The fingerprint lives in the entry's front matter (verified_hash) under .oculpm/, so it is readable without the app.
Yes. Since v2.30.0, Settings → Data → "Import past conversations" opens an export (.json or .zip) from Claude and friends and lists what is inside with dates, titles, turn counts and a guessed entry type; you pick the ones that belong to this project and they become journal entries. Nothing leaves your machine while you read that list — skimming hundreds of conversations is entirely offline and free, and only what you pick calls the background model, once each. Entries land on their original dates, and re-opening the same file marks already-imported ones "imported" rather than billing you twice (they are not hidden — a conversation vanishing with no explanation is worse). Everything imported starts unverified: it did not happen in this repository, so it stays a draft until you read it and tick it off.
Everything local — journals, search, diffs, the planner — keeps working. Where models are involved, three things changed in v2.30.0. ① When your chosen model does not answer and a fallback replies instead, that reply carries a badge — previously a different model answered while the screen still showed the name you picked. The fallback applies to that one call; your settings are untouched. ② Unreachable providers are dimmed rather than hidden — hiding them reads as your settings having been wiped, and blocking them means you cannot pick one the moment connectivity returns (the reason is in the tooltip). ③ Automations defer instead of failing: a run that could not reach the network is recorded as "deferred" and does not burn its slot, so the catch-up rule picks it up once you are back. A failure that did get a reply (a 429, an auth error) means the network was fine — that is still a failure.
No. Since v2.29.0 it no longer reloads the whole plan, the recent journal and the working rules for every question. When a conversation starts it loads a list of what exists exactly once — and that list does not change by a single character until the conversation ends, because a stable prefix is what keeps the model-side prompt cache alive — and bodies are pulled only when needed. Past records attach only to questions carrying a recall signal: "what did I do last week", "what did I say", "how far did the plan get". The decision uses a Korean/English signal-word dictionary, so it calls no AI, and past the ~800 token ceiling it drops whole records starting with the least relevant. Rules used to be truncated at 2,500 characters, and that cut once swallowed the "never write secrets" clause outright — now only three lines stay resident and everything else arrives in full. To push something in deliberately: /rules · /plan · /journal · /skill. Settings → Context shows you exactly what goes in.
Yes. Since v2.28.0 a theme is a file. In Settings → Appearance you create one or "duplicate and edit" any of the five built-ins (Solarized · Sepia · Nord · Dracula · High Contrast), then export and import as .json — the built-ins use the same format, so the built-ins are the examples. There is no separate preview box because the app itself is the preview: change one color and the sidebar, cards, borders and status colors change with it. Colors you did not change are left alone — a theme that sets five background lines is a complete theme, everything else inherits the light or dark default, and each token has "reset to family default". A theme that specifies no accent keeps the accent you chose, and on macOS "follow the system accent" paints it with the OS color. Bind a theme to a project in the project editor and only the window looking at that project wears it. An imported file gets a fresh identifier, and a name collision asks whether to overwrite or keep a copy. A theme can only paint a fixed list of colors, so nothing else in the file reaches the screen.
Yes. Since v2.27.0 every record carries a provenance badge — by hand (a person writing in the app) · by agent (Claude Code and friends following the journaling rules) · auto-draft (a background model after the edits went quiet) · schedule · watcher · MCP · backfill (restored from git history). It shows on journal cards and details and in the Today activity feed, and a filter row above the journal narrows by source — not drawn at all when the list has only one kind, because a filter that cannot narrow anything is noise. No new field was invented: it is read deterministically from the session id and agent id already written in the entry file.
Yes. Since v2.26.0, set an instruction and a frequency in Settings → Automation and the app runs it at that hour — "every Friday at 17:00, summarize this week's commits and open items", for example. Frequencies cover once, every N minutes, every N hours, daily, weekly, monthly, yearly and cron, and the first visit suggests three examples (weekly dev summary, morning briefing, monthly retro) switched off — you create them with a click and decide about enabling afterwards. Definitions are markdown files under .oculpm/automation/, so you can edit them by hand and commit them with your team. Automation is entirely opt-in, and if no background model is chosen it is skipped silently rather than erroring — so nothing bills you without your knowing. There is a daily run cap (20 by default), and the run log records skips, drops and failures with reasons, not just successes. If the Mac was asleep or the app was closed, it catches up exactly once instead of stacking up. Since v2.27.0 the trigger can be the working folder rather than the clock: when file changes go quiet for the interval you set (0.2s to 10min), it drafts a journal entry or updates the plan.
Yes. Since v2.9.0 one window holds several projects as tabs. Drag to reorder, or out of the window to make a new one, and since v2.22.0 you can drop a tab onto another window's tab strip to merge them back (the insertion point is shown while you drag, and moving the last tab closes the empty window). Since v2.24.0 it behaves exactly like Chrome — the moment a tab leaves the strip it becomes a real window that follows your hand off-screen and over other apps, and aiming at another window's tab strip hides the one you are holding and shows where it will land. Since v2.25.0 a torn-off window can be dragged back: a window with a single tab is picked up whole and merges when dropped (Escape puts it back where it was). Right-click a tab for the same operations from a menu, or Shift+F10 from the keyboard. Each project remembers its own screen, filters and sidebar state, and terminal sessions belong to the project, so they survive a tab moving between windows. ⌘T new tab, ⌘W close tab, ⇧⌘N new window.
The Today screen’s Copy standup turns yesterday-to-today into shareable text on your clipboard, and the Branch screen exports that branch’s commits, journal entries, plan items and changed files as a single markdown page. With an AI configured it polishes the result; without one it still works with the default format. v2.46.0 removed the Retro screen and the PR-body and weekly-report deliverables that lived on it — a large surface with little use.
Yes, in beta. Since v2.18.0 the Mac runs a server reachable only inside your Tailscale network, and a phone browser opens today's journal, the planner, discussions and the AI chat. Turn the server on in Settings → Mobile, scan the QR code and pair once with a six-digit code; add it to the home screen and it behaves like an app. The theme you picked on the desktop carries over (themes you built yourself follow only their light/dark family on the phone for now), and your AI API keys stay in the Mac keychain. During the beta, search and the code screen are desktop-only.
From v2.44.0 tool calls are drawn in 15 words of our own. When an agent records through the tool the app plants in your terminal, it reads as "wrote a journal entry"; editing a plan reads as "updated the plan" — all of which used to be a terminal icon and one line of shell. Reads, edits and searches fold into a single row, but approvals, errors and anything touching the record never fold, so the places that need your hand can't hide inside a collapsed group. Expand a row and the agent's raw exchange sits at the bottom, collapsed but always present — an escape hatch for when our classification is wrong. When reading a shell command, anything less than certain is drawn as a plain command: claiming "wrote a journal entry" wrongly would be a lie about the record. And "running" is distinguished from "don't know" — after 15 seconds with no signal it says "running · no signal".
Yes. A plugin hook judges each conversation as it ends, and if code changed with no entry it blocks the turn once and asks for the record (once per conversation). From v2.43.0 that verdict is per conversation. It used to be a single number — the newest journal timestamp anywhere in the project — so with several terminals open a neighbour's entry hid your own miss, and a neighbour's edits were read as yours. Counting the record showed the 164 signals were session segments, not sessions, and of 117 unique conversations only 2 had genuinely skipped. Now, with no grounds it says "undecided" rather than folding not-knowing into not-recorded. Claude Code and Codex conversations opened inside the app count too: judged at the end of each turn, announced once above the conversation, and cleared automatically when you record. In-app conversations and terminal agents recognise each other, so neither is blamed for the other's edits. One deliberate trade-off: while several conversations are alive at once the gate stays silent — rather than accuse the wrong one when attribution is not certain, it says nothing, and still records "undecided" in the ledger.
No. Everything lives in your project's .oculpm/ folder and a local SQLite cache. Exactly five things leave, and you start every one of them: the LLM API calls you make yourself, Claude Code and Codex inside the app (with a one-time adapter download the first time), the update check for new versions, a GitHub fetch when you ask for a plugin bundle, release notes or a theme (themes can also come from the oculpm.com gallery), a one-time embedding-model download when you first turn on semantic search. The v2.18.0 mobile bridge (beta) is the same — it is served only inside your own Tailscale network, never exposed to the public internet, and the phone talks to your Mac without passing through a third-party server.
They survive. Since v2.19.0 terminal sessions are held by a process detached from the app, so after an auto-update or a restart your terminal tabs come back with their scrollback and whatever was running (including Claude Code sessions). v2.34.0 and v2.34.1 were an exception: they renamed the socket where the app meets that process according to the version, so updating onto those builds cut the session loose once. v2.35.0 fixed it — the address no longer moves, the app adopts a process left at an older address, and every path that reaped a session-holding process within minutes is gone. Closing a terminal tab or a project tab yourself still cleans that session up as before. Since v2.35.0 a Claude Code conversation also shows its session id, so you can reopen the same conversation in a terminal with claude --resume <id>.
Yes. In Claude Code, run /plugin marketplace add bunhine0452/Ocul-PM and then /plugin install oculpm@oculpm — two lines that set up the hook bridge, 7 MCP tools and 5 skills across all your projects. Journals and plans accumulate as markdown under .oculpm/, and if you install the app later it reads those records exactly as they are.
Yes. Since v2.8.5, Settings → Appearance lets you pick English or 한국어 and every screen plus the error messages follow. The language the AI writes journals, discussions and planner items in is set separately, so an English UI with Korean records (or the reverse) is possible, and documents already written are never rewritten.
Yes. It is a free download from GitHub Releases — macOS (Apple Silicon), plus Windows (x64) and Linux (x64, AppImage and deb) as a beta. Once installed, later versions update themselves from inside the app (the Linux deb updates through your package manager). Intel Macs are on hold because the embedding runtime has no x86_64 prebuilt.
Yes, as a beta. Windows gets an x64 installer (.exe) for Windows 10 2004 (build 19041) or later and Windows 11; Linux gets an x64 AppImage and a deb. Linux needs glibc 2.35 or later (Ubuntu 22.04 · Debian 12 onward) plus libEGL and libGLESv2, and the AppImage needs FUSE 2. The Windows build is not code-signed, so on first launch SmartScreen shows "Windows protected your PC" — click "More info → Run anyway". If the Visual C++ runtime is missing, the installer installs it first. The Windows install and the AppImage auto-update in-app; the deb updates with the next .deb. Each release only ships the OS builds that passed an install test and a real-app E2E run on CI runners — a version that did not pass has no file for that OS. The beta label comes off once reports from real PCs accumulate.
Yes. Because Ocul-PM is local-first, adopting it on a team does not move your code or your records anywhere. For enterprise rollouts, team adoption and partnership proposals, write to bunhine0452@gmail.com — the person who built it reads and answers.
No sign-up, no setup, no fee. You download it, and that is the whole thing.
beta Windows x64 Linux AppImage Linux .deb
Plenty of developers move fast. The ones who remember go far.