Guide

Configuration

Two files, two owners. Both live at the repo root and both are committed, so the team shares one answer rather than each person configuring their own.

FileOwns
.linchpin.jsonAgent base paths, and optionally the WordPress environments to symlink into
.clickup.jsonWhich ClickUp space and list this repo's tasks belong to

.linchpin.json

Created by linchpin wt config init, which walks you through the questions and writes the result. Home directories are stored collapsed to ~ so the file is portable between machines.

{
  "agents": {
    "codex": "~/Documents/GitHub",
    "conductor": "~/conductor"
  },
  "defaultAgent": "codex",
  "wordpress": {
    "contentType": "plugin",
    "pluginSlug": "my-plugin",
    "defaultEnvironment": "studio",
    "environments": {
      "studio": "~/Studio/mysite/wp-content/plugins/my-plugin",
      "localwp": "~/Local Sites/mysite/app/public/wp-content/plugins/my-plugin"
    }
  }
}

The wordpress block is optional

A repo with no WordPress environments is a perfectly normal state, not a misconfiguration. Commands that do not touch a WordPress install — anything to do with commits, pull requests, tasks or JSON editing — work fine without it. Only the commands that genuinely need an environment ask for one, and they fail with a precondition telling you to run linchpin wt config init.

Agents

agents maps an agent name to the directory its repositories live under. It matters because worktree operations need to find the base repository, and different agents keep checkouts in different places.

defaultAgent is not decorative: the agent you nominate is searched first when locating a repository, on the reasoning that a path you configured is a better guess than a preset one.

Presets exist for conductor (~/conductor), claude-code (~/Documents) and codex (~/Documents/GitHub). A custom entry takes any path you give it.

The older single-agent form is still read:

{ "agent": "codex", "agentBasePath": "~/Documents/GitHub" }

Content types

contentType is one of plugin, theme or wp-content. The last is for repos where the repository is the whole wp-content directory — common on client projects where the repo is named after the client. symlinkName overrides the directory name the symlink is created under, which is what you want when the repo name and the WordPress directory name differ.

.clickup.json

Pins where this repo's tasks live, so creating one is a single confirmation rather than a crawl through the workspace hierarchy. Only space and defaultList are required — a two-key file is already useful.

{
  "space": { "id": "90140688927", "name": "Linchpin", "customIdPrefix": "LINCHPIN" },
  "defaultList": {
    "id": "901418862156",
    "name": "List",
    "path": "Linchpin › CLI › List",
    "use": "All work on the linchpin CLI."
  }
}

Two rules matter more than the shape:

  • IDs are the contract; names are for humans. Lists get renamed and the id survives it.
  • It holds no secrets. Workspace, folder and list ids are not credentials, and the API still requires a token. It is dev-time metadata, so exclude it from any distributable — this repo's files allowlist already does.

The full schema, including the optional lists, folders and moduleRouting maps, is documented in the task-tracking skill.

Was this helpful?