Skip to content

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:

sh
tern plugin link ./integrations/tern-companyos
tern plugin list

Tern reloads linked plugins when their files change. To remove the link, run:

sh
tern plugin unlink companyos

Every probe and action runs the same Company OS command, resolved in this order:

  1. HSM_COMPANY_OS_CLI, when set.
  2. The checkout's scripts/company-os through /bin/bash, where the checkout is HSM_COMPANY_OS_REPO when set, else the checkout this plugin is linked from. The bash launcher works even when a desktop-launched Tern does not inherit Node on PATH.
  3. $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:

ActionWhat it does
Company OS AgentChat — Start governed chatRuns companyos chat in a new Tern tab. Company OS admits the run and opens the OMP TUI.
Open Company OS ConsoleOpens /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 readinessRuns companyos status in a new tab.
Open Company OS cockpitOpens 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 workOpens br ready in a Tern tab at the linked checkout or HSM_COMPANY_OS_REPO.
Open blocked BeadsOpens 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 connectionRuns companyos mcp doctor --client omp in a new tab.
Connect Claude Code to Company OSRuns companyos mcp connect claude in a new tab.
Connect Codex to Company OSRuns companyos mcp connect codex in a new tab.
Start Company OSRuns companyos up, which starts or reuses the local services and opens the Console.
Log this computer in to Company OSRuns companyos login in a new tab.
Open Company OS approvals waiting on youLists pending approvals (approvals --status pending --table). Decide them in the Console or with companyos approval approve|reject ID --confirm.
Open Company OS tasksLists 25 tasks (task list --table).
Open recent Company OS agent runsLists 20 runs (ps --table).
Open Company OS receiptsLists receipts (receipt list --table).
Open Company OS usage and costShows model spend (cost).
Run Company OS doctorRuns the client preflight and host checks (doctor).
Company OS today (prototype): what needs meRuns 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 guideOpens GUIDE.md in Tern.
Ask Carly how Company OS can help me use AIOpens 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:

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 ## slug headings.
  • status() checks the API host (127.0.0.1:3847/api/company/health) and the Console (/api/agent-chat-runtime on 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, and doctor each 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:

SegmentMeaningClick
Company OScompany-os status reported a ready workspace URLStarts governed AgentChat
Company OS not readyThe last check failed or timed outOpens the cockpit
Company OS …No check has finished yetOpens the cockpit
Company OS status staleNo new check result for 3 minutes, so the last one is no longer shownOpens 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:

sh
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.

sh
node scripts/company-os-tern-usage.mjs            # last 14 days, plus actions never used
node scripts/company-os-tern-usage.mjs --days 30 --json

Set 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 status when 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, or br 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/health and 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, and doctor, or br ready|blocked|stats|show. None of them approves, sends, creates, or runs anything.
  • Carly's status() sends GET /api/company/health to the API host and GET /api/agent-chat-runtime to the Console, 5-second timeout each.
  • Self-heal (host half) runs date, tail, and grep on Tern's own log every minute, and tern plugin reload when a Company OS hook was disabled.
  • Opening a bead (Enter or double-click in the Beads list) runs tern new tab with companyos chat --start for 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).
  • br is HSM_COMPANY_OS_BR when set, else ~/.local/bin/br when it exists, else br from PATH, 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.

Company OS documentation — built with VitePress