Documentation
How to use pm7Code
pm7Code is a macOS workspace around the coding agent of your choice. This guide covers the agents it drives, how it talks to them, the layered prompt system, interactive questions, the background Git Service, and the rest of the workspace — so you can treat it as the reference when you need one.
Agents
Bring your own agent, hand sessions over
pm7Code does not ship its own model. You pick a coding agent per topic, and pm7Code is the workspace around it. Six native agents are supported, and you can switch between them or hand a running session over to a different one at any time. New topics default to pm7 unless you pick another agent.
| Agent | Runtime |
|---|---|
| pm7 | Same engine as Pi, plus a project-rules harness that refuses violating tool calls |
| Pi | Multi-provider coding agent, defaults to Google Gemini |
| Claude Code | Anthropic, via the Claude Agent SDK |
| OpenAI Codex | OpenAI's Codex |
| Cursor | Cursor's agent over ACP (Agent Client Protocol) |
| Grok | xAI's Grok over ACP |
One agent per topic
Each topic runs a single agent at a time. The topic, its transcript, prompts, and history live inside the project, not inside the agent.
Handover keeps identity
Switching agents mid-session keeps the same topic and the same conversation. Only the agent flips; the prior transcript is replayed to the new agent as a primer.
Guest turn
Prefix a message with @pm7, @pi, @claude, @codex, @cursor, or @grok to send that one turn to another agent without switching the topic's agent. Add + (@claude+) for the full session; without it, the guest sees recent context only.
Assistant
A second session beside your work
The right sidebar sits next to the workspace and shows one of three panes, chosen from the header: the Assistant — a full CView and Command Center bound to a single window-tab — the Browser (BView), or Files. The header groups the choice: Assistant with General, Project, and Topic sub-modes, plus separate Browser and Files buttons. Only one pane is visible at a time; the others stay mounted so their state survives when you switch.
The Assistant is not a seventh native agent. It is an ordinary topic session parked in its own column so you can keep a helper conversation open while you work in the main view.
General
The session you assign with Use as Assistant — the assistant for pm7-Code as a whole, independent of whichever topic is active in the main view. Right-click a topic in the sidebar and choose Use as Assistant; choose Stop Using as Assistant to clear it. On every non-silent turn it receives a compact [General Assistant] context block (Prompt Context, source Active): a source map of Settings surfaces — prompts, protocols, skills, slash commands, app themes, colors, agents, and other Settings screens — not a dump of secrets. Topic and Project assistants take priority over General; a bound assistant never holds two roles at once. Cmd/Ctrl-click the Assistant splitter to equalize the main column and Assistant widths.
Project
Follows the active project in the main view and shows that project's own assistant. Switch projects and the column switches with you; switching topics within the same project leaves it in place. Start it with Start Project Assistant when none exists yet.
Topic
Follows the active work topic and shows that topic's own assistant. Switch topics and the column follows. A topic assistant is never created automatically — click Start Topic Assistant when you want one.
Hidden from the sidebar by default
The topic used as General assistant stays out of the project list unless you turn it on. Use the Show menu in the Projects sidebar and enable Assistant to list it alongside your regular work.
Files pane
The Filesbutton opens a lazy folder tree of the active project's Project.cwd— the project folder, not the active topic's worktree path — so the tree does not jump when you switch topics. The header shows the real path on disk.
Engine reads, no local fallback
Listing and preview go through the project's engine (filePreview bridge). If the engine is unreachable, the pane shows an error with Retry — there is no silent read from your local disk, which would be wrong for remote projects.
Click to preview
Click a file to open the existing preview overlay (previewType: 'project-file'). Without an active topic tab the pane shows Open a topic to preview files. and files are not clickable.
Lazy tree and paging
Folders expand on demand. Each loaded page holds up to 500 names alphabetically from the engine; Show N more loads the next page. Folders sort before files within each loaded page. There is no file watcher — use the refresh button in the header to clear the cache and reload expanded folders. Switching projects resets the whole tree.
Shortcuts
Cmd+Shift+B from Files switches to the Browser (from Assistant it still toggles Browser as before). Panes stay mounted so expanded folders survive when you switch to Assistant or Browser and back.
Protocol
How pm7Code talks to any agent
pm7Code needs every agent to behave consistently — to ask questions the same way, hand off Git the same way, and respect the same conventions. It does this by injecting its own protocol into each agent through that agent's native system-prompt channel, built once when the agent starts.
One source of truth
The protocol is assembled in a single place and shared by every agent. Change it once and all agents pick it up.
Each agent's own channel
Claude Code gets an appended system prompt, Claude CLI gets an appended-system-prompt flag, and Codex gets developer instructions. Same protocol, agent-native delivery.
Independent by design
pm7Code does not rely on a global CLAUDE.md or AGENTS.md to make its own features work. Remove those files and pm7Code still behaves the same.
Vocabulary
Topic, session, agent session
Three words come back everywhere in pm7Code, and the difference between them decides when a prompt is sent. Read this section once and the rest of the prompt documentation falls into place.
| Word | What it is | How long it lives |
|---|---|---|
| Topic | A subject you work on, with its own settings, prompts, notes, and history. Every row in the sidebar is a topic. | Stays. A topic survives closing the app, and it survives starting a new session about it. |
| Session | One conversation about a topic, identified by its own PM7 Session ID. It starts with your first message and runs until you start a new one. | Ends when you pick Start a new Session. The topic stays exactly where it is; the conversation gets a clean context window and a new PM7 Session ID. |
| Agent session | The technical connection behind a Session: the agent process, its resumable native session ID, and its provider transcript on disk. | Comes and goes on its own. An agent session can sleep and resume without you noticing. Switching agents replaces it while the Session continues. |
A topic is a place
Rename it, give it a note, set its prompts, pin it, archive it. All of that belongs to the topic and outlives any single conversation.
A session is a conversation
Start a new Session when the context window is full or the conversation drifted. You keep the topic and its settings, and you get a fresh conversation with the topic context restated.
An agent session is plumbing
An agent session is the provider connection behind your Session. It can sleep, resume, or be replaced when you switch agents without changing the PM7 Session ID.
Prompt context
The pm7Code prompt flow
pm7Code does not treat every prompt field as the same thing. Some prompts describe stable behavior, some describe the current project, some describe the topic you are working on, and some are temporary instructions for one turn.
User prompt
What you type now
Every message prompt
Added to every message in this topic
Interview prompt
Only when Interview-me is active
The practical result is simple: the first message of a session carries the inherited context down to and including the Topic prompt, and every message after that stays lean — what you typed plus the Every message prompt.
Inheritance
Lower layers can override higher layers
Each layer inherits from the layer above it. When a lower layer enables an override, that value becomes the effective prompt for everything below it. If the override is enabled but left empty, that prompt is intentionally disabled for that branch.
| Prompt | Edited in | When it is sent | What it controls | Override rule |
|---|---|---|---|---|
| System | App / global settings | Once, with the first message of a session | Stable behavior for the agent: how it should work, communicate, and respect pm7Code conventions. | Can be overridden by Group, Project, and Topic. An enabled empty override disables the System prompt for that lower layer. |
| Group | Group settings | Once, with the first message of a session | Shared context for a customer, team, business area, or collection of related projects. | Can be overridden by Project and Topic. A Project override becomes the Group prompt inherited by the topics in that Project. |
| Project | Project settings, stored as Project-prompt.md in the project folder | Once, with the first message of a session | Project-specific operating manual: goals, architecture, conventions, constraints, and important decisions. | Can be overridden by Topic. This lets a single topic suppress or replace project context. |
| Topic | Topic settings, Prompts tab, Topic field, stored as Topic-prompt.md | Once, with the first message of a session | What this topic is about: the assignment, the relevant files, the goal you keep working toward. It travels with the startup context of every new session about the topic. | The lowest inherited layer. A topic can also override the System, Group, and Project prompts it inherits. |
| Every message | Topic settings, Prompts tab, Every message field, stored as Every-Message-prompt.md | With every message you send in this topic | A standing instruction you want repeated on every turn, such as a house style or a language rule. Keep it short — it costs tokens on every single message. | Not inherited by anything. It only applies to this one topic. |
| Interview | Interview-me toggle | Only on the turn where Interview-me is active | A temporary follow-up mode that makes the agent ask questions before it acts. | Not inherited. It is a one-turn suffix that switches off again after sending. |
Injection
When prompts are sent with your message
A session opens with startup context and then stays lean. The first message carries System, Group, Project, and Topic; every message after that is just what you typed plus the Every message prompt.
First message of a session
Startup context- 1Effective System prompt
- 2Effective Group prompt
- 3Effective Project prompt
- 4Topic prompt
- 5User prompt
- 6Every message prompt
- 7Interview prompt, only when Interview-me is active
Every message after that
Normal turn- 1User prompt
- 2Every message prompt
- 3Interview prompt, only when Interview-me is active
Startup context is one-shot
System, Group, Project, and Topic go out together with the first message of a session, and then not again. Start a new session and they are sent once more.
Every message is per turn
The Every message prompt is appended to every single message you send in this topic, in every session. Use it only for instructions that must genuinely be repeated.
Interview is optional
The Interview prompt is added only when Interview-me is active. It is meant for guided follow-up questions and switches off again after one turn.
Overriding it for one message
The prompt-mode button next to the input field decides what rides along with the next message. Grey sends your text without any prompt context, black is the normal turn, and blue forces the full startup context — handy right after you changed a Project or Topic prompt and want it applied immediately. Grey and blue spring back to normal after one message; the struck-out state stays silent until you change it back.
Settings
Where to edit and inspect prompts
Group, Project, and Topic settings all have a Prompts tab. Each tab shows the prompt layers that make sense at that level. Project and Topic prompts are plain .mdfiles under the project's Folder — see Prompts live in your repo — in pm7-Code/<Project-UUID>/. There is no separate Home folder setting; only Folder remains in Project settings. If the Folder is unreachable, the window reports that the prompt files could not be read, or shows “Loading from the Project folder…” while it tries.
Group settings
Use this for context shared across projects in the group. Group can override System.
Project settings
Use this for project-specific context. Project can override System and Group.
Topic settings
Use this for the subject you are working on. A topic can override the inherited startup context and adds its own Topic and Every message prompts.
Suggest prompt
The Suggest prompt button uses a hidden background request to propose a prompt for the exact field you are editing. It can use the current folder, inherited prompts, project files, git context, and recent topic history. The result appears in a preview with Original prompt, User prompt, and Suggested prompt tabs before you replace or append anything.
CView makes prompt injection visible
When pm7Code injects prompt context, CView shows a Prompt Context card. Each row is labeled with its kind and source: Inherited, Group override, Project override, Topic override, Disabled, Topic, Every message, or Active. Open the card and you see the exact text that went out with that message.
Language Directive
Settings → Agents → Prompts includes a Language Directive (ccLanguageAppend) that is appended on every turn. The default requires Dutch for all user-facing prose. Established English technical and IT terms stay English — the default no longer coins Dutch calques for words like watcher, child process, listener, or thread. Accounts that pinned the older default via Reset to default are upgraded automatically when pm7Code detects the stale copy.
Storage
Prompts live in your repo, as files
Project and Topic prompts are not stored inside pm7Code once a project has a Folder. They are plain .mdfiles under the project's Folder, in pm7-Code/<Project-UUID>/, so they are versioned, reviewed, and shared through git like any other file. The settings windows read and write exactly those files.
<Project folder>/pm7-Code/
<Project-UUID>/
Project-prompt.md the project layer
System-override.md project overrides the System prompt
Group-override.md project overrides the Group prompt
System-Copy.md generated reference copy, overwritten
Group-Copy.md generated reference copy, overwritten
<Topic-UUID>/
Topic-prompt.md the topic layer
Every-Message-prompt.md sent with every message
System-override.md topic overrides System
Group-override.md topic overrides Group
Project-override.md topic overrides ProjectThe two *-Copy.md files are different from the rest: pm7Code writes them but never reads them. They exist so you can see in the repo which System and Group prompt apply to this project — the source of truth is the app itself. They are refreshed when the app starts and when a prompt changes, so they can briefly lag behind. Editing them is pointless: every edit is overwritten.
No file
The layer is not set here, so the layer above it applies. Clearing a prompt deletes its file; a prompt you never set never creates one.
File with text
The text in the file is the prompt. It is read fresh while your turn is built, so an edit in your editor counts from the next message.
Empty file
Zero bytes means the layer is deliberately switched off for this project or topic — the same as an enabled but empty override.
Interaction
Agents ask you structured questions
When an agent needs a decision from you, it does not bury the question in a wall of text. pm7Code renders a structured question card with numbered options and an always-present free-text field, right inside the transcript. You answer in the workspace and the agent continues from your choice.
Numbered options
Each question offers a short set of options, numbered by the UI. Pick one, or several when the question allows multiple answers.
Always room for nuance
Every question has an optional text field underneath, so you can add detail, combine it with a choice, or answer in free text alone.
The agent waits for you
Your selection and any free text are returned to the agent as your answer. Nothing is assumed on your behalf while it waits.
Second opinion
Neighbor-agent review
Neighbor-agent review is an optional per-topic setting. When it is on, the active agent consults a designated buddy agent before asking you a question, and presents both recommendations so you can decide with a second opinion in front of you.
Off by default
Review is a per-topic toggle and starts off. Nothing consults a second agent unless you turn it on for that topic.
Both views, clearly labeled
The question card shows the active agent's preference and the buddy agent's recommendation as separate, clearly attributed advice.
Applies from the next session
The setting is baked in when the agent starts, so toggling it takes effect from the next session — like the model and permission settings.
Keyboard
Stopping a turn from the Command Center
When the agent is in the middle of a turn, pressing the Escape key while the Command Center input has focus stops the active turn. This is the same action as the Stop current turn button in the CView title bar.
Only stops a running turn
Escape only sends the stop signal when the agent is actively thinking, running a tool, or compacting. If no turn is in flight, the keypress is forwarded to the terminal as a normal Escape, so terminal apps and TUIs keep working as expected.
Works for every agent
The shortcut calls the matching interrupt API for the active agent: Claude Code, Claude CLI, OpenAI Codex, Pi, Cursor, and Grok. The topic, its CView history, and the underlying agent-session ID are preserved.
Nothing gets closed by accident
Escape only interrupts the current turn. It never ends the session and never archives the topic. Use the CView title menu when you want Start a new Session or Archive.
Status resets immediately
After the interrupt is acknowledged, the CView status returns to idle and the stalled indicators are cleared, so you can send a new prompt without waiting for the agent to wind down.
Projects sidebar
Filter by project, group, or topic
The Projects search field supports qualifiers when you want to narrow the sidebar before applying normal text search. Use this when a word appears in many projects but you only want results from one exact project, group, or topic.
Exact qualifier names
The value after project:, group:, or topic: must match the full name exactly. Matching is not case-sensitive.
Quotes for spaces
Wrap names with spaces in quotes, such as project:"this is a test".
Text still narrows results
Any remaining words are used as the normal search text inside the qualified scope.
| Search | Result |
|---|---|
| project:pm7-code | Shows only the project named pm7-code. |
| project:pm7-code test | Shows only matching topics inside pm7-code, and searches for test inside that project. |
| project:"this is a test" | Shows only the project whose exact name is this is a test. |
| group:clients waiting | Shows matching projects and topics inside the group named clients. |
| topic:"Release notes" deploy | Shows only the topic named Release notes when it also matches deploy. |
| p:pm7-code g:internal t:debug css | Combines the short forms and then searches for css inside the narrowed result set. |
Short forms
project: can be written as p:, group: as g:, and topic: as t:. Long and short forms behave the same way.
Detail search uses the same scope
When you open the topic-detail search from the Projects search field, the same qualifiers restrict which topics are searched.
The filter bar and Show menu also include a Push to master squeeze — see Push dots and filter.
Git Service
How the Git Service works
At the end of a coding turn, an agent can emit a fenced GitServiceRequest block. pm7Code detects that block in the conversation, creates a visible Git Service card, and enqueues a background Git job in the main process.
The important detail is that the request is asynchronous from the user's point of view. Once pm7Code has accepted the request, the agent is free to continue with testing, inspection, or the next task while commit and push run separately.
The agent finishes files
The agent records the exact files it changed in this turn.
pm7Code enqueues Git
The app validates the request and creates a background job.
Work continues
The Git Service commits and pushes while the session can move on.
User interface
What you see in pm7Code
A GitServiceRequest appears as a dedicated card in CView. The card shows the job state, the repository path, the files included in the request, and any error returned by the Git Service.
| Status | Meaning |
|---|---|
| Queued | pm7Code accepted the request and placed it in the Git Service queue. |
| Validating | pm7Code checks the exact files, working tree state, and request shape before touching Git. |
| Staging | Only the files listed in the request are staged. Directories and broad pathspecs are rejected. |
| Committing | The Git Service creates the commit with the message supplied by the agent. |
| Pushing | The commit is pushed while the agent can already continue with other work. |
| Succeeded | The requested files were committed and pushed. |
| Failed | The request was stopped before completion. The card shows the reason and no hidden retry is performed. |
Safety
What pm7Code protects
The Git Service is intentionally strict. It is designed to commit exactly the files from the finished turn and to stop when the repository state no longer matches the request.
Exact file list
The request must list concrete files. Directories and broad patterns are rejected so an agent cannot accidentally commit unrelated work.
Snapshot validation
pm7Code records the file state when the request is enqueued and checks that the same content is staged before committing.
No mixed staged work
Existing staged changes that do not belong to the request are a reason to fail, not a reason to create a mixed commit.
Visible failures
Conflicts, drift, hook failures, and push errors are surfaced on the Git Service card for the user to inspect.
Advanced
GitServiceRequest format
Most users do not need to write this block by hand. pm7Code's agent protocol emits it automatically at the end of a turn when files changed. Advanced users and agent authors can use this shape:
```GitServiceRequest
{
"cwd": "/Users/patrickmast/Dev/pm7-code",
"files": [
"site/app/docs/page.tsx",
"site/app/page.tsx"
],
"scope": "docs",
"commitMessage": "docs: document Git Service"
}
```Memory
pm7Code remembers, independent of the agent
pm7Code keeps its own memory of durable facts, preferences, and conventions. Because the memory belongs to pm7Code and not to any one agent, it works the same whether you are running Claude Code, Codex, Pi, Cursor, or Grok — it is not tied to a single agent's own memory store.
It mirrors the Git Service model: the agent only proposes a memory write, and pm7Code is the authority that decides what is actually stored. A proposal shows up as a Memory card in CView with its own status, just like a Git Service card.
Agents propose, pm7Code decides
During a turn an agent can emit a memory proposal. pm7Code validates and stores it; the agent never writes memory directly and never needs to know where it lives on disk.
Scoped and typed
Items are scoped globally or to a project, and typed as a user fact, feedback, project note, or reference. The most important items can be pinned so they are always in context.
Recalled automatically
At the start of a session pm7Code injects a small primer of pinned essentials, and each turn it weaves in a few concise, relevant memories with source links — for whichever agent is active.
Private and local
Memory is stored locally on your Mac. Suspected secrets are screened out and rejected, and project facts are never automatically promoted to your global memory.
Skills
Write a skill once, every agent uses it
A skill is a small, reusable instruction pack that teaches an agent how to do a recurring job the way you want it done — how you deploy, how you sign a macOS build, how you write a release note. It follows the open Agent Skills standard: a plain SKILL.md file with a name and a description, optionally alongside supporting scripts and assets.
The problem skills usually create is duplication. Every agent keeps its own copy, so the same skill has to be written and maintained several times. Copies drift apart, and you end up tied to whichever agent happens to hold the good version. pm7Code removes that entirely: it keeps one canonical store and shares it with every agent automatically, so a skill written once works the same across all of them.
The same mechanism covers three kinds of instruction pack: skills (used by every agent), plus guides and commands for the agents that understand those concepts. And it no longer stops at one machine: your set lives in your account and follows you to every machine and server you run agents on — see Available everywhere below, and Under the hood for what it does on disk.
One canonical store
Your skills live in a single place that pm7Code owns. There is one copy to edit, one source of truth, and nothing to keep in sync by hand.
Automatic fan-out
pm7Code shares each skill into every agent it detects, just before an agent starts and once when the app launches. You never have to push or copy anything.
Open standard, any agent
Because skills use the open Agent Skills format, every agent that follows it picks them up unchanged. Today that means Claude Code, Codex, Pi, and Grok — new compatible agents work on day one.
Create or edit a skill
Add a skill in Settings, or edit its SKILL.md. One copy, in one place.
pm7Code shares it
The skill is linked into every detected agent automatically — no manual copying.
Every agent uses it
Claude Code, Codex, Pi and Grok all see the same skill the next time they run.
Manage skills in Settings
Settings has a Skills tab where you create, edit, and delete skills. Each skill shows a short description, how many files and how much content it holds, and a set of badges for the agents it is currently live in. Editing opens the SKILL.md in place; a Resync button re-shares everything on demand if you ever want to trigger it yourself. The same tab has an Account sync section to push your local set up to your account and pull it back down.
Agents can propose publishing a skill
Your account is the source of truth for skills. A change that only exists on disk in ~/.pm7-code/skills is treated as drift and is overwritten the next time pm7Code pulls from your account or rematerializes skills at session start.
When an agent creates or edits a skill and wants that change to last, it proposes a publish. CView shows a Skill Publish card with Publish and Reject. Agents never push to your account themselves — only the approved card path does.
Agent proposes
The agent asks to publish a skill. pm7Code snapshots it immediately — not whatever happens to be on disk after later session rematerializations.
You decide
Publish or Reject on the card. Publish uses that snapshot. If the account version of the skill changed since the proposal, publish is refused — ask the agent to propose again on the current version.
Outcome sticks
After Publish or Reject, the card keeps its outcome across reloads: published (with file count and whether it came from the snapshot or disk), rejected, or failed with an error.
Snapshot at proposal time
pm7Code stores the skill under skill-sync/staged/ the moment the agent proposes it, because the local skills folder is an account cache that can be rebuilt before you click Publish.
Invalid frontmatter is refused
If SKILL.md YAML frontmatter does not parse, publish is rejected so a broken skill never reaches your account.
Available on every machine you use
pm7Code can run your agents on more than one machine — your Mac, a home server, a remote box over Tailscale. A skill only helps if it is physically present wherever the agent runs. So your set lives centrally in your account, and every machine materializes the current version automatically, right before an agent starts. You write it once and it is simply there, everywhere.
Push to your account
In Settings → Skills → Account sync, push your local skills, guides and commands up to your account. That is the single source of truth.
Connect to any machine
Open a topic on any machine you use. Just before the agent starts, pm7Code makes that machine's set match your account.
The agent has it
The skill is in place before the first prompt runs — no copying, no per-server setup, no drift between machines.
Does nothing when nothing changed
Each machine compares what it already has against your account and only writes what actually differs. Connect ten times with an unchanged set and nine of them do no work at all.
Delete travels, safely
Remove a skill from your account and it disappears from every machine on the next connect. pm7Code only ever removes what it put there itself — a skill you made by hand on a machine is never touched.
An agent's own skills win
If a machine already has a real skill of the same name that you installed directly, pm7Code leaves it alone. It never overwrites an agent's existing work to force its own copy in.
Under the hood
Everything above is deliberately boring machinery. If you ever need to inspect it, repair it by hand, or reason about why an agent does or does not see a skill, this is exactly what happens on disk.
Where things live
~/.pm7-code/skills/- — the canonical store. One directory per skill, each with a
SKILL.md. A directory without aSKILL.mdis ignored. Sibling foldersguides/andcommands/hold the other two kinds. ~/.claude/skills/,~/.codex/skills/,~/.pi/agent/skills/,~/.grok/skills/- — the fan-out targets. An agent counts as installed when its config directory exists; only then is anything written for it. Guides and commands go to
~/.claude/guides/and~/.claude/commands/only, because Claude Code is the harness that knows those concepts. ~/.pm7-code/skill-plugin/- — a generated plugin that carries the same store into Claude Code agent sessions. See below for why it exists.
What sharing actually does
Nothing is ever copied. Each target gets one absolute symlink per skill, pointing back at the canonical directory — on Windows a directory junction, which needs no administrator rights. The agent reads the same files you edit, so there is no second copy that can drift. Four rules keep it safe to run at any moment: a real folder at the target is never overwritten; a link that is already correct is left alone; a link that points at the wrong place is recreated; and a link into the store whose skill no longer exists is removed. Symlinks pointing somewhere else entirely — skills you installed yourself — are never touched. Every step is best-effort, so one unwritable directory cannot break the rest or block a session.
Why Claude Code gets a plugin
Claude Code only reads ~/.claude/skills/ when a agent session loads the user settings scope — and pm7Code deliberately does not load it, because that same scope would also inject your global CLAUDE.md into every single prompt. Rather than trade prompt weight for skills, pm7Code projects the canonical store a second time into a small local plugin and hands that to the Claude Agent SDK. Plugins are loaded independently of the settings scope, so skills keep working no matter how that setting is tuned. The projection uses the same links as every other target, so it is still one copy on disk, and it is rebuilt just before each agent session — a skill added while the app is running is picked up by the next agent session without a restart.
When it runs, and in what order
Sharing runs once at app launch and again just before every agent session starts, on whichever machine that session runs. There are two layers and they stack: layer 2 makes the machine's canonical store match your account — pulled on connect, and only where content actually differs — and layer 1 then fans that store out to the agents on that machine. Local and remote engines share the same code path, so a Mac, a Linux server and a Windows box behave identically.
Never overwrites your work
Sharing only ever adds links into an agent. If a real skill folder already exists there, pm7Code leaves it untouched rather than replacing it.
Edits are lossless
Editing a skill only changes what you changed. Supporting files — scripts, templates, assets — are preserved exactly, so nothing is lost on save.
Never blocks an agent
Sharing is best-effort and runs in the background of a start. If anything goes wrong, the agent still launches normally; skills can never get in the way of work.
Yours, on your Mac
The canonical store is a normal folder on your machine. You can browse it, back it up, or keep it in version control like any other project asset.
Workspace
Panels around the transcript
Beyond the terminal, the prompt, and the agent transcript, pm7Code adds a few panels that keep the rest of the dev loop in the same window.
Assistant column
A second CView and Command Center in the right sidebar — General, Project, or Topic mode — so a helper session stays open beside your main work. See Assistant column.
Browser View
One of three right-sidebar panes (with Assistant and Files): an in-app browser with its own tabs. Open the deploy you just shipped or your local dev server without leaving the workspace.
Files pane
The third right-sidebar pane: a lazy folder tree of the active project's directory (not the topic worktree). Browse and preview project files via the engine. See Files pane.
Live Summary
Independent summary slots distill a long run into its gist as it happens. Each slot has its own provider and model, so you can scan progress without reading every line.
Remote engines
Route a topic through a remote websocket motor so the agent runs on another machine, while the workspace, transcript, and live status stay local.
Title bar when window tabs are hidden
If the window tab bar is turned off, the build and agent-version status pill moves into the title bar so a running build stays visible. The tab switcher there shows the current window-tab title (ellipsis when long) with a chevron instead of the static label TABS — click to pick another tab.
Connections heal themselves. Switching networks — wifi to 5G, a hotspot, a VPN coming back — removes the old path without telling either side, so a socket can keep reporting that it is open long after nothing gets through. pm7Code therefore does not trust appearances: every connection is checked with a heartbeat. A dead one is spotted within about half a minute, and right away when you return to the window. Agent sessions reattach and replay what you missed; the terminal opens a fresh connection. Nothing to reload.
pm7code command below, or over the WebSocket API.Sessions
Worktree isolation and path claims
Writing sessions can work in their own git worktree instead of editing the project checkout directly. That keeps parallel sessions from stepping on each other, and landing back onto the base branch is an explicit step in CView.
Project setting: Worktree isolation
Open Project settings and choose Worktree-isolatie. The choice is stored in <project>/.pm7-code/project.json and applies to that project only. Default is always.
Uit — write in the base checkout
Agents edit the project folder directly (the old behaviour). No per-session worktree.
Opt-in — choose per new session
When you start a New Session you pick whether to work isolated or do a hotfix in the base checkout.
Altijd — writing sessions isolated
Writing sessions automatically get their own git worktree (default). You can still tick Hotfix direct in basis for a one-off edit in the project folder.
Starting and landing a worktree session
An isolated session runs on a branch under the pm7/s- prefix, in a separate worktree path. The agent is told to stay inside that worktree; landing (merge back to the base branch) happens through the app, not via git worktree or push from the agent.
New Topic and New Session options
When the project's isolation is always, the new topic form shows Hotfix direct in basis — geen eigen worktree voor dit topic so you can opt out of a worktree before the first session starts. When isolation is on, the New Session menu can show Hotfix direct in basis (always mode) or Geïsoleerd werken (eigen branch) (opt-in), plus optional Taak / intent and Claim-paden (comma-separated globs such as src/auth/**). Those Hotfix / worktree / claim / intent choices are applied for every agent (Claude, Codex, Pi, and the others) — not only Claude. Start a New Sessionstays disabled while the project's worktree isolation setting is still loading (tooltip: Worktree-instelling van het project laden…), so you cannot miss the Hotfix checkbox when it appears a moment later. The Hotfix row is a plain checkbox — no plus icon.
Worktree status control (title bar + CView)
When the active session runs in a worktree, a small coloured status dot appears, using the same colour scheme as the project git dot so a colour always means the same thing: gray while loading or when none applies; blue spinner while an action runs; red for a git error (for a worktree: an interrupted Sync that needs recovery); brown when behind the reference (origin for the project, master for the worktree), meaning pull or sync first; orange for own work not shared yet (uncommitted changes, or commits not yet pushed / merged); green when clean and even. Own work wins over behind; tooltip and menu still list every state. The primary placement is the title bar, immediately after the topic name (before in and the project name). The same control also appears in the CView header. Tooltip and aria-label show the branch, comparison against the live base branch name, dirty/ahead/behind counts, and a stale note when the server is unreachable. Click opens the worktree menu (outside click and Escape close it). The menu header is one line: the branch name. Below the header and file lists, a notice appears when the agent is still working on a turn — Sync and Push stay disabled until the turn finishes, because Sync temporarily resets the working tree. Then a separator, then Push as the first action (upward-arrow icon): when the file count is known the label reads Push N file(s) to {baseBranch}; otherwise Push to {baseBranch} (not hard-coded to master). Push only when there is own work to land. Another separator, then Sync with {baseBranch} (with a file count when known), Preview changes, and Clean up…when there is something to prune — the item is omitted entirely when the prune list is empty. Clean up opens a disposable-worktree list (disk-usage warning above ~5 GB; confirmation when unmerged commits or dirty files would be lost). Preview changes opens the git-diff overlay for the relevant file list (Changes vs {baseBranch} for own work, or Incoming from {baseBranch} via Sync when behind only). Preview is disabled when the shown list is empty.
Push to base: local first, then GitHub
There are three places: the topic worktree, the local base checkout (the source of truth) and origin on GitHub (its mirror). Push first pulls origin into the local base branch (plain merge, never a stash; a conflict aborts before anything changed), then merges the worktree, then pushes best-effort. Is the worktree behind, Push syncs first by itself (uncommitted work is snapshotted and restored on conflict) and then merges; it stops only on a real conflict, naming the files, with your work untouched. That stop offers Resolve with agent: it sends the conflict to the topic's own agent as a task (merge the base, resolve by hand, keep your intent, do not push); you push afterwards. A failed push keeps the local merge and shows a warning; the next build fetches, builds from the local base and pushes again. While a CORE build is running on that checkout, Push waits for it to finish. A successful merge lands commits onto the base branch but does not remove the worktree. The worktree resets to the new base HEAD (fast-forward for a normal merge; squash commit for squash), stays active, and baseCommit updates so you can merge again after more commits. The status dot stays. Only Clean up… removes a worktree by hand. Automatically, a worktree is only released when its topic is parked (or falls asleep after 48h idle), archived or deleted, and only when nothing would be lost: no uncommitted or untracked files, no commits the base lacks, and no seeded ignored file (such as .env) that differs from the base. Otherwise the worktree stays, marked sleeping, and shows up in Clean up…. A parked topic that was released gets a fresh worktree when it resumes. Prune refuses without confirmation when the branch still has commits the base lacks or there are uncommitted changes.
Start new session here — blocked on unmerged work
Start new session here (replace the topic in place) is refused while the topic worktree still has work Push would take, or while the engine cannot judge (pending: null) or is unreachable. The dialog title is Unmerged work in this topic; only OK — there is no Continue anyway, because a fresh session would mint a new worktree and hide the old one. Proceed via Push to {baseBranch} in the worktree menu, or use Fork Topic / New topic to start beside it. Topics that run as a hotfix in the base checkout skip the check. The guard uses a fresh engine round (same definition as the orange Push dot), not the polled sidebar state.
Push dots and filter (Projects sidebar)
Topics with an isolated worktree can show a small orange indicator on the topic row. It uses the same definition as the Push to {baseBranch} action: files the worktree changed that still differ from the live base — not merely dirty or ahead on paper.
Orange dot on the topic row
The dot replaces the agent icon on the left of the row — not a badge in the corner. A filled orange dot means the engine judged there is work Push would take. A hollow orange ring means the engine could not judge (pending: null) — never treat unknown as clean. The topic row also gets an orange border: solid when work is pending, dotted when unknown. If the topic is missing from the poll, there is no live worktree (landed, discarded, or cleaned up) and no dot appears. When the server is unreachable, the last known state stays visible but muted (stale).
Push filter in the sidebar
The Show menu and filter bar include a Push section — Push to master — narrows whatever you picked — with the same orange dot icon (not an emoji). Push is an AND squeeze like Today, not an OR status filter: Push + 🔔 shows only waiting topics that also need push. Enabling Push auto-reveals parked topics that still need push, the same way typing a topic name would.
Visualise Worktrees (project and server menus)
Open Visualise Worktrees from the project menu (project name in the CView header or title bar, next to Project Settings) or from the server menu (server name in the title bar). This is not in the worktree status menu. It works even when the current session has no worktree; if it does, that card is highlighted. The dialog stays within the viewport; the body scrolls when there are many worktrees.
Project menu scope
Shows only worktrees whose session belongs to that workspace project. The engine registry is keyed per folder and multiple workspace projects can share one folder, so the client filters here. Worktrees filtered out are counted, not silently omitted: N van andere projecten in deze map and, separately, N zonder bekend topic (deleted or unknown sessions).
Server menu scope
Shows all registered worktrees on that engine — one block per base checkout, with workspace project names that live on that base in the block header. Each card can show its project name. Only worktrees pm7-Code created appear in the registry.
Worktrees dialog
Title Worktrees. The base checkout is a card on top (label Basis). Live worktrees appear as cards below — current session first and highlighted, then active, sleeping, and orphaned. Landed and discarded worktrees sit under a collapsed Historiek section. Refresh reloads from the engine. By default only worktrees with work Push would take are shown (same judgement as the sidebar orange dot), plus any that could not be judged (hollow ring). A checkbox Ook worktrees zonder werk om te pushen tonen reveals the rest; when some are hidden, the UI shows N verborgen. Live and history worktree cards are clickable when this client knows the session: click jumps to that topic, closes the dialog, and reveals it in the sidebar. If the topic is archived (typically landed or discarded), the app opens the read-only archived preview — it does not silently restore it. Cards whose session this client does not know stay inert (tooltip: topic unknown).
What each card shows
Session name (or branch as fallback), branch, orange ↑N / brown ↓N file pills (same meaning as the worktree menu), state badge (actief, slaapt, verweesd, geland, verworpen), and a Preview button. On server scope, the project name appears on the card. Missing checkouts show why git status is unavailable. Clickable cards show a tooltip hint to open the topic or view the archived preview.
Path claims across sessions
Sessions can register which paths they intend to edit so others see the overlap early. Agents emit a fenced pm7_claim block with paths and a human-readable summary; empty paths release the claim.
A claim is a signal, not a lock. Another topic can still edit a claimed file; nothing is blocked and nothing is enforced. What a claim does is make the intent visible: the other agent gets the active claims in its context so it can steer clear or coordinate, and the human sees the overlap strip. Each worktree is its own checkout, so two topics editing the same file only meet at merge time.
Overlap strip
If your paths overlap another session's claim, CView shows a warning strip naming that session and the overlapping paths.
Release
Each overlap row has a Releasebutton that clears the other session's claim without waiting for it. Your own claim is released from the worktree status menu, or automatically when the topic closes.
Intent banner
Optional intent text from a New Session (or another session) can show as a short banner — coordination context, not a hard claim.
WebApp
App Themes
On the WebApp, an App Theme is a named profile that bundles the full look and layout of one device: the active color theme, fonts, visual style, panel visibility, and sidebar and Assistant column open state and width. The macOS desktop app does not use App Themes — there, account settings remain the source.
Each device remembers its own active theme. The theme definitions sync with your account, so the same profiles are available on every WebApp device you use. There is always one active theme; a Default theme is created from your current look the first time you need it.
Settings → Appearance
Create, rename, duplicate, and delete App Themes. Save current as App Theme captures everything the profile covers. Switching themes applies the full bundle immediately.
What travels with the theme
Color theme choice (including window-tab colours such as Active Tab Background), all font settings, CView frame mode, tab bar style, shadows, Browser border color, scrollbar visibility, terminal full-height mode, tab bar visibility, CView tool visibility, and sidebar and Assistant column layout.
URL preview
Add ?theme=<id-or-name> to the URL to preview a theme in that window only. The device choice is untouched; picking a theme in Settings clears the override.
Settings
Settings Backup
Settings → General has a Settings Backup section where you point pm7Code at a private Git repository. Every export saves your settings to your account first, so the backup always matches what you see in Settings, then commits and pushes every setting, prompt, memory, skill, and theme to that repo.
GitHub Repository
Enter a remote URL the engine can already push to — SSH key or gh credentials, never a token in the URL. The repo is cloned once, then updated on each export.
Export Now
Runs an immediate export and push. If secret findings block the export, review the list and choose Export anyway to override.
Automatic Backup
On by default when a repository URL is set. Exports automatically shortly after every settings save. Secret findings always block automatic exports; only Export Now can override them.
CLI
The pm7code command
pm7codeis a one-shot, scriptable entrypoint that ships with pm7Code. Give it a prompt and it runs a single turn, prints the agent's final answer to stdout, and exits — the same shape as claude -p, but through the pm7Code motor. Pass the prompt as an argument or pipe it on stdin.
# One-shot against the local motor (auto-started if needed)
pm7code "summarize what changed in this repo"
# Pipe the prompt via stdin, run it through Codex
echo "write a haiku about pty buffers" | pm7code --agent codexWhen you point it at a loopback address and nothing is listening yet, the CLI starts a local motor for you and reuses it on the next call. Pointed at a remote address, it never spawns anything — it only connects.
# Machine-readable output for scripts/CI: NDJSON events + a final result line
pm7code --json --agent claude "list the open TODOs"
# Continue the most recent session in this folder, then resume an exact one
pm7code --continue "now add tests for it"
pm7code --resume <pm7SessionId> "follow up on that"
# Talk to a remote motor over your tailnet (paired device + opt-in full-auto)
pm7code --server ws://100.x.y.z:8787 \
--device-id "$PM7CODE_DEVICE_ID" \
--device-key "$PM7CODE_DEVICE_KEY" \
--full-auto "deploy the site"Permission stance
Locally it runs full-auto by default so scripts are not blocked. Remote is safer-by-default: it reads, but declines mutating actions unless you pass --full-auto.
Resumable by design
CLI sessions are always persisted. The --json output gives you a durable pm7SessionId you can later --resume, or use --continue for the latest session in a folder.
Three agents
The CLI exposes claude, codex, and pi. Use --model to override the model for the chosen agent.
Options
| Option | What it does |
|---|---|
| --agent <claude|codex|pi> | Agent runtime to use. Default: claude. |
| --model <name> | Model override for the chosen agent. |
| --output <text|verbose|json> | Output mode. Default: text. |
| --verbose | Alias for --output verbose. Progress lines go to stderr. |
| --json | Alias for --output json. NDJSON events plus a final result on stdout. |
| --full-auto / --no-full-auto | Force the permission stance. Default: full-auto locally, safer-by-default remote. |
| --server <ws-url> | Server URL. Default: ws://127.0.0.1:8787 (or env PM7CODE_SERVER). |
| --device-id <id> | Device id for a remote motor, obtained by pairing (or env PM7CODE_DEVICE_ID). |
| --device-key <key> | Device key for a remote motor, obtained by pairing (or env PM7CODE_DEVICE_KEY). |
| --cwd <path> | Working directory for the session. Default: the current directory. |
| --continue, -c | Resume the latest session in this directory for this agent. |
| --resume <id> | Resume a specific pm7 session id (the one returned by --json). |
| --timeout <sec> | Maximum turn duration in seconds. 0 means no timeout. Default: 600. |
| -h, --help | Print the built-in help and exit. |
Environment variables and exit codes
The CLI reads a few environment variables so you can keep the server URL and device credential out of every command line.
| Variable | Meaning |
|---|---|
| PM7CODE_SERVER | Default server URL for the CLI. Falls back to ws://127.0.0.1:8787 when unset. |
| PM7CODE_DEVICE_ID | Device id for a remote motor, obtained by pairing. The motor stores paired devices in ~/.pm7-code/devices.json. |
| PM7CODE_DEVICE_KEY | Device key matching PM7CODE_DEVICE_ID. Sent in the hello; the motor only stores a hash of it. |
| M2_PORT | Port the motor listens on, and the port in the CLI default URL. Default: 8787. |
| M2_HOST | Bind address for the motor. Default 127.0.0.1 (loopback only). Set 0.0.0.0 to expose it over your tailnet. |
Exit codes make the CLI safe to branch on in scripts and CI.
| Exit code | Meaning |
|---|---|
| 0 | Success. |
| 1 | Agent error — the turn itself failed. |
| 2 | Usage error — bad arguments or empty prompt. |
| 3 | Auth error — missing/invalid device key, or a rejected pairing code. |
| 4 | Network / connection error, or a protocol-version mismatch. |
| 124 | Timeout — the turn exceeded --timeout and was interrupted. |
Troubleshooting
When a Git Service job fails
A failed job means pm7Code deliberately stopped before completing the commit or push. Use the card message as the source of truth and send a fresh request after fixing the cause.
| Problem | What to do |
|---|---|
| A file changed after the request was made | Ask the agent to inspect the latest file state and send a new GitServiceRequest. |
| Another topic touched the same file | Resolve which topic owns the file, then let one of them create a fresh request. |
| There are unrelated staged changes | Unstage or commit those changes separately. The Git Service will not mix them into this job. |
| The push failed | Check the visible error in the card. Typical causes are auth, network, or a remote branch update. |
| Nothing changed | No commit is needed. A well-behaved agent should silently skip the request. |