- AGENTS.md: branch naming as a requirement for AI coding agents
- CONTRIBUTING.md: branch naming as a recommendation for human contributors
- Convention: <type>/<number>-<short-slug> where number is issue or PR number
Closes#2677
* chore: bump version to 0.8.13
* chore: begin 0.8.14.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix: while/do-while loop condition reads stale iteration-0 step output
After executing namespaced loop body steps, copy each iteration's
results back to the original unprefixed step key so that
evaluate_condition() sees the latest values instead of stale
iteration-0 data.
Fixes#2592
* address review: cross-platform tests, preserve iteration-0 history
- Rewrite shell scripts in tests to use Python via script files
instead of POSIX syntax, so they pass on Windows CI.
- Snapshot iteration-0 nested-step results under a namespaced key
(parent:child:0) before the first copy-back overwrite, preserving
complete per-iteration history for debugging.
* address review: skip copy-back on paused/failed iterations
Move the status check before the copy-back so that partial results
from paused or failed nested steps (e.g., a gate awaiting input)
do not overwrite the unprefixed key. This preserves correct resume
behavior.
* address review: quote paths in test shell commands
Quote both the Python executable and script file paths in the
run: commands to handle spaces in paths on Windows.
* address review: execute loop body with original IDs
Instead of namespacing step IDs for execution and copying results
back, execute the loop body with original (unprefixed) step IDs so
results naturally land at the right keys. Snapshot previous
iteration results to namespaced keys (parent:child:N) for history
only.
This fixes multi-step loop bodies where step B references step A's
output within the same iteration — previously step B would see
stale data until the copy-back ran after the entire iteration.
* address review: namespaced execution with per-step copy-back
Revert to namespaced step IDs for execution (preserving unique
log entries and state keys per iteration) but copy each step's
result back to the unprefixed key immediately after it completes.
This preserves backward compatibility (same namespaced key format,
same log IDs) while fixing both the condition evaluation bug and
inter-step references within multi-step loop bodies.
* address review: alias after status check, add multi-step body test
- Move per-step aliasing below the PAUSED/FAILED/ABORTED status
check so partial results from incomplete steps are not aliased
back to the unprefixed key.
- Add test_while_loop_multi_step_body_inter_step_refs to exercise
a multi-step loop body where step B reads step A's output within
the same iteration, verifying per-step aliasing works correctly.
Addresses feedback from @doquanghuy (items 2 & 4) and Copilot
review on commit 9d0a222.
* address review: stable fallback IDs, expression-based inter-step test
- Use enumerate() for stable fallback IDs when loop body steps lack
an explicit id (step-0, step-1, etc. instead of always step-0).
- Rewrite multi-step body test so step B uses expression
substitution ({{ steps.step-a.output.stdout }}) instead of
reading the counter file directly, making it a true regression
test for per-step aliasing.
`bool` is a subclass of `int` in Python, so `int(True)` silently returns
`1`. The extension- and preset-catalog config readers coerced priority
with a bare `int(item.get("priority", idx + 1))`, which meant a YAML
config like:
catalogs:
- name: mine
url: https://example.com/catalog.json
priority: yes # parses to True
was silently accepted as a valid priority of 1, quietly reordering the
catalog stack instead of raising the same `Invalid priority` error a
typo of `priority: not-a-number` already raises.
The sibling integration-catalog reader in `src/specify_cli/catalogs.py`
already guards this case (see `catalogs.py:137`). This change mirrors
that pattern in `extensions.py` and `presets.py` so the three catalog
validators stay consistent, and adds regression tests for both readers
matching the existing `test_load_catalog_config_rejects_boolean_priority`
template in `tests/integrations/test_integration_catalog.py`.
* Add agentic workflows for community catalog submissions
Add GitHub Agentic Workflows that automatically process community
extension and preset submission issues:
- add-community-extension.md: triggered by extension-submission issues,
validates the submission, updates extensions/catalog.community.json
and docs/community/extensions.md, then opens a draft PR
- add-community-preset.md: parallel workflow for preset-submission
issues, updates presets/catalog.community.json and
docs/community/presets.md
Both workflows:
- Trigger on opened, edited, or labeled events (maintainers can
retroactively label pre-existing issues)
- Validate ID format, semver, repo existence, required files, release,
and submission checklists
- Label issues with validation-passed or validation-failed
- Create draft PRs with Closes #N for maintainer review
Also includes gh-aw scaffolding (.github/aw/, .gitattributes lock file
rule, dependabot ignore for gh-aw-actions).
* Suppress whitespace checks on generated .lock.yml files
These files are auto-generated by gh aw compile and contain trailing
whitespace in the ASCII art header and indented YAML blocks that we
cannot control. Add -whitespace attribute to skip git whitespace
checks on them.
* feat: add self-check tip to check output
* style: drop trailing period from self-check tip
Aligns the new tip with the other `Tip:` lines in `specify check`,
which don't end in a period. Per Copilot review feedback on #2574.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Consolidate the CLI diagnostic plan, implementation, and test hardening into one reviewable change. The CLI now reports phase and target context for broad failure paths while preserving existing fail-fast behavior for real setup failures and warning-only behavior for optional best-effort work.
The workflow unit tests also avoid discovering real local agent CLIs, so developer machines with tools such as gemini installed do not hang pytest during metadata-only assertions.
Constraint: CLI setup failures must remain fail-fast, while optional preset and cleanup paths should continue with clear warnings.
Rejected: Replace broad handlers across the whole codebase in one pass | too broad for a targeted CLI diagnostic fix
Rejected: Add runtime timeouts to workflow agent dispatch | dispatch may legitimately be long-running and the observed hang was test isolation
Confidence: high
Scope-risk: moderate
Directive: Keep future best-effort CLI warnings tied to the failed phase and target so users can diagnose setup state.
Tested: uvx ruff check src/; uv run pytest tests/integrations/test_cli.py -v; uv run pytest tests/test_workflows.py::TestCommandStep::test_step_override_integration tests/test_workflows.py::TestPromptStep::test_execute_with_step_integration tests/test_workflows.py::TestPromptStep::test_execute_with_model -vv; uv run pytest
Not-tested: Real Nacos/PG/Redis-style external service failure injection; real interactive workflow dispatch against installed gemini CLI
* chore: bump version to 0.8.12
* chore: begin 0.8.13.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(codex): inject dot-to-hyphen hook command note in Codex skills
Hook commands in `.specify/extensions.yml` use dotted ids like
`speckit.git.commit`, but Codex skills are named with hyphens
(`speckit-git-commit`). The Claude integration handles this via an
explicit instruction injected into each generated SKILL.md by
`ClaudeIntegration.post_process_skill_content`, but the Codex
integration had no such override, so Codex would emit
`/speckit.git.commit` (which does not resolve) instead of
`/speckit-git-commit`.
This adds the same `_inject_hook_command_note` helper and a
`post_process_skill_content` override to `CodexIntegration`, plus a
small `setup()` override that applies the post-process to each
generated SKILL.md (mirroring the pattern in `ClaudeIntegration`).
Also widens the existing
`test_non_claude_post_process_is_identity` test to use `agy`
(another `SkillsIntegration` with no override), since asserting
identity behavior on Codex would now incorrectly fail.
Tests:
- New `TestCodexHookCommandNote` class mirrors
`TestClaudeHookCommandNote`: setup-level injection, no-op when
no hook block is present, idempotency, and indentation
preservation.
- `pytest tests/` → 2866 passed, 34 skipped.
Signed-off-by: Chao Zhang <1175468+picklebento@users.noreply.github.com>
* fix(codex): handle empty eol when instruction is final line without newline
The hook-note injection regex allowed end-of-string matches via ``$``,
which left the captured ``eol`` empty. When the matched indent was also
empty, the substitution concatenated the note onto the same line as the
instruction. Default ``eol`` to ``\n`` when the capture is empty.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Signed-off-by: Chao Zhang <1175468+picklebento@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(workflow): support integration: auto to follow project's initialized AI
Closes#2406
(squashed)
* fix(workflow): combine JSONDecodeError and UnicodeDecodeError handling
Address Copilot feedback: UnicodeDecodeError can be raised by both
read_text() and json.loads(), so combining the handlers ensures both
cases produce a consistent, clear error message.
* fix(workflows): honor integration_state schema guard and modern state in 'integration: auto'
Three Copilot follow-ups on PR #2421:
1. engine.py:799 — `_load_project_integration` was bypassing the same
schema guard `_read_integration_json` enforces. It now reads the
schema field directly, returns None on a future schema (so the
workflow falls back to the literal 'auto' default rather than
guessing), and routes through `normalize_integration_state` /
`default_integration_key` so modern installs that record
`default_integration` / `installed_integrations` (without the
legacy top-level `integration` field) resolve correctly.
2. test_workflows.py — added two regression cases:
- `integration: auto` resolves a modern normalized state file
- `integration: auto` falls back when the state file declares a
newer `integration_state_schema` than this CLI supports
3. test_cli.py — added a CLI-level regression for the `UnicodeDecodeError`
branch in `_read_integration_json` to match the existing
malformed-JSON coverage.
* refactor(integration): extract shared try_read_integration_json helper
Address Copilot review on PR #2421:
Both `_read_integration_json` (CLI) and `_load_project_integration`
(workflow engine) were parsing `.specify/integration.json` independently,
duplicating the schema guard and risking drift between the two readers.
Extract the parse + schema validation into a single low-level helper
`try_read_integration_json` in `integration_state.py` that returns either
the normalized state or a structured `IntegrationReadError`. Both callers
now delegate to this helper:
- CLI keeps its loud-fail UX: each error kind ("decode", "os",
"not_object", "schema_too_new") is translated into the existing console
message + typer.Exit(1).
- Engine keeps its silent fallback: any error simply returns None so
`integration: auto` falls back to the workflow's literal default.
This eliminates the divergence Copilot flagged without changing observable
behavior for either caller.
* fix(integration): distinguish missing file from non-regular path
Address Copilot review on PR #2421:
`try_read_integration_json` was collapsing two distinct cases into a
single `(None, None)` return:
1. `.specify/integration.json` truly missing — silent fallback is correct.
2. Path exists but is a directory, socket, or other non-regular file —
this is a misconfiguration the CLI should surface loudly.
Split the check: `exists()` falsey returns `(None, None)`; existing-but-
not-a-regular-file returns `(None, IntegrationReadError(kind="os", ...))`
so the CLI's loud-fail path produces an actionable error while the
engine still treats it as a fallback to the workflow's literal default.
* docs(workflow): clarify version pin, advisory integrations list, enum exemption
- workflow.yml: fix comment that said 0.8.3 was first release with auto
resolution; the pin is >=0.8.5 so the comment now matches the pin.
- workflow.yml: clarify that requires.integrations.any is an advisory,
non-exhaustive compatibility hint, not a closed set.
- engine.py: clarify that the auto-sentinel exemption only skips enum
membership; declared type is still enforced through _coerce_input.
* fix(workflow): resolve auto sentinel for provided values; report stat errors
Two Copilot findings fixed:
1. _resolve_inputs only resolved the ``integration: auto`` sentinel when it
came from the input default. A caller explicitly providing
``{"integration": "auto"}`` (which the workflow prompt advertises as a
valid value) bypassed _resolve_default and the literal "auto" leaked
to dispatch. Provided values now go through the same resolution path
as defaults, and the enum-membership exemption applies in both cases.
Regression test added.
2. try_read_integration_json used Path.exists() / Path.is_file() as a
pre-check. Both return False on some OSErrors (e.g. permission errors
during stat), which silently treated an unreadable-but-present file
as missing — the engine fell back without warning and the CLI failed
to surface the loud error. The pre-check is gone: read_text() is
attempted directly, FileNotFoundError means missing (silent fallback),
IsADirectoryError and other OSErrors become loud IntegrationReadError.
* fix(workflow): enforce declared type for string inputs, reject bool-as-number
Two Copilot findings fixed:
1. _coerce_input previously coerced/validated only ``number`` and
``boolean`` types, so ``type: string`` silently accepted any Python
value (numbers, lists, dicts). A YAML authoring mistake like
``type: string`` + ``default: 5`` slipped through. Strings are now
required to actually be strings; non-strings raise ValueError, which
surfaces as an ``invalid default`` error from validate_workflow.
2. ``type: number`` accepted ``default: true`` because ``bool`` is a
subclass of ``int`` (``float(True) == 1.0``). Bools are now rejected
explicitly in the number path so the YAML mistake fails fast. The
boolean path is also tightened to reject non-bool / non-string
values for symmetry.
Comment on the auto-sentinel enum exemption updated to reflect the
stronger guarantee. Regression tests added for both rejections.
* fix(cli): drop unused normalize_integration_state import to satisfy ruff
CI's `uvx ruff check src/` flagged this as F401: the symbol was imported
under a private alias but never referenced. Tests stay green after
removal.
* chore: bump version to 0.8.11
* chore: begin 0.8.12.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* refactor: extract _version.py from __init__.py (PR-3/8)
Move version-checking helpers and `specify self` sub-commands into a
focused `_version.py` module.
Moved symbols:
- GITHUB_API_LATEST — GitHub releases API endpoint constant
- _get_installed_version — importlib.metadata-based version lookup
- _normalize_tag — strip leading 'v' from release tag strings
- _is_newer — PEP 440 version comparison
- _fetch_latest_release_tag — single outbound call to GitHub API
- self_app — Typer sub-app for `specify self`
- self_check, self_upgrade — `specify self check/upgrade` commands
Dependency rule: _version.py imports only stdlib + packaging + ._console.
Backward compatibility: GITHUB_API_LATEST, self_check, self_upgrade
remain importable from specify_cli via re-exports in __init__.py.
Update test_upgrade.py to import helpers from specify_cli._version and
patch at the correct module path (specify_cli._version.*).
Add test_version_imports.py as regression guard.
* fix(tests): update _fetch_latest_release_tag import path in test_authentication.py
PR-3 moved _fetch_latest_release_tag from specify_cli into
specify_cli._version. test_upgrade.py was updated at the time, but
test_authentication.py::TestFetchLatestReleaseTagDelegation still
imported from the old location, causing ImportError on all three
delegation tests. Update all three inline imports to the correct
module path.
* Add Time Machine extension to community catalog
Add time-machine extension submitted by @teeyo to:
- extensions/catalog.community.json (alphabetical order)
- docs/community/extensions.md community extensions table
Closes#2568
* Fix alphabetical ordering of time-machine entry
Move time-machine before tinyspec in both catalog JSON (by ID)
and docs table (by name), since time < tiny alphabetically.
* docs: fix script name in directory tree examples
Replace update-claude-md.sh with the actual filename setup-tasks.sh
in two directory tree examples (Steps 2 and 4 of the detailed walkthrough).
* docs: fix .specify/scripts layout to show bash/ and powershell/ subdirs
install_shared_infra() installs scripts under .specify/scripts/bash/
(script_type="sh") or .specify/scripts/powershell/ (script_type="ps"),
not directly under .specify/scripts/. Update docs/installation.md and
the _install_shared_infra docstring to reflect the actual on-disk layout.
* docs: update README tree examples to show scripts/bash/ subdirectory
Scripts are installed under .specify/scripts/bash/ (or powershell/)
not directly under .specify/scripts/. Fix both tree diagrams in the
Detailed Process walkthrough to match the actual on-disk layout.
* chore: bump version to 0.8.10
* chore: begin 0.8.11.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
- Shorten README.md install section to single uv command + link to
installation guide for alternatives and troubleshooting
- Add explicit 'Initialize a project' step to README Get Started
- Remove duplicate Troubleshooting section from README
- Reorder 'Make it your own' card on docs landing page so extensions
and presets are explained before the stats
- Update Community nav-card to link to new community overview
- Create docs/community/overview.md landing page (aligned with
reference/overview.md)
- Create dedicated install sub-pages: pipx, one-time (uvx), air-gapped
- Update docs/installation.md to lead with persistent uv install and
link to sub-pages instead of duplicating content
- Update docs/toc.yml with new pages
- Remove stale EOF file
* Add Agent Governance extension to community catalog
Add agent-governance extension submitted by @bigsmartben to:
- extensions/catalog.community.json (alphabetical order)
- README.md community extensions table
Closes#2552
* Move community extensions table from README to docs site
- Create docs/community/extensions.md with full extensions table
- Replace ~120-line table in README.md with summary + link to docs site
- Add Extensions entry to docs/toc.yml under Community
- Update add-community-extension SKILL.md references
* refactor: extract _assets.py and _utils.py from __init__.py
Move bundle path resolution and version lookup into _assets.py (stdlib only,
zero internal imports), and system utilities (subprocess, tool detection,
file operations) into _utils.py (imports only from ._console). Re-export all
moved symbols from __init__.py for backward compatibility. Update
test_check_tool.py to patch both specify_cli and specify_cli._utils namespaces
since constants are now defined in _utils.
* style: apply PR-1 review patterns to _assets.py and _utils.py
- Add module docstring to _assets.py (stdlib-only, zero internal imports)
- Add blank line after `from __future__ import annotations` in both files
- Replace `Optional[X]` with `X | None` throughout _utils.py (PEP 604)
- Remove unused `Optional` import from _utils.py
- Use explicit re-export form (`X as X`) for public symbols in __init__.py
- Remove unused `subprocess` and `tempfile` imports from __init__.py (moved to _utils.py)
* fix(opencode): use commands/ directory (plural) to match OpenCode docs
OpenCode documentation (https://opencode.ai/docs/commands/) uses
.opencode/commands/ (plural) as the canonical command directory.
The OpenCode runtime supports both .opencode/command/ and
.opencode/commands/ via a {command,commands} glob, but the
singular form was the original convention and is now outdated.
Update the OpenCode integration to write to .opencode/commands/
instead of .opencode/command/, aligning with the documented
standard and the OpenSpec fix (Fission-AI/OpenSpec#748).
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)
* feat(registrar): add legacy_dir fallback for backward-compatible directory migration
Add _resolve_agent_dir() to CommandRegistrar that checks a
legacy_dir fallback when the canonical directory does not exist.
When legacy_dir is found, a deprecation warning directs users to
run "specify integration upgrade" to migrate.
The OpenCode integration declares legacy_dir: ".opencode/command"
so that extension and preset registration, as well as command
cleanup, continue working for projects that have not yet migrated
to .opencode/commands/.
The legacy_dir mechanism is opt-in: integrations that do not
declare it get no fallback and no behavioral change.
Add end-to-end test verifying that "specify integration upgrade
opencode" migrates commands from legacy .opencode/command/ to
canonical .opencode/commands/ and removes stale files.
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
Assisted-by: OpenCode (claude-opus-4-6)
* fix(registrar): address PR review feedback on legacy_dir handling
- Fix deprecation warning formatting: quote paths and remove trailing
'/.' that produced confusing '.opencode/commands/.' output
- Eliminate duplicate warnings: pass pre-resolved directory to
register_commands() via _resolved_dir parameter so
_resolve_agent_dir() is only called once per agent
- Fix unregister_commands() to clean both canonical and legacy dirs
when both exist, preventing orphaned command files after upgrade
- Add test_unregister_cleans_legacy_when_both_dirs_exist regression
test and tighten warning count assertion to exactly 1
Assisted-by: OpenCode (claude-opus-4-6)
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
---------
Signed-off-by: Marcus Burghardt <maburgha@redhat.com>
* refactor: extract _console.py from __init__.py
Move Rich UI primitives (BANNER, TAGLINE, StepTracker, get_key,
select_with_arrows, console, BannerGroup, show_banner) into a new
src/specify_cli/_console.py module. Re-export all symbols from
__init__.py to preserve the public API. Add regression guard tests.
* refactor(console): improve type annotations and add guard for empty options
- Add module-level docstring documenting the console layer's purpose and
the dependency-layering rule (no imports from other specify_cli modules)
- Tighten select_with_arrows() signature: options typed as dict[str, str]
and default_key as str | None to align with repo typing style
- Add early ValueError guard when options is empty, preventing downstream
ZeroDivisionError / IndexError inside the Live loop
* refactor(console): improve type safety and code quality in _console.py
- Add Callable import from collections.abc for precise callback typing
- Annotate StepTracker._refresh_cb as Callable[[], None] | None
- Add parameter/return types to attach_refresh()
- Use explicit keyword form typer.Exit(code=1) across all error exits
- Add blank line between StepTracker class and get_key() (PEP 8)
- Add regression test for select_with_arrows() raising ValueError on
empty options dict
* style(cli): add __all__ declaration to fix Ruff F401 lint warnings
- Add explicit __all__ for intentional re-exports (BANNER, TAGLINE, get_key)
- Prevent F401 unused import errors in CI lint checks
- Maintain backward compatibility for external imports
* Preserve public console imports
The CLI package intentionally re-exports console helpers for compatibility, so __all__ must track that public surface instead of narrowing star imports to a partial set.
Constraint: Existing tests import console helpers directly from specify_cli
Rejected: Remove __all__ entirely | keeping an explicit export list documents the intended compatibility surface
Confidence: high
Scope-risk: narrow
Directive: Keep __all__ synchronized when adding or removing specify_cli public re-exports
Tested: uv run pytest tests/test_console_imports.py -q
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* style(cli): use explicit re-export syntax to fix ruff F401 warnings
Use `X as X` form for BANNER, TAGLINE, and get_key imports
to mark them as intentional public re-exports and silence
ruff F401 lint errors.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* chore: bump version to 0.8.9
* chore: begin 0.8.10.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Rewrite docs/index.md from a philosophy essay into a landing page
organized around four pillars: Spec-driven by default, Use any coding
agent, Make it your own, and Integrate into your organization.
- Add hero section with GitHub Spec Kit branding and CTA buttons
- Add 2x2 pillar card grid with GitHub Primer color accents
- Add community stats section (96K stars, 200+ contributors, etc.)
- Add navigation cards and footer install CTA
- Move SDD philosophy content to docs/concepts/sdd.md
- Add custom DocFX template overlay with card CSS and dark mode
- Set landing layout for index.md via fileMetadata
- Update toc.yml and docfx.json for new concepts section
* Add Spec Scope extension to community catalog
Adds spec-kit-scope: effort estimation and scope tracking from spec artifacts.
4 commands:
- /speckit.scope.estimate — data-driven effort estimation with three-point ranges
- /speckit.scope.compare — side-by-side spec scope comparison
- /speckit.scope.creep — scope creep detection via git history
- /speckit.scope.budget — sprint-ready time budget generation
1 hook: after_specify (auto-estimation)
Turns "how long will this take?" into a data-driven answer.
* Add Spec Changelog extension to community catalog
* Add Spec Changelog extension to community catalog
* fix: drop accidental scope entry, restore Intelligent Agent Orchestrator README row, return Spec Reference Loader to original position
Per Copilot review on PR #2177: this branch is supposed to add only the
Spec Changelog extension. The diff against main also showed (1) a duplicate
'scope' catalog entry, (2) a deletion of the Intelligent Agent Orchestrator
README row, and (3) Spec Reference Loader moved out of alphabetical order.
All three were merge artifacts and have been cleaned up here.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* fix: keep Spec Changelog row alphabetically sorted
Address Copilot review on PR #2177: the Community Extensions table is
sorted alphabetically by display name, and 'Changelog' precedes 'Critique',
'Diagram', 'Orchestrator', and 'Reference', so the Spec Changelog row
belongs right after Ship Release Extension. Move it into its sorted slot
and keep Spec Reference Loader in its original alphabetical position
(between Spec Orchestrator and Spec Refine).
* fix: remove duplicate Spec Reference Loader row from README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>