Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ specify init my-project --integration codex --ignore-agent-tools
| 自动扩展 | `agent-context` | `extensions/agent-context` | 维护 AGENTS、CLAUDE、Copilot 等 agent context 文件里的 Spec Kit 受管段。 |
| 默认扩展 | `arch` | `extensions/arch` | 生成或反向生成项目级 4+1 架构视图,形成架构 SSOT。 |
| 默认扩展 | `discovery` | `extensions/discovery` | 在正式计划前做可行性、技术选型、旧代码评估、接口理解、PoC 和场景化技术决策。 |
| 默认扩展 | `intake` | `extensions/intake` | 把 PRD、设计稿、Figma、测试用例等来源归一化为 SDD 可消费的证据包。 |
| 默认扩展 | `intake` | `extensions/intake` | 把 PRD、设计稿、Figma、HTML SSOT、结构化 IR、测试用例等来源归一化为 SDD 可消费的证据包。 |
| 默认扩展 | `preview` | `extensions/preview` | 从规格和计划生成低、中、高保真 Markdown 或自包含 HTML 预览。 |
| 默认扩展 | `repository-governance` | `extensions/repository-governance` | 生成仓库治理 SSOT,帮助 agent 明确目录责任、读取顺序和事实证据。 |
| 默认预设 | `workflow-preset` | `presets/workflow-preset` | 强化 BDD、NFR、UIF、设计产物、任务验证策略和 implement handoff 编排。 |
Expand Down Expand Up @@ -147,20 +147,25 @@ specify init my-project --integration codex --ignore-agent-tools
```text
/speckit.intake.prd
/speckit.intake.visual-design
/speckit.intake.figma2htmlssot
/speckit.intake.ir
/speckit.intake.test-cases
```

支持来源:

- PRD、产品说明、Markdown、PDF、导出的文档。
- 图片、线框图、设计 PDF、Figma 文件、Figma 页面或节点。
- Figma 派生的 HTML visual SSOT 和结构化 UI acceptance IR。
- 既有测试、Gherkin、手工测试用例、QA 导出、测试管理表格。

主要产物:

```text
specs/<feature>/intake/prd/
specs/<feature>/intake/visual-design/
specs/<feature>/intake/visual-design/figma2htmlssot/
specs/<feature>/intake/visual-design/structured-ir/
specs/<feature>/intake/test-cases/
```

