Skip to content
Open
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
40 changes: 40 additions & 0 deletions bundles/assess/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Idea Assessment Bundle

A first-party GitHub Spec Kit bundle that installs an idea-triage pipeline before committing to Spec-Driven Development.

## What it provides

- **Assess extension** (`extensions/assess`) — the `speckit.assess.intake`, `speckit.assess.research`, `speckit.assess.define`, `speckit.assess.shape`, and `speckit.assess.decide` commands.
- **Assess workflow** (`workflows/assess`) — a guided, resumable pipeline:
1. `intake` the raw idea.
2. `research` the evidence.
3. `define` the problem.
4. `shape` the concept.
5. `decide` the verdict.
6. `review-verdict` gate — approve to complete the assessment; reject to abort. A `go` verdict is then handed off manually to `/speckit.specify`.

## Install

```bash
specify bundle install assess
# or
specify bundle add assess
```

## Run the workflow

```bash
specify workflow run assess \
--input idea="Let users work offline and sync when they reconnect" \
--input slug="offline-mode"
```

Required inputs must be supplied with `--input`: `idea` and `slug`. The slug is used as the working directory under `.specify/assessments/<slug>/` for all artifacts.

## Remove

```bash
specify bundle remove assess
```

Removing the bundle uninstalls the workflow and the extension it contributed, unless they are still depended on by another installed bundle (FR-022). Components you installed independently are not attributed to this bundle and survive removal.
25 changes: 25 additions & 0 deletions bundles/assess/bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
schema_version: "1.0"

bundle:
id: "assess"
name: "Idea Assessment Pipeline"
version: "1.0.0"
role: "developer"
description: "Idea triage before Spec-Driven Development: intake, research, define, shape, decide with a verdict review gate; surviving ideas hand off manually to the specify command."
author: "GitHub"
license: "MIT"

requires:
speckit_version: ">=0.9.0"
tools: []
mcp: []

provides:
extensions:
- id: "assess"
version: "1.0.0"
workflows:
- id: "assess"
version: "1.0.0"

tags: ["assessment", "discovery", "triage", "product"]
38 changes: 38 additions & 0 deletions bundles/bugfix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Bug Fix Bundle

A first-party GitHub Spec Kit bundle that installs an orchestrated bug-fixing pipeline.

## What it provides

- **Bug extension** (`extensions/bug`) — the `speckit.bug.assess`, `speckit.bug.fix`, and `speckit.bug.test` commands.
- **Bugfix workflow** (`workflows/bugfix`) — a guided, resumable pipeline:
1. `assess` the bug report.
2. `review-assessment` gate — approve to proceed, reject to abort.
3. `fix` the bug.
4. `test` the fix.

## Install

```bash
specify bundle install bugfix
# or
specify bundle add bugfix
```

## Run the workflow

```bash
specify workflow run bugfix \
--input report="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/example/repo/issues/1234" \
--input slug="callback-token"
```

Required inputs must be supplied with `--input`: `report` and `slug`. The slug is used as the working directory under `.specify/bugs/<slug>/` for all artifacts.

## Remove

```bash
specify bundle remove bugfix
```

Removing the bundle uninstalls the workflow and the extension it contributed, unless they are still depended on by another installed bundle (FR-022). Components you installed independently are not attributed to this bundle and survive removal.
25 changes: 25 additions & 0 deletions bundles/bugfix/bundle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
schema_version: "1.0"

bundle:
id: "bugfix"
name: "Guided Bug Fix"
version: "1.0.0"
role: "developer"
description: "Orchestrated bug triage: assess a bug report, review the assessment behind a human gate, apply the fix, and verify it with tests."
author: "GitHub"
license: "MIT"

requires:
speckit_version: ">=0.9.0"
tools: []
mcp: []

provides:
extensions:
- id: "bug"
version: "1.0.0"
workflows:
- id: "bugfix"
version: "1.0.0"

tags: ["bug", "triage", "workflow", "qa"]
51 changes: 51 additions & 0 deletions bundles/catalog.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"schema_version": "1.0",
"updated_at": "2026-09-10T00:00:00Z",
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/bundles/catalog.json",
"bundles": {
"bugfix": {
"id": "bugfix",
"name": "Guided Bug Fix",
"version": "1.0.0",
"role": "developer",
"description": "Orchestrated bug triage: assess a bug report, review behind a human gate, apply the fix, and verify with tests.",
"author": "GitHub",
"license": "MIT",
"download_url": "https://raw.githubusercontent.com/github/spec-kit/main/bundles/bugfix/bundle.yml",
"repository": "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/github/spec-kit",
"requires": {
"speckit_version": ">=0.9.0"
},
"provides": {
"extensions": 1,
"presets": 0,
"steps": 0,
"workflows": 1
},
"tags": ["bug", "triage", "workflow", "qa"],
"verified": true
},
"assess": {
"id": "assess",
"name": "Idea Assessment Pipeline",
"version": "1.0.0",
"role": "developer",
"description": "Idea triage before Spec-Driven Development: intake, research, define, shape, decide with a verdict review gate; surviving ideas hand off manually to specify.",
"author": "GitHub",
"license": "MIT",
"download_url": "https://raw.githubusercontent.com/github/spec-kit/main/bundles/assess/bundle.yml",
"repository": "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/github/spec-kit",
"requires": {
"speckit_version": ">=0.9.0"
},
"provides": {
"extensions": 1,
"presets": 0,
"steps": 0,
"workflows": 1
},
"tags": ["assessment", "discovery", "triage", "product"],
"verified": true
}
}
}
23 changes: 22 additions & 1 deletion docs/reference/bundles.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ These demonstrate packaging a role-based setup, not filled generated feature
specs; for end-to-end usage examples, see
[community walkthroughs](../community/walkthroughs.md).

