Skip to content

Commit aeb08ad

Browse files
authored
Merge branch 'main' into feat/add-threatmodel-extension
2 parents ac2d72b + 16aa57f commit aeb08ad

10 files changed

Lines changed: 214 additions & 11 deletions

File tree

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.8.2] - 2026-04-28
6+
7+
### Changed
8+
9+
- Add MarkItDown Document Converter extension to community catalog (#2390)
10+
- feat: Speckit preset fiction book v1.7 - Support for RAG (Chroma DB) offline semantic search (#2367)
11+
- fix(extensions): use explicit UTF-8 encoding when reading manifest YAML (#2370)
12+
- catalog: add m365 community extension
13+
- docs: replace deprecated --ai flag with --integration in all documentation (#2359)
14+
- feat(extensions,presets): authenticate GitHub-hosted catalog and download requests with GITHUB_TOKEN/GH_TOKEN (#2331)
15+
- Update extensify to v1.1.0 in community catalog (#2337)
16+
- feat(init): deprecate --no-git flag, gate deprecations at v0.10.0 (#2357)
17+
- Add Spec Orchestrator extension to community catalog (#2350)
18+
- chore: release 0.8.1, begin 0.8.2.dev0 development (#2356)
19+
520
## [0.8.1] - 2026-04-24
621

722
### Changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,11 @@ The following community-contributed extensions are available in [`catalog.commun
228228
| MAQA Jira Integration | Jira integration for MAQA — syncs Stories and Subtasks as features progress through the board | `integration` | Read+Write | [spec-kit-maqa-jira](https://github.com/GenieRobot/spec-kit-maqa-jira) |
229229
| MAQA Linear Integration | Linear integration for MAQA — syncs issues and sub-issues across workflow states as features progress | `integration` | Read+Write | [spec-kit-maqa-linear](https://github.com/GenieRobot/spec-kit-maqa-linear) |
230230
| MAQA Trello Integration | Trello board integration for MAQA — populates board from specs, moves cards, real-time checklist ticking | `integration` | Read+Write | [spec-kit-maqa-trello](https://github.com/GenieRobot/spec-kit-maqa-trello) |
231+
| MarkItDown Document Converter | Convert documents (PDF, Word, PowerPoint, Excel, and more) to Markdown for use as spec reference material | `docs` | Read+Write | [spec-kit-markitdown](https://github.com/BenBtg/spec-kit-markitdown) |
231232
| Memory Loader | Loads .specify/memory/ files before lifecycle commands so LLM agents have project governance context | `docs` | Read-only | [spec-kit-memory-loader](https://github.com/KevinBrown5280/spec-kit-memory-loader) |
232233
| Memory MD | Repository-native durable memory for Spec Kit projects | `docs` | Read+Write | [spec-kit-memory-hub](https://github.com/DyanGalih/spec-kit-memory-hub) |
233234
| MemoryLint | Agent memory governance tool: Automatically audits and fixes boundary conflicts between AGENTS.md and the constitution. | `process` | Read+Write | [memorylint](https://github.com/RbBtSn0w/spec-kit-extensions/tree/main/memorylint) |
235+
| Microsoft 365 Integration | Fetch Teams messages, meeting transcripts, and SharePoint/OneDrive files as local Markdown for spec generation | `integration` | Read+Write | [spec-kit-m365](https://github.com/BenBtg/spec-kit-m365) |
234236
| Onboard | Contextual onboarding and progressive growth for developers new to spec-kit projects. Explains specs, maps dependencies, validates understanding, and guides the next step | `process` | Read+Write | [spec-kit-onboard](https://github.com/dmux/spec-kit-onboard) |
235237
| Optimize | Audit and optimize AI governance for context efficiency — token budgets, rule health, interpretability, compression, coherence, and echo detection | `process` | Read+Write | [spec-kit-optimize](https://github.com/sakitA/spec-kit-optimize) |
236238
| OWASP LLM Threat Model | OWASP Top 10 for LLM Applications 2025 threat analysis on agent artifacts | `code` | Read-only | [spec-kit-threatmodel](https://github.com/NaviaSamal/spec-kit-threatmodel) |

docs/community/presets.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ The following community-contributed presets customize how Spec Kit behaves — o
1111
| Canon Core | Adapts original Spec Kit workflow to work together with Canon extension | 2 templates, 8 commands || [spec-kit-canon](https://github.com/maximiliamus/spec-kit-canon) |
1212
| Claude AskUserQuestion | Upgrades `/speckit.clarify` and `/speckit.checklist` on Claude Code from Markdown-table prompts to the native AskUserQuestion picker, with a recommended option and reasoning on every question | 2 commands || [spec-kit-preset-claude-ask-questions](https://github.com/0xrafasec/spec-kit-preset-claude-ask-questions) |
1313
| Explicit Task Dependencies | Adds explicit `(depends on T###)` dependency declarations and an Execution Wave DAG to tasks.md for parallel scheduling | 1 template, 1 command || [spec-kit-preset-explicit-task-dependencies](https://github.com/Quratulain-bilal/spec-kit-preset-explicit-task-dependencies) |
14-
| Fiction Book Writing | It adapts the Spec-Driven Development workflow for storytelling to create books or audiobooks (with annotations) in 12 languages: features become story elements, specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports single and multi-POV, all major plot structure frameworks, and two style modes: an author voice sample or humanized AI prose. Supports interactive elements like brainstorming, interview, roleplay and extras like statistics, cover builder and bio command. Export with templates for KDP, D2D etc. | 22 templates, 27 commands, 1 script || [speckit-preset-fiction-book-writing](https://github.com/adaumann/speckit-preset-fiction-book-writing) |
14+
| Fiction Book Writing | It adapts the Spec-Driven Development workflow for storytelling to create books or audiobooks (with annotations) in 12 languages: features become story elements, specs become story briefs, plans become story structures, and tasks become scene-by-scene writing tasks. Supports single and multi-POV, all major plot structure frameworks, and two style modes: an author voice sample or humanized AI prose. Supports interactive elements like brainstorming, interview, roleplay and extras like statistics, cover builder and bio command. Export with templates for KDP, D2D etc. | 22 templates, 27 commands, 2 scripts || [speckit-preset-fiction-book-writing](https://github.com/adaumann/speckit-preset-fiction-book-writing) |
15+
| iSAQB Architecture Governance | Adds general iSAQB/CPSA-F and arc42 architecture governance: goals, context, building blocks, runtime and deployment views, quality scenarios, ADRs, risks, and technical debt | 13 templates, 3 commands || [spec-kit-preset-isaqb-architecture-governance](https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance) |
1516
| Jira Issue Tracking | Overrides `speckit.taskstoissues` to create Jira epics, stories, and tasks instead of GitHub Issues via Atlassian MCP tools | 1 command || [spec-kit-preset-jira](https://github.com/luno/spec-kit-preset-jira) |
1617
| Multi-Repo Branching | Coordinates feature branch creation across multiple git repositories (independent repos and submodules) during plan and tasks phases | 2 commands || [spec-kit-preset-multi-repo-branching](https://github.com/sakitA/spec-kit-preset-multi-repo-branching) |
1718
| Pirate Speak (Full) | Transforms all Spec Kit output into pirate speak — specs become "Voyage Manifests", plans become "Battle Plans", tasks become "Crew Assignments" | 6 templates, 9 commands || [spec-kit-presets](https://github.com/mnriem/spec-kit-presets) |

extensions/catalog.community.json

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,44 @@
941941
"created_at": "2026-03-17T00:00:00Z",
942942
"updated_at": "2026-03-17T00:00:00Z"
943943
},
944+
"m365": {
945+
"name": "Microsoft 365 Integration",
946+
"id": "m365",
947+
"description": "Fetch Teams messages, meeting transcripts, and SharePoint/OneDrive files as local Markdown for spec generation.",
948+
"author": "BenBtg",
949+
"version": "1.0.0",
950+
"download_url": "https://github.com/BenBtg/spec-kit-m365/archive/refs/tags/v1.0.0.zip",
951+
"repository": "https://github.com/BenBtg/spec-kit-m365",
952+
"homepage": "https://github.com/BenBtg/spec-kit-m365",
953+
"documentation": "https://github.com/BenBtg/spec-kit-m365/blob/main/README.md",
954+
"changelog": "https://github.com/BenBtg/spec-kit-m365/blob/main/CHANGELOG.md",
955+
"license": "MIT",
956+
"requires": {
957+
"speckit_version": ">=0.1.0",
958+
"tools": [
959+
{
960+
"name": "m365",
961+
"required": true
962+
}
963+
]
964+
},
965+
"provides": {
966+
"commands": 3,
967+
"hooks": 0
968+
},
969+
"tags": [
970+
"microsoft-365",
971+
"teams",
972+
"transcripts",
973+
"collaboration",
974+
"summarization"
975+
],
976+
"verified": false,
977+
"downloads": 0,
978+
"stars": 0,
979+
"created_at": "2026-04-28T00:00:00Z",
980+
"updated_at": "2026-04-28T00:00:00Z"
981+
},
944982
"maqa": {
945983
"name": "MAQA — Multi-Agent & Quality Assurance",
946984
"id": "maqa",
@@ -1167,6 +1205,45 @@
11671205
"created_at": "2026-03-26T00:00:00Z",
11681206
"updated_at": "2026-03-26T00:00:00Z"
11691207
},
1208+
"markitdown": {
1209+
"name": "MarkItDown Document Converter",
1210+
"id": "markitdown",
1211+
"description": "Convert documents (PDF, Word, PowerPoint, Excel, and more) to Markdown for use as spec reference material in Spec Kit workflows.",
1212+
"author": "BenBtg",
1213+
"version": "1.0.0",
1214+
"download_url": "https://github.com/BenBtg/spec-kit-markitdown/archive/refs/tags/v1.0.0.zip",
1215+
"repository": "https://github.com/BenBtg/spec-kit-markitdown",
1216+
"homepage": "https://github.com/BenBtg/spec-kit-markitdown",
1217+
"documentation": "https://github.com/BenBtg/spec-kit-markitdown/blob/main/README.md",
1218+
"changelog": "https://github.com/BenBtg/spec-kit-markitdown/blob/main/CHANGELOG.md",
1219+
"license": "MIT",
1220+
"requires": {
1221+
"speckit_version": ">=0.1.0",
1222+
"tools": [
1223+
{
1224+
"name": "markitdown",
1225+
"version": ">=0.1.0",
1226+
"required": true
1227+
}
1228+
]
1229+
},
1230+
"provides": {
1231+
"commands": 1,
1232+
"hooks": 0
1233+
},
1234+
"tags": [
1235+
"markdown",
1236+
"pdf",
1237+
"document-conversion",
1238+
"reference-material",
1239+
"extraction"
1240+
],
1241+
"verified": false,
1242+
"downloads": 0,
1243+
"stars": 0,
1244+
"created_at": "2026-04-28T00:00:00Z",
1245+
"updated_at": "2026-04-28T00:00:00Z"
1246+
},
11701247
"memory-loader": {
11711248
"name": "Memory Loader",
11721249
"id": "memory-loader",

presets/catalog.community.json

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schema_version": "1.0",
3-
"updated_at": "2026-04-15T00:00:00Z",
3+
"updated_at": "2026-04-27T00:00:00Z",
44
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/presets/catalog.community.json",
55
"presets": {
66
"aide-in-place": {
@@ -108,11 +108,11 @@
108108
"fiction-book-writing": {
109109
"name": "Fiction Book Writing",
110110
"id": "fiction-book-writing",
111-
"version": "1.6.0",
112-
"description": "Spec-Driven Development for novel and long-form fiction. 27 AI commands from idea to submission: story bible governance, 9 POV modes, all major plot structure frameworks, scene-by-scene drafting with quality gates, audiobook pipeline (SSML/ElevenLabs), cover design, sensitivity review, pacing and prose statistics, and pandoc-based export to DOCX/EPUB/LaTeX. Two style modes: author voice sample extraction or humanized-AI prose with 5 craft profiles. 12 languages supported.",
111+
"version": "1.7.0",
112+
"description": "Spec-Driven Development for novel and long-form fiction. 27 AI commands from idea to submission: story bible governance, 9 POV modes, all major plot structure frameworks, scene-by-scene drafting with quality gates, audiobook pipeline (SSML/ElevenLabs), cover design, sensitivity review, pacing and prose statistics, and pandoc-based export to DOCX/EPUB/LaTeX. Two style modes: author voice sample extraction or humanized-AI prose with 5 craft profiles. 12 languages supported. Support for offline semantic search.",
113113
"author": "Andreas Daumann",
114114
"repository": "https://github.com/adaumann/speckit-preset-fiction-book-writing",
115-
"download_url": "https://github.com/adaumann/speckit-preset-fiction-book-writing/archive/refs/tags/v1.6.0.zip",
115+
"download_url": "https://github.com/adaumann/speckit-preset-fiction-book-writing/archive/refs/tags/v1.7.0.zip",
116116
"homepage": "https://github.com/adaumann/speckit-preset-fiction-book-writing",
117117
"documentation": "https://github.com/adaumann/speckit-preset-fiction-book-writing/blob/main/fiction-book-writing/README.md",
118118
"license": "MIT",
@@ -122,7 +122,7 @@
122122
"provides": {
123123
"templates": 22,
124124
"commands": 27,
125-
"scripts": 1
125+
"scripts": 2
126126
},
127127
"tags": [
128128
"writing",
@@ -140,7 +140,35 @@
140140
"language-support"
141141
],
142142
"created_at": "2026-04-09T08:00:00Z",
143-
"updated_at": "2026-04-19T08:00:00Z"
143+
"updated_at": "2026-04-27T08:00:00Z"
144+
},
145+
"isaqb-architecture-governance": {
146+
"name": "iSAQB Architecture Governance",
147+
"id": "isaqb-architecture-governance",
148+
"version": "0.1.0",
149+
"description": "Adds general iSAQB/CPSA-F and arc42 architecture governance, including views, quality scenarios, ADRs, risks, and technical debt.",
150+
"author": "Thorsten Hindermann",
151+
"repository": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance",
152+
"download_url": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance/archive/refs/tags/v0.1.0.zip",
153+
"homepage": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance",
154+
"documentation": "https://github.com/hindermath/spec-kit-preset-isaqb-architecture-governance/blob/main/README.md",
155+
"license": "MIT",
156+
"requires": {
157+
"speckit_version": ">=0.8.0"
158+
},
159+
"provides": {
160+
"templates": 13,
161+
"commands": 3
162+
},
163+
"tags": [
164+
"architecture",
165+
"governance",
166+
"isaqb",
167+
"arc42",
168+
"adr"
169+
],
170+
"created_at": "2026-04-27T00:00:00Z",
171+
"updated_at": "2026-04-27T00:00:00Z"
144172
},
145173
"jira": {
146174
"name": "Jira Issue Tracking",

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.8.2.dev0"
3+
version = "0.8.3.dev0"
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 = [

src/specify_cli/extensions.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,18 @@ def __init__(self, manifest_path: Path):
139139
def _load_yaml(self, path: Path) -> dict:
140140
"""Load YAML file safely."""
141141
try:
142-
with open(path, 'r') as f:
142+
with open(path, 'r', encoding='utf-8') as f:
143143
data = yaml.safe_load(f)
144144
except yaml.YAMLError as e:
145145
raise ValidationError(f"Invalid YAML in {path}: {e}")
146146
except FileNotFoundError:
147147
raise ValidationError(f"Manifest not found: {path}")
148+
except UnicodeDecodeError as e:
149+
raise ValidationError(
150+
f"Manifest is not valid UTF-8: {path} ({e.reason} at byte {e.start})"
151+
)
152+
except OSError as e:
153+
raise ValidationError(f"Could not read manifest {path}: {e}")
148154
if not isinstance(data, dict):
149155
raise ValidationError(
150156
f"Manifest must be a YAML mapping, got {type(data).__name__}: {path}"

src/specify_cli/presets.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,25 @@ def __init__(self, manifest_path: Path):
136136
def _load_yaml(self, path: Path) -> dict:
137137
"""Load YAML file safely."""
138138
try:
139-
with open(path, 'r') as f:
140-
return yaml.safe_load(f) or {}
139+
with open(path, 'r', encoding='utf-8') as f:
140+
data = yaml.safe_load(f)
141141
except yaml.YAMLError as e:
142142
raise PresetValidationError(f"Invalid YAML in {path}: {e}")
143143
except FileNotFoundError:
144144
raise PresetValidationError(f"Manifest not found: {path}")
145+
except UnicodeDecodeError as e:
146+
raise PresetValidationError(
147+
f"Manifest is not valid UTF-8: {path} ({e.reason} at byte {e.start})"
148+
)
149+
except OSError as e:
150+
raise PresetValidationError(f"Could not read manifest {path}: {e}")
151+
if data is None:
152+
return {}
153+
if not isinstance(data, dict):
154+
raise PresetValidationError(
155+
f"Manifest must be a YAML mapping, got {type(data).__name__}: {path}"
156+
)
157+
return data
145158

146159
def _validate(self):
147160
"""Validate manifest structure and required fields."""

tests/test_extensions.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,35 @@ def test_non_mapping_yaml_raises_validation_error(self, temp_dir):
225225
with pytest.raises(ValidationError, match="YAML mapping"):
226226
ExtensionManifest(manifest_path)
227227

228+
def test_utf8_non_ascii_description_loads(self, temp_dir, valid_manifest_data):
229+
"""Regression for #2325: non-ASCII (UTF-8) description loads on any platform.
230+
231+
On Windows, Python's default text-mode encoding is the locale codepage
232+
(e.g. cp1252/GBK), which raises UnicodeDecodeError on UTF-8 bytes
233+
outside the ASCII range. The loader must open with encoding='utf-8'.
234+
"""
235+
import yaml
236+
237+
valid_manifest_data["extension"]["description"] = "中文测试 — émojis 🚀"
238+
manifest_path = temp_dir / "extension.yml"
239+
# Write UTF-8 bytes explicitly so the test exercises the read path,
240+
# not the (locale-dependent) write path.
241+
manifest_path.write_bytes(
242+
yaml.safe_dump(valid_manifest_data, allow_unicode=True).encode("utf-8")
243+
)
244+
245+
manifest = ExtensionManifest(manifest_path)
246+
assert manifest.description == "中文测试 — émojis 🚀"
247+
248+
def test_invalid_utf8_bytes_raises_validation_error(self, temp_dir):
249+
"""Negative case: file containing invalid UTF-8 bytes raises ValidationError, not raw UnicodeDecodeError."""
250+
manifest_path = temp_dir / "extension.yml"
251+
# 0xFF/0xFE are not valid UTF-8 lead bytes.
252+
manifest_path.write_bytes(b"\xff\xfe not valid utf-8 \xff\n")
253+
254+
with pytest.raises(ValidationError, match="not valid UTF-8"):
255+
ExtensionManifest(manifest_path)
256+
228257
def test_invalid_extension_id(self, temp_dir, valid_manifest_data):
229258
"""Test manifest with invalid extension ID format."""
230259
import yaml

tests/test_presets.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,38 @@ def test_invalid_yaml(self, temp_dir):
160160
with pytest.raises(PresetValidationError, match="Invalid YAML"):
161161
PresetManifest(bad_file)
162162

163+
def test_utf8_non_ascii_description_loads(self, temp_dir, valid_pack_data):
164+
"""Regression for #2325: non-ASCII (UTF-8) description loads on any platform.
165+
166+
On Windows, Python's default text-mode encoding is the locale codepage
167+
(e.g. cp1252/GBK), which raises UnicodeDecodeError on UTF-8 bytes
168+
outside the ASCII range. The loader must open with encoding='utf-8'.
169+
"""
170+
valid_pack_data["preset"]["description"] = "中文测试 — émojis 🚀"
171+
manifest_path = temp_dir / "preset.yml"
172+
manifest_path.write_bytes(
173+
yaml.safe_dump(valid_pack_data, allow_unicode=True).encode("utf-8")
174+
)
175+
176+
manifest = PresetManifest(manifest_path)
177+
assert manifest.description == "中文测试 — émojis 🚀"
178+
179+
def test_invalid_utf8_bytes_raises_validation_error(self, temp_dir):
180+
"""Negative case: file containing invalid UTF-8 bytes raises PresetValidationError, not raw UnicodeDecodeError."""
181+
manifest_path = temp_dir / "preset.yml"
182+
manifest_path.write_bytes(b"\xff\xfe not valid utf-8 \xff\n")
183+
184+
with pytest.raises(PresetValidationError, match="not valid UTF-8"):
185+
PresetManifest(manifest_path)
186+
187+
def test_non_mapping_yaml_raises_validation_error(self, temp_dir):
188+
"""Manifest whose YAML root is a scalar or list raises PresetValidationError, not TypeError."""
189+
manifest_path = temp_dir / "preset.yml"
190+
for bad_content in ("42\n", "[1, 2]\n"):
191+
manifest_path.write_text(bad_content, encoding="utf-8")
192+
with pytest.raises(PresetValidationError, match="YAML mapping"):
193+
PresetManifest(manifest_path)
194+
163195
def test_missing_schema_version(self, temp_dir, valid_pack_data):
164196
"""Test missing schema_version field."""
165197
del valid_pack_data["schema_version"]

0 commit comments

Comments
 (0)