Skip to content

Commit cf785ba

Browse files
authored
Merge origin/main into add-verify-review-ship-extension branch
Resolve conflict in catalog updated_at timestamp (keep 2026-07-10 from this branch) and incorporate ears and figma entries from main. Assisted-by: GitHub Copilot (model: claude-sonnet-4.5, autonomous)
2 parents 5f7ac66 + 983a87f commit cf785ba

14 files changed

Lines changed: 327 additions & 17 deletions

File tree

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
fi
3838
3939
- name: Run markdownlint-cli2
40-
uses: DavidAnson/markdownlint-cli2-action@ded1f9488f68a970bc66ea5619e13e9b52e601cd # v23
40+
uses: DavidAnson/markdownlint-cli2-action@8de2aa07cae85fd17c0b35642db70cf5495f1d25 # v24.0.0
4141
with:
4242
globs: |
4343
'**/*.md'

.github/workflows/publish-pypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
ref: refs/tags/${{ inputs.tag }}
3333

3434
- name: Install uv
35-
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
35+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
3636

3737
- name: Set up Python
3838
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
@@ -74,7 +74,7 @@ jobs:
7474
path: dist/
7575

7676
- name: Install uv
77-
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
77+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
7878

7979
- name: Publish to PyPI
8080
run: uv publish

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
1717

1818
- name: Install uv
19-
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
19+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
2020

2121
- name: Set up Python
2222
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
@@ -37,7 +37,7 @@ jobs:
3737
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
3838

3939
- name: Install uv
40-
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
40+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
4141

4242
- name: Set up Python ${{ matrix.python-version }}
4343
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22

33
<!-- insert new changelog below this comment -->
44

