From b606b385124eb385b4ab69ad152ce2c6af8a8221 Mon Sep 17 00:00:00 2001 From: Arun_18 <102735445+arunt14@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:27:31 +0530 Subject: [PATCH 1/6] feat: add 5 lifecycle extensions to community catalog (#2049) * feat: add 5 gstack-inspired lifecycle commands (critique, review, qa, ship, retro) Add 5 new core command templates inspired by Garry Tan's GStack to complete the spec-driven development lifecycle: - /speckit.critique: Dual-lens product + engineering review before implementation - /speckit.review: Staff-level code review (correctness, security, performance) - /speckit.qa: Systematic QA testing (browser-driven and CLI modes) - /speckit.ship: Release automation (pre-flight, changelog, CI, PR creation) - /speckit.retro: Sprint retrospective with metrics and improvement suggestions Each command includes: - Command template in templates/commands/ - Output report template in templates/ - Extension hook support (before_*/after_*) - YAML frontmatter with prerequisite scripts Updated README.md workflow from 6 to 11 steps and added CHANGELOG entry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert "feat: add 5 gstack-inspired lifecycle commands (critique, review, qa, ship, retro)" This reverts commit 6eb15a7a3ee201d1e3743e5f0d92173b1503f9e6. * feat: add 5 lifecycle extensions to community catalog Add the following community extensions: - staff-review: Staff-engineer-level code review - qa: Systematic QA testing with browser/CLI validation - ship: Release engineering automation - retro: Sprint retrospective with metrics - critique: Dual-lens spec and plan critique Each extension is hosted in its own repository under arunt14/ with v1.0.0 releases available. * fix: resolve mojibake encoding, sort keys, rename retro extension - Fix double-encoded em dashes and arrows in catalog.community.json - Sort extension entries alphabetically by key - Rename 'Retrospective Extension' to 'Retro Extension' to avoid name collision with existing 'retrospective' extension --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 5 + extensions/catalog.community.json | 154 +++++++++++++++++++++++++++++- 2 files changed, 157 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e49f025fd..a1ab6bb27 100644 --- a/README.md +++ b/README.md @@ -203,12 +203,17 @@ The following community-contributed extensions are available in [`catalog.commun | Product Forge | Full product lifecycle: research → product spec → SpecKit → implement → verify → test | `process` | Read+Write | [speckit-product-forge](https://github.com/VaiYav/speckit-product-forge) | | Project Health Check | Diagnose a Spec Kit project and report health issues across structure, agents, features, scripts, extensions, and git | `visibility` | Read-only | [spec-kit-doctor](https://github.com/KhawarHabibKhan/spec-kit-doctor) | | Project Status | Show current SDD workflow progress — active feature, artifact status, task completion, workflow phase, and extensions summary | `visibility` | Read-only | [spec-kit-status](https://github.com/KhawarHabibKhan/spec-kit-status) | +| QA Testing Extension | Systematic QA testing with browser-driven or CLI-based validation of acceptance criteria from spec | `code` | Read-only | [spec-kit-qa](https://github.com/arunt14/spec-kit-qa) | | Ralph Loop | Autonomous implementation loop using AI agent CLI | `code` | Read+Write | [spec-kit-ralph](https://github.com/Rubiss/spec-kit-ralph) | | Reconcile Extension | Reconcile implementation drift by surgically updating feature artifacts. | `docs` | Read+Write | [spec-kit-reconcile](https://github.com/stn1slv/spec-kit-reconcile) | +| Retro Extension | Sprint retrospective analysis with metrics, spec accuracy assessment, and improvement suggestions | `process` | Read+Write | [spec-kit-retro](https://github.com/arunt14/spec-kit-retro) | | Retrospective Extension | Post-implementation retrospective with spec adherence scoring, drift analysis, and human-gated spec updates | `docs` | Read+Write | [spec-kit-retrospective](https://github.com/emi-dm/spec-kit-retrospective) | | Review Extension | Post-implementation comprehensive code review with specialized agents for code quality, comments, tests, error handling, type design, and simplification | `code` | Read-only | [spec-kit-review](https://github.com/ismaelJimenez/spec-kit-review) | | SDD Utilities | Resume interrupted workflows, validate project health, and verify spec-to-task traceability | `process` | Read+Write | [speckit-utils](https://github.com/mvanhorn/speckit-utils) | +| Staff Review Extension | Staff-engineer-level code review that validates implementation against spec, checks security, performance, and test coverage | `code` | Read-only | [spec-kit-staff-review](https://github.com/arunt14/spec-kit-staff-review) | | Superpowers Bridge | Orchestrates obra/superpowers skills within the spec-kit SDD workflow across the full lifecycle (clarification, TDD, review, verification, critique, debugging, branch completion) | `process` | Read+Write | [superpowers-bridge](https://github.com/RbBtSn0w/spec-kit-extensions/tree/main/superpowers-bridge) | +| Ship Release Extension | Automates release pipeline: pre-flight checks, branch sync, changelog generation, CI verification, and PR creation | `process` | Read+Write | [spec-kit-ship](https://github.com/arunt14/spec-kit-ship) | +| Spec Critique Extension | Dual-lens critical review of spec and plan from product strategy and engineering risk perspectives | `docs` | Read-only | [spec-kit-critique](https://github.com/arunt14/spec-kit-critique) | | Spec Sync | Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval | `docs` | Read+Write | [spec-kit-sync](https://github.com/bgervin/spec-kit-sync) | | Understanding | Automated requirements quality analysis — 31 deterministic metrics against IEEE/ISO standards with experimental energy-based ambiguity detection | `docs` | Read-only | [understanding](https://github.com/Testimonial/understanding) | | V-Model Extension Pack | Enforces V-Model paired generation of development specs and test specs with full traceability | `docs` | Read+Write | [spec-kit-v-model](https://github.com/leocamello/spec-kit-v-model) | diff --git a/extensions/catalog.community.json b/extensions/catalog.community.json index e24cb2f77..3b64f0bfb 100644 --- a/extensions/catalog.community.json +++ b/extensions/catalog.community.json @@ -1,6 +1,6 @@ { "schema_version": "1.0", - "updated_at": "2026-03-30T00:00:00Z", + "updated_at": "2026-04-01T00:00:00Z", "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json", "extensions": { "aide": { @@ -241,6 +241,36 @@ "created_at": "2026-03-19T12:08:20Z", "updated_at": "2026-03-19T12:08:20Z" }, + "critique": { + "name": "Spec Critique Extension", + "id": "critique", + "description": "Dual-lens critical review of spec and plan from product strategy and engineering risk perspectives.", + "author": "arunt14", + "version": "1.0.0", + "download_url": "https://github.com/arunt14/spec-kit-critique/archive/refs/tags/v1.0.0.zip", + "repository": "https://github.com/arunt14/spec-kit-critique", + "homepage": "https://github.com/arunt14/spec-kit-critique", + "documentation": "https://github.com/arunt14/spec-kit-critique/blob/main/README.md", + "changelog": "https://github.com/arunt14/spec-kit-critique/blob/main/CHANGELOG.md", + "license": "MIT", + "requires": { + "speckit_version": ">=0.1.0" + }, + "provides": { + "commands": 1, + "hooks": 1 + }, + "tags": [ + "docs", + "review", + "planning" + ], + "verified": false, + "downloads": 0, + "stars": 0, + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-04-01T00:00:00Z" + }, "docguard": { "name": "DocGuard — CDD Enforcement", "id": "docguard", @@ -792,7 +822,7 @@ "product-forge": { "name": "Product Forge", "id": "product-forge", - "description": "Full product lifecycle: research \u2192 product spec \u2192 SpecKit \u2192 implement \u2192 verify \u2192 test", + "description": "Full product lifecycle: research → product spec → SpecKit → implement → verify → test", "author": "VaiYav", "version": "1.1.1", "download_url": "https://github.com/VaiYav/speckit-product-forge/archive/refs/tags/v1.1.1.zip", @@ -821,6 +851,36 @@ "created_at": "2026-03-28T00:00:00Z", "updated_at": "2026-03-28T00:00:00Z" }, + "qa": { + "name": "QA Testing Extension", + "id": "qa", + "description": "Systematic QA testing with browser-driven or CLI-based validation of acceptance criteria from spec.", + "author": "arunt14", + "version": "1.0.0", + "download_url": "https://github.com/arunt14/spec-kit-qa/archive/refs/tags/v1.0.0.zip", + "repository": "https://github.com/arunt14/spec-kit-qa", + "homepage": "https://github.com/arunt14/spec-kit-qa", + "documentation": "https://github.com/arunt14/spec-kit-qa/blob/main/README.md", + "changelog": "https://github.com/arunt14/spec-kit-qa/blob/main/CHANGELOG.md", + "license": "MIT", + "requires": { + "speckit_version": ">=0.1.0" + }, + "provides": { + "commands": 1, + "hooks": 1 + }, + "tags": [ + "code", + "testing", + "qa" + ], + "verified": false, + "downloads": 0, + "stars": 0, + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-04-01T00:00:00Z" + }, "ralph": { "name": "Ralph Loop", "id": "ralph", @@ -893,6 +953,36 @@ "created_at": "2026-03-14T00:00:00Z", "updated_at": "2026-03-14T00:00:00Z" }, + "retro": { + "name": "Retro Extension", + "id": "retro", + "description": "Sprint retrospective analysis with metrics, spec accuracy assessment, and improvement suggestions.", + "author": "arunt14", + "version": "1.0.0", + "download_url": "https://github.com/arunt14/spec-kit-retro/archive/refs/tags/v1.0.0.zip", + "repository": "https://github.com/arunt14/spec-kit-retro", + "homepage": "https://github.com/arunt14/spec-kit-retro", + "documentation": "https://github.com/arunt14/spec-kit-retro/blob/main/README.md", + "changelog": "https://github.com/arunt14/spec-kit-retro/blob/main/CHANGELOG.md", + "license": "MIT", + "requires": { + "speckit_version": ">=0.1.0" + }, + "provides": { + "commands": 1, + "hooks": 0 + }, + "tags": [ + "process", + "retrospective", + "metrics" + ], + "verified": false, + "downloads": 0, + "stars": 0, + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-04-01T00:00:00Z" + }, "retrospective": { "name": "Retrospective Extension", "id": "retrospective", @@ -959,6 +1049,36 @@ "created_at": "2026-03-06T00:00:00Z", "updated_at": "2026-03-06T00:00:00Z" }, + "ship": { + "name": "Ship Release Extension", + "id": "ship", + "description": "Automates release pipeline: pre-flight checks, branch sync, changelog generation, CI verification, and PR creation.", + "author": "arunt14", + "version": "1.0.0", + "download_url": "https://github.com/arunt14/spec-kit-ship/archive/refs/tags/v1.0.0.zip", + "repository": "https://github.com/arunt14/spec-kit-ship", + "homepage": "https://github.com/arunt14/spec-kit-ship", + "documentation": "https://github.com/arunt14/spec-kit-ship/blob/main/README.md", + "changelog": "https://github.com/arunt14/spec-kit-ship/blob/main/CHANGELOG.md", + "license": "MIT", + "requires": { + "speckit_version": ">=0.1.0" + }, + "provides": { + "commands": 1, + "hooks": 1 + }, + "tags": [ + "process", + "release", + "automation" + ], + "verified": false, + "downloads": 0, + "stars": 0, + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-04-01T00:00:00Z" + }, "speckit-utils": { "name": "SDD Utilities", "id": "speckit-utils", @@ -991,6 +1111,36 @@ "created_at": "2026-03-18T00:00:00Z", "updated_at": "2026-03-18T00:00:00Z" }, + "staff-review": { + "name": "Staff Review Extension", + "id": "staff-review", + "description": "Staff-engineer-level code review that validates implementation against spec, checks security, performance, and test coverage.", + "author": "arunt14", + "version": "1.0.0", + "download_url": "https://github.com/arunt14/spec-kit-staff-review/archive/refs/tags/v1.0.0.zip", + "repository": "https://github.com/arunt14/spec-kit-staff-review", + "homepage": "https://github.com/arunt14/spec-kit-staff-review", + "documentation": "https://github.com/arunt14/spec-kit-staff-review/blob/main/README.md", + "changelog": "https://github.com/arunt14/spec-kit-staff-review/blob/main/CHANGELOG.md", + "license": "MIT", + "requires": { + "speckit_version": ">=0.1.0" + }, + "provides": { + "commands": 1, + "hooks": 1 + }, + "tags": [ + "code", + "review", + "quality" + ], + "verified": false, + "downloads": 0, + "stars": 0, + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-04-01T00:00:00Z" + }, "status": { "name": "Project Status", "id": "status", From 682ffbfc0d0a34aabf62aba2f7bba151391dd599 Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:26:48 -0500 Subject: [PATCH 2/6] =?UTF-8?q?Stage=204:=20TOML=20integrations=20?= =?UTF-8?q?=E2=80=94=20gemini=20and=20tabnine=20migrated=20to=20plugin=20a?= =?UTF-8?q?rchitecture=20(#2050)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TomlIntegration base class in base.py that mirrors MarkdownIntegration: - Overrides command_filename() for .toml extension - Extracts description from YAML frontmatter for top-level TOML key - Renders prompt body in TOML multiline basic strings with escaped backslashes - Keeps full processed template (including frontmatter) as prompt body - Byte-for-byte parity with v0.4.4 release ZIP output Create integrations/gemini/ and integrations/tabnine/ subpackages: - Config-only __init__.py subclassing TomlIntegration - Integration-specific update-context scripts (sh + ps1) Add TomlIntegrationTests mixin with TOML-specific validations: - Valid TOML parsing, description/prompt keys, {{args}} placeholder - Setup/teardown, manifest tracking, install/uninstall round-trips - CLI auto-promote (--ai) and --integration flag tests - Complete file inventory tests (sh + ps) Register both in INTEGRATION_REGISTRY; --ai auto-promote works automatically. --- src/specify_cli/integrations/__init__.py | 4 + src/specify_cli/integrations/base.py | 135 +++++++ .../integrations/gemini/__init__.py | 21 ++ .../gemini/scripts/update-context.ps1 | 23 ++ .../gemini/scripts/update-context.sh | 28 ++ .../integrations/tabnine/__init__.py | 21 ++ .../tabnine/scripts/update-context.ps1 | 23 ++ .../tabnine/scripts/update-context.sh | 28 ++ .../test_integration_base_toml.py | 346 ++++++++++++++++++ tests/integrations/test_integration_gemini.py | 11 + .../integrations/test_integration_tabnine.py | 11 + 11 files changed, 651 insertions(+) create mode 100644 src/specify_cli/integrations/gemini/__init__.py create mode 100644 src/specify_cli/integrations/gemini/scripts/update-context.ps1 create mode 100644 src/specify_cli/integrations/gemini/scripts/update-context.sh create mode 100644 src/specify_cli/integrations/tabnine/__init__.py create mode 100644 src/specify_cli/integrations/tabnine/scripts/update-context.ps1 create mode 100644 src/specify_cli/integrations/tabnine/scripts/update-context.sh create mode 100644 tests/integrations/test_integration_base_toml.py create mode 100644 tests/integrations/test_integration_gemini.py create mode 100644 tests/integrations/test_integration_tabnine.py diff --git a/src/specify_cli/integrations/__init__.py b/src/specify_cli/integrations/__init__.py index 0d7a71242..ed131103c 100644 --- a/src/specify_cli/integrations/__init__.py +++ b/src/specify_cli/integrations/__init__.py @@ -53,6 +53,7 @@ def _register_builtins() -> None: from .codebuddy import CodebuddyIntegration from .copilot import CopilotIntegration from .cursor_agent import CursorAgentIntegration + from .gemini import GeminiIntegration from .iflow import IflowIntegration from .junie import JunieIntegration from .kilocode import KilocodeIntegration @@ -63,6 +64,7 @@ def _register_builtins() -> None: from .qwen import QwenIntegration from .roo import RooIntegration from .shai import ShaiIntegration + from .tabnine import TabnineIntegration from .trae import TraeIntegration from .vibe import VibeIntegration from .windsurf import WindsurfIntegration @@ -75,6 +77,7 @@ def _register_builtins() -> None: _register(CodebuddyIntegration()) _register(CopilotIntegration()) _register(CursorAgentIntegration()) + _register(GeminiIntegration()) _register(IflowIntegration()) _register(JunieIntegration()) _register(KilocodeIntegration()) @@ -85,6 +88,7 @@ def _register_builtins() -> None: _register(QwenIntegration()) _register(RooIntegration()) _register(ShaiIntegration()) + _register(TabnineIntegration()) _register(TraeIntegration()) _register(VibeIntegration()) _register(WindsurfIntegration()) diff --git a/src/specify_cli/integrations/base.py b/src/specify_cli/integrations/base.py index 0320d7f7a..a88039b9a 100644 --- a/src/specify_cli/integrations/base.py +++ b/src/specify_cli/integrations/base.py @@ -5,6 +5,8 @@ Provides: - ``IntegrationBase`` — abstract base every integration must implement. - ``MarkdownIntegration`` — concrete base for standard Markdown-format integrations (the common case — subclass, set three class attrs, done). +- ``TomlIntegration`` — concrete base for TOML-format integrations + (Gemini, Tabnine — subclass, set three class attrs, done). """ from __future__ import annotations @@ -498,3 +500,136 @@ class MarkdownIntegration(IntegrationBase): created.extend(self.install_scripts(project_root, manifest)) return created + + +# --------------------------------------------------------------------------- +# TomlIntegration — TOML-format agents (Gemini, Tabnine) +# --------------------------------------------------------------------------- + +class TomlIntegration(IntegrationBase): + """Concrete base for integrations that use TOML command format. + + Mirrors ``MarkdownIntegration`` closely: subclasses only need to set + ``key``, ``config``, ``registrar_config`` (and optionally + ``context_file``). Everything else is inherited. + + ``setup()`` processes command templates through the same placeholder + pipeline as ``MarkdownIntegration``, then converts the result to + TOML format (``description`` key + ``prompt`` multiline string). + """ + + def command_filename(self, template_name: str) -> str: + """TOML commands use ``.toml`` extension.""" + return f"speckit.{template_name}.toml" + + @staticmethod + def _extract_description(content: str) -> str: + """Extract the ``description`` value from YAML frontmatter. + + Scans lines between the first pair of ``---`` delimiters for a + top-level ``description:`` key. Returns the value (with + surrounding quotes stripped) or an empty string if not found. + """ + in_frontmatter = False + for line in content.splitlines(): + stripped = line.rstrip("\n\r") + if stripped == "---": + if not in_frontmatter: + in_frontmatter = True + continue + break # second --- + if in_frontmatter and stripped.startswith("description:"): + _, _, value = stripped.partition(":") + return value.strip().strip('"').strip("'") + return "" + + @staticmethod + def _render_toml(description: str, body: str) -> str: + """Render a TOML command file from description and body. + + Uses multiline basic strings (``\"\"\"``) with backslashes + escaped, matching the output of the release script. Falls back + to multiline literal strings (``'''``) if the body contains + ``\"\"\"``, then to an escaped basic string as a last resort. + + The body is rstrip'd so the closing delimiter appears on the line + immediately after the last content line — matching the release + script's ``echo "$body"; echo '\"\"\"'`` pattern. + """ + toml_lines: list[str] = [] + + if description: + desc = description.replace('"', '\\"') + toml_lines.append(f'description = "{desc}"') + toml_lines.append("") + + body = body.rstrip("\n") + + # Escape backslashes for basic multiline strings. + escaped = body.replace("\\", "\\\\") + + if '"""' not in escaped: + toml_lines.append('prompt = """') + toml_lines.append(escaped) + toml_lines.append('"""') + elif "'''" not in body: + toml_lines.append("prompt = '''") + toml_lines.append(body) + toml_lines.append("'''") + else: + escaped_body = ( + body.replace("\\", "\\\\") + .replace('"', '\\"') + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t") + ) + toml_lines.append(f'prompt = "{escaped_body}"') + + return "\n".join(toml_lines) + "\n" + + def setup( + self, + project_root: Path, + manifest: IntegrationManifest, + parsed_options: dict[str, Any] | None = None, + **opts: Any, + ) -> list[Path]: + templates = self.list_command_templates() + if not templates: + return [] + + project_root_resolved = project_root.resolve() + if manifest.project_root != project_root_resolved: + raise ValueError( + f"manifest.project_root ({manifest.project_root}) does not match " + f"project_root ({project_root_resolved})" + ) + + dest = self.commands_dest(project_root).resolve() + try: + dest.relative_to(project_root_resolved) + except ValueError as exc: + raise ValueError( + f"Integration destination {dest} escapes " + f"project root {project_root_resolved}" + ) from exc + dest.mkdir(parents=True, exist_ok=True) + + script_type = opts.get("script_type", "sh") + arg_placeholder = self.registrar_config.get("args", "{{args}}") if self.registrar_config else "{{args}}" + created: list[Path] = [] + + for src_file in templates: + raw = src_file.read_text(encoding="utf-8") + description = self._extract_description(raw) + processed = self.process_template(raw, self.key, script_type, arg_placeholder) + toml_content = self._render_toml(description, processed) + dst_name = self.command_filename(src_file.stem) + dst_file = self.write_file_and_record( + toml_content, dest / dst_name, project_root, manifest + ) + created.append(dst_file) + + created.extend(self.install_scripts(project_root, manifest)) + return created diff --git a/src/specify_cli/integrations/gemini/__init__.py b/src/specify_cli/integrations/gemini/__init__.py new file mode 100644 index 000000000..d66f0b80b --- /dev/null +++ b/src/specify_cli/integrations/gemini/__init__.py @@ -0,0 +1,21 @@ +"""Gemini CLI integration.""" + +from ..base import TomlIntegration + + +class GeminiIntegration(TomlIntegration): + key = "gemini" + config = { + "name": "Gemini CLI", + "folder": ".gemini/", + "commands_subdir": "commands", + "install_url": "https://github.com/google-gemini/gemini-cli", + "requires_cli": True, + } + registrar_config = { + "dir": ".gemini/commands", + "format": "toml", + "args": "{{args}}", + "extension": ".toml", + } + context_file = "GEMINI.md" diff --git a/src/specify_cli/integrations/gemini/scripts/update-context.ps1 b/src/specify_cli/integrations/gemini/scripts/update-context.ps1 new file mode 100644 index 000000000..51c9e0bc8 --- /dev/null +++ b/src/specify_cli/integrations/gemini/scripts/update-context.ps1 @@ -0,0 +1,23 @@ +# update-context.ps1 — Gemini CLI integration: create/update GEMINI.md +# +# Thin wrapper that delegates to the shared update-agent-context script. +# Activated in Stage 7 when the shared script uses integration.json dispatch. +# +# Until then, this delegates to the shared script as a subprocess. + +$ErrorActionPreference = 'Stop' + +# Derive repo root from script location (walks up to find .specify/) +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition +$repoRoot = try { git rev-parse --show-toplevel 2>$null } catch { $null } +# If git did not return a repo root, or the git root does not contain .specify, +# fall back to walking up from the script directory to find the initialized project root. +if (-not $repoRoot -or -not (Test-Path (Join-Path $repoRoot '.specify'))) { + $repoRoot = $scriptDir + $fsRoot = [System.IO.Path]::GetPathRoot($repoRoot) + while ($repoRoot -and $repoRoot -ne $fsRoot -and -not (Test-Path (Join-Path $repoRoot '.specify'))) { + $repoRoot = Split-Path -Parent $repoRoot + } +} + +& "$repoRoot/.specify/scripts/powershell/update-agent-context.ps1" -AgentType gemini diff --git a/src/specify_cli/integrations/gemini/scripts/update-context.sh b/src/specify_cli/integrations/gemini/scripts/update-context.sh new file mode 100644 index 000000000..c4e5003a5 --- /dev/null +++ b/src/specify_cli/integrations/gemini/scripts/update-context.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# update-context.sh — Gemini CLI integration: create/update GEMINI.md +# +# Thin wrapper that delegates to the shared update-agent-context script. +# Activated in Stage 7 when the shared script uses integration.json dispatch. +# +# Until then, this delegates to the shared script as a subprocess. + +set -euo pipefail + +# Derive repo root from script location (walks up to find .specify/) +_script_dir="$(cd "$(dirname "$0")" && pwd)" +_root="$_script_dir" +while [ "$_root" != "/" ] && [ ! -d "$_root/.specify" ]; do _root="$(dirname "$_root")"; done +if [ -z "${REPO_ROOT:-}" ]; then + if [ -d "$_root/.specify" ]; then + REPO_ROOT="$_root" + else + git_root="$(git rev-parse --show-toplevel 2>/dev/null || true)" + if [ -n "$git_root" ] && [ -d "$git_root/.specify" ]; then + REPO_ROOT="$git_root" + else + REPO_ROOT="$_root" + fi + fi +fi + +exec "$REPO_ROOT/.specify/scripts/bash/update-agent-context.sh" gemini diff --git a/src/specify_cli/integrations/tabnine/__init__.py b/src/specify_cli/integrations/tabnine/__init__.py new file mode 100644 index 000000000..2928a214a --- /dev/null +++ b/src/specify_cli/integrations/tabnine/__init__.py @@ -0,0 +1,21 @@ +"""Tabnine CLI integration.""" + +from ..base import TomlIntegration + + +class TabnineIntegration(TomlIntegration): + key = "tabnine" + config = { + "name": "Tabnine CLI", + "folder": ".tabnine/agent/", + "commands_subdir": "commands", + "install_url": "https://docs.tabnine.com/main/getting-started/tabnine-cli", + "requires_cli": True, + } + registrar_config = { + "dir": ".tabnine/agent/commands", + "format": "toml", + "args": "{{args}}", + "extension": ".toml", + } + context_file = "TABNINE.md" diff --git a/src/specify_cli/integrations/tabnine/scripts/update-context.ps1 b/src/specify_cli/integrations/tabnine/scripts/update-context.ps1 new file mode 100644 index 000000000..0ffb3a164 --- /dev/null +++ b/src/specify_cli/integrations/tabnine/scripts/update-context.ps1 @@ -0,0 +1,23 @@ +# update-context.ps1 — Tabnine CLI integration: create/update TABNINE.md +# +# Thin wrapper that delegates to the shared update-agent-context script. +# Activated in Stage 7 when the shared script uses integration.json dispatch. +# +# Until then, this delegates to the shared script as a subprocess. + +$ErrorActionPreference = 'Stop' + +# Derive repo root from script location (walks up to find .specify/) +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition +$repoRoot = try { git rev-parse --show-toplevel 2>$null } catch { $null } +# If git did not return a repo root, or the git root does not contain .specify, +# fall back to walking up from the script directory to find the initialized project root. +if (-not $repoRoot -or -not (Test-Path (Join-Path $repoRoot '.specify'))) { + $repoRoot = $scriptDir + $fsRoot = [System.IO.Path]::GetPathRoot($repoRoot) + while ($repoRoot -and $repoRoot -ne $fsRoot -and -not (Test-Path (Join-Path $repoRoot '.specify'))) { + $repoRoot = Split-Path -Parent $repoRoot + } +} + +& "$repoRoot/.specify/scripts/powershell/update-agent-context.ps1" -AgentType tabnine diff --git a/src/specify_cli/integrations/tabnine/scripts/update-context.sh b/src/specify_cli/integrations/tabnine/scripts/update-context.sh new file mode 100644 index 000000000..fe5050b6e --- /dev/null +++ b/src/specify_cli/integrations/tabnine/scripts/update-context.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# update-context.sh — Tabnine CLI integration: create/update TABNINE.md +# +# Thin wrapper that delegates to the shared update-agent-context script. +# Activated in Stage 7 when the shared script uses integration.json dispatch. +# +# Until then, this delegates to the shared script as a subprocess. + +set -euo pipefail + +# Derive repo root from script location (walks up to find .specify/) +_script_dir="$(cd "$(dirname "$0")" && pwd)" +_root="$_script_dir" +while [ "$_root" != "/" ] && [ ! -d "$_root/.specify" ]; do _root="$(dirname "$_root")"; done +if [ -z "${REPO_ROOT:-}" ]; then + if [ -d "$_root/.specify" ]; then + REPO_ROOT="$_root" + else + git_root="$(git rev-parse --show-toplevel 2>/dev/null || true)" + if [ -n "$git_root" ] && [ -d "$git_root/.specify" ]; then + REPO_ROOT="$git_root" + else + REPO_ROOT="$_root" + fi + fi +fi + +exec "$REPO_ROOT/.specify/scripts/bash/update-agent-context.sh" tabnine diff --git a/tests/integrations/test_integration_base_toml.py b/tests/integrations/test_integration_base_toml.py new file mode 100644 index 000000000..e7b506782 --- /dev/null +++ b/tests/integrations/test_integration_base_toml.py @@ -0,0 +1,346 @@ +"""Reusable test mixin for standard TomlIntegration subclasses. + +Each per-agent test file sets ``KEY``, ``FOLDER``, ``COMMANDS_SUBDIR``, +``REGISTRAR_DIR``, and ``CONTEXT_FILE``, then inherits all verification +logic from ``TomlIntegrationTests``. + +Mirrors ``MarkdownIntegrationTests`` closely — same test structure, +adapted for TOML output format. +""" + +import os + +from specify_cli.integrations import INTEGRATION_REGISTRY, get_integration +from specify_cli.integrations.base import TomlIntegration +from specify_cli.integrations.manifest import IntegrationManifest + + +class TomlIntegrationTests: + """Mixin — set class-level constants and inherit these tests. + + Required class attrs on subclass:: + + KEY: str — integration registry key + FOLDER: str — e.g. ".gemini/" + COMMANDS_SUBDIR: str — e.g. "commands" + REGISTRAR_DIR: str — e.g. ".gemini/commands" + CONTEXT_FILE: str — e.g. "GEMINI.md" + """ + + KEY: str + FOLDER: str + COMMANDS_SUBDIR: str + REGISTRAR_DIR: str + CONTEXT_FILE: str + + # -- Registration ----------------------------------------------------- + + def test_registered(self): + assert self.KEY in INTEGRATION_REGISTRY + assert get_integration(self.KEY) is not None + + def test_is_toml_integration(self): + assert isinstance(get_integration(self.KEY), TomlIntegration) + + # -- Config ----------------------------------------------------------- + + def test_config_folder(self): + i = get_integration(self.KEY) + assert i.config["folder"] == self.FOLDER + + def test_config_commands_subdir(self): + i = get_integration(self.KEY) + assert i.config["commands_subdir"] == self.COMMANDS_SUBDIR + + def test_registrar_config(self): + i = get_integration(self.KEY) + assert i.registrar_config["dir"] == self.REGISTRAR_DIR + assert i.registrar_config["format"] == "toml" + assert i.registrar_config["args"] == "{{args}}" + assert i.registrar_config["extension"] == ".toml" + + def test_context_file(self): + i = get_integration(self.KEY) + assert i.context_file == self.CONTEXT_FILE + + # -- Setup / teardown ------------------------------------------------- + + def test_setup_creates_files(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + assert len(created) > 0 + cmd_files = [f for f in created if "scripts" not in f.parts] + for f in cmd_files: + assert f.exists() + assert f.name.startswith("speckit.") + assert f.name.endswith(".toml") + + def test_setup_writes_to_correct_directory(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + expected_dir = i.commands_dest(tmp_path) + assert expected_dir.exists(), f"Expected directory {expected_dir} was not created" + cmd_files = [f for f in created if "scripts" not in f.parts] + assert len(cmd_files) > 0, "No command files were created" + for f in cmd_files: + assert f.resolve().parent == expected_dir.resolve(), ( + f"{f} is not under {expected_dir}" + ) + + def test_templates_are_processed(self, tmp_path): + """Command files must have placeholders replaced and be valid TOML.""" + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + cmd_files = [f for f in created if "scripts" not in f.parts] + assert len(cmd_files) > 0 + for f in cmd_files: + content = f.read_text(encoding="utf-8") + assert "{SCRIPT}" not in content, f"{f.name} has unprocessed {{SCRIPT}}" + assert "__AGENT__" not in content, f"{f.name} has unprocessed __AGENT__" + assert "{ARGS}" not in content, f"{f.name} has unprocessed {{ARGS}}" + + def test_toml_has_description(self, tmp_path): + """Every TOML command file should have a description key.""" + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + cmd_files = [f for f in created if "scripts" not in f.parts] + for f in cmd_files: + content = f.read_text(encoding="utf-8") + assert 'description = "' in content, f"{f.name} missing description key" + + def test_toml_has_prompt(self, tmp_path): + """Every TOML command file should have a prompt key.""" + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + cmd_files = [f for f in created if "scripts" not in f.parts] + for f in cmd_files: + content = f.read_text(encoding="utf-8") + assert "prompt = " in content, f"{f.name} missing prompt key" + + def test_toml_uses_correct_arg_placeholder(self, tmp_path): + """TOML commands must use {{args}} (from {ARGS} replacement).""" + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + cmd_files = [f for f in created if "scripts" not in f.parts] + # At least one file should contain {{args}} from the {ARGS} placeholder + has_args = any("{{args}}" in f.read_text(encoding="utf-8") for f in cmd_files) + assert has_args, "No TOML command file contains {{args}} placeholder" + + def test_toml_is_valid(self, tmp_path): + """Every generated TOML file must parse without errors.""" + try: + import tomllib + except ModuleNotFoundError: + import tomli as tomllib # type: ignore[no-redef] + + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + cmd_files = [f for f in created if "scripts" not in f.parts] + for f in cmd_files: + raw = f.read_bytes() + try: + parsed = tomllib.loads(raw.decode("utf-8")) + except Exception as exc: + raise AssertionError(f"{f.name} is not valid TOML: {exc}") from exc + assert "prompt" in parsed, f"{f.name} parsed TOML has no 'prompt' key" + + def test_all_files_tracked_in_manifest(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + for f in created: + rel = f.resolve().relative_to(tmp_path.resolve()).as_posix() + assert rel in m.files, f"{rel} not tracked in manifest" + + def test_install_uninstall_roundtrip(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.install(tmp_path, m) + assert len(created) > 0 + m.save() + for f in created: + assert f.exists() + removed, skipped = i.uninstall(tmp_path, m) + assert len(removed) == len(created) + assert skipped == [] + + def test_modified_file_survives_uninstall(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.install(tmp_path, m) + m.save() + modified_file = created[0] + modified_file.write_text("user modified this", encoding="utf-8") + removed, skipped = i.uninstall(tmp_path, m) + assert modified_file.exists() + assert modified_file in skipped + + # -- Scripts ---------------------------------------------------------- + + def test_setup_installs_update_context_scripts(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + created = i.setup(tmp_path, m) + scripts_dir = tmp_path / ".specify" / "integrations" / self.KEY / "scripts" + assert scripts_dir.is_dir(), f"Scripts directory not created for {self.KEY}" + assert (scripts_dir / "update-context.sh").exists() + assert (scripts_dir / "update-context.ps1").exists() + + def test_scripts_tracked_in_manifest(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + i.setup(tmp_path, m) + script_rels = [k for k in m.files if "update-context" in k] + assert len(script_rels) >= 2 + + def test_sh_script_is_executable(self, tmp_path): + i = get_integration(self.KEY) + m = IntegrationManifest(self.KEY, tmp_path) + i.setup(tmp_path, m) + sh = tmp_path / ".specify" / "integrations" / self.KEY / "scripts" / "update-context.sh" + assert os.access(sh, os.X_OK) + + # -- CLI auto-promote ------------------------------------------------- + + def test_ai_flag_auto_promotes(self, tmp_path): + from typer.testing import CliRunner + from specify_cli import app + + project = tmp_path / f"promote-{self.KEY}" + project.mkdir() + old_cwd = os.getcwd() + try: + os.chdir(project) + runner = CliRunner() + result = runner.invoke(app, [ + "init", "--here", "--ai", self.KEY, "--script", "sh", "--no-git", + "--ignore-agent-tools", + ], catch_exceptions=False) + finally: + os.chdir(old_cwd) + assert result.exit_code == 0, f"init --ai {self.KEY} failed: {result.output}" + assert f"--integration {self.KEY}" in result.output + + def test_integration_flag_creates_files(self, tmp_path): + from typer.testing import CliRunner + from specify_cli import app + + project = tmp_path / f"int-{self.KEY}" + project.mkdir() + old_cwd = os.getcwd() + try: + os.chdir(project) + runner = CliRunner() + result = runner.invoke(app, [ + "init", "--here", "--integration", self.KEY, "--script", "sh", "--no-git", + "--ignore-agent-tools", + ], catch_exceptions=False) + finally: + os.chdir(old_cwd) + assert result.exit_code == 0, f"init --integration {self.KEY} failed: {result.output}" + i = get_integration(self.KEY) + cmd_dir = i.commands_dest(project) + assert cmd_dir.is_dir(), f"Commands directory {cmd_dir} not created" + commands = sorted(cmd_dir.glob("speckit.*.toml")) + assert len(commands) > 0, f"No command files in {cmd_dir}" + + # -- Complete file inventory ------------------------------------------ + + COMMAND_STEMS = [ + "analyze", "checklist", "clarify", "constitution", + "implement", "plan", "specify", "tasks", "taskstoissues", + ] + + def _expected_files(self, script_variant: str) -> list[str]: + """Build the expected file list for this integration + script variant.""" + i = get_integration(self.KEY) + cmd_dir = i.registrar_config["dir"] + files = [] + + # Command files (.toml) + for stem in self.COMMAND_STEMS: + files.append(f"{cmd_dir}/speckit.{stem}.toml") + + # Integration scripts + files.append(f".specify/integrations/{self.KEY}/scripts/update-context.ps1") + files.append(f".specify/integrations/{self.KEY}/scripts/update-context.sh") + + # Framework files + files.append(f".specify/integration.json") + files.append(f".specify/init-options.json") + files.append(f".specify/integrations/{self.KEY}.manifest.json") + files.append(f".specify/integrations/speckit.manifest.json") + + if script_variant == "sh": + for name in ["check-prerequisites.sh", "common.sh", "create-new-feature.sh", + "setup-plan.sh", "update-agent-context.sh"]: + files.append(f".specify/scripts/bash/{name}") + else: + for name in ["check-prerequisites.ps1", "common.ps1", "create-new-feature.ps1", + "setup-plan.ps1", "update-agent-context.ps1"]: + files.append(f".specify/scripts/powershell/{name}") + + for name in ["agent-file-template.md", "checklist-template.md", + "constitution-template.md", "plan-template.md", + "spec-template.md", "tasks-template.md"]: + files.append(f".specify/templates/{name}") + + files.append(".specify/memory/constitution.md") + return sorted(files) + + def test_complete_file_inventory_sh(self, tmp_path): + """Every file produced by specify init --integration --script sh.""" + from typer.testing import CliRunner + from specify_cli import app + + project = tmp_path / f"inventory-sh-{self.KEY}" + project.mkdir() + old_cwd = os.getcwd() + try: + os.chdir(project) + result = CliRunner().invoke(app, [ + "init", "--here", "--integration", self.KEY, "--script", "sh", + "--no-git", "--ignore-agent-tools", + ], catch_exceptions=False) + finally: + os.chdir(old_cwd) + assert result.exit_code == 0, f"init failed: {result.output}" + actual = sorted(p.relative_to(project).as_posix() + for p in project.rglob("*") if p.is_file()) + expected = self._expected_files("sh") + assert actual == expected, ( + f"Missing: {sorted(set(expected) - set(actual))}\n" + f"Extra: {sorted(set(actual) - set(expected))}" + ) + + def test_complete_file_inventory_ps(self, tmp_path): + """Every file produced by specify init --integration --script ps.""" + from typer.testing import CliRunner + from specify_cli import app + + project = tmp_path / f"inventory-ps-{self.KEY}" + project.mkdir() + old_cwd = os.getcwd() + try: + os.chdir(project) + result = CliRunner().invoke(app, [ + "init", "--here", "--integration", self.KEY, "--script", "ps", + "--no-git", "--ignore-agent-tools", + ], catch_exceptions=False) + finally: + os.chdir(old_cwd) + assert result.exit_code == 0, f"init failed: {result.output}" + actual = sorted(p.relative_to(project).as_posix() + for p in project.rglob("*") if p.is_file()) + expected = self._expected_files("ps") + assert actual == expected, ( + f"Missing: {sorted(set(expected) - set(actual))}\n" + f"Extra: {sorted(set(actual) - set(expected))}" + ) diff --git a/tests/integrations/test_integration_gemini.py b/tests/integrations/test_integration_gemini.py new file mode 100644 index 000000000..9be5985e2 --- /dev/null +++ b/tests/integrations/test_integration_gemini.py @@ -0,0 +1,11 @@ +"""Tests for GeminiIntegration.""" + +from .test_integration_base_toml import TomlIntegrationTests + + +class TestGeminiIntegration(TomlIntegrationTests): + KEY = "gemini" + FOLDER = ".gemini/" + COMMANDS_SUBDIR = "commands" + REGISTRAR_DIR = ".gemini/commands" + CONTEXT_FILE = "GEMINI.md" diff --git a/tests/integrations/test_integration_tabnine.py b/tests/integrations/test_integration_tabnine.py new file mode 100644 index 000000000..95eb47cc1 --- /dev/null +++ b/tests/integrations/test_integration_tabnine.py @@ -0,0 +1,11 @@ +"""Tests for TabnineIntegration.""" + +from .test_integration_base_toml import TomlIntegrationTests + + +class TestTabnineIntegration(TomlIntegrationTests): + KEY = "tabnine" + FOLDER = ".tabnine/agent/" + COMMANDS_SUBDIR = "commands" + REGISTRAR_DIR = ".tabnine/agent/commands" + CONTEXT_FILE = "TABNINE.md" From 4df6d963dca281343bd4b780cca6ddc9d88be7f5 Mon Sep 17 00:00:00 2001 From: Quratulain-bilal Date: Wed, 1 Apr 2026 22:51:06 +0500 Subject: [PATCH 3/6] Add fix-findings extension to community catalog (#2039) - Extension ID: fix-findings - Version: 1.0.0 - Author: Quratulain-bilal - Description: Automated analyze-fix-reanalyze loop that resolves spec findings until clean - Addresses #2011 Co-authored-by: Claude Sonnet 4.6 --- README.md | 1 + extensions/catalog.community.json | 32 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/README.md b/README.md index a1ab6bb27..462c24c05 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,7 @@ The following community-contributed extensions are available in [`catalog.commun | Conduct Extension | Orchestrates spec-kit phases via sub-agent delegation to reduce context pollution. | `process` | Read+Write | [spec-kit-conduct-ext](https://github.com/twbrandon7/spec-kit-conduct-ext) | | DocGuard — CDD Enforcement | Canonical-Driven Development enforcement. Validates, scores, and traces project documentation with automated checks, AI-driven workflows, and spec-kit hooks. Zero NPM runtime dependencies. | `docs` | Read+Write | [spec-kit-docguard](https://github.com/raccioly/docguard) | | Extensify | Create and validate extensions and extension catalogs | `process` | Read+Write | [extensify](https://github.com/mnriem/spec-kit-extensions/tree/main/extensify) | +| Fix Findings | Automated analyze-fix-reanalyze loop that resolves spec findings until clean | `code` | Read+Write | [spec-kit-fix-findings](https://github.com/Quratulain-bilal/spec-kit-fix-findings) | | Fleet Orchestrator | Orchestrate a full feature lifecycle with human-in-the-loop gates across all SpecKit phases | `process` | Read+Write | [spec-kit-fleet](https://github.com/sharathsatish/spec-kit-fleet) | | Iterate | Iterate on spec documents with a two-phase define-and-apply workflow — refine specs mid-implementation and go straight back to building | `docs` | Read+Write | [spec-kit-iterate](https://github.com/imviancagrace/spec-kit-iterate) | | Jira Integration | Create Jira Epics, Stories, and Issues from spec-kit specifications and task breakdowns with configurable hierarchy and custom field support | `integration` | Read+Write | [spec-kit-jira](https://github.com/mbachorik/spec-kit-jira) | diff --git a/extensions/catalog.community.json b/extensions/catalog.community.json index 3b64f0bfb..7a37430fb 100644 --- a/extensions/catalog.community.json +++ b/extensions/catalog.community.json @@ -375,6 +375,38 @@ "created_at": "2026-03-18T00:00:00Z", "updated_at": "2026-03-18T00:00:00Z" }, + "fix-findings": { + "name": "Fix Findings", + "id": "fix-findings", + "description": "Automated analyze-fix-reanalyze loop that resolves spec findings until clean.", + "author": "Quratulain-bilal", + "version": "1.0.0", + "download_url": "https://github.com/Quratulain-bilal/spec-kit-fix-findings/archive/refs/tags/v1.0.0.zip", + "repository": "https://github.com/Quratulain-bilal/spec-kit-fix-findings", + "homepage": "https://github.com/Quratulain-bilal/spec-kit-fix-findings", + "documentation": "https://github.com/Quratulain-bilal/spec-kit-fix-findings/blob/main/README.md", + "changelog": "https://github.com/Quratulain-bilal/spec-kit-fix-findings/blob/main/CHANGELOG.md", + "license": "MIT", + "requires": { + "speckit_version": ">=0.1.0" + }, + "provides": { + "commands": 1, + "hooks": 1 + }, + "tags": [ + "code", + "analysis", + "quality", + "automation", + "findings" + ], + "verified": false, + "downloads": 0, + "stars": 0, + "created_at": "2026-04-01T00:00:00Z", + "updated_at": "2026-04-01T00:00:00Z" + }, "fleet": { "name": "Fleet Orchestrator", "id": "fleet", From 97b9f0f00d269832d879b3053e2bf4465956f7b1 Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Wed, 1 Apr 2026 16:22:48 -0500 Subject: [PATCH 4/6] docs: remove dead Cognitive Squad and Understanding extension links and from extensions/catalog.community.json (#2057) * docs: remove dead Cognitive Squad and Understanding extension links Both repos (Testimonial/cognitive-squad and Testimonial/understanding) have been deleted by their author. No forks or relocations exist. * chore: remove dead extensions from community catalog Remove cognitive-squad and understanding entries whose repos have been deleted by their author. --- README.md | 2 - extensions/catalog.community.json | 85 ------------------------------- 2 files changed, 87 deletions(-) diff --git a/README.md b/README.md index 462c24c05..aefcefb9c 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,6 @@ The following community-contributed extensions are available in [`catalog.commun | Azure DevOps Integration | Sync user stories and tasks to Azure DevOps work items using OAuth authentication | `integration` | Read+Write | [spec-kit-azure-devops](https://github.com/pragya247/spec-kit-azure-devops) | | Checkpoint Extension | Commit the changes made during the middle of the implementation, so you don't end up with just one very large commit at the end | `code` | Read+Write | [spec-kit-checkpoint](https://github.com/aaronrsun/spec-kit-checkpoint) | | Cleanup Extension | Post-implementation quality gate that reviews changes, fixes small issues (scout rule), creates tasks for medium issues, and generates analysis for large issues | `code` | Read+Write | [spec-kit-cleanup](https://github.com/dsrednicki/spec-kit-cleanup) | -| Cognitive Squad | Multi-agent cognitive system with Triadic Model: understanding, internalization, application — with quality gates, backpropagation verification, and self-healing | `docs` | Read+Write | [cognitive-squad](https://github.com/Testimonial/cognitive-squad) | | Conduct Extension | Orchestrates spec-kit phases via sub-agent delegation to reduce context pollution. | `process` | Read+Write | [spec-kit-conduct-ext](https://github.com/twbrandon7/spec-kit-conduct-ext) | | DocGuard — CDD Enforcement | Canonical-Driven Development enforcement. Validates, scores, and traces project documentation with automated checks, AI-driven workflows, and spec-kit hooks. Zero NPM runtime dependencies. | `docs` | Read+Write | [spec-kit-docguard](https://github.com/raccioly/docguard) | | Extensify | Create and validate extensions and extension catalogs | `process` | Read+Write | [extensify](https://github.com/mnriem/spec-kit-extensions/tree/main/extensify) | @@ -216,7 +215,6 @@ The following community-contributed extensions are available in [`catalog.commun | Ship Release Extension | Automates release pipeline: pre-flight checks, branch sync, changelog generation, CI verification, and PR creation | `process` | Read+Write | [spec-kit-ship](https://github.com/arunt14/spec-kit-ship) | | Spec Critique Extension | Dual-lens critical review of spec and plan from product strategy and engineering risk perspectives | `docs` | Read-only | [spec-kit-critique](https://github.com/arunt14/spec-kit-critique) | | Spec Sync | Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval | `docs` | Read+Write | [spec-kit-sync](https://github.com/bgervin/spec-kit-sync) | -| Understanding | Automated requirements quality analysis — 31 deterministic metrics against IEEE/ISO standards with experimental energy-based ambiguity detection | `docs` | Read-only | [understanding](https://github.com/Testimonial/understanding) | | V-Model Extension Pack | Enforces V-Model paired generation of development specs and test specs with full traceability | `docs` | Read+Write | [spec-kit-v-model](https://github.com/leocamello/spec-kit-v-model) | | Verify Extension | Post-implementation quality gate that validates implemented code against specification artifacts | `code` | Read-only | [spec-kit-verify](https://github.com/ismaelJimenez/spec-kit-verify) | | Verify Tasks Extension | Detect phantom completions: tasks marked [X] in tasks.md with no real implementation | `code` | Read-only | [spec-kit-verify-tasks](https://github.com/datastone-inc/spec-kit-verify-tasks) | diff --git a/extensions/catalog.community.json b/extensions/catalog.community.json index 7a37430fb..f04b37067 100644 --- a/extensions/catalog.community.json +++ b/extensions/catalog.community.json @@ -167,50 +167,6 @@ "created_at": "2026-02-22T00:00:00Z", "updated_at": "2026-02-22T00:00:00Z" }, - "cognitive-squad": { - "name": "Cognitive Squad", - "id": "cognitive-squad", - "description": "Multi-agent cognitive system with Triadic Model: understanding, internalization, application — with quality gates, backpropagation verification, and self-healing", - "author": "Testimonial", - "version": "0.1.0", - "download_url": "https://github.com/Testimonial/cognitive-squad/archive/refs/tags/v0.1.0.zip", - "repository": "https://github.com/Testimonial/cognitive-squad", - "homepage": "https://github.com/Testimonial/cognitive-squad", - "documentation": "https://github.com/Testimonial/cognitive-squad/blob/main/README.md", - "changelog": "https://github.com/Testimonial/cognitive-squad/blob/main/CHANGELOG.md", - "license": "MIT", - "requires": { - "speckit_version": ">=0.3.0", - "tools": [ - { - "name": "understanding", - "version": ">=3.4.0", - "required": false - }, - { - "name": "spec-kit-reverse-eng", - "version": ">=1.0.0", - "required": false - } - ] - }, - "provides": { - "commands": 10, - "hooks": 1 - }, - "tags": [ - "ai-agents", - "cognitive", - "full-lifecycle", - "verification", - "multi-agent" - ], - "verified": false, - "downloads": 0, - "stars": 0, - "created_at": "2026-03-16T00:00:00Z", - "updated_at": "2026-03-18T00:00:00Z" - }, "conduct": { "name": "Conduct Extension", "id": "conduct", @@ -1280,47 +1236,6 @@ "created_at": "2026-03-02T00:00:00Z", "updated_at": "2026-03-02T00:00:00Z" }, - "understanding": { - "name": "Understanding", - "id": "understanding", - "description": "Automated requirements quality analysis — validates specs against IEEE/ISO standards using 31 deterministic metrics. Catches ambiguity, missing testability, and structural issues before they reach implementation. Includes experimental energy-based ambiguity detection using local LM token perplexity.", - "author": "Ladislav Bihari", - "version": "3.4.0", - "download_url": "https://github.com/Testimonial/understanding/archive/refs/tags/v3.4.0.zip", - "repository": "https://github.com/Testimonial/understanding", - "homepage": "https://github.com/Testimonial/understanding", - "documentation": "https://github.com/Testimonial/understanding/blob/main/extension/README.md", - "changelog": "https://github.com/Testimonial/understanding/blob/main/extension/CHANGELOG.md", - "license": "MIT", - "requires": { - "speckit_version": ">=0.1.0", - "tools": [ - { - "name": "understanding", - "version": ">=3.4.0", - "required": true - } - ] - }, - "provides": { - "commands": 3, - "hooks": 1 - }, - "tags": [ - "quality", - "metrics", - "requirements", - "validation", - "readability", - "IEEE-830", - "ISO-29148" - ], - "verified": false, - "downloads": 0, - "stars": 0, - "created_at": "2026-03-07T00:00:00Z", - "updated_at": "2026-03-07T00:00:00Z" - }, "v-model": { "name": "V-Model Extension Pack", "id": "v-model", From ea60efe2faee43a4235c37ebf643b40ccaa367f8 Mon Sep 17 00:00:00 2001 From: Ismael <712805+ismaelJimenez@users.noreply.github.com> Date: Wed, 1 Apr 2026 23:26:25 +0200 Subject: [PATCH 5/6] docs: add community extensions website link to README and extensions docs (#2014) --- README.md | 2 ++ extensions/README.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index aefcefb9c..ac7d9f407 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,8 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c ## 🧩 Community Extensions +🔍 **Browse and search community extensions on the [Community Extensions website](https://speckit-community.github.io/extensions/).** + The following community-contributed extensions are available in [`catalog.community.json`](extensions/catalog.community.json): **Categories:** diff --git a/extensions/README.md b/extensions/README.md index a8eedf7ce..379e2d92f 100644 --- a/extensions/README.md +++ b/extensions/README.md @@ -68,6 +68,8 @@ specify extension add --from https://github.com/org/spec-kit-ex ## Available Community Extensions +🔍 **Browse and search community extensions on the [Community Extensions website](https://speckit-community.github.io/extensions/).** + See the [Community Extensions](../README.md#-community-extensions) section in the main README for the full list of available community-contributed extensions. For the raw catalog data, see [`catalog.community.json`](catalog.community.json). From 0945df9ec8103b2e61c1ddb114afaa045d261853 Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:41:18 -0500 Subject: [PATCH 6/6] Add community content disclaimers (#2058) * Add community content disclaimers Add notes clarifying that community extensions, presets, walkthroughs, and community friends are independently created and maintained by their respective authors and are not reviewed, nor endorsed, nor supported by GitHub. Disclaimers added to: - README.md: Community Extensions, Community Presets, Community Walkthroughs, and Community Friends sections - extensions/README.md: Community Reference Catalog and Available Community Extensions sections - presets/README.md: Catalog Management section * Refine community disclaimers per PR review feedback - Clarify that GitHub/maintainers may review catalog PRs for formatting and policy compliance, but do not review, audit, endorse, or support the extension/preset code itself (avoids contradiction with submission process that mentions PR reviews) - Add missing 'use at your own discretion' guidance to Community Walkthroughs and Community Friends sections for consistency --- README.md | 12 ++++++++++++ extensions/README.md | 6 ++++++ presets/README.md | 3 +++ 3 files changed, 21 insertions(+) diff --git a/README.md b/README.md index ac7d9f407..129d69413 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,9 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c ## 🧩 Community Extensions +> [!NOTE] +> Community extensions are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the extension code itself**. The Community Extensions website is also a third-party resource. Review extension source code before installation and use at your own discretion. + 🔍 **Browse and search community extensions on the [Community Extensions website](https://speckit-community.github.io/extensions/).** The following community-contributed extensions are available in [`catalog.community.json`](extensions/catalog.community.json): @@ -225,6 +228,9 @@ To submit your own extension, see the [Extension Publishing Guide](extensions/EX ## 🎨 Community Presets +> [!NOTE] +> Community presets are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the preset code itself**. Review preset source code before installation and use at your own discretion. + The following community-contributed presets customize how Spec Kit behaves — overriding templates, commands, and terminology without changing any tooling. Presets are available in [`catalog.community.json`](presets/catalog.community.json): | Preset | Purpose | Provides | Requires | URL | @@ -236,6 +242,9 @@ To build and publish your own preset, see the [Presets Publishing Guide](presets ## 🚶 Community Walkthroughs +> [!NOTE] +> Community walkthroughs are independently created and maintained by their respective authors. They are **not reviewed, nor endorsed, nor supported by GitHub**. Review their content before following along and use at your own discretion. + See Spec-Driven Development in action across different scenarios with these community-contributed walkthroughs: - **[Greenfield .NET CLI tool](https://github.com/mnriem/spec-kit-dotnet-cli-demo)** — Builds a Timezone Utility as a .NET single-binary CLI tool from a blank directory, covering the full spec-kit workflow: constitution, specify, plan, tasks, and multi-pass implement using GitHub Copilot agents. @@ -254,6 +263,9 @@ See Spec-Driven Development in action across different scenarios with these comm ## 🛠️ Community Friends +> [!NOTE] +> Community projects listed here are independently created and maintained by their respective authors. They are **not reviewed, nor endorsed, nor supported by GitHub**. Review their source code before installation and use at your own discretion. + Community projects that extend, visualize, or build on Spec Kit: - **[cc-sdd](https://github.com/rhuss/cc-sdd)** - A Claude Code plugin that adds composable traits on top of Spec Kit with [Superpowers](https://github.com/obra/superpowers)-based quality gates, spec/code review, git worktree isolation, and parallel implementation via agent teams. diff --git a/extensions/README.md b/extensions/README.md index 379e2d92f..f535ba539 100644 --- a/extensions/README.md +++ b/extensions/README.md @@ -24,6 +24,9 @@ specify extension search # Now uses your organization's catalog instead of the ### Community Reference Catalog (`catalog.community.json`) +> [!NOTE] +> Community extensions are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the extension code itself**. Review extension source code before installation and use at your own discretion. + - **Purpose**: Browse available community-contributed extensions - **Status**: Active - contains extensions submitted by the community - **Location**: `extensions/catalog.community.json` @@ -68,6 +71,9 @@ specify extension add --from https://github.com/org/spec-kit-ex ## Available Community Extensions +> [!NOTE] +> Community extensions are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the extension code itself**. The Community Extensions website is also a third-party resource. Review extension source code before installation and use at your own discretion. + 🔍 **Browse and search community extensions on the [Community Extensions website](https://speckit-community.github.io/extensions/).** See the [Community Extensions](../README.md#-community-extensions) section in the main README for the full list of available community-contributed extensions. diff --git a/presets/README.md b/presets/README.md index f039b83d4..dd3997b23 100644 --- a/presets/README.md +++ b/presets/README.md @@ -67,6 +67,9 @@ Presets **override**, they don't merge. If two presets both provide `spec-templa Presets are discovered through catalogs. By default, Spec Kit uses the official and community catalogs: +> [!NOTE] +> Community presets are independently created and maintained by their respective authors. GitHub and the Spec Kit maintainers may review pull requests that add entries to the community catalog for formatting, catalog structure, or policy compliance, but they do **not review, audit, endorse, or support the preset code itself**. Review preset source code before installation and use at your own discretion. + ```bash # List active catalogs specify preset catalog list