Skip to content

Commit 7224580

Browse files
authored
Merge branch 'main' into add-retrospective-to-community-catalog
2 parents 8525202 + 6757c90 commit 7224580

11 files changed

Lines changed: 260 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ Recent changes to the Specify CLI and templates are documented here.
77
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
88
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
99

10+
## [0.1.13] - 2026-03-03
11+
12+
### Fixed
13+
14+
- **Copilot Extension Commands Not Visible**: Fixed extension commands not appearing in GitHub Copilot when installed via `specify extension add --dev`
15+
- Changed Copilot file extension from `.md` to `.agent.md` in `CommandRegistrar.AGENT_CONFIGS` so Copilot recognizes agent files
16+
- Added generation of companion `.prompt.md` files in `.github/prompts/` during extension command registration, matching the release packaging behavior
17+
- Added cleanup of `.prompt.md` companion files when removing extensions via `specify extension remove`
18+
1019
## [0.1.12] - 2026-03-02
1120

1221
### Changed

extensions/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,11 @@ The following community-contributed extensions are available in [`catalog.commun
7272

7373
| Extension | Purpose | URL |
7474
|-----------|---------|-----|
75-
| V-Model Extension Pack | Enforces V-Model paired generation of development specs and test specs with full traceability | [spec-kit-v-model](https://github.com/leocamello/spec-kit-v-model) |
7675
| 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 | [spec-kit-cleanup](https://github.com/dsrednicki/spec-kit-cleanup) |
7776
| Retrospective Extension | Post-implementation retrospective with spec adherence scoring, drift analysis, and human-gated spec updates | [spec-kit-retrospective](https://github.com/emi-dm/spec-kit-retrospective) |
77+
| Spec Sync | Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval | [spec-kit-sync](https://github.com/bgervin/spec-kit-sync) |
78+
| V-Model Extension Pack | Enforces V-Model paired generation of development specs and test specs with full traceability | [spec-kit-v-model](https://github.com/leocamello/spec-kit-v-model) |
79+
7880

7981
## Adding Your Extension
8082

extensions/catalog.community.json

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schema_version": "1.0",
3-
"updated_at": "2026-02-24T00:00:00Z",
3+
"updated_at": "2026-03-02T05:42:00Z",
44
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/extensions/catalog.community.json",
55
"extensions": {
66
"cleanup": {
@@ -55,6 +55,32 @@
5555
"created_at": "2026-02-24T00:00:00Z",
5656
"updated_at": "2026-02-24T00:00:00Z"
5757
},
58+
"sync": {
59+
"name": "Spec Sync",
60+
"id": "sync",
61+
"description": "Detect and resolve drift between specs and implementation. AI-assisted resolution with human approval.",
62+
"author": "bgervin",
63+
"version": "0.1.0",
64+
"download_url": "https://github.com/bgervin/spec-kit-sync/archive/refs/tags/v0.1.0.zip",
65+
"repository": "https://github.com/bgervin/spec-kit-sync",
66+
"homepage": "https://github.com/bgervin/spec-kit-sync",
67+
"documentation": "https://github.com/bgervin/spec-kit-sync/blob/main/README.md",
68+
"changelog": "https://github.com/bgervin/spec-kit-sync/blob/main/CHANGELOG.md",
69+
"license": "MIT",
70+
"requires": {
71+
"speckit_version": ">=0.1.0"
72+
},
73+
"provides": {
74+
"commands": 5,
75+
"hooks": 1
76+
},
77+
"tags": ["sync", "drift", "validation", "bidirectional", "backfill"],
78+
"verified": false,
79+
"downloads": 0,
80+
"stars": 0,
81+
"created_at": "2026-03-02T00:00:00Z",
82+
"updated_at": "2026-03-02T00:00:00Z"
83+
},
5884
"v-model": {
5985
"name": "V-Model Extension Pack",
6086
"id": "v-model",

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.1.12"
3+
version = "0.1.13"
44
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
55
requires-python = ">=3.11"
66
dependencies = [

scripts/bash/create-new-feature.sh

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ if [ -z "$FEATURE_DESCRIPTION" ]; then
6767
exit 1
6868
fi
6969

70+
# Trim whitespace and validate description is not empty (e.g., user passed only whitespace)
71+
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | xargs)
72+
if [ -z "$FEATURE_DESCRIPTION" ]; then
73+
echo "Error: Feature description cannot be empty or contain only whitespace" >&2
74+
exit 1
75+
fi
76+
7077
# Function to find the repository root by searching for existing project markers
7178
find_repo_root() {
7279
local dir="$1"
@@ -272,7 +279,16 @@ if [ ${#BRANCH_NAME} -gt $MAX_BRANCH_LENGTH ]; then
272279
fi
273280

274281
if [ "$HAS_GIT" = true ]; then
275-
git checkout -b "$BRANCH_NAME"
282+
if ! git checkout -b "$BRANCH_NAME" 2>/dev/null; then
283+
# Check if branch already exists
284+
if git branch --list "$BRANCH_NAME" | grep -q .; then
285+
>&2 echo "Error: Branch '$BRANCH_NAME' already exists. Please use a different feature name or specify a different number with --number."
286+
exit 1
287+
else
288+
>&2 echo "Error: Failed to create git branch '$BRANCH_NAME'. Please check your git configuration and try again."
289+
exit 1
290+
fi
291+
fi
276292
else
277293
>&2 echo "[specify] Warning: Git repository not detected; skipped branch creation for $BRANCH_NAME"
278294
fi

scripts/powershell/create-new-feature.ps1

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ if (-not $FeatureDescription -or $FeatureDescription.Count -eq 0) {
3535

3636
$featureDesc = ($FeatureDescription -join ' ').Trim()
3737

38+
# Validate description is not empty after trimming (e.g., user passed only whitespace)
39+
if ([string]::IsNullOrWhiteSpace($featureDesc)) {
40+
Write-Error "Error: Feature description cannot be empty or contain only whitespace"
41+
exit 1
42+
}
43+
3844
# Resolve repository root. Prefer git information when available, but fall back
3945
# to searching for repository markers so the workflow still functions in repositories that
4046
# were initialized with --no-git.
@@ -242,10 +248,26 @@ if ($branchName.Length -gt $maxBranchLength) {
242248
}
243249

244250
if ($hasGit) {
251+
$branchCreated = $false
245252
try {
246-
git checkout -b $branchName | Out-Null
253+
git checkout -b $branchName 2>$null | Out-Null
254+
if ($LASTEXITCODE -eq 0) {
255+
$branchCreated = $true
256+
}
247257
} catch {
248-
Write-Warning "Failed to create git branch: $branchName"
258+
# Exception during git command
259+
}
260+
261+
if (-not $branchCreated) {
262+
# Check if branch already exists
263+
$existingBranch = git branch --list $branchName 2>$null
264+
if ($existingBranch) {
265+
Write-Error "Error: Branch '$branchName' already exists. Please use a different feature name or specify a different number with -Number."
266+
exit 1
267+
} else {
268+
Write-Error "Error: Failed to create git branch '$branchName'. Please check your git configuration and try again."
269+
exit 1
270+
}
249271
}
250272
} else {
251273
Write-Warning "[specify] Warning: Git repository not detected; skipped branch creation for $branchName"

src/specify_cli/extensions.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,12 @@ def remove(self, extension_id: str, keep_config: bool = False) -> bool:
455455
if cmd_file.exists():
456456
cmd_file.unlink()
457457

458+
# Also remove companion .prompt.md for Copilot
459+
if agent_name == "copilot":
460+
prompt_file = self.project_root / ".github" / "prompts" / f"{cmd_name}.prompt.md"
461+
if prompt_file.exists():
462+
prompt_file.unlink()
463+
458464
if keep_config:
459465
# Preserve config files, only remove non-config files
460466
if extension_dir.exists():
@@ -597,7 +603,7 @@ class CommandRegistrar:
597603
"dir": ".github/agents",
598604
"format": "markdown",
599605
"args": "$ARGUMENTS",
600-
"extension": ".md"
606+
"extension": ".agent.md"
601607
},
602608
"cursor": {
603609
"dir": ".cursor/commands",
@@ -871,16 +877,40 @@ def register_commands_for_agent(
871877
dest_file = commands_dir / f"{cmd_name}{agent_config['extension']}"
872878
dest_file.write_text(output)
873879

880+
# Generate companion .prompt.md for Copilot agents
881+
if agent_name == "copilot":
882+
self._write_copilot_prompt(project_root, cmd_name)
883+
874884
registered.append(cmd_name)
875885

876886
# Register aliases
877887
for alias in cmd_info.get("aliases", []):
878888
alias_file = commands_dir / f"{alias}{agent_config['extension']}"
879889
alias_file.write_text(output)
890+
# Generate companion .prompt.md for alias too
891+
if agent_name == "copilot":
892+
self._write_copilot_prompt(project_root, alias)
880893
registered.append(alias)
881894

882895
return registered
883896

897+
@staticmethod
898+
def _write_copilot_prompt(project_root: Path, cmd_name: str) -> None:
899+
"""Generate a companion .prompt.md file for a Copilot agent command.
900+
901+
Copilot requires a .prompt.md file in .github/prompts/ that references
902+
the corresponding .agent.md file in .github/agents/ via an ``agent:``
903+
frontmatter field.
904+
905+
Args:
906+
project_root: Path to project root
907+
cmd_name: Command name (used as the file stem, e.g. 'speckit.my-ext.example')
908+
"""
909+
prompts_dir = project_root / ".github" / "prompts"
910+
prompts_dir.mkdir(parents=True, exist_ok=True)
911+
prompt_file = prompts_dir / f"{cmd_name}.prompt.md"
912+
prompt_file.write_text(f"---\nagent: {cmd_name}\n---\n")
913+
884914
def register_commands_for_all_agents(
885915
self,
886916
manifest: ExtensionManifest,

templates/commands/checklist.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,10 @@ You **MUST** consider the user input before proceeding (if not empty).
9494
- Generate unique checklist filename:
9595
- Use short, descriptive name based on domain (e.g., `ux.md`, `api.md`, `security.md`)
9696
- Format: `[domain].md`
97-
- If file exists, append to existing file
98-
- Number items sequentially starting from CHK001
99-
- Each `/speckit.checklist` run creates a NEW file (never overwrites existing checklists)
97+
- File handling behavior:
98+
- If file does NOT exist: Create new file and number items starting from CHK001
99+
- If file exists: Append new items to existing file, continuing from the last CHK ID (e.g., if last item is CHK015, start new items at CHK016)
100+
- Never delete or replace existing checklist content - always preserve and append
100101

101102
**CORE PRINCIPLE - Test the Requirements, Not the Implementation**:
102103
Every checklist item MUST evaluate the REQUIREMENTS THEMSELVES for:
@@ -208,13 +209,13 @@ You **MUST** consider the user input before proceeding (if not empty).
208209

209210
6. **Structure Reference**: Generate the checklist following the canonical template in `templates/checklist-template.md` for title, meta section, category headings, and ID formatting. If template is unavailable, use: H1 title, purpose/created meta lines, `##` category sections containing `- [ ] CHK### <requirement item>` lines with globally incrementing IDs starting at CHK001.
210211

211-
7. **Report**: Output full path to created checklist, item count, and remind user that each run creates a new file. Summarize:
212+
7. **Report**: Output full path to checklist file, item count, and summarize whether the run created a new file or appended to an existing one. Summarize:
212213
- Focus areas selected
213214
- Depth level
214215
- Actor/timing
215216
- Any explicit user-specified must-have items incorporated
216217

217-
**Important**: Each `/speckit.checklist` command invocation creates a checklist file using short, descriptive names unless file already exists. This allows:
218+
**Important**: Each `/speckit.checklist` command invocation uses a short, descriptive checklist filename and either creates a new file or appends to an existing one. This allows:
218219

219220
- Multiple checklists of different types (e.g., `ux.md`, `test.md`, `security.md`)
220221
- Simple, memorable filenames that indicate checklist purpose

templates/commands/clarify.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Execution steps:
8989
- Information is better deferred to planning phase (note internally)
9090

9191
3. Generate (internally) a prioritized queue of candidate clarification questions (maximum 5). Do NOT output them all at once. Apply these constraints:
92-
- Maximum of 10 total questions across the whole session.
92+
- Maximum of 5 total questions across the whole session.
9393
- Each question must be answerable with EITHER:
9494
- A short multiple‑choice selection (2–5 distinct, mutually exclusive options), OR
9595
- A one-word / short‑phrase answer (explicitly constrain: "Answer in <=5 words").

templates/commands/implement.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ You **MUST** consider the user input before proceeding (if not empty).
8888
- **Rust**: `target/`, `debug/`, `release/`, `*.rs.bk`, `*.rlib`, `*.prof*`, `.idea/`, `*.log`, `.env*`
8989
- **Kotlin**: `build/`, `out/`, `.gradle/`, `.idea/`, `*.class`, `*.jar`, `*.iml`, `*.log`, `.env*`
9090
- **C++**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.so`, `*.a`, `*.exe`, `*.dll`, `.idea/`, `*.log`, `.env*`
91-
- **C**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.a`, `*.so`, `*.exe`, `Makefile`, `config.log`, `.idea/`, `*.log`, `.env*`
91+
- **C**: `build/`, `bin/`, `obj/`, `out/`, `*.o`, `*.a`, `*.so`, `*.exe`, `autom4te.cache/`, `config.status`, `config.log`, `.idea/`, `*.log`, `.env*`
9292
- **Swift**: `.build/`, `DerivedData/`, `*.swiftpm/`, `Packages/`
9393
- **R**: `.Rproj.user/`, `.Rhistory`, `.RData`, `.Ruserdata`, `*.Rproj`, `packrat/`, `renv/`
9494
- **Universal**: `.DS_Store`, `Thumbs.db`, `*.tmp`, `*.swp`, `.vscode/`, `.idea/`

0 commit comments

Comments
 (0)