5+
## [0.12.10] - 2026-07-10
6+
7+
### Changed
8+
9+
- chore(deps): bump astral-sh/setup-uv from 8.2.0 to 8.3.2 (#3439)
10+
- chore(deps): bump DavidAnson/markdownlint-cli2-action (#3438)
11+
- fix(templates): correct phase numbering in plan.md (#3416)
12+
- fix(git-ext): honor explicit -Number 0 in PowerShell branch creation (#3412)
13+
- docs: add 'spectatui' entry to friends.md (#3362)
14+
- test: pin interpreter probe so py-template render test passes on Windows (#3428)
15+
- feat(workflows): make shell step timeout configurable (#3404)
16+
- fix: find plans in nested spec directories (#3405)
17+
- feat(templates): add py: lines to command templates' scripts frontmatter (#3403)
18+
- chore: release 0.12.9, begin 0.12.10.dev0 development (#3426)
19+
520
## [0.12.9] - 2026-07-09
621

722
### Changed

docs/community/extensions.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ The following community-contributed extensions are available in [`catalog.commun
5252
| Cost Tracker | Track real LLM dollar cost across SDD workflows — per-feature budgets, per-integration comparison, and finance-ready exports | `visibility` | Read+Write | [spec-kit-cost](https://github.com/Quratulain-bilal/spec-kit-cost) |
5353
| Data Model Diagram | Generates Mermaid ER diagrams from Spec Kit data models after planning | `docs` | Read+Write | [spec-kit-data-model-diagram](https://github.com/benizzio/spec-kit-data-model-diagram) |
5454
| DocGuard — CDD Enforcement | The only doc-integrity engine with an MCP server, SARIF output, and a deterministic zero-LLM core. Validates, scores, and traces documentation against code — 24 validators, stable finding codes, GitHub Action with PR annotations, spec-kit hooks. Pure Node.js, one pinned dep. | `docs` | Read+Write | [spec-kit-docguard](https://github.com/raccioly/docguard) |
55+
| EARS Requirements Syntax | Author, lint, and convert requirements using EARS - the five industry-standard sentence patterns for unambiguous, testable requirements | `docs` | Read+Write | [spec-kit-ears](https://github.com/dhruv-15-03/spec-kit-ears) |
5556
| Extensify | Create and validate extensions and extension catalogs | `process` | Read+Write | [extensify](https://github.com/mnriem/spec-kit-extensions/tree/main/extensify) |
5657
| 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) |
5758
| FixIt Extension | Spec-aware bug fixing — maps bugs to spec artifacts, proposes a plan, applies minimal changes | `code` | Read+Write | [spec-kit-fixit](https://github.com/speckit-community/spec-kit-fixit) |
@@ -117,6 +118,7 @@ The following community-contributed extensions are available in [`catalog.commun
117118
| 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) |
118119
| Spec Diagram | Auto-generate Mermaid diagrams of SDD workflow state, feature progress, and task dependencies | `visibility` | Read-only | [spec-kit-diagram-](https://github.com/Quratulain-bilal/spec-kit-diagram-) |
119120
| Spec Kit Discovery Extension | Run technical discovery commands for feasibility, technology selection, scenario-specific technical decisions, legacy codebase assessment, implementation understanding, and proof-of-concept validation | `process` | Read+Write | [spec-kit-discovery](https://github.com/bigsmartben/spec-kit-discovery) |
121+
| Spec Kit Figma | Agent-agnostic SpecKit extension that grounds spec, plan & task generation in Figma design context — REST + optional MCP, single/mono/multi-repo, macOS/Linux/Windows. | `integration` | Read+Write | [spec-kit-figma](https://github.com/Fyloss/spec-kit-figma) |
120122
| Spec Kit Preview | Generate evidence-backed low, mid, or high fidelity previews from Spec Kit artifacts as Markdown or self-contained HTML | `docs` | Read+Write | [spec-kit-preview](https://github.com/bigsmartben/spec-kit-preview) |
121123
| Spec Kit Schedule | Optimal multi-agent task scheduling via CP-SAT — DAG precedence, hallucination-aware caps, file-conflict avoidance, stochastic durations, replanning, and interactive HTML output | `process` | Read+Write | [spec-kit-schedule](https://github.com/jfranc38/spec-kit-schedule) |
122124
| Spec Kit TLDR | Render a feature's spec.md / plan.md into a review-oriented TLDR (self-contained HTML dashboard + PR-native Markdown) that surfaces risks for faster PR review. | `visibility` | Read+Write | [speckit-tldr](https://github.com/qurore/speckit-tldr) |

extensions/catalog.community.json

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,39 @@
11801180
"created_at": "2026-03-13T00:00:00Z",
11811181
"updated_at": "2026-03-13T00:00:00Z"
11821182
},
1183+
"ears": {
1184+
"name": "EARS Requirements Syntax",
1185+
"id": "ears",
1186+
"description": "Author, lint, and convert requirements using EARS (Easy Approach to Requirements Syntax) - the five industry-standard sentence patterns for unambiguous, testable requirements.",
1187+
"author": "dhruv-15-03",
1188+
"version": "1.0.0",
1189+
"download_url": "https://github.com/dhruv-15-03/spec-kit-ears/archive/refs/tags/v1.0.0.zip",
1190+
"repository": "https://github.com/dhruv-15-03/spec-kit-ears",
1191+
"homepage": "https://github.com/dhruv-15-03/spec-kit-ears",
1192+
"documentation": "https://github.com/dhruv-15-03/spec-kit-ears/blob/main/README.md",
1193+
"changelog": "https://github.com/dhruv-15-03/spec-kit-ears/blob/main/CHANGELOG.md",
1194+
"license": "MIT",
1195+
"category": "docs",
1196+
"effect": "read-write",
1197+
"requires": {
1198+
"speckit_version": ">=0.9.0"
1199+
},
1200+
"provides": {
1201+
"commands": 3,
1202+
"hooks": 0
1203+
},
1204+
"tags": [
1205+
"ears",
1206+
"requirements",
1207+
"specification",
1208+
"quality"
1209+
],
1210+
"verified": false,
1211+
"downloads": 0,
1212+
"stars": 0,
1213+
"created_at": "2026-07-08T00:00:00Z",
1214+
"updated_at": "2026-07-08T00:00:00Z"
1215+
},
11831216
"extensify": {
11841217
"name": "Extensify",
11851218
"id": "extensify",
@@ -1213,6 +1246,47 @@
12131246
"created_at": "2026-03-18T00:00:00Z",
12141247
"updated_at": "2026-04-23T00:00:00Z"
12151248
},
1249+
"figma": {
1250+
"name": "Spec Kit Figma",
1251+
"id": "figma",
1252+
"description": "Agent-agnostic SpecKit extension that grounds spec, plan & task generation in Figma design context — REST + optional MCP, single/mono/multi-repo, macOS/Linux/Windows.",
1253+
"author": "Fyloss",
1254+
"version": "1.6.0",
1255+
"download_url": "https://github.com/Fyloss/spec-kit-figma/archive/refs/tags/v1.6.0.zip",
1256+
"repository": "https://github.com/Fyloss/spec-kit-figma",
1257+
"homepage": "https://github.com/Fyloss/spec-kit-figma",
1258+
"documentation": "https://github.com/Fyloss/spec-kit-figma/blob/main/docs/INSTALL.md",
1259+
"changelog": "https://github.com/Fyloss/spec-kit-figma/blob/main/CHANGELOG.md",
1260+
"license": "MIT",
1261+
"category": "integration",
1262+
"effect": "read-write",
1263+
"requires": {
1264+
"speckit_version": ">=0.1.0",
1265+
"tools": [
1266+
{ "name": "git", "required": true },
1267+
{ "name": "bash", "required": false },
1268+
{ "name": "curl", "required": false },
1269+
{ "name": "jq", "required": false },
1270+
{ "name": "pwsh", "required": false }
1271+
]
1272+
},
1273+
"provides": {
1274+
"commands": 5,
1275+
"hooks": 6
1276+
},
1277+
"tags": [
1278+
"figma",
1279+
"design",
1280+
"frontend",
1281+
"ui",
1282+
"design-system"
1283+
],
1284+
"verified": false,
1285+
"downloads": 0,
1286+
"stars": 0,
1287+
"created_at": "2026-07-08T00:00:00Z",
1288+
"updated_at": "2026-07-08T00:00:00Z"
1289+
},
12161290
"fix-findings": {
12171291
"name": "Fix Findings",
12181292
"id": "fix-findings",

extensions/git/scripts/powershell/create-new-feature-branch.ps1

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,10 @@ if ($env:GIT_BRANCH_NAME) {
446446
$branchSuffix = Get-BranchName -Description $featureDesc
447447
}
448448

449-
if ($Timestamp -and $Number -ne 0) {
449+
# Warn if -Number and -Timestamp are both specified. Use ContainsKey (not
450+
# `-ne 0`) so an explicit `-Number 0` is also detected, matching the bash twin's
451+
# `[ -n "$BRANCH_NUMBER" ]` check.
452+
if ($Timestamp -and $PSBoundParameters.ContainsKey('Number')) {
450453
Write-Warning "[specify] Warning: -Number is ignored when -Timestamp is used"
451454
$Number = 0
452455
}
@@ -456,7 +459,10 @@ if ($env:GIT_BRANCH_NAME) {
456459
$branchName = New-BranchName -FeatureNum $featureNum -BranchSuffix $branchSuffix
457460
} else {
458461
$branchScopePrefix = Get-BranchScopePrefix -Template $branchTemplate -BranchSuffix $branchSuffix
459-
if ($Number -eq 0) {
462+
# Auto-detect the next number only when -Number was not supplied; an
463+
# explicit value (including 0) is honored, matching the bash twin's
464+
# `[ -z "$BRANCH_NUMBER" ]` check.
465+
if (-not $PSBoundParameters.ContainsKey('Number')) {
460466
if ($DryRun -and $hasGit) {
461467
$Number = Get-NextBranchNumber -SpecsDir $specsDir -SkipFetch -ScopePrefix $branchScopePrefix
462468
} elseif ($DryRun) {

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "specify-cli"
3-
version = "0.12.10.dev0"
3+
version = "0.12.11.dev0"
44
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
55
readme = "README.md"
66
requires-python = ">=3.11"

src/specify_cli/workflows/_commands.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,15 @@ def _validate_and_install_local(yaml_path: Path, source_label: str) -> None:
601601
except (ValueError, yaml.YAMLError) as exc:
602602
console.print(f"[red]Error:[/red] Invalid workflow YAML: {exc}")
603603
raise typer.Exit(1)
604-
if not definition.id or not definition.id.strip():
604+
# Non-string ids (e.g. unquoted ``id: 123`` or ``id: 0``) fall through
605+
# to validate_workflow below, which reports a typed error instead of
606+
# crashing on ``.strip()`` here. Only None/empty/whitespace-only ids
607+
# are rejected as missing.
608+
if (
609+
definition.id is None
610+
or definition.id == ""
611+
or (isinstance(definition.id, str) and not definition.id.strip())
612+
):
605613
console.print("[red]Error:[/red] Workflow definition has an empty or missing 'id'")
606614
raise typer.Exit(1)
607615

src/specify_cli/workflows/engine.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,26 +129,49 @@ def validate_workflow(definition: WorkflowDefinition) -> list[str]:
129129
errors: list[str] = []
130130

131131
# -- Schema version ---------------------------------------------------
132-
if definition.schema_version not in ("1.0", "1"):
132+
# str() so an unquoted ``schema_version: 1.0`` (YAML float) is accepted —
133+
# rejecting it would print "Unsupported schema_version 1.0. Expected '1.0'."
134+
if str(definition.schema_version) != "1.0":
133135
errors.append(
134136
f"Unsupported schema_version {definition.schema_version!r}. "
135137
f"Expected '1.0'."
136138
)
137139

138140
# -- Top-level fields -------------------------------------------------
139-
if not definition.id:
141+
# YAML parses unquoted scalars like ``id: 123`` or ``version: 1.0`` as
142+
# int/float; check types before regex/string operations so authoring
143+
# mistakes surface as validation errors instead of tracebacks. Only
144+
# ``None``/empty-string count as missing so falsey non-strings
145+
# (``id: 0``, ``name: false``) still get the typed error.
146+
if definition.id is None or definition.id == "":
140147
errors.append("Workflow is missing 'workflow.id'.")
148+
elif not isinstance(definition.id, str):
149+
errors.append(
150+
f"'workflow.id' must be a string, got "
151+
f"{type(definition.id).__name__} ({definition.id!r})."
152+
)
141153
elif not _ID_PATTERN.match(definition.id):
142154
errors.append(
143155
f"Workflow ID {definition.id!r} must be lowercase alphanumeric "
144156
f"with hyphens."
145157
)
146158

147-
if not definition.name:
159+
if definition.name is None or definition.name == "":
148160
errors.append("Workflow is missing 'workflow.name'.")
161+
elif not isinstance(definition.name, str):
162+
errors.append(
163+
f"'workflow.name' must be a string, got "
164+
f"{type(definition.name).__name__} ({definition.name!r})."
165+
)
149166

150-
if not definition.version:
167+
if definition.version is None or definition.version == "":
151168
errors.append("Workflow is missing 'workflow.version'.")
169+
elif not isinstance(definition.version, str):
170+
errors.append(
171+
f"'workflow.version' must be a string, got "
172+
f"{type(definition.version).__name__} ({definition.version!r}) — "
173+
f'quote it in YAML (version: "1.0.0").'
174+
)
152175
elif not re.match(r"^\d+\.\d+\.\d+$", definition.version):
153176
errors.append(
154177
f"Workflow version {definition.version!r} is not valid "
@@ -256,9 +279,15 @@ def _validate_steps(
256279
continue
257280

258281
step_id = step_config.get("id")
259-
if not step_id:
282+
if step_id is None or step_id == "":
260283
errors.append("Step is missing 'id' field.")
261284
continue
285+
if not isinstance(step_id, str):
286+
errors.append(
287+
f"Step ID must be a string, got "
288+
f"{type(step_id).__name__} ({step_id!r})."
289+
)
290+
continue
262291

263292
if ":" in step_id:
264293
errors.append(

0 commit comments

Comments
 (0)