* feat: add PyPI publishing workflow and readme metadata
- Add readme = "README.md" to pyproject.toml for PyPI project description
- Add manual publish-pypi.yml workflow using trusted publishers (OIDC)
- Update release.yml install instructions to prefer PyPI
The publish workflow is manually triggered after a release, checks out the
specified tag, verifies version consistency, builds with uv, and publishes
using trusted publishing (no API tokens required).
Prerequisites before first use:
- Take ownership of the specify-cli PyPI project (#2908)
- Create a 'pypi' environment in repo settings
- Configure trusted publisher on PyPI for this repo/workflow
Closes#2908
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address PR review feedback on publish workflow
- Add actions: read permission (required for artifact upload/download)
- Move version check after uv install and use uv run python (ensures
Python >=3.11 with tomllib is available regardless of runner image)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: use absolute URLs for README images (PyPI compatibility)
PyPI does not host images from the repository, so relative paths like
./media/logo.webp render as broken images. Switch to absolute
raw.githubusercontent.com URLs so images display on both GitHub and PyPI.
Ref: https://github.com/pypi/warehouse/issues/5246
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address second review round
- Convert remaining /media/ image path to absolute URL for PyPI
- Pin release install to specific version (specify-cli==X.Y.Z)
- Align setup-uv to v8.2.0 matching rest of CI
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address third review round
- Use job-level permissions: actions:write on build (for upload-artifact),
actions:read on publish (for download-artifact)
- Include both @latest and pinned version in release notes
- Add note that PyPI may lag behind the GitHub release
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: add contents:read to build job, clarify manual publish
- Build job needs contents:read for checkout (job-level perms replace
workflow-level)
- Clarify that PyPI publishing is manually triggered, not automatic
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: force tag resolution and validate before checkout
Move tag format validation before checkout and use refs/tags/ prefix
to ensure we always check out a tag, not a branch with the same name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address review - links, install cmd, python pin
- Convert all relative .md links in README to absolute GitHub URLs
for PyPI rendering compatibility
- Fix release notes: use 'uv tool install specify-cli' (no @latest)
- Pin Python 3.13 via uv python install for deterministic builds
and use python3 directly instead of uv run
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: address review - python setup, docs alignment, publish flag
- Use actions/setup-python (pinned v6, Python 3.13) instead of
uv python install for deterministic builds
- Use python instead of python3 for setup-python compatibility
- Remove unsupported --trusted-publishing always flag from uv publish
(OIDC is auto-detected with id-token: write)
- Update README install to lead with PyPI, source as fallback
- Update installation guide: replace PyPI disclaimer with official
package note, add PyPI as primary install method
- Release notes: pin to exact version, clarify PyPI timing
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: clarify PyPI availability timing in docs
- README: note source install is useful when PyPI version lags
- Installation guide: explain PyPI follows GitHub releases and may
lag briefly; source installs are always immediately available
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: quote version specifier in release notes install command
uv tool install accepts PEP 508 specifiers when quoted. Add quotes
around 'specify-cli==VERSION' so users can copy-paste directly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: use specify-cli@latest consistently
Use @latest to force a fresh PyPI resolve (bypasses uv's cached tool
version), matching the issue acceptance criteria. Source install remains
as fallback when PyPI lags.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: pin release notes to exact version, clarify manual publish
Release notes (versioned changelog) must always reference the specific
release version, not @latest. Use 'specify-cli==VERSION' for
reproducibility.
Also clarify that PyPI publishing is 'performed after' (not 'follows')
each release, making the manual nature clearer.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: keep source install as primary, PyPI as alternative
Until PyPI ownership is fully transferred and first publish is
confirmed, source installs from GitHub remain the primary recommended
method. PyPI install is listed as a convenient alternative.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: align checkout pin, soften PyPI wording, absolute links
- Align actions/checkout to v7.0.0 (same SHA as test.yml/release.yml)
- Remove assertion that PyPI is published by maintainers (ownership
transfer still pending); keep as availability statement
- Use 'once published for this release' wording in release notes
- Convert remaining relative links in README to absolute URLs for
PyPI rendering
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: align docs and release notes with pre-transfer state
- docs/installation.md: qualify PyPI as available 'once official
publishing is enabled' (ownership transfer still pending)
- release.yml: use specify-cli@VERSION syntax (consistent with
README/docs @latest form)
- PR description updated to match
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: revert release notes to match main
The release.yml release notes template should not change in this PR.
PyPI install instructions can be added to release notes in a future
PR once publishing is confirmed working.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: revert README and installation docs to match main
Do not mention PyPI in documentation until the first official PyPI
release has been published. This PR only adds the workflow and readme
metadata in pyproject.toml.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: fail fast if build produces no artifacts
Add if-no-files-found: error to upload-artifact so a missing/empty
dist/ directory fails the build job immediately rather than causing
a confusing failure in the publish job.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix: align artifact action pins with repo lockfiles
Update upload-artifact to v7.0.1 and download-artifact to v8.0.1,
matching the pins used in the repo's gh-aw workflow lockfiles.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Manfred Riem <mnriem@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chore: bump version to 0.11.4
* chore: begin 0.11.5.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.11.3
* chore: begin 0.11.4.dev0 development
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* chore: bump version to 0.11.2
* chore: begin 0.11.3.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.11.1
* chore: begin 0.11.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.11.0
* chore: begin 0.11.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.10.4
* chore: begin 0.10.5.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.10.3
* chore: begin 0.10.4.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.10.2
* chore: begin 0.10.3.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.10.1
* chore: begin 0.10.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.10.0
* chore: begin 0.10.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.9.5
* chore: begin 0.9.6.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat(extensions): add bundled bug triage workflow extension (#2870)
Add a bundled 'bug' extension providing a three-stage bug triage workflow:
- speckit.bug.assess: triage a bug report (pasted text or URL), locate
suspected code paths, and propose a remediation
- speckit.bug.fix: apply the proposed remediation and record what changed
- speckit.bug.test: validate the fix and record the verification result
Each bug gets its own directory under .specify/bugs/<slug>/ with one
Markdown report per stage (assessment.md, fix.md, test.md). The slug is
the only handle the three commands share; existing bug directories are
never overwritten.
Mirrors the layout of the existing bundled extensions (git, agent-context):
- extensions/bug/extension.yml, README.md, commands/
- extensions/catalog.json: register 'bug' (alphabetical, between
agent-context and git)
- pyproject.toml: add wheel mapping to specify_cli/core_pack/extensions/bug
Closes#2870
* address Copilot review on #2871
- speckit.bug.assess.md: drop POSIX-specific 'mkdir -p' example;
reword the prerequisite to describe the requirement (ensure BUG_DIR
exists) without assuming a specific shell.
- speckit.bug.fix.md: fix the slug-resolution fallback wording. It
listed '.specify/bugs/*/assessment.md' but then keyed off whether
'exactly one bug directory' existed; now it correctly keys off whether
exactly one matching 'assessment.md' was found and uses the slug from
its parent directory.
- tests/extensions/bug/test_bug_extension.py: add a smoke test analogous
to the agent-context extension's coverage. Validates the bundled
layout, catalog registration, '_locate_bundled_extension("bug")'
resolution, and that 'ExtensionManager.install_from_directory' installs
the three commands.
All 333 tests in tests/extensions/, tests/test_extensions.py, and
tests/test_extension_registration.py pass.
* address Copilot review on #2871 (round 2)
- Import _locate_bundled_extension from the public 'specify_cli'
package (it is re-exported in __init__.py) instead of the private
'specify_cli._assets' module, so the test does not depend on internal
module layout.
- Clarify module docstring: install_from_directory is called with
register_commands=False, so commands are copied and recorded in the
installed manifest but not registered with AI agents. Wording updated
to avoid implying otherwise.
* address Copilot review on #2871 (round 3)
- tests/extensions/bug/test_bug_extension.py: read extension.yml as
UTF-8 explicitly to avoid platform-dependent default encoding (notably
on Windows). Matches how the README is read in the same module.
- extensions/bug/commands/speckit.bug.assess.md: add a 'Safety When
Fetching URLs' section. Instructs the agent to treat fetched page
content as untrusted input (no obeying embedded prompt-injection
directives), forbids supplying credentials/secrets that a page asks
for, scopes the fetch to the URL the user provided (no following
redirects to other resources), and requires suspicious content to be
quoted verbatim under an 'Unverified' heading rather than acted on.
- extensions/catalog.json: bump 'updated_at' to today (2026-06-05) so
consumers that cache by this field invalidate when 'bug' is added.
- extensions/bug/README.md: minor grammar fix ('a reproduction that was
not actually performed').
All 251 tests in tests/extensions/bug/, tests/test_extensions.py, and
tests/test_extension_registration.py pass.
* speckit.bug.assess: add URL Trust Policy for fetched bug-report URLs
Builds on the 'Safety When Fetching URLs' section by adding a tiered
classification rule the agent applies before any fetch:
1. Refuse outright (no fetch, no prompt) for non-http(s) schemes,
loopback, link-local, RFC1918 private space, and known cloud
instance-metadata endpoints (169.254.169.254, metadata.google.internal,
100.100.100.200, metadata.azure.com). This closes the SSRF /
internal-recon vector opened by 'paste any URL'.
2. Fetch silently for an explicit allowlist of widely-used public
bug-report sources (github, gitlab, bitbucket, atlassian.net, linear,
stackoverflow/stackexchange, sentry). This preserves the paste-a-URL
ergonomics the workflow is built for.
3. Otherwise prompt once in interactive mode (default 'no', naming the
resolved host explicitly); in automated mode skip the fetch and
record '[UNVERIFIED - fetch skipped: host not on safe list: <host>]'
in assessment.md so a human can decide later.
In every case, assessment.md records the verbatim URL, the resolved host,
and which branch of the policy was taken (allowlisted /
confirmed-by-user / auto-refused: <reason>) so the per-bug directory's
audit trail is complete. Preflight HEAD probes are explicitly forbidden
since the probe itself is the request the policy gates.
Execution step 1 now defers to the policy before fetching.
* speckit.bug.assess: remove 'post-redirect-resolution' inconsistency
The URL Trust Policy explicitly forbids following redirects, but the
audit-trail bullet asked the agent to record the host
'post-redirect-resolution', which contradicted that rule and could lead
agents to follow redirects unintentionally to determine what to log.
Reword both call sites to refer to the host parsed from the URL the user
supplied (no resolution implied):
- Tier-3 interactive prompt: '...naming the host parsed from the URL
explicitly...'
- Recorded fields: 'The host parsed from that URL (no redirect following
- see the rule above).'
No behavior change; clarification only.
* chore: bump version to 0.9.4
* chore: begin 0.9.5.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.9.3
* chore: begin 0.9.4.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.9.2
* chore: begin 0.9.3.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.9.1
* chore: begin 0.9.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.9.0
* chore: begin 0.9.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Initial plan
* Extract agent context updates into bundled agent-context extension
* Potential fix for pull request finding 'Unused import'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Potential fix for pull request finding 'Unused import'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: address review comments on agent-context extension
- bash: parse init-options.json with a single python3 invocation instead
of three separate read_json_field calls, for parity with the PowerShell
ConvertFrom-Json approach and to avoid divergent error semantics
- bash: use parameter expansion to strip PROJECT_ROOT prefix from plan
path instead of sed interpolation, avoiding special-character fragility
- powershell: limit Get-ChildItem to -Depth 1 so plan.md discovery matches
the bash glob specs/*/plan.md (one level deep) — fixes cross-platform
inconsistency with nested plan.md files
- powershell: replace Substring+Length relative-path with
[System.IO.Path]::GetRelativePath for robustness across case/PSDrive
differences
- __init__.py: move agent-context extension install to after
save_init_options so init-options.json is present when hooks run
- __init__.py: seed context_markers in init-options only when
context_file is truthy; avoids noise for integrations without a context
file
- integrations/base.py: narrow blanket except Exception in
_resolve_context_markers to ImportError / (OSError, ValueError) so
unexpected bugs surface instead of being silently swallowed
* fix: gate context_markers in _update_init_options_for_integration on context_file
Apply the same gating logic used during `specify init`: only write
context_markers to init-options.json when the integration actually has a
context_file set. When switching to an integration without a context file
the stale markers are removed, keeping the two init paths consistent.
* fix: move context_file/context_markers from init-options.json to agent-context extension config
* Potential fix for pull request finding 'Unused global variable'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* fix: clarify local import comment in agents.py
* Fix remaining agent-context review findings
* Fix follow-up agent-context review issues
* Address review feedback: narrow except, improve PyYAML messaging, surface config-written note
* Fix double-space in PyYAML install hint message
* Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Potential fix for pull request finding 'Empty except'
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* Address latest agent-context review feedback
* Harden bash config parse output handling
* Clarify ImportError-only fallback comment
* Apply review feedback: drop dead try/except, guard ext-config creation, explicit ConvertFrom-Yaml check
* Remove redundant $Options = $null in PS1 catch block
* Add constitution directives, deprecation warning, agent-context auto-install, and init flow fix
- Add constitution-loading directive to specify, clarify, tasks, checklist, taskstoissues commands
- Add deprecation warning (v0.12.0) in upsert_context_section()
- Auto-install agent-context extension during specify init
- Move context_file from init-options.json to agent-context extension config
- Add tests: deprecation warning, corrupt config, constitution directives
- Update file inventories across all integration tests
* Address review: fix init ordering, test coverage, and hermes inventory
- Move agent-context extension install after init-options.json is saved
so skill registration can read ai_skills + integration key
- Write extension config after install (avoids template overwriting context_file)
- Fix test_defaults_when_markers_field_missing to truly test missing markers key
- Update hermes tests to allow extension-installed agent-context skill
* Address review: chmod ordering, preserve markers, PS1 Python check, YAML key order
- Move ensure_executable_scripts after agent-context extension install
so extension scripts get execute bits set
- Use preserve_markers=True on reinit to keep user-customized markers
- Add Python 3 version check in PowerShell fallback (matching bash behavior)
- Add sort_keys=False to yaml.safe_dump for stable config output
* Address review: path traversal guards and docstring fix
- Reject absolute paths and '..' segments in context_file in both bash and
PowerShell scripts to prevent writes outside the project root
- Fix docstring in _update_init_options_for_integration to accurately
describe marker preservation behavior
* Address review: strict enabled check, docstring, segment-level path traversal
- Use 'is not False' for enabled check so only literal False disables
- Update upsert_context_section docstring to mention disabled-extension return
- Fix path traversal guards to check actual path segments, not substrings
(allows filenames like 'notes..md' while rejecting '../' traversal)
* Address review: UnicodeError handling, missing extension warning
- Add UnicodeError to exception tuples in _load_agent_context_config and
_resolve_context_markers so garbled UTF-8 config files fall back to defaults
- Emit error (with reinstall command) instead of silent skip when bundled
agent-context extension is not found during init
* Address review: bash backslash traversal guard, wheel packaging
- Reject backslash separators and Windows drive-letter paths in bash
context_file validation (prevents traversal on Git-Bash/Windows)
- Add extensions/agent-context to pyproject.toml force-include so the
bundled extension is included in wheel builds
* Address review: write extension config before init-options.json
- Reorder writes in _update_init_options_for_integration so the
agent-context extension config is updated first; if it fails,
init-options.json remains consistent with the previous state
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Manfred Riem <15701806+mnriem@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
* chore: bump version to 0.8.18
* chore: begin 0.8.19.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.17
* chore: begin 0.8.18.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.16
* chore: begin 0.8.17.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.15
* chore: begin 0.8.16.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.14
* chore: begin 0.8.15.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.13
* chore: begin 0.8.14.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.12
* chore: begin 0.8.13.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.11
* chore: begin 0.8.12.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.10
* chore: begin 0.8.11.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.9
* chore: begin 0.8.10.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.8
* chore: begin 0.8.9.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.7
* chore: begin 0.8.8.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.6
* chore: begin 0.8.7.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.5
* chore: begin 0.8.6.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.4
* chore: begin 0.8.5.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.3
* chore: begin 0.8.4.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.1
* chore: begin 0.8.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.8.0
* chore: begin 0.8.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.7.5
* chore: begin 0.7.6.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.7.4
* chore: begin 0.7.5.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.7.3
* chore: begin 0.7.4.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* Replace shell-based context updates with marker-based upsert
Replace ~3500 lines of bash/PowerShell agent context update scripts
with a Python-based approach using <!-- SPECKIT START/END --> markers.
IntegrationBase now manages the agent context file directly:
- upsert_context_section(): creates or updates the marked section at
init/install/switch time with a directive to read the current plan
- remove_context_section(): removes the section at uninstall, deleting
the file only if it becomes empty
- __CONTEXT_FILE__ placeholder in command templates is resolved per
integration so the plan command references the correct agent file
- context_file is persisted in init-options.json for extension access
The plan command template instructs the LLM to update the plan
reference between the markers in the agent context file.
Removed:
- scripts/bash/update-agent-context.sh (857 lines)
- scripts/powershell/update-agent-context.ps1 (515 lines)
- 56 integration wrapper scripts (update-context.sh/.ps1)
- templates/agent-file-template.md
- agent_scripts frontmatter key and {AGENT_SCRIPT} replacement logic
- update-context reference from integration.json
- tests/test_cursor_frontmatter.py (tested deleted scripts)
Added:
- upsert/remove context section methods on IntegrationBase
- __CONTEXT_FILE__ placeholder support in process_template()
- context_file field in init-options.json (init/switch/uninstall)
- Per-integration tests: context file correctness, plan reference,
init-options persistence (78 new context_file tests)
- End-to-end CLI validation across all 28 integrations
* fix: search for end marker after start marker in context section methods
Address Copilot review: content.find(CONTEXT_MARKER_END) searched from
the start of the file rather than after the located start marker. If
the file contained a stray end marker before the start marker, the
wrong slice could be replaced.
Now both upsert_context_section() and remove_context_section() pass
start_idx as the second argument to find() and validate end_idx >
start_idx before performing the replacement.
* fix: address Copilot review feedback on context section handling
1. Fix grammar in _build_context_section() directive text — add commas
for a complete sentence.
2. Resolve __CONTEXT_FILE__ in resolve_skill_placeholders() — skills
generated via extensions/presets for codex/kimi now replace the
placeholder using the context_file value from init-options.json.
3. Handle Cursor .mdc frontmatter — when creating a new .mdc context
file, prepend alwaysApply: true YAML frontmatter so Cursor
auto-loads the rules.
4. Fix empty-file leading newline — when the context file exists but
is empty, write the section directly instead of prepending a blank
line.
* fix: address second round of Copilot review feedback
1. Ensure .mdc frontmatter on existing files — upsert_context_section()
now checks for missing YAML frontmatter on .mdc files during updates
(not just creation), so pre-existing Cursor files get alwaysApply.
2. Guard against context_file=None — use 'or ""' instead of a default
arg so explicit null values in init-options.json don't cause a
TypeError in str.replace().
3. Clean up .mdc files on removal — remove_context_section() treats
files containing only the Speckit-generated frontmatter block as
empty, deleting them rather than leaving orphaned frontmatter.
* fix: address third round of Copilot review feedback
1. CRLF-safe .mdc frontmatter check — use lstrip().startswith('---')
instead of startswith('---\n') so CRLF files don't get duplicate
frontmatter.
2. CRLF-safe .mdc removal check — normalize line endings before
comparing against the sentinel frontmatter string.
3. Call remove_context_section() during integration_uninstall() — the
manifest-only uninstall was leaving the managed SPECKIT markers
behind in the agent context file.
4. Fix stale docstring — remove 'agent_scripts' mention from
test_lean_commands_have_no_scripts().
* fix: address fourth round of Copilot review feedback
1. Remove unused script_type parameter from _write_integration_json()
and all 3 call sites — the parameter was no longer referenced after
the update-context script removal.
2. Fix _build_context_section() docstring — correct example path from
'.specify/plans/plan.md' to 'specs/<feature>/plan.md'.
3. Improve .mdc frontmatter-only detection in remove_context_section()
— use regex to match any YAML frontmatter block (not just the exact
Speckit-generated one), so .mdc files with additional frontmatter
keys are also cleaned up when no body content remains.
* fix: handle corrupted markers and parse .mdc frontmatter robustly
1. Handle partial/corrupted markers in upsert_context_section() —
if only the START marker exists (no END), replace from START
through EOF. If only the END marker exists, replace from BOF
through END. This keeps upsert idempotent even when a user
accidentally deletes one marker.
2. Parse .mdc YAML frontmatter properly — new _ensure_mdc_frontmatter()
helper parses existing frontmatter and ensures alwaysApply: true is
set, rather than just checking for the --- delimiter. Handles
missing frontmatter, existing frontmatter without alwaysApply, and
already-correct frontmatter.
* fix: preserve .mdc frontmatter, add tests, clean up on switch
1. Rewrite _ensure_mdc_frontmatter() with regex — preserves comments,
formatting, and custom keys in existing frontmatter instead of
destructively re-serializing via yaml.safe_dump(). Inserts or
fixes alwaysApply: true in place.
2. Add 6 focused .mdc frontmatter tests to cursor-agent test file:
new file creation, missing frontmatter, preserved custom keys,
wrong alwaysApply value, idempotent upserts, removal cleanup.
3. Call remove_context_section() during integration switch Phase 1 —
prevents stale SPECKIT markers from being left in the old
integration's context file. Also clear context_file from
init-options during the metadata reset.
* fix: remove unused MDC_FRONTMATTER, preserve inline comments, normalize bare CR
1. Remove unused MDC_FRONTMATTER class variable — dead code after
_ensure_mdc_frontmatter() was rewritten with regex.
2. Preserve inline comments when fixing alwaysApply — the regex
substitution now captures trailing '# comment' text and keeps it.
3. Normalize bare CR in upsert_context_section() — match the
behavior of remove_context_section() which already normalizes
both CRLF and bare CR.
4. Clarify .mdc removal comment — 'treat frontmatter-only as empty'
instead of misleading 'strip frontmatter'.
* fix: handle corrupted markers in remove, CRLF-safe end-marker consumption
1. Handle corrupted markers in remove_context_section() — mirror
upsert's behavior: start-only removes start→EOF, end-only removes
BOF→end. Previously bailed out leaving partial markers behind.
2. CRLF-safe end-marker consumption — both upsert and remove now
handle \r\n after the end marker, not just \n. Prevents extra
blank lines at replacement boundaries in CRLF files.
3. Clarify path rule in plan template — distinguish filesystem
operations (absolute paths) from documentation/agent context
references (project-relative paths).
* fix: only remove context section when both markers are well-ordered
remove_context_section() previously treated mismatched markers as
corruption and aggressively removed from BOF→end-marker or
start-marker→EOF, which could delete user-authored content if only
one marker remained. Now it only removes when both START and END
markers exist and are properly ordered, returning False otherwise.
* chore: bump version to 0.7.2
* chore: begin 0.7.3.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.7.1
* chore: begin 0.7.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.7.0
* chore: begin 0.7.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.6.2
* chore: begin 0.6.3.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: bump version to 0.6.1
* chore: begin 0.6.2.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: add bundled lean preset with minimal workflow commands
Add a lean preset that overrides the 5 core workflow commands (specify,
plan, tasks, implement, constitution) with minimal prompts that produce
exactly one artifact each — no extension hooks, no scripts, no git
branching, no templates.
Bundled preset infrastructure:
- Add _locate_bundled_preset() mirroring _locate_bundled_extension()
- Update 'specify init --preset' to try bundled -> catalog fallback
- Update 'specify preset add' to try bundled -> catalog fallback
- Add bundled guard in download_pack() for presets without download URLs
- Add lean to presets/catalog.json with 'bundled: true' marker
- Add lean to pyproject.toml force-include for wheel packaging
- Align error messages with bundled extension error pattern
Tests: 15 new tests (TestLeanPreset + TestBundledPresetLocator)
* refactor: address review — clean up unused imports, strengthen test assertions
- Remove unused MagicMock import and cache_dir setup in download test
- Assert 'bundled' and 'reinstall' in CLI error output (not just exit code)
- Mock catalog in missing-locally test for deterministic bundled error path
- Fix test versions to satisfy updated speckit_version >=0.6.0 requirement
* refactor: address review — fix constitution paths, add REINSTALL_COMMAND to presets.py
- Fix constitution path to .specify/memory/constitution.md in plan, tasks,
implement commands (matching core command convention)
- Include REINSTALL_COMMAND in download_pack() bundled guard for consistent
recovery instructions across bundled extensions and presets
* refactor: address review — explicit feature_directory paths, ZIP cleanup in finally
- Prefix spec.md/plan.md/tasks.md with <feature_directory>/ in plan, tasks,
and implement commands so the agent doesn't operate on repo root by mistake
- Move ZIP unlink into finally block in init --preset path so cleanup runs
even when install_from_zip raises (matching preset_add pattern)
* refactor: address review — replace Unicode em dashes with ASCII, fix grammar
- Replace all Unicode em dashes with ASCII hyphens in preset.yml and
catalog.json to avoid decode errors on non-UTF-8 environments
- Fix grammar: 'store it in tasks.md' -> 'store them in tasks.md'
* refactor: address review - align task format between tasks and implement
- Remove undefined [P] marker from implement (lean uses sequential execution)
- Clarify checkbox update: 'change - [ ] to - [x]' instead of ambiguous '[X]'
- Simplify implement to execute tasks in order without parallel complexity
* refactor: address review - parse frontmatter instead of raw substring search
- Use CommandRegistrar.parse_frontmatter() to check for scripts/agent_scripts
keys in YAML frontmatter instead of brittle 'scripts:' substring search
* chore: bump version to 0.6.0
* chore: begin 0.6.1.dev0 development
---------
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>