Expand Down Expand Up @@ -479,6 +484,8 @@ specify bundle build --path ./my-bundle
```text
/speckit.intake.prd
/speckit.intake.visual-design
/speckit.intake.figma2htmlssot
/speckit.intake.ir
/speckit.intake.test-cases
/speckit.specify
/speckit.clarify
Expand All @@ -489,6 +496,8 @@ specify bundle build --path ./my-bundle

```text
/speckit.intake.visual-design
/speckit.intake.figma2htmlssot
/speckit.intake.ir
/speckit.specify
/speckit.preview.low-md
/speckit.plan
Expand Down
2 changes: 1 addition & 1 deletion docs/community/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ The following community-contributed extensions are available in [`catalog.commun
| GitHub Issues Integration 2 | Creates and syncs local specs from an existing GitHub issue | `integration` | Read+Write | [spec-kit-issue](https://github.com/aaronrsun/spec-kit-issue) |
| Golden Demo | Extracts acceptance criteria from specs, builds test vectors, and produces a behavioral drift report — complementary to Architecture Guard and CDD | `docs` | Read+Write | [spec-kit-golden-demo](https://github.com/jasstt/spec-kit-golden-demo) |
| Improve Extension | Audits any codebase as a senior advisor and writes prioritized, self-contained spec prompts under specs/ that the spec-kit lifecycle can process | `process` | Read+Write | [spec-kit-improve](https://github.com/d0whc3r/spec-kit-improve) |
| Intake | Normalize PRD, design, HTML SSOT, and test-case evidence into SDD-ready intake artifacts | `docs` | Read+Write | [spec-kit-intake](https://github.com/bigsmartben/spec-kit-intake) |
| Intake | Normalize PRD, design, HTML SSOT, structured IR, and test-case evidence into SDD-ready intake artifacts | `docs` | Read+Write | [spec-kit-intake](https://github.com/bigsmartben/spec-kit-intake) |
| Intelligent Agent Orchestrator | Cross-catalog agent discovery and intelligent prompt-to-command routing | `process` | Read+Write | [spec-kit-orchestrator](https://github.com/pragya247/spec-kit-orchestrator) |
| 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) |
Expand Down
71 changes: 56 additions & 15 deletions extensions/catalog.community.json
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,7 @@
"intake": {
"name": "Intake",
"id": "intake",
"description": "Normalize PRD, design, HTML SSOT, and test-case evidence into SDD-ready intake artifacts.",
"description": "Normalize PRD, design, HTML SSOT, structured IR, and test-case evidence into SDD-ready intake artifacts",
"author": "bigsmartben",
"version": "0.1.3",
"download_url": "https://github.com/bigsmartben/spec-kit-intake/archive/refs/tags/v0.1.3.zip",
Expand All @@ -1425,7 +1425,7 @@
]
},
"provides": {
"commands": 4,
"commands": 5,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Publish a new release before advertising five commands

This catalog entry now advertises five Intake commands and the structured-IR capability, but the surrounding metadata still points consumers at version/download_url v0.1.3 while the extension changelog keeps the new IR command under Unreleased. Users installing from the catalog URL will fetch the old release archive without speckit.intake.ir, so the catalog should be updated to a source release that actually contains the fifth command or keep the command count at the released artifact's contents.

Useful? React with 👍 / 👎.

"hooks": 1
},
"tags": [
Expand Down Expand Up @@ -1557,12 +1557,31 @@
"requires": {
"speckit_version": ">=0.1.0",
"tools": [
{ "name": "bash", "version": ">=4.4", "required": true },
{ "name": "git", "required": true },
{ "name": "curl", "required": true },
{ "name": "jq", "required": true },
{ "name": "gitleaks", "required": false },
{ "name": "trufflehog", "required": false }
{
"name": "bash",
"version": ">=4.4",
"required": true
},
{
"name": "git",
"required": true
},
{
"name": "curl",
"required": true
},
{
"name": "jq",
"required": true
},
{
"name": "gitleaks",
"required": false
},
{
"name": "trufflehog",
"required": false
}
]
},
"provides": {
Expand Down Expand Up @@ -3745,8 +3764,14 @@
"requires": {
"speckit_version": ">=0.2.0",
"tools": [
{ "name": "gh", "required": true },
{ "name": "python3", "required": true }
{
"name": "gh",
"required": true
},
{
"name": "python3",
"required": true
}
]
},
"provides": {
Expand Down Expand Up @@ -4040,11 +4065,27 @@
"requires": {
"speckit_version": ">=0.10.0",
"tools": [
{ "name": "rtk", "required": false },
{ "name": "headroom", "required": false },
{ "name": "token-router", "required": false },
{ "name": "ollama", "required": false },
{ "name": "python", "version": ">=3.10", "required": false }
{
"name": "rtk",
"required": false
},
{
"name": "headroom",
"required": false
},
{
"name": "token-router",
"required": false
},
{
"name": "ollama",
"required": false
},
{
"name": "python",
"version": ">=3.10",
"required": false
}
]
},
"provides": {
Expand Down
4 changes: 2 additions & 2 deletions extensions/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"name": "Intake",
"id": "intake",
"version": "0.1.3",
"description": "Normalize PRD, design, HTML SSOT, and test-case evidence into SDD-ready intake artifacts",
"description": "Normalize PRD, design, HTML SSOT, structured IR, and test-case evidence into SDD-ready intake artifacts",
"author": "bigsmartben",
"repository": "https://github.com/bigsmartben/spec-kit-intake",
"license": "MIT",
Expand All @@ -82,7 +82,7 @@
"speckit_version": ">=0.8.10.dev0"
},
"provides": {
"commands": 4,
"commands": 5,
"hooks": 1
},
"tags": [
Expand Down
172 changes: 172 additions & 0 deletions extensions/intake/.github/workflows/community-source-contract.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
name: Community Source Contract

permissions:
contents: read

on:
pull_request:
push:
branches: ["main"]
workflow_dispatch:

concurrency:
group: community-source-contract-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
source-contract:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 1

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"

- name: Install manifest validation dependencies
run: python3 -m pip install --upgrade PyYAML jsonschema

- name: Validate source manifest contract
run: |
python3 - <<'PY'
import re
from pathlib import Path

import yaml

root = Path.cwd()
manifest_path = root / "extension.yml"
manifest_kind = "extension"
if not manifest_path.is_file():
manifest_path = root / "preset.yml"
manifest_kind = "preset"
if not manifest_path.is_file():
raise SystemExit("expected extension.yml or preset.yml")

data = yaml.safe_load(manifest_path.read_text(encoding="utf-8"))
if not isinstance(data, dict):
raise SystemExit(f"{manifest_path.name} must contain a mapping")
if data.get("schema_version") != "1.0":
raise SystemExit("schema_version must be 1.0")

meta = data.get(manifest_kind)
if not isinstance(meta, dict):
raise SystemExit(f"missing {manifest_kind} metadata")

item_id = meta.get("id")
version = meta.get("version")
repository = meta.get("repository")
if not isinstance(item_id, str) or not re.fullmatch(r"[a-z][a-z0-9-]*", item_id):
raise SystemExit(f"invalid {manifest_kind} id: {item_id!r}")
if not isinstance(version, str) or not re.fullmatch(r"\d+\.\d+\.\d+", version):
raise SystemExit(f"invalid semver version: {version!r}")
if not isinstance(repository, str) or not repository.startswith("https://github.com/"):
raise SystemExit("repository must be an https GitHub URL")

for required_file in ("README.md", "LICENSE", "CHANGELOG.md"):
if not (root / required_file).is_file():
raise SystemExit(f"missing {required_file}")

requires = data.get("requires")
if not isinstance(requires, dict) or not requires.get("speckit_version"):
raise SystemExit("requires.speckit_version is required")

provides = data.get("provides")
if not isinstance(provides, dict):
raise SystemExit("provides must be a mapping")

referenced_files = []
if manifest_kind == "extension":
commands = provides.get("commands")
if not isinstance(commands, list) or not commands:
raise SystemExit("extension provides.commands must be a non-empty list")
for command in commands:
if not isinstance(command, dict):
raise SystemExit("command entries must be mappings")
name = command.get("name")
file_name = command.get("file")
if not isinstance(name, str) or not name.startswith("speckit."):
raise SystemExit(f"invalid command name: {name!r}")
if not isinstance(file_name, str):
raise SystemExit(f"command {name!r} missing file")
referenced_files.append(file_name)
for config in provides.get("config", []) or []:
if isinstance(config, dict) and isinstance(config.get("template"), str):
referenced_files.append(config["template"])
else:
templates = provides.get("templates")
if not isinstance(templates, list) or not templates:
raise SystemExit("preset provides.templates must be a non-empty list")
for template in templates:
if not isinstance(template, dict):
raise SystemExit("template entries must be mappings")
file_name = template.get("file")
if not isinstance(file_name, str):
raise SystemExit(f"template {template.get('name')!r} missing file")
referenced_files.append(file_name)

missing = [path for path in referenced_files if not (root / path).is_file()]
if missing:
raise SystemExit(f"manifest references missing files: {missing}")

tags = data.get("tags")
if not isinstance(tags, list) or not tags:
raise SystemExit("tags must be a non-empty list")
PY

- name: Validate integration repository boundary
run: |
python3 - <<'PY'
from pathlib import Path

blocked_patterns = (
"gh pr create --repo " + "github/spec-kit",
"repos/" + "github/spec-kit" + "/dispatches",
"repos/" + "github/spec-kit" + "/pulls",
"github.com/" + "github/spec-kit" + "/compare",
)
offenders = []
for workflow in Path(".github/workflows").glob("*.yml"):
text = workflow.read_text(encoding="utf-8")
for pattern in blocked_patterns:
if pattern in text:
offenders.append(f"{workflow}: {pattern}")
if offenders:
raise SystemExit(
"source repositories must not automate direct submissions to github/spec-kit:\n"
+ "\n".join(offenders)
)
PY

- name: Install project test dependencies
if: hashFiles('requirements-dev.txt') != ''
run: python3 -m pip install -r requirements-dev.txt

- name: Run shell contract tests
if: hashFiles('tests/repository-first-contract.sh') != ''
run: bash tests/repository-first-contract.sh

- name: Run extension validator
if: hashFiles('tests/validate-extension.py') != ''
run: python3 tests/validate-extension.py

- name: Run Python tests from requirements projects
if: hashFiles('requirements-dev.txt') != '' && hashFiles('tests/test_*.py') != ''
run: |
python3 - <<'PY'
import importlib.util
import subprocess
import sys

if importlib.util.find_spec("pytest") is not None:
subprocess.run([sys.executable, "-m", "pytest", "-q"], check=True)
else:
subprocess.run(
[sys.executable, "-m", "unittest", "discover", "-s", "tests", "-p", "test_*.py"],
check=True,
)
PY
Loading