Files
github-spec-kit/templates/commands/taskstoissues.md
Copilot 1a9e4d1d8d feat: Git extension stage 1 — bundled extensions/git with hooks on all core commands (#1941)
* feat: add git extension with hooks on all core commands

- Create extensions/git/ with 5 commands: initialize, feature,
  validate, remote, commit
- 18 hooks covering before/after for all 9 core commands
- Scripts: create-new-feature, initialize-repo, auto-commit,
  git-common (bash + powershell)
- Configurable: branch_numbering, init_commit_message,
  per-command auto-commit with custom messages
- Add hooks to analyze, checklist, clarify, constitution,
  taskstoissues command templates
- Allow hooks-only extensions (no commands required)
- Bundle extension in wheel via pyproject.toml force-include
- Resolve bundled extensions locally before catalog lookup
- Remove planned-but-unimplemented before/after_commit hook refs
- Update extension docs (API ref, dev guide, user guide)
- 37 new tests covering manifest, install, all scripts (bash+pwsh),
  config reading, graceful degradation

Stage 1: opt-in via 'specify extension add git'. No auto-install,
no changes to specify.md or core git init code.

Refs: #841, #1382, #1066, #1791, #1191

* fix: set git identity env vars in extension tests for CI runners

* fix: address PR review comments

- Fix commands property KeyError for hooks-only extensions
- Fix has_git() operator precedence in git-common.sh
- Align default commit message to '[Spec Kit] Initial commit' across
  config-template, extension.yml defaults, and both init scripts
- Update README to reflect all 5 commands and 18 hooks

* fix: address second round of PR review comments

- Add type validation for provides.commands (must be list) and hooks
  (must be dict) in manifest _validate()
- Tighten malformed timestamp detection in git-common.sh to catch
  7-digit dates without trailing slug (e.g. 2026031-143022)
- Pass REPO_ROOT to has_git/Test-HasGit in create-new-feature scripts
- Fix initialize command docs: surface errors on git failures, only
  skip when git is not installed
- Fix commit command docs: 'skips with a warning' not 'silently'
- Add tests for commands:null and hooks:list rejection

* fix: address third round of PR review comments

- Remove scripts frontmatter from command files (CommandRegistrar
  rewrites ../../scripts/ to .specify/scripts/ which points at core
  scripts, not extension scripts)
- Update speckit.git.commit command to derive event name from hook
  context rather than using a static example
- Clarify that hook argument passthrough works via AI agent context
  (the agent carries conversation state including user's original
  feature description)

* fix: address fourth round of PR review comments

- Validate extension_id against ^[a-z0-9-]+$ in _locate_bundled_extension
  to prevent path traversal (security fix)
- Move defaults under config.defaults in extension.yml to match
  ConfigManager._get_extension_defaults() schema
- Ship git-config.yml in extension directory so it's copied during
  install (provides.config template isn't materialized by ExtensionManager)
- Condition handling in hook templates: intentionally matches existing
  pattern from specify/plan/tasks/implement templates (not a new issue)

* fix: add --allow-empty to git commit in initialize-repo scripts

Ensures git init succeeds even on empty repos where nothing has been
staged yet.

* fix: resolve display names to bundled extensions before catalog download

When 'specify extension add "Git Branching Workflow"' is used with a
display name instead of the ID, the catalog resolver now runs first to
map the name to an ID, then checks bundled extensions again with the
resolved ID before falling back to network download.

Also noted: EXECUTE_COMMAND_INVOCATION and condition handling match the
existing pattern in specify/plan/tasks/implement templates (pre-existing,
not introduced by this PR).

* fix: handle before_/after_ prefixes in auto-commit message derivation

- Strip both before_ and after_ prefixes when deriving command name
  (fixes misleading 'Auto-commit after before_plan' messages)
- Include phase (before/after) in default commit messages
- Clarify README config example is an override, not default behavior

* fix: use portable grep -qw for word boundary in create-new-feature.sh

BSD grep (macOS) doesn't support \b as a word boundary. Replace with
grep -qw which is POSIX-portable.

* fix: validate hook values, numeric --number, and PS warning routing

- Validate each hook value is a dict with a 'command' field during
  manifest _validate() (prevents crash at install time)
- Validate --number is a non-negative integer in bash create-new-feature
  (clear error instead of cryptic shell arithmetic failure)
- Route PowerShell no-git warning to stderr in JSON mode so stdout
  stays valid JSON

---------

Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
2026-04-07 08:39:35 -05:00

3.9 KiB

description, tools, scripts
description tools scripts
Convert existing tasks into actionable, dependency-ordered GitHub issues for the feature based on available design artifacts.
github/github-mcp-server/issue_write
sh ps
scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks scripts/powershell/check-prerequisites.ps1 -Json -RequireTasks -IncludeTasks

User Input

$ARGUMENTS

You MUST consider the user input before proceeding (if not empty).

Pre-Execution Checks

Check for extension hooks (before tasks-to-issues conversion):

  • Check if .specify/extensions.yml exists in the project root.
  • If it exists, read it and look for entries under the hooks.before_taskstoissues key
  • If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
  • Filter out hooks where enabled is explicitly false. Treat hooks without an enabled field as enabled by default.
  • For each remaining hook, do not attempt to interpret or evaluate hook condition expressions:
    • If the hook has no condition field, or it is null/empty, treat the hook as executable
    • If the hook defines a non-empty condition, skip the hook and leave condition evaluation to the HookExecutor implementation
  • For each executable hook, output the following based on its optional flag:
    • Optional hook (optional: true):
      ## Extension Hooks
      
      **Optional Pre-Hook**: {extension}
      Command: `/{command}`
      Description: {description}
      
      Prompt: {prompt}
      To execute: `/{command}`
      
    • Mandatory hook (optional: false):
      ## Extension Hooks
      
      **Automatic Pre-Hook**: {extension}
      Executing: `/{command}`
      EXECUTE_COMMAND: {command}
      
      Wait for the result of the hook command before proceeding to the Outline.
      
  • If no hooks are registered or .specify/extensions.yml does not exist, skip silently

Outline

  1. Run {SCRIPT} from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'''m Groot' (or double-quote if possible: "I'm Groot").
  2. From the executed script, extract the path to tasks.
  3. Get the Git remote by running:
git config --get remote.origin.url

Caution

ONLY PROCEED TO NEXT STEPS IF THE REMOTE IS A GITHUB URL

  1. For each task in the list, use the GitHub MCP server to create a new issue in the repository that is representative of the Git remote.

Caution

UNDER NO CIRCUMSTANCES EVER CREATE ISSUES IN REPOSITORIES THAT DO NOT MATCH THE REMOTE URL

Post-Execution Checks

Check for extension hooks (after tasks-to-issues conversion): Check if .specify/extensions.yml exists in the project root.

  • If it exists, read it and look for entries under the hooks.after_taskstoissues key
  • If the YAML cannot be parsed or is invalid, skip hook checking silently and continue normally
  • Filter out hooks where enabled is explicitly false. Treat hooks without an enabled field as enabled by default.
  • For each remaining hook, do not attempt to interpret or evaluate hook condition expressions:
    • If the hook has no condition field, or it is null/empty, treat the hook as executable
    • If the hook defines a non-empty condition, skip the hook and leave condition evaluation to the HookExecutor implementation
  • For each executable hook, output the following based on its optional flag:
    • Optional hook (optional: true):
      ## Extension Hooks
      
      **Optional Hook**: {extension}
      Command: `/{command}`
      Description: {description}
      
      Prompt: {prompt}
      To execute: `/{command}`
      
    • Mandatory hook (optional: false):
      ## Extension Hooks
      
      **Automatic Hook**: {extension}
      Executing: `/{command}`
      EXECUTE_COMMAND: {command}
      
  • If no hooks are registered or .specify/extensions.yml does not exist, skip silently