## First-party Bundles

Spec Kit ships a first-party bundle catalog in `bundles/catalog.json`. These bundles are curated, marked `verified: true`, and resolve through the built-in `builtin://default` catalog source.

| Bundle | Role | Components | Use case |
| --------- | ----------- | ----------------------------------------------------- | --------------------------------------- |
| `bugfix` | `developer` | `bug` extension + `bugfix` workflow | Guided assess → gate → fix → test |
| `assess` | `developer` | `assess` extension + `assess` workflow | Idea triage before Spec-Driven Development |

Install a first-party bundle the same way you install any bundle (`add` is an alias for `install`):

```bash
specify bundle install bugfix
specify bundle add assess
```

The first-party catalog is fetched from the repository online and falls back to the packaged wheel snapshot offline so discovery works without network access. A local bundle manifest can install bundled extensions and workflows with `--offline`. Catalog-discovered bundle manifests still resolve from their `download_url`, so `specify bundle add <id>` requires network today; fully offline catalog installation is tracked as follow-up work.

## Search Available Bundles

```bash
Expand Down Expand Up @@ -146,7 +164,10 @@ If your bundle references components from non-default catalogs, document those c

## Manage Catalog Sources

Bundles are discovered through a priority-ordered stack of catalog sources (project, user, and built-in scopes).
Bundles are discovered through a priority-ordered stack of catalog sources (project, user, and built-in scopes). The built-in sources are:

- `builtin://default` — first-party bundles shipped in `bundles/catalog.json` (`bugfix`, `assess`, ...), install-allowed.
- `builtin://community` — community submissions in `bundles/catalog.community.json`, discovery-only.

Each source has an install policy. `install-allowed` sources can be installed
from; `discovery-only` sources appear in `search` and `info` but refuse
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,15 @@ packages = ["src/specify_cli"]
"extensions/agent-context" = "specify_cli/core_pack/extensions/agent-context"
"extensions/assess" = "specify_cli/core_pack/extensions/assess"
"extensions/bug" = "specify_cli/core_pack/extensions/bug"
# Bundled workflows (auto-installed during `specify init`)
# Bundled workflows (`specify init` installs only `speckit`; `bugfix`/`assess` are opt-in via first-party bundles)
"workflows/speckit" = "specify_cli/core_pack/workflows/speckit"
"workflows/bugfix" = "specify_cli/core_pack/workflows/bugfix"
"workflows/assess" = "specify_cli/core_pack/workflows/assess"
Comment thread
markuswondrak marked this conversation as resolved.
# Bundled presets (installable via `specify preset add <name>` or `specify init --preset <name>`)
"presets/lean" = "specify_cli/core_pack/presets/lean"
"presets/constitution-sync" = "specify_cli/core_pack/presets/constitution-sync"
# Community bundle catalog snapshot (used for offline discovery)
# Bundle catalog snapshots (used for offline discovery)
"bundles/catalog.json" = "specify_cli/core_pack/bundles/catalog.json"
"bundles/catalog.community.json" = "specify_cli/core_pack/bundles/catalog.community.json"

[project.optional-dependencies]
Expand Down
8 changes: 6 additions & 2 deletions src/specify_cli/authentication/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,13 @@ def _hostname_in_hosts(hostname: str, hosts: tuple[str, ...]) -> bool:
RedirectValidator = Callable[[str, str], None]


class RedirectPolicyError(urllib.error.URLError):
"""A redirect rejected because it violates the client's security policy."""


def _validate_strict_redirect(old_url: str, new_url: str) -> None:
if not is_safe_download_redirect(old_url, new_url):
raise urllib.error.URLError(
raise RedirectPolicyError(
f"unsafe redirect to {new_url}: target must use HTTPS with a hostname, "
"must not enter a local target from a remote host, and may use HTTP only "
"within loopback (for example localhost, 127.0.0.1, ::1)"
Expand Down Expand Up @@ -100,7 +104,7 @@ def redirect_request(self, req, fp, code, msg, headers, newurl):
except ValueError as exc:
# Malformed redirect target (e.g. unterminated IPv6 bracket).
# Surface as URLError so callers' download error handling applies.
raise urllib.error.URLError(f"malformed redirect URL: {exc}") from exc
raise RedirectPolicyError(f"malformed redirect URL: {exc}") from exc

if self._redirect_validator is not None:
self._redirect_validator(req.full_url, newurl)
Expand Down
Loading