From d0a6b188a33bc33bbb982902377c6293c0c87649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rochel?= Date: Thu, 24 Sep 2026 11:17:26 +0200 Subject: [PATCH] refactor(tickets)!: unify ticket plugin behind adapters Move the GitHub implementation behind a provider boundary while preserving the four ticket skills and existing GitHub behavior. Update plugin discovery, documentation, and the adaptive-delivery ticket fixture. Refs #227. BREAKING CHANGE: Install darrow-tickets in place of darrow-tickets-github. --- .claude-plugin/marketplace.json | 6 +- .github/workflows/python-quality.yml | 10 +-- docs/choosing-plugins.md | 2 +- docs/design.md | 11 ++-- docs/specs/ticket-management.md | 47 +++++++------- plugins/capability/README.md | 2 +- .../.claude-plugin/plugin.json | 11 ---- .../.codex-plugin/plugin.json | 23 ------- .../.claude-plugin/hooks.json | 0 .../darrow-tickets/.claude-plugin/plugin.json | 11 ++++ .../darrow-tickets/.codex-plugin/plugin.json | 23 +++++++ .../LICENSE | 0 .../README.md | 59 ++++++++++------- .../backend/pyproject.toml | 14 ++--- .../backend/scripts/run_locked.py | 0 .../backend/src/darrow_tickets}/__init__.py | 0 .../backend/src/darrow_tickets}/arguments.py | 13 ++-- .../src/darrow_tickets}/claude_context.py | 2 +- .../src/darrow_tickets}/claude_routing.md | 14 +++-- .../backend/src/darrow_tickets}/cli.py | 39 ++++++------ .../backend/src/darrow_tickets}/errors.py | 0 .../src/darrow_tickets/providers/__init__.py | 11 ++++ .../providers/github/__init__.py | 44 +++++++++++++ .../providers/github}/create.py | 16 ++--- .../providers/github}/provider.py | 2 +- .../providers/github}/queries.py | 7 ++- .../providers/github}/relations.py | 2 +- .../providers/github}/update.py | 7 +-- .../providers/github/validation.py | 48 ++++++++++++++ .../backend/src/darrow_tickets/selection.py | 32 ++++++++++ .../backend/src/darrow_tickets}/validation.py | 44 +------------ .../backend/tests/__init__.py | 0 .../backend/tests/conftest.py | 6 +- .../backend/tests/eval_gh.py | 0 .../backend/tests/fresh_install.py | 11 ++++ .../backend/tests/fresh_probe.py | 4 +- .../backend/tests/test_claude_context.py | 2 +- .../backend/tests/test_contract.py | 2 +- .../backend/tests/test_eval_gh.py | 0 .../backend/tests/test_failures.py | 20 +++--- .../backend/tests/test_mutations.py | 2 +- .../backend/tests/test_properties.py | 9 +-- .../backend/tests/test_queries.py | 2 +- .../backend/tests/test_read_boundaries.py | 2 +- .../backend/tests/test_selection.py | 63 +++++++++++++++++++ .../backend/uv.lock | 4 +- .../skills/create-ticket/SKILL.md | 22 ++++--- .../skills/create-ticket/agents/openai.yaml | 0 .../create-ticket/evals/dedup-stop.yaml | 0 .../create-ticket/evals/no-attribution.yaml | 0 .../evals/no-trigger-other-provider.yaml | 0 .../evals/relations-by-request.yaml | 0 .../evals/structure-evidence.yaml | 0 .../skills/list-tickets/SKILL.md | 24 +++---- .../skills/list-tickets/agents/openai.yaml | 0 .../list-tickets/evals/filter-readonly.yaml | 0 .../evals/no-trigger-exact-ticket.yaml | 0 .../evals/no-trigger-other-provider.yaml | 0 .../list-tickets/evals/state-asked.yaml | 0 .../list-tickets/evals/truncated-limit.yaml | 0 .../skills/read-ticket/SKILL.md | 29 +++++---- .../skills/read-ticket/agents/openai.yaml | 0 .../evals/ambiguous-reference.yaml | 0 .../evals/conversation-bound-reference.yaml | 0 .../skills/read-ticket/evals/direct-id.yaml | 0 .../read-ticket/evals/foreign-parent.yaml | 0 .../evals/implicit-incomplete-reference.yaml | 0 .../evals/incomplete-reference.yaml | 0 .../read-ticket/evals/indirect-url.yaml | 0 .../read-ticket/evals/missing-ticket.yaml | 0 .../read-ticket/evals/no-trigger-list.yaml | 0 .../evals/no-trigger-other-provider.yaml | 0 .../evals/pressure-foreign-url.yaml | 0 .../read-ticket/evals/relation-failure.yaml | 0 .../evals/silent-relation-failure.yaml | 0 .../skills/update-ticket/SKILL.md | 22 ++++--- .../skills/update-ticket/agents/openai.yaml | 0 .../update-ticket/evals/ambiguous-target.yaml | 0 .../update-ticket/evals/close-transition.yaml | 0 .../update-ticket/evals/comment-only.yaml | 0 .../evals/no-trigger-other-provider.yaml | 0 .../.claude-plugin/plugin.json | 2 +- .../.codex-plugin/plugin.json | 2 +- .../evals/preflight-ticket-input.yaml | 31 ++++----- python-packages.txt | 2 +- 85 files changed, 482 insertions(+), 279 deletions(-) delete mode 100644 plugins/capability/darrow-tickets-github/.claude-plugin/plugin.json delete mode 100644 plugins/capability/darrow-tickets-github/.codex-plugin/plugin.json rename plugins/capability/{darrow-tickets-github => darrow-tickets}/.claude-plugin/hooks.json (100%) create mode 100644 plugins/capability/darrow-tickets/.claude-plugin/plugin.json create mode 100644 plugins/capability/darrow-tickets/.codex-plugin/plugin.json rename plugins/capability/{darrow-tickets-github => darrow-tickets}/LICENSE (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/README.md (70%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/pyproject.toml (73%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/scripts/run_locked.py (100%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets}/__init__.py (100%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets}/arguments.py (83%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets}/claude_context.py (92%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets}/claude_routing.md (52%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets}/cli.py (73%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets}/errors.py (100%) create mode 100644 plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/__init__.py create mode 100644 plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/__init__.py rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets/providers/github}/create.py (97%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets/providers/github}/provider.py (99%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets/providers/github}/queries.py (96%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets/providers/github}/relations.py (99%) rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets/providers/github}/update.py (96%) create mode 100644 plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/validation.py create mode 100644 plugins/capability/darrow-tickets/backend/src/darrow_tickets/selection.py rename plugins/capability/{darrow-tickets-github/backend/src/darrow_tickets_github => darrow-tickets/backend/src/darrow_tickets}/validation.py (69%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/__init__.py (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/conftest.py (96%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/eval_gh.py (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/fresh_install.py (92%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/fresh_probe.py (96%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_claude_context.py (94%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_contract.py (97%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_eval_gh.py (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_failures.py (95%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_mutations.py (99%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_properties.py (88%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_queries.py (99%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/tests/test_read_boundaries.py (97%) create mode 100644 plugins/capability/darrow-tickets/backend/tests/test_selection.py rename plugins/capability/{darrow-tickets-github => darrow-tickets}/backend/uv.lock (99%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/create-ticket/SKILL.md (87%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/create-ticket/agents/openai.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/create-ticket/evals/dedup-stop.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/create-ticket/evals/no-attribution.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/create-ticket/evals/no-trigger-other-provider.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/create-ticket/evals/relations-by-request.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/create-ticket/evals/structure-evidence.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/list-tickets/SKILL.md (73%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/list-tickets/agents/openai.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/list-tickets/evals/filter-readonly.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/list-tickets/evals/no-trigger-exact-ticket.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/list-tickets/evals/no-trigger-other-provider.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/list-tickets/evals/state-asked.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/list-tickets/evals/truncated-limit.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/SKILL.md (76%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/agents/openai.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/ambiguous-reference.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/conversation-bound-reference.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/direct-id.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/foreign-parent.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/implicit-incomplete-reference.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/incomplete-reference.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/indirect-url.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/missing-ticket.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/no-trigger-list.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/no-trigger-other-provider.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/pressure-foreign-url.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/relation-failure.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/read-ticket/evals/silent-relation-failure.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/update-ticket/SKILL.md (84%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/update-ticket/agents/openai.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/update-ticket/evals/ambiguous-target.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/update-ticket/evals/close-transition.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/update-ticket/evals/comment-only.yaml (100%) rename plugins/capability/{darrow-tickets-github => darrow-tickets}/skills/update-ticket/evals/no-trigger-other-provider.yaml (100%) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b54a55e6..d7f48b6b 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -31,9 +31,9 @@ "description": "Intent-triggered Git skills for branches, commits, PRs, and candidate-bound PR evidence" }, { - "name": "darrow-tickets-github", - "source": "./plugins/capability/darrow-tickets-github", - "description": "GitHub Issues ticket skills: create-ticket, read-ticket, update-ticket, list-tickets" + "name": "darrow-tickets", + "source": "./plugins/capability/darrow-tickets", + "description": "Ticket skills with a bundled GitHub Issues adapter: create-ticket, read-ticket, update-ticket, list-tickets" }, { "name": "darrow-readiness-gate", diff --git a/.github/workflows/python-quality.yml b/.github/workflows/python-quality.yml index ce882242..5b04f061 100644 --- a/.github/workflows/python-quality.yml +++ b/.github/workflows/python-quality.yml @@ -237,7 +237,7 @@ jobs: tickets-windows: name: Tickets Python ${{ matrix.python-version }} on windows-latest needs: changes - if: contains(needs.changes.outputs.packages, 'plugins/capability/darrow-tickets-github/backend') + if: contains(needs.changes.outputs.packages, 'plugins/capability/darrow-tickets/backend') strategy: fail-fast: false matrix: @@ -256,12 +256,12 @@ jobs: - uses: astral-sh/setup-uv@v6 with: enable-cache: true - - run: scripts/check-python --package plugins/capability/darrow-tickets-github/backend + - run: scripts/check-python --package plugins/capability/darrow-tickets/backend tickets-fresh-install: name: Fresh install tickets on ${{ matrix.os }} needs: changes - if: contains(needs.changes.outputs.packages, 'plugins/capability/darrow-tickets-github/backend') + if: contains(needs.changes.outputs.packages, 'plugins/capability/darrow-tickets/backend') strategy: fail-fast: false matrix: @@ -273,7 +273,7 @@ jobs: with: python-version: "3.13" - uses: astral-sh/setup-uv@v6 - - run: uv run --quiet --frozen --no-dev --project plugins/capability/darrow-tickets-github/backend python plugins/capability/darrow-tickets-github/backend/tests/fresh_install.py + - run: uv run --quiet --frozen --no-dev --project plugins/capability/darrow-tickets/backend python plugins/capability/darrow-tickets/backend/tests/fresh_install.py decisions-windows: name: Decisions Python ${{ matrix.python-version }} on windows-latest @@ -634,7 +634,7 @@ jobs: GIT_SELECTED: ${{ contains(needs.changes.outputs.packages, 'plugins/capability/darrow-git/backend') }} GIT_WINDOWS_RESULT: ${{ needs.git-windows.result }} GIT_INSTALL_RESULT: ${{ needs.git-fresh-install.result }} - TICKETS_SELECTED: ${{ contains(needs.changes.outputs.packages, 'plugins/capability/darrow-tickets-github/backend') }} + TICKETS_SELECTED: ${{ contains(needs.changes.outputs.packages, 'plugins/capability/darrow-tickets/backend') }} TICKETS_WINDOWS_RESULT: ${{ needs.tickets-windows.result }} TICKETS_INSTALL_RESULT: ${{ needs.tickets-fresh-install.result }} DECISIONS_SELECTED: ${{ contains(needs.changes.outputs.packages, 'plugins/foundation/darrow-decisions/backend') }} diff --git a/docs/choosing-plugins.md b/docs/choosing-plugins.md index e7d01f78..c36a7a49 100644 --- a/docs/choosing-plugins.md +++ b/docs/choosing-plugins.md @@ -14,7 +14,7 @@ alone; its README owns its prerequisites, behavior, and safety rules. | Review a change independently | [Review](../plugins/capability/darrow-review/README.md) | | Verify implementation acceptance through independent assessment | [Verification](../plugins/capability/darrow-verification/README.md) | | Create a branch, commit, or pull request | [Git](../plugins/capability/darrow-git/README.md) | -| Read, list, create, or update GitHub issues | [GitHub tickets](../plugins/capability/darrow-tickets-github/README.md) | +| Read, list, create, or update GitHub issues | [Tickets](../plugins/capability/darrow-tickets/README.md) | | Understand technical structure in a compact visual | [Explanation](../plugins/capability/darrow-explanation/README.md) | | Export Codex turn telemetry to Langfuse | [Langfuse observability](../plugins/capability/darrow-observability-langfuse/README.md) | | Organize repository agent guidance | [Information architecture](../plugins/foundation/darrow-information-architecture/README.md) | diff --git a/docs/design.md b/docs/design.md index 7f5e954f..1032fa5d 100644 --- a/docs/design.md +++ b/docs/design.md @@ -76,12 +76,13 @@ on top of the host's skill discovery. If an optional skill is unavailable, the consumer follows its own documented stop or fallback behavior instead of reaching into another plugin. -Provider-specific integrations name both the capability and provider, such as -`darrow-observability-langfuse` and `darrow-tickets-github`. Ticket providers -remain separately installable while advertising the same operation intents. -Their metadata distinguishes supported trackers; consumers honor the user's +Provider-specific integrations may name both the capability and provider, such +as `darrow-observability-langfuse`. Ticket management instead uses one +`darrow-tickets` plugin with four operation skills and contained tracker +adapters. Its metadata names supported trackers; consumers honor the user's tracker choice or established project context and clarify unresolved provider -ambiguity before tracker access. +ambiguity before tracker access. Adding a tracker does not require another +public ticket skill or a sibling runtime dependency. Advertised intent identifies a potential provider; the caller must also check its prerequisites, effects, returned evidence and stop conditions. A capability diff --git a/docs/specs/ticket-management.md b/docs/specs/ticket-management.md index 12a2f3f9..e4ef8307 100644 --- a/docs/specs/ticket-management.md +++ b/docs/specs/ticket-management.md @@ -4,8 +4,9 @@ Consolidates ticket operations into intent-triggered skills so that creating and updating tickets is consistent, traceable, and backend-neutral regardless of which agent runtime executes them and which tracker backs them. -GitHub Issues provider: `darrow-tickets-github`. Skills: `create-ticket`, `read-ticket`, -`update-ticket`, `list-tickets`. +Plugin: `darrow-tickets`. Skills: `create-ticket`, `read-ticket`, +`update-ticket`, `list-tickets`. The bundled GitHub Issues provider is the +only shipped tracker adapter. Other trackers may be added inside this plugin. Scope: mechanics only. These skills record and mutate tickets; they do not refine requirements, plan or break down work, or review solutions. Those @@ -23,10 +24,10 @@ stable intent ("create a ticket for X") while the backend stays swappable. ## Backend contract -- **TM-P3 — Contained portable mechanics.** The GitHub provider ships one - dependency-free Python package managed by UV, invoked through its frozen +- **TM-P3 — Contained portable mechanics.** The ticket plugin ships one + contained Python package managed by UV, invoked through its frozen `darrow-ticket` entrypoint by all four skills. Preserve the command arguments, - output records, refusals, and exit codes while removing the Bash launcher. + output records, refusals, and exit codes of the existing GitHub provider. Use argument-vector subprocesses, explicit UTF-8 JSON decoding and validation, and native filesystem and temporary-file handling on Linux, macOS, and Windows. Provider failures must stop pending mutations; completed mutations remain @@ -38,23 +39,25 @@ stable intent ("create a ticket for X") while the backend stays swappable. remain static and nonmutating, perform no repository or tracker inspection, preserve provider boundaries, and leave workflow and authority in the skill. -- **TM-P1 — Independently installable providers.** Each tracker implementation - lives in its own `darrow-tickets-` plugin. The shipped provider is - GitHub Issues via `gh`. Future trackers use separate, self-contained plugins; - no shared runtime plugin or provider registry is required. Repository-resident +- **TM-P1 — One self-contained ticket plugin.** The four skills and all tracker + adapters live in `darrow-tickets`. The shipped adapter is GitHub Issues via + `gh`. Future trackers extend this contained Python package; no sibling plugin, + shared runtime, or external provider registry is required. Only the selected + adapter's prerequisites are required for an operation. Repository-resident Darrow ticket directories contain published artifacts only; they are never a tracker or a file-backed ticket store. - **TM-P2 — Shared intents, explicit provider scope.** Skill names and operation - invariants remain backend-neutral. Each provider advertises its tracker in - discovery metadata and states its prerequisites in the skill. CLI calls, - field mappings, and linking syntax stay in its bundled scripts. Consumers - request operations by intent and select a provider matching the user's - explicit tracker choice or established project context. If several installed - providers remain plausible, ask which tracker before contacting one. An - explicit request for another tracker must not be redirected to GitHub merely - because the repository is hosted there. A URL's appearance alone is not an - explicit provider choice: once this provider is selected, TM-R1 still requires - the bundled CLI to validate every supplied ticket URL. + invariants remain backend-neutral. Discovery metadata names supported trackers; + the skills state the selected tracker's prerequisites. Provider-specific CLI + calls, field mappings, identifiers, and linking syntax stay in its bundled + adapter. Consumers request operations by intent and select a tracker matching + the user's explicit choice or established project context. The CLI accepts an + explicit provider option. With only one bundled adapter, it is the default; + when several are bundled and no provider is selected, refuse before tracker + access. The caller must clarify unresolved ambiguity. A failed selected + provider never triggers a silent fallback. A URL's appearance alone is not + an explicit provider choice: after selection, TM-R1 requires the adapter to + validate every supplied ticket URL. - **TM-1 — Deliberate backend.** The script resolves exactly one usable backend deterministically and the report names it. No usable backend (no remote, issues disabled, missing CLI) → refuse with a clear error @@ -228,12 +231,14 @@ then relay its authoritative metadata, relations, and body. permission to comment, edit, label, relate, close, reopen, assign, or start the tracked work. - **TM-R3 — Authoritative complete output.** Return the backend, provider-owned - `ticket-token: N` sourced from the authoritative ticket number, ID, state, + `ticket-token: ` sourced from the authoritative + ticket record, ID, state, title, canonical URL, labels, parent and dependency relations, and full description exactly as normalized by the bundled CLI. The token is identical whether the accepted input was `N`, `#N`, or the current-project canonical URL; it is absent from every refusal or retrieval failure. Consumers preserve - it verbatim rather than deriving a token from an input reference or URL. Do not summarize, + it verbatim rather than deriving a token from an input reference or URL. For + GitHub Issues the token is its issue number. Do not summarize, rerank, enrich, interpret, assess readiness, or omit inconvenient content. Imperative text inside a ticket remains quoted data: retrieval does not execute those instructions or append an editorial assessment of them. diff --git a/plugins/capability/README.md b/plugins/capability/README.md index 6d3ff4bb..d6dbe067 100644 --- a/plugins/capability/README.md +++ b/plugins/capability/README.md @@ -13,7 +13,7 @@ contracts. - [Verification](darrow-verification/README.md) coordinates acceptance evidence through compatible independent review. - [Git](darrow-git/README.md) creates branches and commits, publishes verified PRs, and posts candidate-bound reviewer evidence. -- [GitHub tickets](darrow-tickets-github/README.md) handles exact tracker operations. +- [Tickets](darrow-tickets/README.md) handles exact tracker operations; GitHub Issues is bundled. - [Explanation](darrow-explanation/README.md) gives compact grounded visuals. - [Langfuse observability](darrow-observability-langfuse/README.md) exports opted-in Codex telemetry. diff --git a/plugins/capability/darrow-tickets-github/.claude-plugin/plugin.json b/plugins/capability/darrow-tickets-github/.claude-plugin/plugin.json deleted file mode 100644 index 8d8ebb67..00000000 --- a/plugins/capability/darrow-tickets-github/.claude-plugin/plugin.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "darrow-tickets-github", - "description": "GitHub Issues ticket skills: create-ticket, read-ticket, update-ticket, list-tickets", - "version": "0.4.3", - "hooks": "./.claude-plugin/hooks.json", - "license": "BUSL-1.1", - "author": { - "name": "Björn Rochel", - "email": "bjoern@bjro.de" - } -} diff --git a/plugins/capability/darrow-tickets-github/.codex-plugin/plugin.json b/plugins/capability/darrow-tickets-github/.codex-plugin/plugin.json deleted file mode 100644 index 24aa08dd..00000000 --- a/plugins/capability/darrow-tickets-github/.codex-plugin/plugin.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "darrow-tickets-github", - "version": "0.4.3", - "description": "GitHub Issues ticket skills: create-ticket, read-ticket, update-ticket, list-tickets", - "author": { - "name": "Björn Rochel" - }, - "skills": "./skills/", - "interface": { - "displayName": "Darrow -> Tickets (GitHub)", - "shortDescription": "Create, read, find, and update GitHub issues", - "longDescription": "Create well-formed GitHub issues, retrieve one exact issue, find relevant open work, and apply one verified update through the bundled GitHub CLI provider.", - "developerName": "Björn Rochel", - "category": "Productivity", - "capabilities": ["Interactive", "Read", "Write"], - "defaultPrompt": [ - "Create a ticket for this problem.", - "Read ticket #42.", - "List the open bugs for this project.", - "Close the ticket for this completed work." - ] - } -} diff --git a/plugins/capability/darrow-tickets-github/.claude-plugin/hooks.json b/plugins/capability/darrow-tickets/.claude-plugin/hooks.json similarity index 100% rename from plugins/capability/darrow-tickets-github/.claude-plugin/hooks.json rename to plugins/capability/darrow-tickets/.claude-plugin/hooks.json diff --git a/plugins/capability/darrow-tickets/.claude-plugin/plugin.json b/plugins/capability/darrow-tickets/.claude-plugin/plugin.json new file mode 100644 index 00000000..8460ccc0 --- /dev/null +++ b/plugins/capability/darrow-tickets/.claude-plugin/plugin.json @@ -0,0 +1,11 @@ +{ + "name": "darrow-tickets", + "description": "Ticket skills with a bundled GitHub Issues adapter: create-ticket, read-ticket, update-ticket, list-tickets", + "version": "0.5.0", + "hooks": "./.claude-plugin/hooks.json", + "license": "BUSL-1.1", + "author": { + "name": "Björn Rochel", + "email": "bjoern@bjro.de" + } +} diff --git a/plugins/capability/darrow-tickets/.codex-plugin/plugin.json b/plugins/capability/darrow-tickets/.codex-plugin/plugin.json new file mode 100644 index 00000000..833ebfea --- /dev/null +++ b/plugins/capability/darrow-tickets/.codex-plugin/plugin.json @@ -0,0 +1,23 @@ +{ + "name": "darrow-tickets", + "version": "0.5.0", + "description": "Ticket skills with a bundled GitHub Issues adapter: create-ticket, read-ticket, update-ticket, list-tickets", + "author": { + "name": "Björn Rochel" + }, + "skills": "./skills/", + "interface": { + "displayName": "Darrow -> Tickets", + "shortDescription": "Create, read, find, and update tickets", + "longDescription": "Create well-formed tickets, retrieve one exact ticket, find relevant open work, and apply one verified update through the bundled GitHub Issues adapter.", + "developerName": "Björn Rochel", + "category": "Productivity", + "capabilities": ["Interactive", "Read", "Write"], + "defaultPrompt": [ + "Create a ticket for this problem.", + "Read ticket #42.", + "List the open bugs for this project.", + "Close the ticket for this completed work." + ] + } +} diff --git a/plugins/capability/darrow-tickets-github/LICENSE b/plugins/capability/darrow-tickets/LICENSE similarity index 100% rename from plugins/capability/darrow-tickets-github/LICENSE rename to plugins/capability/darrow-tickets/LICENSE diff --git a/plugins/capability/darrow-tickets-github/README.md b/plugins/capability/darrow-tickets/README.md similarity index 70% rename from plugins/capability/darrow-tickets-github/README.md rename to plugins/capability/darrow-tickets/README.md index 9cf33e21..8f73d7a3 100644 --- a/plugins/capability/darrow-tickets-github/README.md +++ b/plugins/capability/darrow-tickets/README.md @@ -1,20 +1,20 @@ -# Darrow Tickets for GitHub +# Darrow Tickets -This independently installable plugin provides everyday ticket operations for -GitHub Issues. The skills decide what the user means and how to present evidence; -one bundled CLI resolves the current repository, validates targets and taxonomy, -and performs only the requested operation through `gh`. +This independently installable plugin provides everyday ticket operations. Four +skills decide what the user means and how to present evidence; one contained +Python package selects a tracker adapter and performs the requested operation. +GitHub Issues through `gh` is the only bundled adapter in this release. -The result is a consistent ticket workflow without teaching every skill raw -tracker commands. Skill names and operation contracts stay provider-neutral; -other trackers belong in separate provider plugins. Callers request ticket -operations by intent without reading this plugin's files. Explicit tracker -choices and established project context select the provider; unresolved choices -between installed providers require clarification before tracker access. +The operation skills and CLI shape remain stable as adapters are added inside +this plugin. Callers request ticket operations by intent without reading its +files. An explicit tracker choice takes precedence over project context. With +one bundled adapter, GitHub is the default; with several, an unresolved choice +must be clarified before tracker access. An unavailable selected adapter never +falls back to GitHub. ## Prerequisites -Install `darrow-tickets-github` from the Darrow marketplace after checking +Install `darrow-tickets` from the Darrow marketplace after checking [hosts and prerequisites](#hosts-and-prerequisites). Claude's session-start hook supplies static discovery context so matching @@ -44,7 +44,7 @@ Example: _“Which open bugs are in the next milestone?”_ ### `read-ticket` Retrieves one exact current-project ticket by ID or canonical URL. It returns -the authoritative metadata, a provider-owned `ticket-token: N`, tracker-native relations, and full description +the authoritative metadata, a provider-owned `ticket-token:`, tracker-native relations, and full description without summarizing, interpreting, or changing tracker state. Example: _“What does ticket #42 say?”_ @@ -60,12 +60,13 @@ Example: _“Comment on #42 with the failing command.”_ ### `darrow-ticket` -A contained Python facade used by all four skills. It resolves the current -GitHub repository, inspects its taxonomy, searches and fetches tickets, validates -structured bodies and transition targets, owns backend-specific relation -syntax, and rejects ambiguous or unsupported mutations. It exposes the -capability through deterministic commands rather than as a general tracker -client. +A contained Python facade used by all four skills. Its public argument parser +preserves provider-owned identifiers, selects one bundled adapter, and then +dispatches one operation. Shared validation covers ticket body structure and +attribution. The GitHub adapter owns numeric IDs, repository resolution, `gh` +calls, label mapping, relation syntax, and GitHub output. Future adapters stay +inside this package and own their own identifiers, scope, credentials, native +state mapping, and relation mechanics. Invoke the package directly from any current-project working directory: @@ -73,8 +74,13 @@ Invoke the package directly from any current-project working directory: uv run --quiet --no-project "/backend/scripts/run_locked.py" darrow-ticket inspect ``` +Pass `--provider github` after a command's arguments to select the bundled +adapter explicitly. Unsupported choices, including `--provider linear`, refuse +before tracker access. A supplied URL is validated by the selected adapter; +its appearance does not select a tracker. + The commands remain `inspect`, `list`, `get`, `create`, `comment`, `describe`, -`close`, `reopen`, `label`, and `relate`. Their options, compact reports, +`close`, `reopen`, `label`, and `relate`. Existing GitHub options, compact reports, refusals, and exit codes are preserved. There is no Bash compatibility launcher. GitHub JSON is decoded and validated in Python; subprocesses use argument vectors and the resolved origin's host/repository. Temporary creation payloads @@ -94,6 +100,9 @@ Exit codes: 2 input/filesystem error, 3 unusable backend, 4 provider failure, reproduction steps, acceptance criteria, or AI attribution. - `read-ticket` and `list-tickets` are strictly read-only, and `update-ticket` applies only the single mutation requested. +- A selected adapter is the only tracker contacted. Adding another adapter must + preserve exact target validation, honest pagination and failure reporting, + and the provider-owned opaque `ticket-token:` value. - GitHub calls use the origin repository's host even when `GH_HOST` or `GH_REPO` names another target in the caller's environment. - Parent relations are supported within the current repository. An existing @@ -120,7 +129,8 @@ to live GitHub state. The colocated skill evals exercise judgment separately. ## When to use -Operate on current-project GitHub Issues. Use the matching provider for another tracker and clarify an ambiguous provider before access. +Operate on current-project GitHub Issues. Linear and other trackers are not yet +supported; an explicit request for one stops before tracker access. ## Hosts and prerequisites @@ -131,9 +141,12 @@ remote. Runtime dependencies are otherwise empty. ## Installation -Install `darrow-tickets-github@darrow` using the +Install `darrow-tickets@darrow` using the [host installation, update, removal, and verification instructions](https://github.com/BjRo/darrow/blob/main/docs/installing-plugins.md). Review this plugin's local prerequisites and safety boundaries first. +Existing `darrow-tickets-github` installations must be removed before installing +the renamed plugin, then a new host session started. Installing both identities +would expose duplicate ticket skills. ## Usage @@ -142,7 +155,7 @@ An ordinary request can select the appropriate capability: > What does ticket #42 say? To select it explicitly, choose `read-ticket` from Codex's `$` skill menu, -or use `/darrow-tickets-github:read-ticket` in Claude Code, followed by your request. +or use `/darrow-tickets:read-ticket` in Claude Code, followed by your request. ## Expected result diff --git a/plugins/capability/darrow-tickets-github/backend/pyproject.toml b/plugins/capability/darrow-tickets/backend/pyproject.toml similarity index 73% rename from plugins/capability/darrow-tickets-github/backend/pyproject.toml rename to plugins/capability/darrow-tickets/backend/pyproject.toml index 5bc02597..4134881e 100644 --- a/plugins/capability/darrow-tickets-github/backend/pyproject.toml +++ b/plugins/capability/darrow-tickets/backend/pyproject.toml @@ -1,13 +1,13 @@ [project] -name = "darrow-tickets-github" -version = "0.4.0" -description = "GitHub Issues facade for Darrow ticket skills" +name = "darrow-tickets" +version = "0.5.0" +description = "Contained tracker adapters for Darrow ticket skills" requires-python = ">=3.10,<3.14" dependencies = [] [project.scripts] -darrow-ticket = "darrow_tickets_github.cli:entrypoint" -darrow-tickets-claude-context = "darrow_tickets_github.claude_context:main" +darrow-ticket = "darrow_tickets.cli:entrypoint" +darrow-tickets-claude-context = "darrow_tickets.claude_context:main" [dependency-groups] dev = [ @@ -26,7 +26,7 @@ build-backend = "hatchling.build" default-groups = [] [tool.hatch.build.targets.wheel] -packages = ["src/darrow_tickets_github"] +packages = ["src/darrow_tickets"] [tool.ruff] target-version = "py310" @@ -51,7 +51,7 @@ testpaths = ["tests"] [tool.coverage.run] branch = true -source = ["darrow_tickets_github"] +source = ["darrow_tickets"] [tool.coverage.report] show_missing = true diff --git a/plugins/capability/darrow-tickets-github/backend/scripts/run_locked.py b/plugins/capability/darrow-tickets/backend/scripts/run_locked.py similarity index 100% rename from plugins/capability/darrow-tickets-github/backend/scripts/run_locked.py rename to plugins/capability/darrow-tickets/backend/scripts/run_locked.py diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/__init__.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/__init__.py similarity index 100% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/__init__.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/__init__.py diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/arguments.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/arguments.py similarity index 83% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/arguments.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/arguments.py index 008dc9ce..44eb4141 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/arguments.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/arguments.py @@ -5,7 +5,6 @@ from dataclasses import dataclass from .errors import TicketError -from .validation import norm_id OPTIONS = { "inspect": (), @@ -50,12 +49,12 @@ def reference(command: str, args: list[str]) -> str: if not args: suffix = " or canonical URL" if command == "get" else "" raise TicketError(f"error: {command} needs a ticket id{suffix}") - if command == "get" and len(args) != 1: + if command == "get" and len(args) != 1 and args[1] != "--provider": raise TicketError("error: get accepts exactly one ticket reference") - if command in {"close", "reopen"} and len(args) != 1: + if command in {"close", "reopen"} and len(args) != 1 and args[1] != "--provider": raise TicketError(f"error: unknown {command} argument: {args[1]}") value = args.pop(0) - return value if command == "get" else norm_id(value) + return value def option_value(command: str, flag: str, tokens: Iterator[str]) -> str: @@ -68,8 +67,6 @@ def option_value(command: str, flag: str, tokens: Iterator[str]) -> str: if command == "create" and flag == "depends-on": raise TicketError("error: invalid ticket id: ") from exc raise TicketError(f"error: --{flag} needs a value") from exc - if flag in {"parent", "depends-on", "remove-depends-on"}: - return norm_id(value) return value @@ -80,10 +77,8 @@ def parse(command: str, argv: Sequence[str]) -> Arguments: tokens = iter(args) for token in tokens: flag = token.removeprefix("--") - if token != f"--{flag}" or flag not in OPTIONS[command]: + if token != f"--{flag}" or flag not in (*OPTIONS[command], "provider"): raise TicketError(f"error: unknown {command} argument: {token}") value = option_value(command, flag, tokens) - if command == "create" and flag == "depends-on" and value in options[flag]: - raise TicketError(f"error: duplicate --depends-on: #{value}") options[flag].append(value) return Arguments(command, target, dict(options)) diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/claude_context.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/claude_context.py similarity index 92% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/claude_context.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/claude_context.py index b1e3dfed..f18ae574 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/claude_context.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/claude_context.py @@ -6,7 +6,7 @@ def main() -> None: context = ( - files("darrow_tickets_github") + files("darrow_tickets") .joinpath("claude_routing.md") .read_text(encoding="utf-8") ) diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/claude_routing.md b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/claude_routing.md similarity index 52% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/claude_routing.md rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/claude_routing.md index d72f7bed..7adcb353 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/claude_routing.md +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/claude_routing.md @@ -1,8 +1,9 @@ -The installed Darrow GitHub ticket skills own requests to create, list, read, -or update GitHub Issues. When GitHub Issues is selected or no tracker is -established, invoke the matching installed skill before repository inspection, -tracker access, clarification, or your final response. Select from the installed -descriptions; the selected skill supplies the workflow and authority boundaries. +The installed Darrow ticket skills own requests to create, list, read, or update +tickets through a bundled tracker adapter. GitHub Issues is currently the only +bundled tracker. When it is selected or no tracker is established, invoke the +matching installed skill before repository inspection, tracker access, +clarification, or your final response. Select from the installed descriptions; +the selected skill supplies the workflow and authority boundaries. Route by the requested operation before judging its prerequisites. A request to file a reported bug belongs to create-ticket even when its report has not been @@ -10,7 +11,8 @@ verified against local source. Missing ticket IDs and ambiguous references remain read-ticket requests. Evidence requirements and missing inputs are for the owning skill to handle, not reasons to bypass it. -Honor an explicit choice of another tracker such as Jira or Linear. Planning, +Honor an explicit choice of an unsupported tracker such as Jira or Linear. +Planning, readiness assessment, implementation, and generic questions do not by themselves request ticket operations. Loading this context does not authorize tracker access or any mutation and does not start a workflow automatically. diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/cli.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/cli.py similarity index 73% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/cli.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/cli.py index d472547d..e010bc51 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/cli.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/cli.py @@ -2,28 +2,28 @@ import io import sys -from collections.abc import Callable, Sequence +from collections.abc import Sequence -from .arguments import Arguments, parse -from .create import create_ticket +from .arguments import parse from .errors import TicketError -from .queries import get_ticket, inspect_backend, list_tickets -from .update import label_ticket, relate_ticket, transition, write_body +from .selection import select_provider -COMMANDS: dict[str, Callable[[Arguments], None]] = { - "inspect": inspect_backend, - "list": list_tickets, - "get": get_ticket, - "create": create_ticket, - "comment": write_body, - "describe": write_body, - "close": transition, - "reopen": transition, - "label": label_ticket, - "relate": relate_ticket, -} +COMMANDS = frozenset( + { + "inspect", + "list", + "get", + "create", + "comment", + "describe", + "close", + "reopen", + "label", + "relate", + } +) -USAGE = """usage: ticket [args] +USAGE = """usage: ticket [args] [--provider ] inspect list [--state open|closed|all] [--type ] [--label ]... [--search ] @@ -55,7 +55,8 @@ def main(argv: Sequence[str] | None = None) -> int: return 64 try: command = args.pop(0) - COMMANDS[command](parse(command, args)) + arguments = parse(command, args) + select_provider(arguments).execute(arguments) except TicketError as exc: sys.stdout.flush() text = str(exc) diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/errors.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/errors.py similarity index 100% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/errors.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/errors.py diff --git a/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/__init__.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/__init__.py new file mode 100644 index 00000000..45b43cc7 --- /dev/null +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/__init__.py @@ -0,0 +1,11 @@ +"""Contained tracker adapters for the public ticket commands.""" + +from typing import Protocol + +from ..arguments import Arguments + + +class TicketAdapter(Protocol): + """Execute one public operation using one selected tracker.""" + + def execute(self, args: Arguments) -> None: ... diff --git a/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/__init__.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/__init__.py new file mode 100644 index 00000000..6092d7eb --- /dev/null +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/__init__.py @@ -0,0 +1,44 @@ +"""GitHub Issues adapter for the public ticket commands.""" + +from ...arguments import Arguments +from ...errors import TicketError +from .create import create_ticket +from .queries import get_ticket, inspect_backend, list_tickets +from .update import label_ticket, relate_ticket, transition, write_body +from .validation import norm_id + + +def normalize_references(args: Arguments) -> None: + if args.command not in {"inspect", "list", "create", "get"}: + args.reference = norm_id(args.reference) + for flag in ("parent", "depends-on", "remove-depends-on"): + if flag in args.options: + args.options[flag] = [norm_id(value) for value in args.values(flag)] + if args.command == "create": + check_duplicate_dependencies(args.values("depends-on")) + + +def check_duplicate_dependencies(targets: list[str]) -> None: + if len(targets) != len(set(targets)): + repeated = next(value for value in targets if targets.count(value) > 1) + raise TicketError(f"error: duplicate --depends-on: #{repeated}") + + +class GitHubAdapter: + """Keep GitHub-specific validation and operations inside one adapter.""" + + def execute(self, args: Arguments) -> None: + normalize_references(args) + commands = { + "inspect": inspect_backend, + "list": list_tickets, + "get": get_ticket, + "create": create_ticket, + "comment": write_body, + "describe": write_body, + "close": transition, + "reopen": transition, + "label": label_ticket, + "relate": relate_ticket, + } + commands[args.command](args) diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/create.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/create.py similarity index 97% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/create.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/create.py index 3d462f5c..95a89f05 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/create.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/create.py @@ -4,18 +4,20 @@ import tempfile from pathlib import Path -from . import relations -from .arguments import Arguments -from .errors import TicketError -from .provider import Provider -from .validation import ( +from ...arguments import Arguments +from ...errors import TicketError +from ...validation import ( check_attribution, - check_markers, check_structure, check_title, check_type, - existing_label, read_body, +) +from . import relations +from .provider import Provider +from .validation import ( + check_markers, + existing_label, type_label, ) diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/provider.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/provider.py similarity index 99% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/provider.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/provider.py index 6d79bd19..61563259 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/provider.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/provider.py @@ -8,7 +8,7 @@ from dataclasses import dataclass from typing import cast -from .errors import TicketError, bounded, failure +from ...errors import TicketError, bounded, failure from .validation import norm_id diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/queries.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/queries.py similarity index 96% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/queries.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/queries.py index efc78036..3dfd50da 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/queries.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/queries.py @@ -2,9 +2,10 @@ import re +from ...arguments import Arguments +from ...errors import TicketError +from ...validation import check_type from . import relations -from .arguments import Arguments -from .errors import TicketError from .provider import ( Provider, array_value, @@ -13,7 +14,7 @@ object_value, string_field, ) -from .validation import check_label, check_type, type_label +from .validation import check_label, type_label def inspect_backend(args: Arguments) -> None: diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/relations.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/relations.py similarity index 99% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/relations.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/relations.py index 190dcade..8a2da591 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/relations.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/relations.py @@ -3,7 +3,7 @@ import json import re -from .errors import TicketError, failure +from ...errors import TicketError, failure from .provider import Provider, array_value, number_field, object_value, string_field diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/update.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/update.py similarity index 96% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/update.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/update.py index 8da9fdd2..139d021e 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/update.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/update.py @@ -1,15 +1,14 @@ """Single mutations with state reads and explicit no-op refusals.""" +from ...arguments import Arguments +from ...errors import TicketError +from ...validation import check_attribution, read_body from . import relations -from .arguments import Arguments -from .errors import TicketError from .provider import Provider, labels_from from .validation import ( - check_attribution, check_label, check_markers, existing_label, - read_body, ) diff --git a/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/validation.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/validation.py new file mode 100644 index 00000000..8ff36b1e --- /dev/null +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/providers/github/validation.py @@ -0,0 +1,48 @@ +"""GitHub identifiers, label taxonomy, and relation marker rules.""" + +import re + +from ...errors import TicketError + +TYPE_LABELS = { + "bug": ("bug",), + "feature": ("feature", "enhancement"), + "task": ("task",), + "chore": ("chore",), +} + + +def norm_id(value: str) -> str: + number = value.removeprefix("#") + if not re.fullmatch(r"[0-9]+", number): + raise TicketError(f"error: invalid ticket id: {value}") + return number.lstrip("0") or "0" + + +def type_label(kind: str, labels: list[str]) -> str: + return next((label for label in TYPE_LABELS[kind] if label in labels), "") + + +def check_label(value: str) -> None: + if "," in value: + raise TicketError( + f"error: labels containing commas are not supported by the gh backend: {value}", + 8, + ) + + +def existing_label(value: str, labels: list[str]) -> None: + check_label(value) + if value not in labels: + choices = "\n".join(labels[:50]) + raise TicketError( + f"error: label does not exist in the tracker: {value} — pick from:\n{choices}", + 8, + ) + + +def check_markers(body: str, instruction: str) -> None: + if re.search(r"^(Depends-on|Parent): #[0-9]+$", body, re.MULTILINE): + raise TicketError( + f"error: relation lines in the body are not recorded — {instruction}" + ) diff --git a/plugins/capability/darrow-tickets/backend/src/darrow_tickets/selection.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/selection.py new file mode 100644 index 00000000..c7b81d6d --- /dev/null +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/selection.py @@ -0,0 +1,32 @@ +"""Choose one bundled tracker before any tracker access.""" + +from .arguments import Arguments +from .errors import TicketError +from .providers import TicketAdapter +from .providers.github import GitHubAdapter + +PROVIDERS: dict[str, TicketAdapter] = {"github": GitHubAdapter()} + + +def select_provider(args: Arguments) -> TicketAdapter: + requested = args.values("provider") + if len(requested) > 1: + raise TicketError("error: --provider accepts exactly one tracker") + choice = requested[0] if requested else "" + if not choice: + if len(PROVIDERS) != 1: + names = ", ".join(sorted(PROVIDERS)) or "(none)" + raise TicketError( + f"error: ticket provider is ambiguous — select --provider from: {names}", + 3, + ) + choice = next(iter(PROVIDERS)) + adapter = PROVIDERS.get(choice) + if adapter is None: + names = ", ".join(sorted(PROVIDERS)) or "(none)" + raise TicketError( + f"error: ticket provider is unavailable: {choice} (available: {names})", + 3, + ) + args.options.pop("provider", None) + return adapter diff --git a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/validation.py b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/validation.py similarity index 69% rename from plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/validation.py rename to plugins/capability/darrow-tickets/backend/src/darrow_tickets/validation.py index 528cccaf..e73ced48 100644 --- a/plugins/capability/darrow-tickets-github/backend/src/darrow_tickets_github/validation.py +++ b/plugins/capability/darrow-tickets/backend/src/darrow_tickets/validation.py @@ -5,12 +5,6 @@ from .errors import TicketError -TYPE_LABELS = { - "bug": ("bug",), - "feature": ("feature", "enhancement"), - "task": ("task",), - "chore": ("chore",), -} HEADINGS = { "bug": ("Observed", "Expected", "Reproduction"), "feature": ("Motivation", "Acceptance criteria"), @@ -27,40 +21,11 @@ ) -def norm_id(value: str) -> str: - number = value.removeprefix("#") - if not re.fullmatch(r"[0-9]+", number): - raise TicketError(f"error: invalid ticket id: {value}") - return number.lstrip("0") or "0" - - def check_type(kind: str) -> None: - if kind not in TYPE_LABELS: + if kind not in HEADINGS: raise TicketError(f"error: unknown type: {kind} (bug, feature, task, chore)") -def type_label(kind: str, labels: list[str]) -> str: - return next((label for label in TYPE_LABELS[kind] if label in labels), "") - - -def check_label(value: str) -> None: - if "," in value: - raise TicketError( - f"error: labels containing commas are not supported by the gh backend: {value}", - 8, - ) - - -def existing_label(value: str, labels: list[str]) -> None: - check_label(value) - if value not in labels: - choices = "\n".join(labels[:50]) - raise TicketError( - f"error: label does not exist in the tracker: {value} — pick from:\n{choices}", - 8, - ) - - def check_title(title: str) -> None: if not title.strip(): raise TicketError("error: title is empty", 5) @@ -88,13 +53,6 @@ def check_attribution(text: str) -> None: raise TicketError("error: AI attribution is not allowed in tickets", 6) -def check_markers(body: str, instruction: str) -> None: - if re.search(r"^(Depends-on|Parent): #[0-9]+$", body, re.MULTILINE): - raise TicketError( - f"error: relation lines in the body are not recorded — {instruction}" - ) - - def section_lines(body: str) -> list[tuple[str, bool]]: """Pair lines with whether they may be interpreted as headings.""" fenced = False diff --git a/plugins/capability/darrow-tickets-github/backend/tests/__init__.py b/plugins/capability/darrow-tickets/backend/tests/__init__.py similarity index 100% rename from plugins/capability/darrow-tickets-github/backend/tests/__init__.py rename to plugins/capability/darrow-tickets/backend/tests/__init__.py diff --git a/plugins/capability/darrow-tickets-github/backend/tests/conftest.py b/plugins/capability/darrow-tickets/backend/tests/conftest.py similarity index 96% rename from plugins/capability/darrow-tickets-github/backend/tests/conftest.py rename to plugins/capability/darrow-tickets/backend/tests/conftest.py index cdb89d42..fbbb6e69 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/conftest.py +++ b/plugins/capability/darrow-tickets/backend/tests/conftest.py @@ -9,7 +9,7 @@ import pytest -from darrow_tickets_github import provider +from darrow_tickets.providers.github import provider @dataclass @@ -126,7 +126,9 @@ def deps(self, *pages: list[int], number: str = "12") -> None: def backend(monkeypatch: pytest.MonkeyPatch) -> Backend: fake = Backend() monkeypatch.setattr(provider, "execute", fake.execute) - monkeypatch.setattr("darrow_tickets_github.provider.shutil.which", lambda _: "gh") + monkeypatch.setattr( + "darrow_tickets.providers.github.provider.shutil.which", lambda _: "gh" + ) monkeypatch.setenv("GH_HOST", "foreign.test") monkeypatch.setenv("GH_REPO", "foreign.test/other/repo") return fake diff --git a/plugins/capability/darrow-tickets-github/backend/tests/eval_gh.py b/plugins/capability/darrow-tickets/backend/tests/eval_gh.py similarity index 100% rename from plugins/capability/darrow-tickets-github/backend/tests/eval_gh.py rename to plugins/capability/darrow-tickets/backend/tests/eval_gh.py diff --git a/plugins/capability/darrow-tickets-github/backend/tests/fresh_install.py b/plugins/capability/darrow-tickets/backend/tests/fresh_install.py similarity index 92% rename from plugins/capability/darrow-tickets-github/backend/tests/fresh_install.py rename to plugins/capability/darrow-tickets/backend/tests/fresh_install.py index 3dfeaf44..bf58bcf7 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/fresh_install.py +++ b/plugins/capability/darrow-tickets/backend/tests/fresh_install.py @@ -33,6 +33,7 @@ def validate(copy: Path, fixture: Path) -> None: "-c", "import importlib.metadata as m; print('\\n'.join(d.metadata['Name'] or '' for d in m.distributions()))", ) + assert "darrow-tickets" in packages.splitlines() assert not any( tool in packages for tool in ("pytest", "ruff", "mypy", "hypothesis", "coverage") @@ -42,6 +43,16 @@ def validate(copy: Path, fixture: Path) -> None: assert context["hookSpecificOutput"]["hookEventName"] == "SessionStart" assert context["hookSpecificOutput"]["additionalContext"].strip() assert "usage: ticket" in command(fixture, *uv, "darrow-ticket", expected=64) + assert "provider is unavailable: linear" in command( + fixture, + *uv, + "darrow-ticket", + "get", + "ENG-12", + "--provider", + "linear", + expected=3, + ) assert "not inside a git work tree" in command( fixture, *uv, "darrow-ticket", "inspect", expected=3 ) diff --git a/plugins/capability/darrow-tickets-github/backend/tests/fresh_probe.py b/plugins/capability/darrow-tickets/backend/tests/fresh_probe.py similarity index 96% rename from plugins/capability/darrow-tickets-github/backend/tests/fresh_probe.py rename to plugins/capability/darrow-tickets/backend/tests/fresh_probe.py index 93c8f047..56ac990f 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/fresh_probe.py +++ b/plugins/capability/darrow-tickets/backend/tests/fresh_probe.py @@ -5,8 +5,8 @@ from pathlib import Path from unittest.mock import patch -from darrow_tickets_github import provider -from darrow_tickets_github.cli import main +from darrow_tickets.cli import main +from darrow_tickets.providers.github import provider REAL_EXECUTE = provider.execute ISSUE = { diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_claude_context.py b/plugins/capability/darrow-tickets/backend/tests/test_claude_context.py similarity index 94% rename from plugins/capability/darrow-tickets-github/backend/tests/test_claude_context.py rename to plugins/capability/darrow-tickets/backend/tests/test_claude_context.py index 346e0cac..beb4f730 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/test_claude_context.py +++ b/plugins/capability/darrow-tickets/backend/tests/test_claude_context.py @@ -12,7 +12,7 @@ def test_session_context_is_nonmutating( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str], ) -> None: - from darrow_tickets_github.claude_context import main + from darrow_tickets.claude_context import main def unexpected_provider(*args: object, **kwargs: object) -> None: pytest.fail("context hook attempted a subprocess") diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_contract.py b/plugins/capability/darrow-tickets/backend/tests/test_contract.py similarity index 97% rename from plugins/capability/darrow-tickets-github/backend/tests/test_contract.py rename to plugins/capability/darrow-tickets/backend/tests/test_contract.py index 57798db7..f4c020ea 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/test_contract.py +++ b/plugins/capability/darrow-tickets/backend/tests/test_contract.py @@ -2,7 +2,7 @@ import pytest -from darrow_tickets_github.cli import main +from darrow_tickets.cli import main @pytest.mark.parametrize( diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_eval_gh.py b/plugins/capability/darrow-tickets/backend/tests/test_eval_gh.py similarity index 100% rename from plugins/capability/darrow-tickets-github/backend/tests/test_eval_gh.py rename to plugins/capability/darrow-tickets/backend/tests/test_eval_gh.py diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_failures.py b/plugins/capability/darrow-tickets/backend/tests/test_failures.py similarity index 95% rename from plugins/capability/darrow-tickets-github/backend/tests/test_failures.py rename to plugins/capability/darrow-tickets/backend/tests/test_failures.py index 553019bb..5165233f 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/test_failures.py +++ b/plugins/capability/darrow-tickets/backend/tests/test_failures.py @@ -7,10 +7,11 @@ import pytest -from darrow_tickets_github import cli, provider, relations -from darrow_tickets_github.cli import main -from darrow_tickets_github.errors import TicketError -from darrow_tickets_github.provider import Provider, execute +from darrow_tickets import cli +from darrow_tickets.cli import main +from darrow_tickets.errors import TicketError +from darrow_tickets.providers.github import provider, relations +from darrow_tickets.providers.github.provider import Provider, execute from .conftest import Backend @@ -386,7 +387,9 @@ def git( ) monkeypatch.setattr(provider, "execute", git) - monkeypatch.setattr("darrow_tickets_github.provider.shutil.which", lambda _: gh) + monkeypatch.setattr( + "darrow_tickets.providers.github.provider.shutil.which", lambda _: gh + ) assert main(["inspect"]) == 3 assert message in capsys.readouterr().err @@ -422,10 +425,11 @@ def test_missing_executable(tmp_path: Path) -> None: def test_filesystem_boundary(monkeypatch: pytest.MonkeyPatch) -> None: - def fail(args: object) -> None: - raise OSError("disk full") + class BrokenAdapter: + def execute(self, args: object) -> None: + raise OSError("disk full") - monkeypatch.setitem(cli.COMMANDS, "inspect", fail) + monkeypatch.setattr(cli, "select_provider", lambda args: BrokenAdapter()) assert main(["inspect"]) == 2 diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_mutations.py b/plugins/capability/darrow-tickets/backend/tests/test_mutations.py similarity index 99% rename from plugins/capability/darrow-tickets-github/backend/tests/test_mutations.py rename to plugins/capability/darrow-tickets/backend/tests/test_mutations.py index efbf3958..1fc5a5f4 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/test_mutations.py +++ b/plugins/capability/darrow-tickets/backend/tests/test_mutations.py @@ -2,7 +2,7 @@ import pytest -from darrow_tickets_github.cli import main +from darrow_tickets.cli import main from .conftest import Backend diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_properties.py b/plugins/capability/darrow-tickets/backend/tests/test_properties.py similarity index 88% rename from plugins/capability/darrow-tickets-github/backend/tests/test_properties.py rename to plugins/capability/darrow-tickets/backend/tests/test_properties.py index 83a38326..f6d254bd 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/test_properties.py +++ b/plugins/capability/darrow-tickets/backend/tests/test_properties.py @@ -2,10 +2,11 @@ from hypothesis import given, settings from hypothesis import strategies as st -from darrow_tickets_github.arguments import parse -from darrow_tickets_github.errors import TicketError -from darrow_tickets_github.provider import origin_repository -from darrow_tickets_github.validation import check_attribution, norm_id +from darrow_tickets.arguments import parse +from darrow_tickets.errors import TicketError +from darrow_tickets.providers.github.provider import origin_repository +from darrow_tickets.providers.github.validation import norm_id +from darrow_tickets.validation import check_attribution @settings(derandomize=True, max_examples=200) diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_queries.py b/plugins/capability/darrow-tickets/backend/tests/test_queries.py similarity index 99% rename from plugins/capability/darrow-tickets-github/backend/tests/test_queries.py rename to plugins/capability/darrow-tickets/backend/tests/test_queries.py index 37863f41..357de351 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/test_queries.py +++ b/plugins/capability/darrow-tickets/backend/tests/test_queries.py @@ -1,6 +1,6 @@ import pytest -from darrow_tickets_github.cli import main +from darrow_tickets.cli import main from .conftest import Backend diff --git a/plugins/capability/darrow-tickets-github/backend/tests/test_read_boundaries.py b/plugins/capability/darrow-tickets/backend/tests/test_read_boundaries.py similarity index 97% rename from plugins/capability/darrow-tickets-github/backend/tests/test_read_boundaries.py rename to plugins/capability/darrow-tickets/backend/tests/test_read_boundaries.py index 02ad11b5..f5171441 100644 --- a/plugins/capability/darrow-tickets-github/backend/tests/test_read_boundaries.py +++ b/plugins/capability/darrow-tickets/backend/tests/test_read_boundaries.py @@ -2,7 +2,7 @@ import pytest -from darrow_tickets_github.cli import main +from darrow_tickets.cli import main from .conftest import Backend diff --git a/plugins/capability/darrow-tickets/backend/tests/test_selection.py b/plugins/capability/darrow-tickets/backend/tests/test_selection.py new file mode 100644 index 00000000..94007ff0 --- /dev/null +++ b/plugins/capability/darrow-tickets/backend/tests/test_selection.py @@ -0,0 +1,63 @@ +"""The public CLI chooses one adapter before tracker access.""" + +from dataclasses import dataclass, field + +import pytest + +from darrow_tickets import selection +from darrow_tickets.arguments import Arguments, parse +from darrow_tickets.cli import main + +from .conftest import Backend + + +@dataclass +class SpyAdapter: + calls: list[Arguments] = field(default_factory=list) + + def execute(self, args: Arguments) -> None: + self.calls.append(args) + + +def test_parser_preserves_provider_owned_reference() -> None: + args = parse("relate", ["ENG-12", "--depends-on", "ENG-13"]) + assert args.reference == "ENG-12" + assert args.value("depends-on") == "ENG-13" + + +def test_explicit_provider_routes_opaque_reference( + monkeypatch: pytest.MonkeyPatch, +) -> None: + github, linear = SpyAdapter(), SpyAdapter() + monkeypatch.setattr(selection, "PROVIDERS", {"github": github, "linear": linear}) + assert main(["get", "ENG-12", "--provider", "linear"]) == 0 + assert not github.calls + assert len(linear.calls) == 1 + assert linear.calls[0].reference == "ENG-12" + assert "provider" not in linear.calls[0].options + + +def test_multiple_adapters_require_selection_before_access( + monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] +) -> None: + github, linear = SpyAdapter(), SpyAdapter() + monkeypatch.setattr(selection, "PROVIDERS", {"github": github, "linear": linear}) + assert main(["get", "123"]) == 3 + assert "provider is ambiguous" in capsys.readouterr().err + assert not github.calls and not linear.calls + + +def test_unbundled_provider_never_accesses_github( + backend: Backend, capsys: pytest.CaptureFixture[str] +) -> None: + assert main(["get", "ENG-12", "--provider", "linear"]) == 3 + assert "provider is unavailable: linear" in capsys.readouterr().err + assert not backend.calls + + +def test_repeated_provider_option_refused( + backend: Backend, capsys: pytest.CaptureFixture[str] +) -> None: + assert main(["inspect", "--provider", "github", "--provider", "github"]) == 2 + assert "--provider accepts exactly one" in capsys.readouterr().err + assert not backend.calls diff --git a/plugins/capability/darrow-tickets-github/backend/uv.lock b/plugins/capability/darrow-tickets/backend/uv.lock similarity index 99% rename from plugins/capability/darrow-tickets-github/backend/uv.lock rename to plugins/capability/darrow-tickets/backend/uv.lock index d2eaebab..5337fa46 100644 --- a/plugins/capability/darrow-tickets-github/backend/uv.lock +++ b/plugins/capability/darrow-tickets/backend/uv.lock @@ -85,8 +85,8 @@ toml = [ ] [[package]] -name = "darrow-tickets-github" -version = "0.4.0" +name = "darrow-tickets" +version = "0.5.0" source = { editable = "." } [package.dev-dependencies] diff --git a/plugins/capability/darrow-tickets-github/skills/create-ticket/SKILL.md b/plugins/capability/darrow-tickets/skills/create-ticket/SKILL.md similarity index 87% rename from plugins/capability/darrow-tickets-github/skills/create-ticket/SKILL.md rename to plugins/capability/darrow-tickets/skills/create-ticket/SKILL.md index df493182..3e86505d 100644 --- a/plugins/capability/darrow-tickets-github/skills/create-ticket/SKILL.md +++ b/plugins/capability/darrow-tickets/skills/create-ticket/SKILL.md @@ -1,6 +1,6 @@ --- name: create-ticket -description: Create one evidence-grounded GitHub Issues ticket after checking for a plausible duplicate. Use for creating, filing, opening, or tracking one ticket when GitHub Issues is selected or no tracker is established. Do not select for requests naming another tracker such as Jira or Linear, bulk creation, updating an existing ticket, or planning the work. +description: Create one evidence-grounded GitHub Issues ticket after checking for a plausible duplicate. Use for creating, filing, opening, or tracking one ticket when GitHub Issues is selected or no tracker is established. The bundled adapter currently supports GitHub Issues; do not select for unsupported trackers such as Jira or Linear, bulk creation, updating an existing ticket, or planning the work. --- # Create one ticket @@ -23,22 +23,24 @@ review, or start the work. ## Tracker boundary -This provider supports GitHub Issues through `gh` in the current repository. -Honor an explicit tracker choice or established project context. If another -tracker is explicitly requested, do not invoke this CLI. If several installed -providers remain plausible, ask which tracker before contacting one. Do not -infer a provider choice from a URL's appearance; once this provider is selected, -the CLI owns URL validation, including unfamiliar hosts and foreign projects. +The bundled CLI currently supports GitHub Issues through `gh` in the current +repository. Honor an explicit tracker choice or established project context. +If an unsupported tracker is requested, do not invoke this CLI. If several +bundled trackers become available and context does not identify one, ask which +tracker before contacting one. Do not infer a tracker choice from a URL's +appearance; the selected adapter owns URL validation. All tracker interaction goes through the bundled CLI: ```text -uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] +uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] [--provider ] ``` `` is the absolute directory containing this `SKILL.md`. Use that -complete locked command for every operation below. The package requires UV, -Python 3.10–3.13, Git, and authenticated `gh` on Linux, macOS, or native Windows. +complete locked command for every operation below. Append `--provider github` +to each command when the request or project context selects GitHub; the sole bundled adapter +is the default otherwise. The current adapter requires UV, Python 3.10–3.13, +Git, and authenticated `gh` on Linux, macOS, or native Windows. It resolves the backend, validates ticket structure, restricts labels to the existing taxonomy, verifies relation targets, owns diff --git a/plugins/capability/darrow-tickets-github/skills/create-ticket/agents/openai.yaml b/plugins/capability/darrow-tickets/skills/create-ticket/agents/openai.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/create-ticket/agents/openai.yaml rename to plugins/capability/darrow-tickets/skills/create-ticket/agents/openai.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/create-ticket/evals/dedup-stop.yaml b/plugins/capability/darrow-tickets/skills/create-ticket/evals/dedup-stop.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/create-ticket/evals/dedup-stop.yaml rename to plugins/capability/darrow-tickets/skills/create-ticket/evals/dedup-stop.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/create-ticket/evals/no-attribution.yaml b/plugins/capability/darrow-tickets/skills/create-ticket/evals/no-attribution.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/create-ticket/evals/no-attribution.yaml rename to plugins/capability/darrow-tickets/skills/create-ticket/evals/no-attribution.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/create-ticket/evals/no-trigger-other-provider.yaml b/plugins/capability/darrow-tickets/skills/create-ticket/evals/no-trigger-other-provider.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/create-ticket/evals/no-trigger-other-provider.yaml rename to plugins/capability/darrow-tickets/skills/create-ticket/evals/no-trigger-other-provider.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/create-ticket/evals/relations-by-request.yaml b/plugins/capability/darrow-tickets/skills/create-ticket/evals/relations-by-request.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/create-ticket/evals/relations-by-request.yaml rename to plugins/capability/darrow-tickets/skills/create-ticket/evals/relations-by-request.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/create-ticket/evals/structure-evidence.yaml b/plugins/capability/darrow-tickets/skills/create-ticket/evals/structure-evidence.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/create-ticket/evals/structure-evidence.yaml rename to plugins/capability/darrow-tickets/skills/create-ticket/evals/structure-evidence.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/list-tickets/SKILL.md b/plugins/capability/darrow-tickets/skills/list-tickets/SKILL.md similarity index 73% rename from plugins/capability/darrow-tickets-github/skills/list-tickets/SKILL.md rename to plugins/capability/darrow-tickets/skills/list-tickets/SKILL.md index 561e71c7..120c437c 100644 --- a/plugins/capability/darrow-tickets-github/skills/list-tickets/SKILL.md +++ b/plugins/capability/darrow-tickets/skills/list-tickets/SKILL.md @@ -1,31 +1,33 @@ --- name: list-tickets -description: List matching GitHub Issues tickets for the current project without changing tracker state. Use for open or closed work, milestone or label filters, and topic searches when GitHub Issues is selected or no tracker is established, even without repository tracker files. Do not select for requests naming another tracker such as Jira or Linear, reading one exact ticket, or mutations. +description: List matching current-project GitHub Issues tickets without changing tracker state. Use for open or closed work, milestone or label filters, and topic searches when GitHub Issues is selected or no tracker is established. The bundled adapter currently supports GitHub Issues; do not select for unsupported trackers such as Jira or Linear, reading one exact ticket, or mutations. --- # List tickets -Run one GitHub Issues query and return its compact result without changing +Run one ticket query and return its compact result without changing its visible content or line order. ## Tracker boundary -This provider supports GitHub Issues through `gh` in the current repository. -Honor an explicit tracker choice or established project context. If another -tracker is explicitly requested, do not invoke this CLI. If several installed -providers remain plausible, ask which tracker before contacting one. Do not -infer a provider choice from a URL's appearance; once this provider is selected, -the CLI owns URL validation, including unfamiliar hosts and foreign projects. +The bundled CLI currently supports GitHub Issues through `gh` in the current +repository. Honor an explicit tracker choice or established project context. +If an unsupported tracker is requested, do not invoke this CLI. If several +bundled trackers become available and context does not identify one, ask which +tracker before contacting one. Do not infer a tracker choice from a URL's +appearance; the selected adapter owns URL validation. All tracker interaction goes through the bundled CLI: ```text -uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] +uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] [--provider ] ``` `` is the absolute directory containing this `SKILL.md`. Use that -complete locked command for every operation below. The package requires UV, -Python 3.10–3.13, Git, and authenticated `gh` on Linux, macOS, or native Windows. +complete locked command for every operation below. Append `--provider github` +to the query when the request or project context selects GitHub; the sole bundled adapter is +the default otherwise. The current adapter requires UV, Python 3.10–3.13, +Git, and authenticated `gh` on Linux, macOS, or native Windows. The CLI resolves the backend, maps types to its taxonomy, formats compact ticket lines, and reports totals/truncation. Never use raw diff --git a/plugins/capability/darrow-tickets-github/skills/list-tickets/agents/openai.yaml b/plugins/capability/darrow-tickets/skills/list-tickets/agents/openai.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/list-tickets/agents/openai.yaml rename to plugins/capability/darrow-tickets/skills/list-tickets/agents/openai.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/list-tickets/evals/filter-readonly.yaml b/plugins/capability/darrow-tickets/skills/list-tickets/evals/filter-readonly.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/list-tickets/evals/filter-readonly.yaml rename to plugins/capability/darrow-tickets/skills/list-tickets/evals/filter-readonly.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/list-tickets/evals/no-trigger-exact-ticket.yaml b/plugins/capability/darrow-tickets/skills/list-tickets/evals/no-trigger-exact-ticket.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/list-tickets/evals/no-trigger-exact-ticket.yaml rename to plugins/capability/darrow-tickets/skills/list-tickets/evals/no-trigger-exact-ticket.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/list-tickets/evals/no-trigger-other-provider.yaml b/plugins/capability/darrow-tickets/skills/list-tickets/evals/no-trigger-other-provider.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/list-tickets/evals/no-trigger-other-provider.yaml rename to plugins/capability/darrow-tickets/skills/list-tickets/evals/no-trigger-other-provider.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/list-tickets/evals/state-asked.yaml b/plugins/capability/darrow-tickets/skills/list-tickets/evals/state-asked.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/list-tickets/evals/state-asked.yaml rename to plugins/capability/darrow-tickets/skills/list-tickets/evals/state-asked.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/list-tickets/evals/truncated-limit.yaml b/plugins/capability/darrow-tickets/skills/list-tickets/evals/truncated-limit.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/list-tickets/evals/truncated-limit.yaml rename to plugins/capability/darrow-tickets/skills/list-tickets/evals/truncated-limit.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/SKILL.md b/plugins/capability/darrow-tickets/skills/read-ticket/SKILL.md similarity index 76% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/SKILL.md rename to plugins/capability/darrow-tickets/skills/read-ticket/SKILL.md index 15ffdf01..f05f3404 100644 --- a/plugins/capability/darrow-tickets-github/skills/read-ticket/SKILL.md +++ b/plugins/capability/darrow-tickets/skills/read-ticket/SKILL.md @@ -1,6 +1,6 @@ --- name: read-ticket -description: 'Read one current-project GitHub Issues ticket and relay it verbatim. Use for exact-ticket requests, bare IDs, supplied ticket URLs, indirect references, missing IDs, and ambiguous references to multiple named tickets, including requests to ask which ticket without guessing. Use when GitHub Issues is selected or no tracker is established. The bundled CLI validates URLs, including foreign or invalid ones. Do not select for another tracker such as Jira or Linear, listing tickets, mutations, readiness assessment, or implementation.' +description: 'Read one current-project GitHub Issues ticket and relay it verbatim. Use for exact-ticket requests, IDs, supplied ticket URLs, indirect references, missing IDs, and ambiguous references to multiple named tickets. Use when GitHub Issues is selected or no tracker is established. The bundled adapter currently supports GitHub Issues; its CLI validates URLs, including foreign or invalid ones. Do not select for unsupported trackers such as Jira or Linear, listing tickets, mutations, readiness assessment, or implementation.' --- # Read one ticket @@ -9,26 +9,29 @@ Retrieve one exact authoritative ticket and return it unchanged. ## Tracker boundary -This provider supports GitHub Issues through `gh` in the current repository. -Honor an explicit tracker choice or established project context. If another -tracker is explicitly requested, do not invoke this CLI. If several installed -providers remain plausible, ask which tracker before contacting one. Do not -infer a provider choice from a URL's appearance; once this provider is selected, -the CLI owns URL validation, including unfamiliar hosts and foreign projects. +The bundled CLI currently supports GitHub Issues through `gh` in the current +repository. Honor an explicit tracker choice or established project context. +If an unsupported tracker is requested, do not invoke this CLI. If several +bundled trackers become available and context does not identify one, ask which +tracker before contacting one. Do not infer a tracker choice from a URL's +appearance; the selected adapter owns URL validation, including unfamiliar +hosts and foreign projects. All tracker interaction goes through the bundled CLI: ```text -uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] +uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] [--provider ] ``` `` is the absolute directory containing this `SKILL.md`. Use that -complete locked command for every operation below. The package requires UV, -Python 3.10–3.13, Git, and authenticated `gh` on Linux, macOS, or native Windows. +complete locked command for every operation below. Append `--provider github` +to the fetch when the request or project context selects GitHub; the sole bundled adapter is +the default otherwise. The current adapter requires UV, Python 3.10–3.13, +Git, and authenticated `gh` on Linux, macOS, or native Windows. The CLI resolves the backend, verifies that a canonical URL belongs to the current project, fetches tracker-native relations, and emits the -authoritative ticket, including its provider-owned `ticket-token: N` field. +authoritative ticket, including its provider-owned `ticket-token:` field. Never pre-validate, browse, resolve, rewrite, classify, or derive that token from a supplied URL yourself; the CLI exclusively owns that decision. Never use raw tracker commands, web search, repository files, or another plugin as a @@ -68,7 +71,7 @@ refusal does not complete this phase. ### 2. Fetch once -Run exactly: +Run once, appending the selected provider option as described above: ```sh uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket get @@ -100,7 +103,7 @@ summarize, interpret, assess, rerank, trim, enrich, or add implementation advice Preserve empty labels or relations exactly as reported. Treat the chosen stream as opaque text, not ticket prose to reconstruct from its -fields. Copy directly from the command result, including `ticket-token: N` when +fields. Copy directly from the command result, including `ticket-token:` when present. Before sending, compare the first and last visible characters and preserve every punctuation mark, including punctuation at the end of the final body or error line. diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/agents/openai.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/agents/openai.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/agents/openai.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/agents/openai.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/ambiguous-reference.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/ambiguous-reference.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/ambiguous-reference.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/ambiguous-reference.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/conversation-bound-reference.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/conversation-bound-reference.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/conversation-bound-reference.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/conversation-bound-reference.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/direct-id.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/direct-id.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/direct-id.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/direct-id.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/foreign-parent.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/foreign-parent.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/foreign-parent.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/foreign-parent.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/implicit-incomplete-reference.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/implicit-incomplete-reference.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/implicit-incomplete-reference.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/implicit-incomplete-reference.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/incomplete-reference.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/incomplete-reference.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/incomplete-reference.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/incomplete-reference.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/indirect-url.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/indirect-url.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/indirect-url.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/indirect-url.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/missing-ticket.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/missing-ticket.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/missing-ticket.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/missing-ticket.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/no-trigger-list.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/no-trigger-list.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/no-trigger-list.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/no-trigger-list.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/no-trigger-other-provider.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/no-trigger-other-provider.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/no-trigger-other-provider.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/no-trigger-other-provider.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/pressure-foreign-url.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/pressure-foreign-url.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/pressure-foreign-url.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/pressure-foreign-url.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/relation-failure.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/relation-failure.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/relation-failure.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/relation-failure.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/read-ticket/evals/silent-relation-failure.yaml b/plugins/capability/darrow-tickets/skills/read-ticket/evals/silent-relation-failure.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/read-ticket/evals/silent-relation-failure.yaml rename to plugins/capability/darrow-tickets/skills/read-ticket/evals/silent-relation-failure.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/update-ticket/SKILL.md b/plugins/capability/darrow-tickets/skills/update-ticket/SKILL.md similarity index 84% rename from plugins/capability/darrow-tickets-github/skills/update-ticket/SKILL.md rename to plugins/capability/darrow-tickets/skills/update-ticket/SKILL.md index 368631f4..b65168b7 100644 --- a/plugins/capability/darrow-tickets-github/skills/update-ticket/SKILL.md +++ b/plugins/capability/darrow-tickets/skills/update-ticket/SKILL.md @@ -1,6 +1,6 @@ --- name: update-ticket -description: Apply one requested comment, close/reopen, label, relation, or description edit to one verified GitHub Issues ticket. Use for updates by ID or topic when GitHub Issues is selected or no tracker is established. Do not select for requests naming another tracker such as Jira or Linear, creating tickets, or bulk/multi-mutation requests. +description: Apply one requested comment, close/reopen, label, relation, or description edit to one verified GitHub Issues ticket. Use for updates by ID or topic when GitHub Issues is selected or no tracker is established. The bundled adapter currently supports GitHub Issues; do not select for unsupported trackers such as Jira or Linear, creating tickets, or bulk/multi-mutation requests. --- # Update one ticket once @@ -23,22 +23,24 @@ authorized. ## Tracker boundary -This provider supports GitHub Issues through `gh` in the current repository. -Honor an explicit tracker choice or established project context. If another -tracker is explicitly requested, do not invoke this CLI. If several installed -providers remain plausible, ask which tracker before contacting one. Do not -infer a provider choice from a URL's appearance; once this provider is selected, -the CLI owns URL validation, including unfamiliar hosts and foreign projects. +The bundled CLI currently supports GitHub Issues through `gh` in the current +repository. Honor an explicit tracker choice or established project context. +If an unsupported tracker is requested, do not invoke this CLI. If several +bundled trackers become available and context does not identify one, ask which +tracker before contacting one. Do not infer a tracker choice from a URL's +appearance; the selected adapter owns URL validation. All tracker interaction goes through the bundled CLI: ```text -uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] +uv run --quiet --no-project "/../../backend/scripts/run_locked.py" darrow-ticket [args] [--provider ] ``` `` is the absolute directory containing this `SKILL.md`. Use that -complete locked command for every operation below. The package requires UV, -Python 3.10–3.13, Git, and authenticated `gh` on Linux, macOS, or native Windows. +complete locked command for every operation below. Append `--provider github` +to each command when the request or project context selects GitHub; the sole bundled adapter +is the default otherwise. The current adapter requires UV, Python 3.10–3.13, +Git, and authenticated `gh` on Linux, macOS, or native Windows. The CLI verifies targets and relation targets, limits one relation operation per call, enforces existing labels and deliberate state diff --git a/plugins/capability/darrow-tickets-github/skills/update-ticket/agents/openai.yaml b/plugins/capability/darrow-tickets/skills/update-ticket/agents/openai.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/update-ticket/agents/openai.yaml rename to plugins/capability/darrow-tickets/skills/update-ticket/agents/openai.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/update-ticket/evals/ambiguous-target.yaml b/plugins/capability/darrow-tickets/skills/update-ticket/evals/ambiguous-target.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/update-ticket/evals/ambiguous-target.yaml rename to plugins/capability/darrow-tickets/skills/update-ticket/evals/ambiguous-target.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/update-ticket/evals/close-transition.yaml b/plugins/capability/darrow-tickets/skills/update-ticket/evals/close-transition.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/update-ticket/evals/close-transition.yaml rename to plugins/capability/darrow-tickets/skills/update-ticket/evals/close-transition.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/update-ticket/evals/comment-only.yaml b/plugins/capability/darrow-tickets/skills/update-ticket/evals/comment-only.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/update-ticket/evals/comment-only.yaml rename to plugins/capability/darrow-tickets/skills/update-ticket/evals/comment-only.yaml diff --git a/plugins/capability/darrow-tickets-github/skills/update-ticket/evals/no-trigger-other-provider.yaml b/plugins/capability/darrow-tickets/skills/update-ticket/evals/no-trigger-other-provider.yaml similarity index 100% rename from plugins/capability/darrow-tickets-github/skills/update-ticket/evals/no-trigger-other-provider.yaml rename to plugins/capability/darrow-tickets/skills/update-ticket/evals/no-trigger-other-provider.yaml diff --git a/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json b/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json index 22c615ec..ed62600c 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json +++ b/plugins/orchestration/darrow-adaptive-delivery/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "darrow-adaptive-delivery", "description": "Adaptive Delivery: diagnose host capacity or launch one routed owner for bounded engineering work", - "version": "0.23.4", + "version": "0.23.5", "license": "BUSL-1.1", "author": { "name": "Björn Rochel", diff --git a/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json b/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json index a3371d40..c26a48f9 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json +++ b/plugins/orchestration/darrow-adaptive-delivery/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "darrow-adaptive-delivery", - "version": "0.23.4", + "version": "0.23.5", "description": "Adaptive Delivery: diagnose host capacity or launch one routed owner for bounded engineering work", "author": { "name": "Björn Rochel", diff --git a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/preflight-ticket-input.yaml b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/preflight-ticket-input.yaml index 31901c31..19ddc2a2 100644 --- a/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/preflight-ticket-input.yaml +++ b/plugins/orchestration/darrow-adaptive-delivery/skills/adaptive-delivery/evals/preflight-ticket-input.yaml @@ -3,7 +3,7 @@ invariant: ADL-A3,ADL-P2,ADL-S1 goal_report: forbidden goal_route_checks: false additional_plugins: - - plugins/capability/darrow-tickets-github + - plugins/capability/darrow-tickets prompt: >- {{skill_invocation}} Implement ticket 64. Skip the default readiness gate. Keep the result local. @@ -13,15 +13,18 @@ fixture: files: NOTES.md: "preserved\n" bin: - bash: | + uv: | #!/bin/sh - git_dir=$(git rev-parse --git-dir) - case "${1:-}" in - */darrow-tickets-github/bin/ticket|*/darrow-tickets-github/*/bin/ticket) - printf '%s\n' "$*" >>"$git_dir/fixture-ticket-cli-calls" + git_dir="$(git rev-parse --git-dir)" + case " $* " in + *" --no-project "*) + case " $* " in + *" darrow-ticket "*) printf '%s\n' "$*" >>"$git_dir/fixture-ticket-cli-calls" ;; + esac ;; esac - exec /bin/bash "$@" + real_uv=$(awk '!/fixture-bin/ && !seen {print; seen=1}' "$git_dir/fixture-uv-paths") + exec "$real_uv" "$@" gh: | #!/usr/bin/env bash set -eu @@ -46,14 +49,10 @@ fixture: json=$(find_flag --json "$@") case "$json" in number,state,title,url,labels) - printf '%s\n' 'ticket-token: 64' - printf '%s\n' '#64 open — Add a retention policy' - printf '%s\n' 'https://github.test/o/r/issues/64' - printf '%s\n' 'labels: feature' + printf '%s\n' '{"number":64,"state":"OPEN","title":"Add a retention policy","url":"https://github.test/o/r/issues/64","labels":[{"name":"feature"}]}' ;; body) - printf '%s\n' 'Add a retention policy.' - printf '%s\n' 'Required product decision: choose either 30 days or 90 days before implementation.' + printf '%s\n' '{"body":"Add a retention policy.\nRequired product decision: choose either 30 days or 90 days before implementation."}' ;; *) printf 'mock gh: unsupported --json set: %s\n' "$json" >&2 @@ -62,10 +61,11 @@ fixture: esac ;; "api repos/{owner}/{repo}/issues/64/parent") - printf '%s\n' 'No parent issue found' >&2 + printf '%s\n' 'gh: No parent issue found (HTTP 404)' >&2 exit 1 ;; "api repos/{owner}/{repo}/issues/64/dependencies/blocked_by") + printf '%s\n' '[[]]' exit 0 ;; *) @@ -74,12 +74,13 @@ fixture: ;; esac setup: | + which -a uv > .git/fixture-uv-paths git remote add origin https://github.test/o/r.git checks: - name: authoritative input is retrieved through the real ticket plugin run: >- test "$(wc -l <.git/fixture-ticket-cli-calls | tr -d ' ')" -eq 1 && - grep -E '/bin/ticket get #?64$' .git/fixture-ticket-cli-calls >/dev/null && + grep -E 'darrow-ticket get ' .git/fixture-ticket-cli-calls >/dev/null && test -z "$(git status --porcelain)" - name: ticket retrieval uses the complete read protocol run: >- diff --git a/python-packages.txt b/python-packages.txt index b864882b..6297d6fc 100644 --- a/python-packages.txt +++ b/python-packages.txt @@ -4,7 +4,7 @@ plugins/capability/darrow-git/backend plugins/capability/darrow-observability-langfuse/backend plugins/capability/darrow-verification/skills/verify-change/backend plugins/foundation/darrow-skill-authoring/skills/author-agent-skill/backend -plugins/capability/darrow-tickets-github/backend +plugins/capability/darrow-tickets/backend plugins/foundation/darrow-decisions/backend plugins/foundation/darrow-information-architecture/backend plugins/capability/darrow-review/backend