Canopydocs v0.4.7

settings.json & state.json

Both live in the platform app-config directory under com.midhunkumare.canopy. See Where settings live for the path on each platform.

settings.json#

Everything you configure in Settings, except the repository's provisioning file and appearance (which lives in localStorage).

{
  "version": 1,
  "editor": { "command": "code" },        // "Open in editor"
  "terminal": "Terminal",                 // "Open in terminal"
  "showSwitchBranch": true,               // offer Switch branch… and ⌘\
  "repos": [
    {
      "id": "tooljet",                    // stable id; part of every service key
      "name": "ToolJet",
      "path": "/Users/me/code/ToolJet",   // the main checkout
      "worktreeDir": ".worktrees",        // absolute, or relative to the repo; "" = <repo>/.worktrees
      "resetDb": "npm run db:reset",      // the "Reset database" action
      "migrateDb": "npm run db:migrate",  // "Run migration"; "" falls back to .worktreemanager.json
      "services": [
        {
          "id": "frontend",
          "name": "Frontend",
          "kind": "web",                  // web | server | worker
          "command": "npm start -- --port $PORT",
          "cwd": "frontend",              // relative to the worktree root; "" = root
          "basePort": 8082,               // null for a service that does not listen
          "env": { "NODE_ENV": "development" }
        },
        {
          "id": "server",
          "name": "Server",
          "kind": "server",
          "command": "npm run start:dev",
          "cwd": "server",
          "basePort": 3000,
          "env": {}
        }
      ],
      "customCommands": [
        { "label": "Lint", "command": "npm run lint", "group": "Checks" },
        { "label": "Unit tests", "command": "npm test -- --run", "group": "Checks" }
      ],
      "agents": [
        { "id": "a1", "name": "Claude Code", "command": "claude", "promptOnLaunch": true },
        { "id": "a2", "name": "Codex", "command": "codex", "promptOnLaunch": true }
      ],
      "agentCommand": "claude"            // legacy single-agent field, kept in sync with agents[0]
    }
  ]
}

Field notes#

FieldNotes
versionSchema version. Missing fields fall back to defaults on read, so an older file still loads.
editor.commandDefaults to code.
terminalEmpty means Canopy detects a sensible terminal for the platform.
showSwitchBranchDefaults to true. false removes the action and the shortcut.
repos[].idUsed in service keys (<worktree path>::<service id>) and as the database-name prefix. Don't rename it casually; port indices are keyed by it.
repos[].worktreeDirRelative paths resolve against the repo root.
services[].basePortnull is valid, for a worker with no port. It then exposes no port variable.
customCommands[].groupOptional. Empty means ungrouped, which is where every command starts.
agentsThe first entry is the default launcher.
agentCommandKept for older files. Saving sets it to the first agent's command.

Entries with an empty id or command (services), an empty label or command (custom commands), or a missing id, name or command (agents) are dropped when Settings saves.

Edit it by hand while Canopy is closed. While it's running, the backend holds the authoritative copy in memory and rewrites the file on save.

state.json#

Runtime bookkeeping. Not configuration, and not for hand-editing.

{
  "portIndices": {
    "tooljet": {
      "/Users/me/code/ToolJet": 0,                          // the main checkout is always 0
      "/Users/me/code/ToolJet/.worktrees/feat_x": 1,
      "/Users/me/code/ToolJet/.worktrees/fix_y": 2
    }
  },
  "portOverrides": {
    "/Users/me/code/ToolJet/.worktrees/feat_x::frontend": 8099
  },
  "orphans": [
    { "svcKey": "…::server", "pgid": 41233, "spawnTimeSecs": 1754899200 }
  ]
}
FieldPurpose
portIndicesThe stable per-worktree index behind basePort + index × 10. Index spaces are per repository, and a freed slot is reclaimed by the next worktree.
portOverridesExplicit per-service overrides, keyed by service key, set from the service-detail dialog.
orphansProcess groups Canopy spawned, with their start time, so a crashed run's leftovers can be swept on the next launch without killing an unrelated process that reused the pid.

There's also a terminal-orphan list on Unix for PTY sessions, maintained the same way.

Stored elsewhere#

SettingActually stored in
Theme, density, accent, text zoomlocalStoragecanopy.appearance
Per-worktree agent contextlocalStoragecanopy.ctx.<worktree path>
Pinned worktrees, multi-selectionlocalStorage
Provisioned files, setup, migrate, teardown<repo>/.worktreemanager.json
Which layout you last used, sidebar visibilityNot persisted; they reset with the window

Documentation for Canopy 0.4.7. Controls marked coming soon are present in the interface but have no implementation behind them yet.