feat(extensions): add bundled github extension for taskstoissues - #4488
Open
Yash-Chindam wants to merge 2 commits into
Open
feat(extensions): add bundled github extension for taskstoissues#4488Yash-Chindam wants to merge 2 commits into
github extension for taskstoissues#4488Yash-Chindam wants to merge 2 commits into
Conversation
…hub#4421) Stage 1 of moving GitHub issue tracking out of core: add a bundled, opt-in `github` extension that provides `speckit.github.taskstoissues`. The core `speckit.taskstoissues` command is untouched and no alias claims its name, so the two coexist while stage 2 (deprecate) and stage 3 (remove) are still ahead. `github` is the GitHub-platform domain, alongside `git` for local VCS workflow, so later platform work (milestone grouping, github#4370) has a home without spawning a second narrow extension. The extension owns its feature-resolution script in all three runtimes rather than reaching into core `check-prerequisites` via `../../scripts/`. That keeps the plain `scripts:` frontmatter spelling correct — it renders to `.specify/extensions/github/scripts/...`, which the extension actually ships — and leaves nothing recoupled to core internals that stage 3 will remove. `resolve-tasks` is a trimmed twin of `check-prerequisites`: it resolves the project root and active feature, requires tasks.md, and lists the design docs beside it, without core's plan.md/spec.md gating and without persisting feature.json. Tests cover the bundled layout, catalog and wheel packaging, manifest validation, install/uninstall, the hook contract, and — the failure mode that a verbatim copy of the core command would have shipped — that the rendered `{SCRIPT}` path resolves to a file that exists, in both command mode and skills mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The acceptance criteria ask for the command "for every supported integration", which the previous tests only evidenced for Copilot in both layouts. Parametrise the render assertion over all 40 entries in AGENT_CONFIGS, redirecting HOME so the Hermes integration — which installs to ~/.hermes/skills rather than a project-local directory — is exercised without touching the developer's real home. Also split the behaviour assertions so deduplication and pagination are checked by name (cursor paging, perPage, the early exit, and the four-digit-safe task ID pattern) rather than folded into the remote validation test, and add a diff-based test asserting the command body differs from core only in the script invocation and the two lines that read the new TASKS value — so the two cannot silently drift while both commands exist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9 tasks
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Resolver escaping and Windows output issues can break execution, and PowerShell behavior lacks parity coverage.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds an opt-in bundled github extension as the stage-one replacement for core task-to-issue functionality.
Changes:
- Adds the namespaced command and three resolver runtimes.
- Registers, packages, documents, and tests the extension.
- Preserves the existing core command and hook contract.
File summaries
| File | Description |
|---|---|
extensions/github/extension.yml |
Defines extension metadata and command. |
extensions/github/commands/speckit.github.taskstoissues.md |
Implements task-to-issue workflow. |
extensions/github/scripts/bash/resolve-tasks.sh |
Adds Bash task resolver. |
extensions/github/scripts/powershell/resolve-tasks.ps1 |
Adds PowerShell task resolver. |
extensions/github/scripts/python/resolve_tasks.py |
Adds Python task resolver. |
extensions/github/README.md |
Documents usage and migration. |
extensions/catalog.json |
Registers the bundled extension. |
pyproject.toml |
Includes extension in wheels. |
README.md |
Announces the migration. |
docs/installation.md |
Updates installed-command guidance. |
tests/extensions/github/__init__.py |
Marks the test package. |
tests/extensions/github/test_github_extension.py |
Tests layout, installation, rendering, and behavior. |
tests/test_ps1_encoding.py |
Extends PowerShell encoding coverage. |
Review details
- Files reviewed: 12/13 changed files
- Comments generated: 5
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+56
to
+64
| json_escape() { | ||
| local s="$1" | ||
| s="${s//\/\\}" | ||
| s="${s//\"/\\\"}" | ||
| s="${s//$'\n'/\n}" | ||
| s="${s//$'\r'/\r}" | ||
| s="${s//$'\t'/\t}" | ||
| printf '%s' "$s" | ||
| } |
| print(f"TASKS:{tasks}") | ||
| print("AVAILABLE_DOCS:") | ||
| for doc in docs: | ||
| print(f" \u2713 {doc}") |
| "core" | ||
| ] | ||
| }, | ||
| "github": { |
| | ------------------------------ | -------------------------------------------------------------------- | | ||
| | `speckit.github.taskstoissues` | Convert tasks from `tasks.md` into dependency-ordered GitHub issues. | | ||
|
|
||
| > NOTE: The command ID above is canonical. Invoke it using the syntax for your integration: `/speckit.github.taskstoissues` for dot-command integrations; `/speckit-github-taskstoissues` for hyphen/skills integrations (including Forge and Cline); `$speckit-github-taskstoissues` for Codex or ZCode in skills mode; or `/skill:speckit-github-taskstoissues` for Kimi. |
Comment on lines
+502
to
+503
| @requires_bash | ||
| class TestResolveTasksBash: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes #4421 — stage 1 of the three-stage migration (stage 2 deprecates the core command, stage 3 removes it).
Adds a bundled, opt-in
githubextension providingspeckit.github.taskstoissues. The corespeckit.taskstoissuescommand is untouched and nothing here claims its name, so the two coexist for the whole of stage 1.Shape
The issue proposed a
github-issuesextension. Per @mnriem's scoping comment this lands asgithubinstead — the home for GitHub-platform functionality, withgitstaying the local-VCS-workflow domain. Milestone grouping (#4370) gets a natural home in the same extension rather than spawning a second narrow one.The extension owns its feature-resolution script rather than reaching back into core
check-prerequisitesthrough../../scripts/. That matters for more than tidiness: an extension that escapes its own directory re-couples it to core internals, which is the exact thing stage 3 removes. Because the extension now shipsresolve-tasks, the plainscripts:spelling is the correct one — it renders to.specify/extensions/github/scripts/…, a path that exists — and the escape hatch disappears instead of being tested around.resolve-tasksis a trimmed twin of corecheck-prerequisites, not a copy. It resolves the project root (honouringSPECIFY_INIT_DIR) and the active feature directory (SPECIFY_FEATURE_DIRECTORY, else.specify/feature.json), requirestasks.md, and reports the design docs beside it. It performs none of core'splan.md/spec.mdgating, and it never persistsfeature.json— resolution here is read-only, so running the command cannot dirty the working tree.Why the script path deserved a test
A verbatim copy of the core command's frontmatter renders
.specify/extensions/github/scripts/bash/check-prerequisites.sh— a file no extension ships. That install validates, registers the command, auto-registers the skill, and prints✓ Extension installed successfully!. The failure only surfaces when a user runs the command.TestScriptPathResolutioninstalls the extension, renders it, and asserts the resolved path is a file that exists on disk — in command mode, in skills mode, and for all three script runtimes.Acceptance criteria
specify extension add …catalog.json(bundled: true) +pyproject.tomlforce-include;TestCatalogEntry,TestExtensionInstall. Installed asgithub, notgithub-issues, per the scoping comment.test_every_supported_integration_renders_the_commandparametrises over all 40 entries inAGENT_CONFIGSand asserts each renders with{SCRIPT}resolved to the extension-local path. Covers command-file agents, skills-mode agents, TOML/YAML formats, and Hermes (which installs to~/.hermes/skills, so the test redirectsHOME).test_body_differs_from_core_only_in_the_script_invocationdiffs the command against core and requires the only changed lines to be the script invocation plus the two that read the newTASKSvalue.test_preserves_remote_validationandtest_preserves_deduplication_and_paginationadditionally pin the GitHub-URL guard, the both-states listing, cursor paging (perPage,after,endCursor, the early exit), and the four-digit-safe\bT\d{3,}\bpattern.before_taskstoissues/after_taskstoissueshooks execute as before.specify/extensions.yml, so copying the body preserves the contract.TestCommandBodypins both keys and asserts the live consumers — thegitextension's auto-commit hooks — still target them.speckit.taskstoissuesremains available and unchangedgit diff main -- templates/ scripts/ src/is empty. Verified end to end: after installing the extension, the core command and itscheck-prerequisitesinvocation both still resolve and run.test_no_alias_claims_the_core_command. Worth noting this is author discipline, not something the system enforces — see "Split out" below.tests/extensions/github/.extensions/github/README.md(install, removal/disable, command table with per-integration invocation syntax, behaviour, hooks, requirements, scripts, and a migration section with the three stages and a before/after table), plus pointers fromREADME.mdanddocs/installation.md.Answering the assessment's blocking questions
The stage-5 decision returned
needs-clarification. Four of its six blocking questions are settled by the scoping comment and this implementation:.specify/extensions/github/scripts/{bash,powershell,python}/, asserted per-runtime.scripts:frontmatter stripping part of this request? No — split out, see below.The remaining two — usage/adoption baselines for gating stage 2, and which release carries stage 1 — are product decisions rather than implementation ones, and nothing here forecloses either.
Split out, as requested
Two findings from the scoping investigation are defects in shared paths rather than in this extension, and are deliberately not touched here so stage 1 stays additive:
scripts:from the agent-facing frontmatter, while the core render does (step 3 ofprocess_command_template,integrations/base.py). Visible in this PR's own output: the generated.github/agents/speckit.github.taskstoissues.agent.mdretains the key, where core's does not. No bundled extension declaredscripts:before this one, so nothing had exercised it._validate_install_conflictsoverstates what it checks. It is documented as rejecting installs that "would shadow core or installed extension commands", but_get_installed_command_name_maponly walksself.registry— installed extensions. Core command names are never in that map, so an extension declaring an alias ofspeckit.taskstoissuesis accepted today, with the core command present.Both are ready to file with the reproductions.
Files
extensions/github/extension.ymlid: github, one namespaced command, and therequires.speckit_versionblock (absent from the issue body's proposed manifest, which install rejects)extensions/github/commands/speckit.github.taskstoissues.mdextensions/github/scripts/{bash,powershell,python}/resolve-tasks.*extensions/github/README.mdextensions/catalog.json,pyproject.tomlREADME.md,docs/installation.mdtests/extensions/github/tests/test_ps1_encoding.py.ps1directory added to the ASCII-only (PowerShell 5.1) guardTesting
uv run specify --helpuv sync && uv run pytestFull suite, this branch vs. a
mainworktree on the same machine:main(0c8e31f)Identical failure and error counts; the delta is exactly the tests added. Those 68/38 are pre-existing on this Windows box, not regressions — they sit in
test_setup_tasks.py,test_check_prerequisites_python_parity.py,test_setup_plan_no_overwrite.pyandtest_setup_plan_feature_json.py, which exercise core scripts this PR does not touch. The box has nopwsh, and barebashresolves to the WSL launcher rather than Git Bash, so those PowerShell and parity tests error the same way in both trees; running those four files against the baseline worktree directly gives byte-identical results (5 failed / 8 passed / 40 skipped / 27 errors).That table predates the final commit, which adds 42 tests. Extension-relevant suites re-run after it —
tests/extensions,tests/test_ps1_encoding.py,tests/test_extension_registration.py,tests/test_extension_skills.py,tests/test_extensions.py→ 803 passed, 222 skipped, plus the same two symlink-privilege failures that fail identically onmain(test_scaffold_config_rejects_symlink_template,test_scaffold_config_rejects_symlinked_config_root).The two bash-twin tests skip here for the WSL reason above; I ran both scenarios by hand against Git Bash and confirmed output and exit codes match the assertions.
End-to-end, against scratch projects scaffolded from this branch:
specify extension add githubinstalls cleanly;.github/agents/speckit.github.taskstoissues.agent.mdrenders{SCRIPT}to.specify/extensions/github/scripts/bash/resolve-tasks.sh --json; that file exists and returns the expectedFEATURE_DIR/TASKS/AVAILABLE_DOCS. Corespeckit.taskstoissues.agent.mdand.specify/scripts/bash/check-prerequisites.share still present and still work..github/skills/speckit-github-taskstoissues/SKILL.mdresolves the same path.specify extension remove githubremoves the skill and the extension directory, leaving the core skill intact.FEATURE_DIR,TASKS, andAVAILABLE_DOCS, including the single-element-array case and the missing-tasks.mderror path (exit 1, same message). The PowerShell twin is ASCII-only for PowerShell 5.1 and readsfeature.jsonas UTF-8 explicitly (fix: decode feature.json as UTF-8 in Windows PowerShell #4359).Manual test results
Agent: GitHub Copilot (scaffolded via
specify init) | OS/Shell: Windows 11 / Git Bash + PowerShell 5.1specify extension add github.specify/extensions/github/scripts/.specify extension remove githubtaskstoissuesuntouched./speckit.github.taskstoissues{SCRIPT}invocation directly (bash, PowerShell, Python) against a fixture feature. The issue-creation steps need a GitHub MCP server and a GitHub remote, which I did not exercise against a live repository — that portion of the body is carried over from the core command unchanged, and the diff test above pins that it stays that way./speckit.taskstoissues(core)check-prerequisitesinvocation still resolves.AI Disclosure
I used Claude Code (model Claude Opus 5) to help write the extension, the vendored scripts, the tests, and this description. I reviewed the changes and ran the verification described above myself.
🤖 Generated with Claude Code