diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index de4eb9a30b..9084b8d981 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -10,6 +10,21 @@ on:
jobs:
markdownlint:
runs-on: ubuntu-latest
+ env:
+ DOC_GLOBS: |
+ docs/**/*.md
+ README.md
+ README.zh-CN.md
+ CHANGELOG.md
+ CODE_OF_CONDUCT.md
+ CONTRIBUTING.md
+ DEVELOPMENT.md
+ SECURITY.md
+ SUPPORT.md
+ spec-driven.md
+ integrations/*.md
+ presets/*.md
+ workflows/*.md
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -36,12 +51,46 @@ jobs:
git diff --check refs/checks/push-before HEAD
fi
+ # Documentation only. Commands, skills, prompt templates, agent
+ # instructions (AGENTS.md) and .github/ content are inputs to coding
+ # agents rather than prose, and are deliberately left unlinted so a
+ # documentation pass never reformats them. Add new documentation
+ # paths to the DOC_GLOBS list above.
+ - name: Verify the documentation globs match files
+ shell: bash
+ run: |
+ set -euo pipefail
+ shopt -s globstar nullglob
+
+ # Checked per glob, not on the total: one stale entry among several
+ # still leaves that part of the documentation unlinted, which is the
+ # failure #4526 was about.
+ count=0
+ empty=()
+ while IFS= read -r glob; do
+ [ -z "$glob" ] && continue
+ matched=0
+ for path in $glob; do
+ [ -f "$path" ] && matched=$((matched + 1))
+ done
+ # An array, not a string: nullglob is on, so re-expanding an
+ # unquoted list of unmatched globs would erase it.
+ [ "$matched" -eq 0 ] && empty+=("$glob")
+ count=$((count + matched))
+ done <<< "$DOC_GLOBS"
+
+ echo "documentation files matched: $count"
+ if [ ${#empty[@]} -gt 0 ]; then
+ for glob in "${empty[@]}"; do
+ echo "::error::markdownlint glob matches no files: $glob (see #4526)"
+ done
+ exit 1
+ fi
+
- name: Run markdownlint-cli2
uses: DavidAnson/markdownlint-cli2-action@21c1be1b93ad9ed58fa840aacc3f279cde2a72ff # v24.2.0
with:
- globs: |
- '**/*.md'
- !extensions/**/*.md
+ globs: ${{ env.DOC_GLOBS }}
shellcheck:
runs-on: ubuntu-latest
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d2534c829f..0b75796fae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -487,7 +487,7 @@
- fix(agent-context): apply default markers when config markers are blank (bash) (#3736)
- fix: escape Rich markup in catalog list output (#3738)
- fix(workflows): guard non-mapping 'workflow:' block in WorkflowDefinition (#3694)
-- fix(bundler): reject unsupported schema_version in _merge_config (align readers) (#3711)
+- fix(bundler): reject unsupported schema_version in `_merge_config` (align readers) (#3711)
- Update Linear Weave extension to v1.0.1 (#3762)
- Add Intake Sequencing Governance preset to community catalog (#3761)
- Update Quality Gates (Enforcement Layer) extension to v0.3.3 (#3760)
@@ -602,7 +602,7 @@
### Changed
- fix(integrations): escape Rich markup in --integration-options error messages (#3458)
-- docs: document __SPECKIT_COMMAND_ token for portable cross-command references (#3503)
+- docs: document `__SPECKIT_COMMAND_` token for portable cross-command references (#3503)
- [preset] Add Parallel Autonomous Run Governance preset to community catalog (#3614)
- docs(workflows): fix stale FanOutStep docstring claiming sequential-only execution (#3639)
- [bundle] Add SicarioSpec Security & Governance Bundle to community catalog (#3636)
@@ -1179,7 +1179,7 @@
- docs: document spec persistence models (#2856)
- chore(catalog): bump Linear Integration to v0.3.0 (repo renamed to spec-kit-linear-sync) (#2893)
- chore: update DocGuard extension to v0.25.0 (#2707)
-- chore: remove unused open_github_url/_StripAuthOnRedirect from _github_http.py (#2883)
+- chore: remove unused `open_github_url/_StripAuthOnRedirect` from `_github_http.py` (#2883)
- fix(catalogs): validate extension and preset catalog payload shape (#2621)
- feat(integration): add status reporting (#2674)
- chore: release 0.10.0, begin 0.10.1.dev0 development (#2904)
@@ -1316,7 +1316,7 @@
- docs: update landing page stats and branch naming convention (#2727)
- feat(workflows): expose {{ context.run_id }} template variable (#2664)
-- fix: resolve __SPECKIT_COMMAND_*__ refs in preset skill rendering (#2717) (#2718)
+- fix: resolve `__SPECKIT_COMMAND_*__` refs in preset skill rendering (#2717) (#2718)
- Add Workflow Preset to community catalog (#2725)
- fix: paths-only skips branch validation, setup-plan preserves existing plan (#2672)
- docs: fix broken pipx homepage URLs to point to pipx.pypa.io (#2670)
@@ -1376,7 +1376,7 @@
- Add Team Assign extension to community catalog (#2642)
- refactor: migrate extension catalog stack parsing to shared base (#2576)
- Update Architecture Workflow extension to v1.1.0 (#2588)
-- fix(workflow): support integration: auto to follow project's initialized AI (#2421)
+- fix(workflow): support integration: auto to follow project's initialized AI (#2421)
- Add Superpowers Implementation Bridge extension to community catalog (#2586)
- Add Interactive HTML Preview extension to community catalog (#2585)
- chore: release 0.8.11, begin 0.8.12.dev0 development (#2584)
@@ -1386,7 +1386,7 @@
### Changed
-- refactor: extract _version.py from __init__.py (PR-3/8) (#2550)
+- refactor: extract `_version.py` from `__init__.py` (PR-3/8) (#2550)
- Add Time Machine extension to community catalog (#2580)
- fix(powershell): ensure UTF-8 templates are written without BOM (#2280)
- docs: document high-assurance spec workflow (#2518)
@@ -1406,9 +1406,9 @@
- Add Agent Governance extension to community catalog (#2559)
- Add Reqnroll BDD extension to community catalog (#2545)
- fix(cli): harden extension registration and discovery workflows (#2499)
-- refactor: extract _assets.py and _utils.py from __init__.py (PR-2/8) (#2543)
+- refactor: extract `_assets.py` and `_utils.py` from `__init__.py` (PR-2/8) (#2543)
- fix(opencode): use commands/ directory (plural) to match OpenCode docs (#2453)
-- refactor: extract _console.py from __init__.py (PR-1/8) (#2474)
+- refactor: extract `_console.py` from `__init__.py` (PR-1/8) (#2474)
- Fix constitution reference in README (#2491)
- chore: release 0.8.9, begin 0.8.10.dev0 development (#2532)
@@ -2192,15 +2192,15 @@
- Update CHANGELOG.md
- Update agent configuration
- Update scripts/powershell/create-new-feature.ps1
-- Update src/specify_cli/__init__.py
+- Update `src/specify_cli/__init__.py`
- Create create-release-packages.ps1
- Script changes
- Update taskstoissues.md
- Create taskstoissues.md
-- Update src/specify_cli/__init__.py
+- Update `src/specify_cli/__init__.py`
- Update CONTRIBUTING.md
- Potential fix for code scanning alert no. 3: Workflow does not contain permissions
-- Update src/specify_cli/__init__.py
+- Update `src/specify_cli/__init__.py`
- Update CHANGELOG.md
- Fixes #970
- Fixes #975
@@ -2383,13 +2383,13 @@
### Changed
-- Update __init__.py
+- Update `__init__.py`
- Consolidate Cursor naming
- Update CHANGELOG.md
- Git errors are now highlighted.
-- Update __init__.py
+- Update `__init__.py`
- Refactor agent configuration
-- Update src/specify_cli/__init__.py
+- Update `src/specify_cli/__init__.py`
- Update scripts/powershell/update-agent-context.ps1
- Update AGENTS.md
- Update templates/commands/implement.md
@@ -2453,10 +2453,10 @@
- Update URLs to Contributing and Support Guides in Docs
- fix: add UTF-8 encoding to file read/write operations in update-agent-context.ps1
-- Update __init__.py
-- Update src/specify_cli/__init__.py
+- Update `__init__.py`
+- Update `src/specify_cli/__init__.py`
- docs: fix the paths of generated files (moved under a `.specify/` folder)
-- Update src/specify_cli/__init__.py
+- Update `src/specify_cli/__init__.py`
- feat: support 'specify init .' for current directory initialization
- feat: Add emacs-style up/down keys
@@ -2466,7 +2466,7 @@
- Update CONTRIBUTING.md
- Refine `plan-template.md` with improved project type detection, clarified structure decision process, and enhanced research task guidance.
-- Update __init__.py
+- Update `__init__.py`
## [0.0.53] - 2025-09-24
@@ -2576,23 +2576,23 @@
- Update update-agent-context.ps1
- Update package release
- Update config
-- Update __init__.py
-- Update __init__.py
+- Update `__init__.py`
+- Update `__init__.py`
- Remove Codex-specific logic in the initialization script
- Update version rev
-- Update __init__.py
+- Update `__init__.py`
- Enhance Codex support by auto-syncing prompt files, allowing spec generation without git, and documenting clearer /specify usage.
- Consistency tweaks
- Consistent step coloring
-- Update __init__.py
-- Update __init__.py
+- Update `__init__.py`
+- Update `__init__.py`
- Quick UI tweak
- Update package release
- Limit workspace command seeding to Codex init and update Codex documentation accordingly.
- Clarify Codex-specific README note with rationale for its different workflow.
- Bump to 0.0.7 and document Codex support
- Normalize Codex command templates to the scripts-based schema and auto-upgrade generated commands.
-- Fix remaining merge conflict markers in __init__.py
+- Fix remaining merge conflict markers in `__init__.py`
- Add Codex CLI support with AGENTS.md and commands bootstrap
## [0.0.45] - 2025-09-19
@@ -2608,7 +2608,7 @@
### Changed
- Update specify.md
-- Update __init__.py
+- Update `__init__.py`
## [0.0.43] - 2025-09-18
@@ -2746,11 +2746,11 @@
### Changed
- Update config
-- Update __init__.py
+- Update `__init__.py`
- Refactor with platform-specific constraints
- Update README.md
- Update CLI reference
-- Update __init__.py
+- Update `__init__.py`
- refactor: extract Claude local path to constant for maintainability
- fix: support Claude CLI installed via migrate-installer
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4f0d0123e1..cac9fa8597 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -278,7 +278,7 @@ Any change that affects a slash command's behavior requires manually testing tha
Paste this into your PR:
-~~~markdown
+```markdown
## Manual test results
**Agent**: [e.g., GitHub Copilot in VS Code] | **OS/Shell**: [e.g., macOS/zsh]
@@ -286,13 +286,13 @@ Paste this into your PR:
| Command tested | Notes |
|----------------|-------|
| `/speckit.command` | |
-~~~
+```
#### Determining which tests to run
Copy this prompt into your agent. Include the agent's response (selected tests plus a brief explanation of the mapping) in your PR.
-~~~text
+```text
Read CONTRIBUTING.md, then run `git diff --name-only main` to get my changed files.
For each changed file, determine which slash commands it affects by reading
the command templates in templates/commands/ to understand what each command
@@ -324,7 +324,7 @@ Number each test sequentially (T1, T2, ...). List prerequisite tests first.
- T1: /speckit.command — (reason)
- T2: /speckit.command — (reason)
-~~~
+```
## AI contributions in Spec Kit
diff --git a/docs/community/friends.md b/docs/community/friends.md
index 2a7fdad5c1..1ff68c63e0 100644
--- a/docs/community/friends.md
+++ b/docs/community/friends.md
@@ -17,4 +17,4 @@ Community projects that extend, visualize, or build on Spec Kit:
- **[spectatui](https://github.com/tinesoft/spectatui)** — A terminal UI (TUI) dashboard for Spec Kit that lets you track features, manage specifications, integrations, presets, workflows, and extensions, and monitor AI agent workflows. Attach to existing AI sessions or launch new ones from your terminal. Keyboard and mouse support. Light/dark theme support. Customizable and performance-oriented. Requires the `specify` CLI in your PATH.
-- **[spec-kit-copilot](https://github.com/github/spec-kit-copilot)** — _First-party GitHub project._ A GitHub Copilot **skills plugin** that exposes the Spec Kit `specify` CLI to the Copilot agent in both the Copilot CLI and the GitHub Copilot app. It provides a focused skill per `specify` command group — setup, init, check, extensions, presets, bundles, workflows, workflow steps, and self-upgrade — so you can navigate and drive the entire Spec Kit ecosystem through natural language, letting Copilot decide when and how to run the right `specify` commands on your behalf.
+- **[spec-kit-copilot](https://github.com/github/spec-kit-copilot)** — *First-party GitHub project.* A GitHub Copilot **skills plugin** that exposes the Spec Kit `specify` CLI to the Copilot agent in both the Copilot CLI and the GitHub Copilot app. It provides a focused skill per `specify` command group — setup, init, check, extensions, presets, bundles, workflows, workflow steps, and self-upgrade — so you can navigate and drive the entire Spec Kit ecosystem through natural language, letting Copilot decide when and how to run the right `specify` commands on your behalf.
diff --git a/docs/index.md b/docs/index.md
index d7f4ca97ee..8ed2b1d41c 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -36,6 +36,9 @@ Adding Spec Kit to an established codebase? Start with the
+
+
### Spec-driven by default
The core SDD process ships ready to use: **Specify → Plan → Tasks → Implement → Converge**.
diff --git a/docs/install/air-gapped.md b/docs/install/air-gapped.md
index bf2a46380e..b0e20ec0ca 100644
--- a/docs/install/air-gapped.md
+++ b/docs/install/air-gapped.md
@@ -38,8 +38,8 @@ specify init my-project --integration copilot
```
> **Note:** Python 3.11+ is required.
-
-> **Windows note:** Offline scaffolding requires PowerShell 7+ (`pwsh`), not Windows PowerShell 5.x (`powershell.exe`). Install from https://aka.ms/powershell.
+>
+> **Windows note:** Offline scaffolding requires PowerShell 7+ (`pwsh`), not Windows PowerShell 5.x (`powershell.exe`). Install from
.
## Git Credential Manager on Linux
diff --git a/docs/installation.md b/docs/installation.md
index a8a68fc992..2fcb1577ee 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -6,7 +6,7 @@
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [CodeBuddy CLI](https://www.codebuddy.cn/docs/cli/installation), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Pi Coding Agent](https://pi.dev), or [Oh My Pi](https://www.npmjs.com/package/@oh-my-pi/pi-coding-agent)
- [uv](https://docs.astral.sh/uv/) for package management (recommended) or [pipx](https://pipx.pypa.io/) for persistent installation
- [Python 3.11+](https://www.python.org/downloads/)
-- [Git](https://git-scm.com/downloads) _(optional — required only when the git extension is enabled)_
+- [Git](https://git-scm.com/downloads) *(optional — required only when the git extension is enabled)*
## Installation
diff --git a/docs/reference/authentication.md b/docs/reference/authentication.md
index 059052cd8f..de369c980b 100644
--- a/docs/reference/authentication.md
+++ b/docs/reference/authentication.md
@@ -22,6 +22,7 @@ Create `~/.specify/auth.json` to enable authentication:
```
> **Security:** Restrict the file to owner-only access:
+>
> ```bash
> chmod 600 ~/.specify/auth.json
> ```
diff --git a/docs/reference/core.md b/docs/reference/core.md
index b70aebe236..0842b1bfda 100644
--- a/docs/reference/core.md
+++ b/docs/reference/core.md
@@ -56,9 +56,9 @@ specify init my-project --integration copilot --preset compliance
| `SPECIFY_FEATURE` | Explicitly override the active feature **label** (e.g. `001-photo-albums`) — the identifier the core helpers report as the current feature/branch (`get_current_branch` in Bash, `Get-CurrentBranch` in PowerShell). Those helpers never inspect Git: when the variable is set they return it verbatim, and when it is unset they return an empty string. The basename fallback happens later — `get_feature_paths` / `Get-FeaturePathsEnv` substitute the resolved feature directory's basename so the reported label is still usable — so calling the named helpers directly does **not** give you that fallback. You set it yourself: the Bash and Python feature scripts only **print** a commented `export SPECIFY_FEATURE=…` / `$env:SPECIFY_FEATURE = …` hint for you to run, because a child process cannot change its parent's environment, and `/speckit.specify` persists `feature_directory` to `.specify/feature.json` instead of setting this variable. (The PowerShell feature scripts do assign `$env:SPECIFY_FEATURE`, but that only reaches you when the script runs inside your current PowerShell session.) It does **not** locate the feature directory: with only `SPECIFY_FEATURE` set, `get_feature_paths` fails with *"Feature directory not found. Set `SPECIFY_FEATURE_DIRECTORY` or run the specify command to create `.specify/feature.json`."* Use `SPECIFY_FEATURE_DIRECTORY` (above) or `.specify/feature.json` to select the directory. |
> **Two resolution axes.** `SPECIFY_INIT_DIR` selects the **project** (which directory contains `.specify/`); `SPECIFY_FEATURE_DIRECTORY` / `.specify/feature.json` select the **feature** within that project. They are independent — project first, then feature.
-
+>
> **Version control.** `specify init` scaffolds a managed `.specify/.gitignore` that excludes machine-local state — `feature.json` (the current-feature pointer, rewritten on every feature switch) and per-machine extension `extensions/*/local-config.yml` overrides — while leaving everything else under `.specify/` (constitution, templates, scripts, extension config) shareable so teams stay aligned. Like the rest of `.specify/`'s shared scripts and templates, the file is tracked in the shared-infrastructure manifest: your edits are preserved on re-init and `specify init --here --force` restores the managed content. It is intentionally left in place by `specify integration uninstall`, which only removes the uninstalled agent's own files.
-
+>
> **Symlinked project roots.** `SPECIFY_INIT_DIR` relocates *where* the project is, not *how* a command treats symlinks: each command keeps its existing cwd-path stance. Commands that traverse and write project files through broad input paths (`bundle`, `workflow run `) refuse a symlinked `.specify/` to preserve write confinement. Other project-scoped commands keep their existing behavior when `SPECIFY_INIT_DIR` points at a project root, which may include following a symlinked `.specify/`.
## Check Installed Tools
diff --git a/docs/reference/extensions.md b/docs/reference/extensions.md
index 22357ccea0..3c309b58a8 100644
--- a/docs/reference/extensions.md
+++ b/docs/reference/extensions.md
@@ -126,10 +126,12 @@ Catalogs come in two kinds, and the distinction is a **security boundary**, not
> **Do not flip a discovery-only catalog to `install_allowed`.** That defeats the entire point of separating discovery from installation. There are two correct ways to install something you found via `community`:
>
> 1. **Install a single vetted extension directly** with `--from` (no catalog authoring needed). Get the candidate archive URL from `specify extension info ` — for a discovery-only entry it prints a "Candidate archive" URL. Review that release archive, then install it:
+>
> ```bash
> specify extension info # shows the candidate archive URL
> specify extension add --from
> ```
+>
> Treat the URL as untrusted until you have vetted it — it comes from an unvetted catalog.
> 2. **Curate your own catalog** you control and vet, and mark *that* catalog `install_allowed: true` — for when you want a governed, reusable install source (e.g. for an org).
@@ -208,6 +210,7 @@ To set up configuration for a newly installed extension, copy the template:
cp .specify/extensions//-config.template.yml \
.specify/extensions//-config.yml
```
+
## Project Extension and Hook Configuration
Spec Kit stores project-level extension registration and hook configuration in:
@@ -215,6 +218,7 @@ Spec Kit stores project-level extension registration and hook configuration in:
```text
.specify/extensions.yml
```
+
The file contains installed extensions, global settings, and hooks that are surfaced before or after Spec Kit commands.
```yaml
@@ -262,6 +266,7 @@ Each hook entry supports the following fields:
| `prompt` | Message shown when asking whether to run an optional hook. |
| `description` | Human-readable explanation of what the hook does. |
| `condition` | Optional expression evaluated by `HookExecutor` (using `config.` or `env.` with `is set`, `==`, or `!=`). Current command templates do not evaluate conditions and skip hooks with a non-empty condition. |
+
Hook event names identify when a hook is invoked. They generally use `before_` or `after_`, such as `before_implement`, `after_implement`, `before_tasks`, and `after_tasks`.
Extension manifests reject invalid hook priorities during installation. For existing `.specify/extensions.yml` entries, `HookExecutor.get_hooks_for_event()` sorts with `normalize_priority()`: missing values, booleans, non-numeric values rejected by `int()`, and values less than `1` fall back to `10`; numeric strings and finite floats are coerced with `int()`, while non-finite floats are unsupported and may fail instead of falling back.
diff --git a/docs/reference/workflows.md b/docs/reference/workflows.md
index a547a10e42..ad2c1965a3 100644
--- a/docs/reference/workflows.md
+++ b/docs/reference/workflows.md
@@ -335,6 +335,7 @@ When an installed workflow is refreshed or reinstalled, project overlays in `.sp
- An overlay that targets a step id that does not exist in the base workflow will raise a validation error when the workflow is resolved.
- Overlays cannot target steps added by other overlays.
- Overlays cannot add new inputs or change the input schema of the base workflow.
+
## Update Workflows
```bash
diff --git a/integrations/CONTRIBUTING.md b/integrations/CONTRIBUTING.md
index 77a50d4d98..c441ecc068 100644
--- a/integrations/CONTRIBUTING.md
+++ b/integrations/CONTRIBUTING.md
@@ -94,22 +94,22 @@ provides:
1. **Fork** the [spec-kit repository](https://github.com/github/spec-kit)
2. **Add your entry** under the `integrations` key in `integrations/catalog.community.json`:
-```json
-{
- "schema_version": "1.0",
- "integrations": {
- "my-agent": {
- "id": "my-agent",
- "name": "My Agent",
- "version": "1.0.0",
- "description": "Integration for My Agent",
- "author": "your-name",
- "repository": "https://github.com/your-name/speckit-my-agent",
- "tags": ["cli"]
- }
- }
-}
-```
+ ```json
+ {
+ "schema_version": "1.0",
+ "integrations": {
+ "my-agent": {
+ "id": "my-agent",
+ "name": "My Agent",
+ "version": "1.0.0",
+ "description": "Integration for My Agent",
+ "author": "your-name",
+ "repository": "https://github.com/your-name/speckit-my-agent",
+ "tags": ["cli"]
+ }
+ }
+ }
+ ```
3. **Open a pull request** with:
- Your catalog entry
diff --git a/presets/ARCHITECTURE.md b/presets/ARCHITECTURE.md
index 2ef78add27..db540f7c72 100644
--- a/presets/ARCHITECTURE.md
+++ b/presets/ARCHITECTURE.md
@@ -37,6 +37,7 @@ flowchart TD
When multiple presets are installed, they're sorted by their `priority` field (lower number = higher precedence). This is set via `--priority` on `specify preset add`.
The resolution is implemented three times to ensure consistency:
+
- **Python**: `PresetResolver` in `src/specify_cli/presets.py`
- **Bash**: `resolve_template()` in `scripts/bash/common.sh`
- **PowerShell**: `Resolve-Template` in `scripts/powershell/common.ps1`
@@ -55,6 +56,7 @@ Templates, commands, and scripts support a `strategy` field that controls how a
Composition is recursive — multiple composing presets chain. The `PresetResolver.resolve_content()` method walks the full priority stack bottom-up and applies each layer's strategy.
Content resolution functions for composition:
+
- **Python**: `PresetResolver.resolve_content()` in `src/specify_cli/presets.py` (templates, commands, and scripts)
- **Bash**: `resolve_template_content()` in `scripts/bash/common.sh` (templates only; command/script composition is handled by the Python resolver)
- **PowerShell**: `Resolve-TemplateContent` in `scripts/powershell/common.ps1` (templates only; command/script composition is handled by the Python resolver)
@@ -146,7 +148,7 @@ Catalogs are fetched with a 1-hour cache (per-URL, SHA256-hashed cache files). E
## Repository Layout
-```
+```text
presets/
├── ARCHITECTURE.md # This file
├── PUBLISHING.md # Guide for submitting presets to the catalog
@@ -176,7 +178,7 @@ presets/
## Module Structure
-```
+```text
src/specify_cli/
├── agents.py # CommandRegistrar — shared infrastructure for writing
│ # command files to agent directories
diff --git a/presets/README.md b/presets/README.md
index 539da08786..2559c3d0cb 100644
--- a/presets/README.md
+++ b/presets/README.md
@@ -138,7 +138,7 @@ See [scaffold/](scaffold/) for a scaffold you can copy to create your own preset
| `SPECKIT_PRESET_CATALOG_URL` | Override the full catalog stack with a single URL (replaces all defaults) | Built-in default stack |
| `GH_TOKEN` / `GITHUB_TOKEN` | GitHub token for authenticated requests to GitHub-hosted URLs (`raw.githubusercontent.com`, `github.com`, `api.github.com`, `codeload.github.com`). Required when your catalog JSON or preset ZIPs are hosted in a private GitHub repository. | None |
-#### Example: Using a private GitHub-hosted catalog
+### Example: Using a private GitHub-hosted catalog
```bash
# Authenticate with a token (gh CLI, PAT, or GITHUB_TOKEN in CI)
diff --git a/workflows/ARCHITECTURE.md b/workflows/ARCHITECTURE.md
index 680cfc4237..db5d48f52f 100644
--- a/workflows/ARCHITECTURE.md
+++ b/workflows/ARCHITECTURE.md
@@ -191,7 +191,7 @@ When `specify workflow add ` installs from catalog, it downloads the workflo
## Module Structure
-```
+```text
src/specify_cli/
├── workflows/
│ ├── __init__.py # STEP_REGISTRY + _register_builtin_steps()
diff --git a/workflows/README.md b/workflows/README.md
index 2c1a9f2bb7..1940420a45 100644
--- a/workflows/README.md
+++ b/workflows/README.md
@@ -532,7 +532,7 @@ specify workflow catalog remove
## Repository Layout
-```
+```text
workflows/
├── ARCHITECTURE.md # Internal architecture documentation
├── PUBLISHING.md # Guide for submitting workflows to the catalog