Skip to content

Capability: distribution ​

Shipping the-loop as an installable plugin for Claude Code, Cursor and Codex from a single repository — no bespoke marketplace publishing.

What it is ​

The packaging that makes the-loop installable across harnesses: thin plugin manifests over one shared set of skills, commands and templates. The templates and the config schemas are internal to the plugin — read from it when authoring artifacts or validating a config, never copied into the projects the-loop is run on.

Current behaviour ​

  • the-loop SHALL be installable in Claude Code directly from GitHub (/plugin marketplace add MadaraUchiha-314/the-loop + /plugin install the-loop@the-loop) via .claude-plugin/plugin.json + marketplace.json.
  • the-loop SHALL be installable in Cursor (≥ 2.5) from the same repo via .cursor-plugin/plugin.json + marketplace.json, or by cloning under ~/.cursor/plugins/local/.
  • The Claude Code plugin SHALL also be installable non-interactively from a terminal with the-loop install claude [--scope user|project] (and moved forward with the-loop upgrade), which drives claude's own plugin CLI where it has one and otherwise falls back to the settings keys above. The Cursor plugin SHALL keep its documented in-editor routes until issue-157 establishes a terminal one — the CLI reports cursor as an unknown component rather than half-supporting it (owner decision on PR #153). See cli and decision-057; the in-session /plugin and /add-plugin routes remain exactly as documented.
  • Both plugins SHALL reuse the SAME skills/ (Agent Skills standard) and commands/; nothing is forked per harness.
  • Plugin and marketplace manifest version fields SHALL carry the released version: cz bump rewrites them in lockstep on every release (see release-publishing, decision-028).
  • WHERE Claude Code uses the SessionStart hook (hooks/hooks.json) the Cursor package SHALL use the always-applied rule rules/the-loop.mdc instead. hooks/hooks.json SHALL also declare the Stop gate (the-loop-gate.py) and, since issue-370, the PostToolUse recorder (the-loop-link-pr.py, which runs sessions link-pr --discover after a git push or a pull-request-creating call — issue-447, no gh output parsed); Cursor has a hook surface for the first and none for the second, so there the skill's prose rule is what records a pull request.
  • Work-item and process templates SHALL be internal to the plugin, shipped under skills/the-loop/templates/ (manifest.templatesDir) and read from ${CLAUDE_PLUGIN_ROOT} when an artifact is authored. /the-loop:init SHALL NOT copy them into a project; a project carries only its own generated artifacts.
  • WHEN /the-loop:upgrade-the-loop runs on a project that an older version scaffolded a .the-loop/templates/ folder into THEN it SHALL remove that folder (per manifest.deprecated), confirming first only if the user has added their own files under it.
  • Config schemas SHALL be internal to the plugin too (issue-220, decision-080), shipped under ${CLAUDE_PLUGIN_ROOT}/.the-loop/ and declared once as manifest.schemasDir — the same shape templatesDir has. /the-loop:init SHALL NOT create harness-config.schema.json, collaborators.schema.json or cli-config.schema.json in a project, and the opt-in .the-loop/cli-config.yaml SHALL be scaffolded alone.
  • WHEN init or upgrade validates a config, or drives the x-onboarding walkthrough, THEN it SHALL read the schema from manifest.schemasDir on disk — the absence of a project-local copy SHALL NOT weaken, skip, or move that validation onto the network.
  • WHEN /the-loop:upgrade-the-loop runs on a project holding a schema copy an older version left behind THEN it SHALL delete it and report it under removed (deprecated); WHERE that copy differs from the plugin's shipped schema the difference SHALL be surfaced first, and WHERE the file cannot be established as a the-loop copy it SHALL be left in place and reported under needs-user. Deletion is name-driven from manifest.deprecated; a path resolving outside the project's .the-loop/ SHALL be refused.
  • Every config the-loop scaffolds SHALL open, on its first line, with a # yaml-language-server: $schema=<published url> modeline, so an operator's editor validates the file with no local schema. It is a comment: the loop SHALL never read it, and SHALL never fetch a schema over the network.
  • WHEN /the-loop:init scaffolds .the-loop/harness-config.yaml THEN it SHALL establish the config with the user via a guided onboarding driven by the schema's x-onboarding groups: related keys clubbed and decided together, each group explained, enum keys presented with ALL possibilities, free-form keys with schema examples, and sensible defaults resolved as existing answer → detected signal → schema default (see the skill's reference/onboarding.md).
  • Each config has its own init command (issue-514, decision-149). /the-loop:init SHALL set up the repository only — the harness config, collaborators, manifest, the docs trees, the loop:<phase> labels, hooks and CI parity — and SHALL NOT ask a daemon question, check a credential, or create or modify a CLI config, a Slack app manifest or a gateway-config.yaml; its report SHALL name /the-loop:init-cli and /the-loop:init-gateway. /the-loop:init-cli SHALL own the CLI config — the posture questions, its walkthrough, Slack, Jira, the routing.autoExecuteLabels labels and the credential preflight — and SHALL need no harness config. /the-loop:init-gateway SHALL own gateway-config.yaml and its own secrets' preflight. All three SHALL take --dry-run and --defaults and SHALL be idempotent and non-clobbering. Where a bullet below says "init" of the CLI config, it is /the-loop:init-cli.
  • The CLI config is onboarded by the same walkthrough (issue-415). cli-config.schema.json SHALL carry its own x-onboarding block — ordered groups, ask levels, per-group explain — and init SHALL walk it after the harness config, whichever answer the user gave to where that file lives: the path is a storage question, and what the file turns on is why they ran init. Every top-level property except version SHALL belong to exactly one group, so no block ships that nobody is ever asked about.
  • The user's threshold for automation is ONE question (issue-415). x-onboarding.profiles SHALL declare an ordered autonomy ladder — rungs each carrying a title, a summary, a stillHuman line and the values they propose — and init SHALL present the whole ladder with the recommendation marked, before the groups those values seed. A rung is a proposal: init SHALL show the resolved values before writing them, and SHALL record the applied rung as a provenance comment in the written config and by id in the final report. The least autonomous rung SHALL be the shipped defaults, so "not yet" is a supported answer.
  • No autonomy profile SHALL buy away a human gate. A rung's values SHALL be confined to webhooks, polling, routing, service, selfDiagnosis and channels, and SHALL NOT include routing.authorizedUsers — the phase-selection checklist, the artifact approval gates, the pull-request approval and the risk tiers are fixed rules of the skill and the process graph, not settings, and authority is granted by name and never implied by a threshold. cli/tests/test_onboarding_schema.py enforces both.
  • Init SHALL establish the deployment shape before proposing an ingress (issue-415): a personal machine proposes the poller, an always-on host with an inbound route proposes the webhook receiver, and neither is proposed when the user has nowhere to run it. The answer is used and never stored — a second source of truth for "is the webhook enabled" would only drift.
  • Init SHALL walk the Slack setup and verify it (issue-415): the short explanation (one thread per work item; subscribe is what the channel hears; publish is the channel's authority; GitHub stays the ledger), then the app manifest, the two tokens, the invite and the conversation id, then the-loop channels status [--probe] and the-loop doctor slack. A declined Slack SHALL end the group immediately, and a verification that could not run SHALL be reported as unverified, never as working.
  • Init SHALL preflight every credential its configs name, presence only (issue-415). The variable names SHALL be read from the written configs — not a hardcoded list — and init SHALL report whether each is set and nothing else: never a value, a prefix, a length or a hash, and never a credential written into any file. An unset variable SHALL be a needs-user line naming what it gates and where to obtain it; a variable only the daemon's environment would carry SHALL be reported as unverified, distinctly from unset. WHERE an env.file is proposed init SHALL say it holds secrets and verify .gitignore covers it, offering the line when it does not.
  • WHERE --defaults is passed init SHALL apply sensible defaults without interaction and report the remaining gaps under needs-user — including, since issue-415, the unanswered autonomy profile and every unset credential, and saying plainly that no automation is enabled; WHEN init re-runs it SHALL raise only gaps, never re-asking established answers, the autonomy profile included.
  • WHEN /the-loop:upgrade-the-loop finds a removed schema key that still carries live operational settings (not just a stale default) THEN it SHALL migrate the data, not merely flag and drop it — e.g. a pre-decision-032 .the-loop/harness-config.yaml still carrying webhooks/polling/observability.eventLog SHALL have that block extracted, eventLog-renamed, and written to a CLI config (asking the same yes/no location question /init asks), both resulting files validated, and the migration reported as its own line — never silently dropped.
  • WHEN a release adds a config key whose default changes runtime behaviour THEN upgrade SHALL surface it under needs-user rather than adding it silently — the add-with-defaults rule covers opt-in keys, not behaviour flips. The first instance is routing.control.requireStartCommand (issue-106): its default demotes the auto-execute label to necessary but not sufficient, so upgrade asks whether to keep the previous behaviour (false) or adopt the gate (true). Related state moves are offered, never performed silently: an older state layout (.the-loop/poll-state.json, then <state.root>/sessions/) may be tidied away, but the daemon keeps READING what is there until each work item has been written forward, because an empty ledger would re-forward every watched thread (issue-106, issue-128).
  • A CLI config that lives in the operator's home directory is outside upgrade's reach (it reconciles project files). Upgrade SHALL say so and print what to paste, and the runtime SHALL stay correct for an un-migrated config — every key added this way is optional and falls back to the same defaults.

Design ​

docs/specs/issue-12/design.md · architecture § distribution

History ​

Work itemWhat changedLinks
issue-514/the-loop:init split three ways: /init sets up the repository only (harness config, collaborators, docs trees, phase labels, hooks), /init-cli the operator's CLI config (posture, walkthrough, Slack, Jira, arming labels, credentials), /init-gateway a gateway host's gateway-config.yaml; a repository that only uses the-loop is no longer asked about a daemonspec, decision-149
issue-447The plugin's PostToolUse recorder became trigger-agnostic — a push or any pull-request-creating call runs sessions link-pr --discover — and the skill and slash commands send the harness's GitHub acts through the-loop verbs instead of ghspec, decision-140, issue
issue-415The onboarding covers what people install the-loop for: cli-config.schema.json gained its own x-onboarding block (seven groups, every block covered), an autonomy ladder turns "how much should it do unattended?" into one question with three rungs that provably cannot reach a human gate, init establishes the deployment shape before proposing an ingress, walks and probes the Slack setup, and preflights every credential the configs name — presence only, never a valuespec, onboarding guide, issue
issue-220Config schemas made internal to the plugin (manifest.schemasDir); init stops copying up to 118 KB of them into each project, upgrade deletes the copies already there, and scaffolded configs carry a # yaml-language-server: $schema= modeline insteadspec, decision-080, issue
issue-152The Claude Code plugin became installable and upgradable from the CLI (the-loop install / upgrade), at user or project scope, without opening a session — the terminal-side counterpart to the marketplace routes. Cursor stays in-editor-only, split out as issue-157spec, decision-057, cli, issue
issue-106A key whose default changes behaviour (routing.control.requireStartCommand) is reported as needs-user, not silently added; the state/control blocks are added with defaults and the poll-state move is offered, not forcedspec, decision-040, issue
issue-63/upgrade migrates (not just flags) removed schema keys with live data — the webhooks/polling/observability.eventLog → CLI config extractionspec, decision-032
issue-46Plugin/marketplace manifest versions bumped by the release engine (were frozen at 0.1.0)spec, decision-028
issue-49Guided, schema-driven config onboarding in /init (x-onboarding groups, ask levels, --defaults mode, examples on gap-prone keys)spec, decision-024
issue-36Templates made internal to the plugin (skills/the-loop/templates/); init no longer copies them into projects, and upgrade cleans up the deprecated .the-loop/templates/ folderspec
issue-12Added Cursor packaging (.cursor-plugin/, rules/the-loop.mdc) reusing the same skills/commandsspec, decision-015
issue-1Shipped the Claude Code plugin + marketplace manifests (v0)spec, decision-001

Codex installation (issue-449) ​

the-loop install codex --scope project --project-dir <repo> checks the Codex binary and prepares native instructions and project hooks without starting a model session. --scope user prepares ~/.codex/AGENTS.md and hooks.json. all includes Codex, and the default includes it when its binary is on PATH. A missing binary is a named failed step, and --dry-run performs no writes. Existing instructions and hook definitions are preserved. Trust new hook definitions with /hooks before relying on unattended execution.

The repository also ships .codex-plugin/plugin.json, using the shared skills and Codex-specific lifecycle hooks under hooks/codex-hooks.json. Native plugin hook installation follows Codex's own trust review. Plugin versions remain in commitizen's version lockstep. The CLI distribution bundles the operating skill, its required sibling writing skill and both sets of references, command procedures and hook scripts so installation from a wheel or source distribution has the same instruction surface as a repository checkout.

Released under the MIT License.