Company OS plugin for Tern
This Tern plugin makes governed Company OS AgentChat the primary entry point and adds a native operations cockpit for workspace health and the Beads work queue. The Company Console is available as a separate browser action, and the optional OMP MCP profile stays read-only.
Install
From the repository root, link the plugin into Tern:
tern plugin link ./integrations/tern-companyos
tern plugin listTern reloads linked plugins when their files change. To remove the link, run:
tern plugin unlink companyosEvery probe and action runs the same Company OS command, resolved in this order:
HSM_COMPANY_OS_CLI, when set.- The checkout's
scripts/company-osthrough/bin/bash, where the checkout isHSM_COMPANY_OS_REPOwhen set, else the checkout this plugin is linked from. The bash launcher works even when a desktop-launched Tern does not inherit Node onPATH. $HOME/.local/bin/companyos.
The same checkout is the Beads workspace. Actions open a new tab in the focused pane's current directory, when available. For remote Tern sessions, install the Company OS CLI on that session host or set HSM_COMPANY_OS_CLI there.
Palette actions
Open the command palette and search for Company OS:
| Action | What it does |
|---|---|
| Company OS AgentChat — Start governed chat | Runs companyos chat in a new Tern tab. Company OS admits the run and opens the OMP TUI. |
| Open Company OS Console | Opens /workspace/start on 127.0.0.1, using HSM_COMPANY_CONSOLE_PORT or port 3050. It does not start the Company OS services. |
| Check Company OS readiness | Runs companyos status in a new tab. |
| Open Company OS cockpit | Opens a native Tern block with workspace health (the API host and the Console checked separately, with a link to the launcher log), Beads queue counts, and a selectable Beads list (top five; e shows all, v or a count chip switches between ready, in progress, blocked and open). ↑/↓ (or j/k) select a bead and Enter (or a double-click) opens it in a governed OMP session, see Working a bead in OMP. Press r or F5 (or ↻ Refresh) to rerun the read-only checks, l to open the log, w to open the workspace. |
| Open Beads ready work | Opens br ready in a Tern tab at the linked checkout or HSM_COMPANY_OS_REPO. |
| Open blocked Beads | Opens br blocked in a Tern tab at the linked checkout or HSM_COMPANY_OS_REPO. |
| Connect OMP to Company OS (read-only) | Runs companyos mcp connect omp. This explicit action installs the read-only Company OS MCP profile in OMP. |
| Check OMP connection | Runs companyos mcp doctor --client omp in a new tab. |
| Connect Claude Code to Company OS | Runs companyos mcp connect claude in a new tab. |
| Connect Codex to Company OS | Runs companyos mcp connect codex in a new tab. |
| Start Company OS | Runs companyos up, which starts or reuses the local services and opens the Console. |
| Log this computer in to Company OS | Runs companyos login in a new tab. |
| Open Company OS approvals waiting on you | Lists pending approvals (approvals --status pending --table). Decide them in the Console or with companyos approval approve|reject ID --confirm. |
| Open Company OS tasks | Lists 25 tasks (task list --table). |
| Open recent Company OS agent runs | Lists 20 runs (ps --table). |
| Open Company OS receipts | Lists receipts (receipt list --table). |
| Open Company OS usage and cost | Shows model spend (cost). |
| Run Company OS doctor | Runs the client preflight and host checks (doctor). |
| Company OS today (prototype): what needs me | Runs scripts/company-os-today.mjs: one list of what needs you (login or host problems, approvals, failed runs, tasks waiting on you, P0 Beads, P1 bugs), most urgent first, each with the command that acts on it; then running agents and the next ready Beads. It tries out the cockpit's content before it is ported to the block. |
| Open the Company OS guide | Opens GUIDE.md in Tern. |
| Ask Carly how Company OS can help me use AI | Opens Carly with that question. |
The approvals, tasks, runs, receipts, usage, and doctor actions run the checkout's operator CLI, services/company-os-mcp-server/dist/company-os-cli.js, with an explicit Node path, because a desktop-launched Tern has no Node on its PATH. From a terminal, the same verbs also work as companyos task list, companyos approvals, and so on. Build it once with npm --prefix services/company-os-mcp-server run build.
The Tern integration is available from the command palette. The cockpit shows the Company OS readiness result and a read-only Beads snapshot: open, ready, in-progress, and blocked counts plus a selectable list of ready, in-progress, blocked, or open issues. The Company OS readiness probe reports workspace, API, and AgentChat together; the Beads counts and ready list come from the linked checkout's Beads workspace. Each section has an explicit loading or error state. Refresh reruns both checks. Use Open Beads ready work or Open blocked Beads to continue in the Beads CLI, and use AgentChat for governed conversational work. CLI actions leave an interactive shell open after the command finishes so their output remains visible. companyos in the table means the command resolved as described under Install.
The OMP profile is useful for read-only Company OS tools. Use Company OS AgentChat — Start governed chat when a turn needs the host-admitted terminal run and its Company OS governance receipts.
To add a key binding, add an unused chord to Tern's settings.json:
{
"keybinds": {
"cmd+shift+g": "plugin.companyos.agentchat"
}
}Choose a chord that is free in your active Tern preset. User key bindings take precedence over plugin defaults.
Carly
The plugin teaches Tern's assistant, Carly, to use Company OS:
- Context. Every Carly request carries one line saying Company OS is installed, its last known status, and to read the guide first.
- Guide.
plugins.companyos.guide()returns the overview from GUIDE.md: what Company OS is, how it helps people use AI, and the topic list.guide("approvals"),guide("ai-help"), and the other topics return one section each. Edit GUIDE.md to change what Carly knows; topics are the lowercase## slugheadings. status()checks the API host (127.0.0.1:3847/api/company/health) and the Console (/api/agent-chat-runtimeon the Console port) separately and says which one is down. Carly is told to state Company OS health only from it.- Read-only tools.
tasks,task,approvals,runs,run,receipts,verify,memory,usage,beads,tools,connectors, anddoctoreach run one read command and return{ok, command, output, truncated, stderr, hint}, with output cut at about 9 KB. Arguments go to argv, never a shell, and a value starting with-is refused, so Carly cannot add flags such as--confirm. - Open.
open(target, page?)opens one of this plugin's palette actions or a Console page, so a person makes every decision.
Run help("plugins.companyos") in Carly to see each tool's signature. Tern allows 16 exports per plugin; this plugin uses all 16.
Palette actions and Carly's tools register from timers just after load, not during it: under machine load the window load hook has overrun its 50 ms budget, and a failed load drops every palette action.
Self-heal. Tern disables any plugin hook or Carly tool that overruns its time budget, until tern plugin reload, and logs it in ~/Library/Logs/Tern/tern.log. The host half checks that log every minute and, when a Company OS hook was disabled, runs tern plugin reload itself: at most once per 3 minutes and 6 times an hour, after which it stops and logs that the machine is too loaded. Set HSM_COMPANY_OS_TERN_SELF_HEAL=0 to turn it off, and HSM_TERN_BIN if tern is not at /Applications/Tern.app or ~/.local/bin/tern. A reload reloads every linked Tern plugin.
Desktop-launched Tern has PATH=/usr/bin:/bin:/usr/sbin:/sbin. Carly's tools find Node through HSM_COMPANY_OS_NODE, then /opt/homebrew/bin/node, then /usr/local/bin/node.
Status line
When Tern's status line is on (Settings → Status line, "status_bar": true), the plugin adds a Company OS segment:
| Segment | Meaning | Click |
|---|---|---|
Company OS | company-os status reported a ready workspace URL | Starts governed AgentChat |
Company OS not ready | The last check failed or timed out | Opens the cockpit |
Company OS … | No check has finished yet | Opens the cockpit |
Company OS status stale | No new check result for 3 minutes, so the last one is no longer shown | Opens the cockpit |
The host half runs company-os status when the plugin loads and then every 60 seconds, and stores the result (ready or error, plus a counter) in the plugin's key/value store. The cockpit's own readiness check stores its result too. The window half never runs the check: it only re-reads that small store every 5 seconds and on focus changes and finished commands, and the status formatter only looks up a segment built at load, with no IO. That keeps it far under Tern's 4 ms chrome budget in normal use, but not always under heavy memory pressure: if the formatter overruns, Tern hides the segment until the plugin reloads (tern plugin reload). The host keeps polling, and the cockpit still works.
Process work stays out of the window half because it runs on Tern's UI thread. There, spawning the check from a timer went over the 50 ms hook budget when the machine was under memory pressure, and Tern disabled the timer until the plugin reloaded, freezing the segment. Because three window hooks keep the segment current, one disabled hook no longer freezes it, and a stopped host poller shows as stale instead of an old result.
Working a bead in OMP
In the cockpit's Beads list, select a bead and press Enter (or double-click it). A new Tern tab runs governed AgentChat with the bead as its first message:
companyos chat --start "Work on Beads issue <id>: <title> …"Company OS admits the run as usual, and OMP opens with that message sent: it reads the bead (br show), its dependencies and the code, then posts a preflight and a plan and waits for your go-ahead before editing. It claims the bead through node scripts/br-governed.mjs only when work starts; the cockpit itself changes nothing. The tab stays open when the command ends, so a refusal such as "Not logged in" stays readable. --start is the interactive counterpart of --prompt, which runs one turn and exits.
Usage counts
To decide what the cockpit should show, the plugin counts what you use: each palette action (palette:<id>, which includes clicks on the status segment), each cockpit key or click (cockpit:key:<name>, cockpit:click:refresh), and each Carly tool call (carly:<name>). A count is one line, {"t": <epoch seconds>, "e": "<event>"}, in <Tern plugin data>/companyos/usage/<YYYY-MM-DD>.<window|host>.jsonl (~/Library/Application Support/Tern/plugin-data/companyos/usage/ by default). Nothing else is written, and nothing leaves this computer.
node scripts/company-os-tern-usage.mjs # last 14 days, plus actions never used
node scripts/company-os-tern-usage.mjs --days 30 --jsonSet HSM_COMPANY_OS_TERN_USAGE=0 to stop recording. Opening the cockpit is counted from the palette, not from the block, because Tern re-creates an open cockpit on every plugin reload.
Governed chat and the managed host
AgentChat admission, provider evidence, and receipt settlement always target the managed Company OS runtime on 127.0.0.1:3847, which scripts/company-os-agentchat.sh pins. The terminal chat client ignores ambient API-base aliases on purpose, so another loopback service cannot receive a governed admission request.
Trust and scope
Tern plugins run with the current user's access to files and programs; Tern does not prompt for per-plugin permissions. This plugin opens the Console URL, registers palette actions, adds one status-line segment, provides one native block, and gives Carly one context line and 16 tools (15 read-only tools and open). It does not read or store Company OS credentials or send telemetry, and its only direct HTTP requests are the two unauthenticated health checks behind Carly's status(), both to loopback. Review window.luau and host.luau before linking it.
What runs, and when:
- Palette actions start the fixed command shown above in a new tab, through Tern's login shell, and leave a shell open afterwards so the output stays visible.
- The status line comes from the host half, which runs the read-only
company-os statuswhen the plugin loads and every 60 seconds after that, with a 15-second timeout. - The cockpit block runs the read-only
company-os status,br stats --no-activity --json, and the shown list (br ready --limit 0,br list --status in_progress|open, orbr blocked, each--json) when it opens, when you refresh it, and when you switch lists, plus two 5-second GETs that say which part is down: the API host's/api/company/healthand the Console's/api/agent-chat-runtime. - Carly's tools run one read command each when Carly calls them, with a 30-second timeout: the operator CLI's
task list|show|events,approvals,ps,what|why|timeline|inspect|events,receipt list|show|verify,verify task|run,memory search,cost|usage|stats|operator-status,mcp tools,mcp connectors list, anddoctor, orbr ready|blocked|stats|show. None of them approves, sends, creates, or runs anything. - Carly's
status()sendsGET /api/company/healthto the API host andGET /api/agent-chat-runtimeto the Console, 5-second timeout each. - Self-heal (host half) runs
date,tail, andgrepon Tern's own log every minute, andtern plugin reloadwhen a Company OS hook was disabled. - Opening a bead (
Enteror double-click in the Beads list) runstern new tabwithcompanyos chat --startfor that bead; see Working a bead in OMP. - Usage counts append one line to a local file per palette action, cockpit key or click, and Carly tool call (see Usage counts).
brisHSM_COMPANY_OS_BRwhen set, else~/.local/bin/brwhen it exists, elsebrfromPATH, for both the cockpit and the Beads tabs.
None of these probes change Beads issues or Company OS state. They run on the current session host, so remote sessions need the CLI and Beads workspace at the configured paths there.