{"id":50775925,"url":"https://github.com/launchapp-dev/animus-subject-linear","last_synced_at":"2026-06-12T00:00:19.028Z","repository":{"id":358545855,"uuid":"1239114011","full_name":"launchapp-dev/animus-subject-linear","owner":"launchapp-dev","description":"Linear subject backend plugin for Animus — coming in v0.4.0","archived":false,"fork":false,"pushed_at":"2026-06-08T02:50:01.000Z","size":72,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T04:23:07.688Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://github.com/launchapp-dev/animus-cli","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/launchapp-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-14T19:20:51.000Z","updated_at":"2026-06-08T02:49:58.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/launchapp-dev/animus-subject-linear","commit_stats":null,"previous_names":["launchapp-dev/animus-subject-linear"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/launchapp-dev/animus-subject-linear","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchapp-dev%2Fanimus-subject-linear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchapp-dev%2Fanimus-subject-linear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchapp-dev%2Fanimus-subject-linear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchapp-dev%2Fanimus-subject-linear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/launchapp-dev","download_url":"https://codeload.github.com/launchapp-dev/animus-subject-linear/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/launchapp-dev%2Fanimus-subject-linear/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34222709,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-12T00:00:17.925Z","updated_at":"2026-06-12T00:00:19.021Z","avatar_url":"https://github.com/launchapp-dev.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# animus-subject-linear\n\nA [Linear](https://linear.app) subject backend plugin for [Animus](https://github.com/launchapp-dev/animus-cli).\n\n## What this is\n\nAnimus v0.4.0+ makes subjects (units of dispatchable work) pluggable. This repository ships `animus-subject-linear`, a standalone stdio plugin that exposes Linear issues as Animus subjects. Workflows dispatch agents over your Linear backlog without your team moving off Linear.\n\n## Install\n\n```bash\nanimus plugin install launchapp-dev/animus-subject-linear\nexport LINEAR_API_TOKEN=lin_api_…\nexport LINEAR_TEAM_ID=\u003cyour-team-uuid\u003e   # required for status discovery + scoped queries\n```\n\n## Subject kind\n\nThis backend advertises a single subject kind: **`issue`**. Animus addresses it via the kind-scoped routing contract — `issue/list`, `issue/get`, `issue/update`. CLI calls use `--kind issue`:\n\n```bash\nanimus subject list --kind issue --status ready\nanimus subject get  --kind issue --id linear:ENG-123\nanimus subject update --kind issue --id linear:ENG-123 --status in-progress \\\n    --comment \"kicked off implementation\"\n```\n\nSubject ids are namespaced as `linear:\u003cidentifier\u003e` (e.g. `linear:ENG-123`) so the daemon can route writes back to this backend from the id prefix alone.\n\n## Workflow YAML example\n\n```yaml\n# .animus/workflows/standard.yaml\nsubjects:\n  linear-eng:\n    plugin: animus-subject-linear\n    config:\n      api_token_env: LINEAR_API_TOKEN\n      team: ENG\n    status_map:\n      ready:       [\"Backlog\", \"Todo\"]\n      in_progress: [\"In Progress\", \"In Review\"]\n      done:        [\"Done\", \"Cancelled\"]\n\nworkflows:\n  - id: linear-impl\n    subject_kind: issue\n    phases: [...]\n```\n\n## Supported operations\n\n| Method            | Backed by                          | Notes                                                                                                                                       |\n|-------------------|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|\n| `subject/list`    | `issues(filter, first, after)`     | Pagination cursor returned in `next_cursor`.                                                                                                |\n| `subject/get`     | `issue(id: $identifier)`           | `id` argument is the Linear identifier (e.g. `ENG-123`) or UUID.                                                                            |\n| `subject/update`  | `issueUpdate` + `commentCreate`    | `patch.status`, `patch.assignee`, `patch.labels_add/remove`, `patch.custom` ride on `issueUpdate`. `patch.comment` posts a real Linear comment via `commentCreate` — it does **not** overwrite the issue body. |\n| `subject/schema`  | static + runtime workflow states   | `kinds: [\"issue\"]`; native states discovered lazily from the team's workflow.                                                               |\n| `health/check`    | `viewer { id name }`               | Returns `Unhealthy` (without hitting the network) when `LINEAR_API_TOKEN` is unset.                                                          |\n\nThe protocol does not yet expose a `subject/create` verb — see [`docs/architecture/subject-backend-plugins.md`](https://github.com/launchapp-dev/animus-cli/blob/main/docs/architecture/subject-backend-plugins.md) for the current contract. The schema's `supports_create` flag is reserved for the protocol expansion that adds it.\n\n## Configuring status mapping\n\nLinear lets every team customize the names of their workflow states (e.g.\n`\"Spec\"`, `\"Implementation\"`, `\"Code Review\"`, `\"Shipped\"`), so a hardcoded\nname map only works for teams using Linear's default template. Instead, the\nplugin discovers the team's actual workflow at startup and auto-maps each\nstate to one of the Animus statuses (`Ready`, `InProgress`, `Blocked`,\n`Done`, `Cancelled`).\n\n### Auto-mapping (default)\n\nOn the first `list`/`get`/`update` call the plugin queries Linear's\n`team.states.nodes { id name type position }` and uses the **`type`**\nfield to map every state. The `type` is fixed by Linear regardless of\nwhat the team renames the state to:\n\n| Linear `WorkflowState.type`                | Animus `SubjectStatus` |\n|--------------------------------------------|------------------------|\n| `triage`, `backlog`, `unstarted`           | `Ready`                |\n| `started`                                  | `InProgress`           |\n| `completed`                                | `Done`                 |\n| `cancelled`                                | `Cancelled`            |\n\nUnknown future types default to `Ready` so a Linear-side addition won't\nfreeze your dispatch loop.\n\n### Overrides via `LINEAR_STATUS_MAP`\n\nIf your team uses semantics that don't match the type-based mapping\n(for example, you want `\"Code Review\"` to count as `Done` rather than\n`InProgress`), set the `LINEAR_STATUS_MAP` env var to a JSON object\nkeyed by Linear state **name** (case-sensitive):\n\n```bash\nexport LINEAR_STATUS_MAP='{\n  \"Spec\":         \"Ready\",\n  \"Implementation\": \"InProgress\",\n  \"Code Review\":  \"InProgress\",\n  \"Shipped\":      \"Done\"\n}'\n```\n\nValues must be one of `Ready`, `InProgress`, `Blocked`, `Done`,\n`Cancelled` (PascalCase or kebab-case both accepted). Unknown values\nare silently skipped — the rest of the map still applies. A malformed\nJSON blob falls back to the type-based auto-map and logs a warning.\n\n### Ambiguity resolution on the write path\n\nIf multiple Linear states map to the same animus status (e.g. both\n`\"Spec\"` and `\"Backlog\"` map to `Ready`), `update()` picks the one with\nthe **lowest `position`** — Linear's default \"first\" state for that\ncategory. This keeps writes deterministic without forcing you to\ndisambiguate in `LINEAR_STATUS_MAP`.\n\n### Why `stateId` and not `stateName`?\n\nThe Linear API takes a `stateId` (UUID) on `issueUpdate(input: { stateId })`.\nSending a name string is tolerated but not the documented shape and breaks\nif two teams have a state with the same name. This plugin always sends\nthe UUID it discovered for the team in question.\n\n## Design\n\nThe subject backend plugin protocol is defined in the Animus core repo:\n\n- **Protocol design:** [`docs/architecture/subject-backend-plugins.md`](https://github.com/launchapp-dev/animus-cli/blob/main/docs/architecture/subject-backend-plugins.md)\n- **Naming contract:** [`docs/architecture/naming-contract.md`](https://github.com/launchapp-dev/animus-cli/blob/main/docs/architecture/naming-contract.md)\n- **Repository name:** `animus-subject-linear`\n- **Crate name (published to crates.io):** `animus-subject-linear`\n- **Binary name:** `animus-subject-linear`\n\nPer the v0.4.0 naming convention: repo, crate, and binary all share the same `animus-{kind}-{name}` name. There is no longer an `ao-` prefix anywhere.\n\n## Roadmap\n\n- [x] `SubjectBackend` trait implementation against Linear's GraphQL API\n- [x] Status mapping (auto-discovered from team workflow; `LINEAR_STATUS_MAP` overrides)\n- [x] Authentication via `LINEAR_API_TOKEN` env var\n- [x] Pagination\n- [x] `patch.comment` posts a Linear comment via `commentCreate` (since v0.1.5; earlier versions incorrectly overwrote `description`)\n- [ ] Webhook support for real-time updates (`subject/watch`)\n- [ ] `subject/create` (waiting on protocol expansion; tracked in [animus-cli](https://github.com/launchapp-dev/animus-cli))\n- [ ] Release binaries (macOS aarch64/x86_64, Linux x86_64)\n\nFollow the [Animus core repo](https://github.com/launchapp-dev/animus-cli) for protocol-level progress.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaunchapp-dev%2Fanimus-subject-linear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaunchapp-dev%2Fanimus-subject-linear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaunchapp-dev%2Fanimus-subject-linear/lists"}