16 Commits

Author SHA1 Message Date
Ali jawwad 2b36f0ce94 fix(powershell): stop Out-Null swallowing the AVAILABLE_DOCS status lines (#3891)
Test-FileExists / Test-DirHasFiles report their line with Write-Output and
ALSO return $true/$false — both on the Success stream. The callers piped
the whole call to `| Out-Null` to discard the boolean, which discarded the
report line with it, so text mode printed the header and nothing under it:

  BEFORE (measured, powershell.exe -NoProfile -File ... -IncludeTasks):
    FEATURE_DIR:...\specs\001-f
    AVAILABLE_DOCS:
    (2 lines)

  AFTER:
    FEATURE_DIR:...\specs\001-f
    AVAILABLE_DOCS:
      [OK] research.md
      [FAIL] data-model.md
      [FAIL] contracts/
      [FAIL] quickstart.md
      [FAIL] tasks.md
    (7 lines)

The bash and Python twins both list every document under that header, so
the PowerShell variant silently returned less information for the same
inputs.

Filter out only the boolean, keeping the report lines. Adds the first
PowerShell text-mode test in this file (every existing PS test is -Json).
File stays ASCII-only (verified 0 non-ASCII bytes).

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 14:58:46 -05:00
Manfred Riem 16cfab7724 feat(presets): resolve constitution templates at command time (#3984)
* feat(presets): resolve constitutions at command time

Gate install-time constitution materialization behind the constitution-sync preset while preserving one-time init seeding and authored-file safeguards.

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84

* fix(presets): emit composed template content

Add a machine-readable preset resolve mode backed by PresetResolver.resolve_content and require the constitution command to consume it.

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84

* fix(presets): unify runtime template composition

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84

* fix(presets): secure runtime template resolution

Align runtime resolution across script variants, validate registry path components, and honor canonical extension ordering and convention paths.

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(presets): align runtime priority semantics

Normalize and tie-break preset priorities consistently across script variants, and preserve template bytes when Python materializes generated files.

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(presets): stop at effective template base

Avoid parsing irrelevant lower layers once resolution reaches a replace base, and decode raw bytes so Python preserves source line endings.

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(presets): align extension template resolution

Support root-level extension templates across runtime resolvers, fail safely when Bash cannot parse an extension registry, and validate requested templates in every prerequisite output mode.

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(presets): resolve dotted command identifiers

Route safe dotted names through command resolution, correct traversal coverage, and make Windows CI text decoding explicit.

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Avoid orphan feature directories on template errors

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Align malformed preset manifest handling

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

* Complete runtime resolver parity

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

* Fail closed on resolver input errors

Assisted-by: GitHub Copilot (model: GPT-5.6 Sol, autonomous)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

* Force UTF-8 and full manifest validation

Force UTF-8 decoding for registry and manifest reads in the Bash and
PowerShell embedded-Python parsers so resolution no longer depends on the
process locale, and validate every manifest template entry's required
fields, type, and strategy consistent with the canonical PresetManifest.

Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

* Fail closed on empty manifests and corrupt registries

Reject manifests missing the provides/templates sections or declaring an
empty template list in all three runtime resolvers, matching the canonical
PresetManifest which treats those as invalid instead of silently degrading a
composing layer to a convention `replace` lookup.

Make a corrupt or unreadable extension registry fail closed in Bash,
PowerShell, and Python instead of swallowing the error and treating every
on-disk extension directory as unregistered-and-enabled, which could activate
a disabled extension.

Read the preset and extension registries as explicit UTF-8 in the PowerShell
resolver so priority/enabled-state decoding no longer depends on the process
code page under Windows PowerShell 5.1.

Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

* fix(presets): fail closed when extension registry is not a regular file

The Bash and Python resolvers used is_file()/`-f` to gate reading the
extension `.registry`, which returns false for a directory or a broken
symlink at that path. In those cases the resolvers treated the registry
as absent and scanned every on-disk extension directory as unregistered
and enabled — a fail-open path. Detect any filesystem entry at the
registry path (including broken symlinks) and reject unless it is a
readable regular file. PowerShell now rejects a non-leaf entry explicitly
for parity. Adds directory- and broken-symlink parity regressions.

Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

* fix(presets): fail closed on corrupt registry in canonical resolver and PowerShell

Two remaining fail-open paths for an invalid extension registry:

- The canonical PresetResolver enumerated extensions through
  ExtensionRegistry, whose _load() normalizes a corrupt or unreadable
  registry to an empty mapping. The directory scan then admitted every
  on-disk extension directory as unregistered-and-enabled, so a corrupt
  registry could still supply constitution content at init and through
  constitution-sync materialization. Add a non-invasive is_corrupt()
  probe (recovery behavior for install/enable/disable is unchanged) and
  raise from _get_all_extensions_by_priority() when the registry exists
  but is invalid. _load() now also recovers from OSError/UnicodeDecodeError
  so a directory or unreadable registry no longer crashes construction.

- The PowerShell resolver gated the registry read with Test-Path, which
  returns false for a dangling symlink on Windows, letting a broken
  .registry symlink bypass the guard and enable every on-disk extension.
  Detect the entry via directory enumeration (which observes a broken
  symlink) and reject it unless it is a readable regular file.

Adds canonical corrupt/directory-registry regressions and extends the
broken-symlink parity test to PowerShell.

Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

* fix(presets): detect dangling registry symlink in ExtensionRegistry.is_corrupt

is_corrupt() gated on Path.exists(), which follows symlinks and returns
False for a dangling .registry symlink — so the canonical PresetResolver
treated it as an absent registry and fell back to scanning every on-disk
extension directory as unregistered-and-enabled, reopening the fail-open
path this guard closes. Detect lexical existence with os.path.lexists and
require a regular file before parsing, so a broken symlink (or directory)
is reported corrupt and resolution fails closed. Adds a canonical
broken-symlink regression alongside the directory case.

Assisted-by: GitHub Copilot (model: claude-opus-4.8, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7dbce70f-80c6-4e14-a30d-78cb358bcb84
Copilot-Session: 3158e06f-95df-4e3a-843f-f159a35aa30c
2026-08-10 10:22:31 -05:00
Vincent Lee c8ce488073 chore: add pre-commit config and fix trailing whitespace/end-of-file (#3430)
issues
2026-07-10 11:47:36 -05:00
Noor ul ain ea1827769a fix: stop check-prerequisites --paths-only from writing feature.json (#3025) (#3190)
* fix: stop check-prerequisites --paths-only from writing feature.json (#3025)

check-prerequisites --paths-only / -PathsOnly is documented as pure,
read-only path resolution, but when SPECIFY_FEATURE_DIRECTORY was set it
called the persist routine and rewrote .specify/feature.json. That dirtied
the working tree and overwrote a pinned feature directory during what should
be a no-op.

Add an explicit opt-out at the resolver boundary instead of a global env
back-channel:

- bash: get_feature_paths accepts a leading --no-persist flag that skips
  _persist_feature_json; check-prerequisites.sh passes it in --paths-only mode.
- PowerShell: Get-FeaturePathsEnv gains a -NoPersist switch that skips
  Save-FeatureJson; check-prerequisites.ps1 passes it in -PathsOnly mode.

Normal (non-paths-only) invocations are unchanged and still persist the
override, so future sessions without the env var keep working.

Add regression tests asserting --paths-only/-PathsOnly leaves a pinned
feature.json untouched even when the env override differs, plus a guard that
normal mode still persists.

* fix: use ASCII hyphen in common.ps1 comment for PS 5.1 compatibility

The em-dash in the persist comment introduced non-ASCII bytes, failing
test_ps1_file_is_ascii_only which enforces ASCII-only PowerShell sources
for Windows PowerShell 5.1 compatibility.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: add PowerShell normal-mode persistence guard (#3025)

Addresses Copilot review feedback on #3190: the bash side had a
`test_normal_mode_still_persists_feature_json` guard, but there was no
symmetric PowerShell test asserting that running check-prerequisites.ps1
*without* -PathsOnly still persists the SPECIFY_FEATURE_DIRECTORY override
into .specify/feature.json.

Add test_ps_normal_mode_still_persists_feature_json, which guards against
accidentally passing -NoPersist unconditionally (or flipping the default)
in a future refactor. Verified it fails when -NoPersist is passed in the
non -PathsOnly branch and passes with the current conditional.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 06:38:59 -05:00
Quratulain-bilal 0ef53eb91f fix(scripts): send check-prerequisites.ps1 errors to stderr (#3123)
* fix(scripts): send check-prerequisites.ps1 errors to stderr

The validation errors and run-hints in check-prerequisites.ps1 were
written with Write-Output, so they went to stdout. This script is
usually run with -Json and its stdout parsed by the agent, so an error
(e.g. missing plan.md) leaves the parser with an error string instead
of JSON. The bash counterpart already writes these to stderr (>&2), as
do the sibling PowerShell scripts (setup-tasks.ps1, common.ps1's
Get-FeaturePathsEnv). Switch the six error/hint lines to
[Console]::Error.WriteLine so stdout stays clean and the two shells
match.

* test(scripts): assert check-prerequisites errors stay on stderr

Per the #3122 bug assessment, tighten the failure-path tests so they
verify stdout stays clean (empty / valid JSON) and the error text only
appears on stderr, instead of checking the combined stdout+stderr
string. Covers all three PowerShell validation paths (missing feature
dir, missing plan.md, missing tasks.md with -RequireTasks) and the bash
counterpart. The two new error-routing tests fail on the pre-fix
script (errors on stdout) and pass after it.
2026-06-23 14:09:17 -05:00
Copilot 927f54feea feat: make git extension opt-in and remove --no-git at v0.10.0 (#2873)
* feat(init)!: make git extension opt-in and remove --no-git at v0.10.0

- Remove --no-git parameter from specify init command
- Remove git extension auto-installation from init flow
- Git repository initialization (git init) still runs when git is available
- Remove --no-git from all test invocations across the test suite
- Update docs to reflect opt-in git extension behavior
- Replace TestGitExtensionAutoInstall with TestGitExtensionOptIn tests

BREAKING CHANGE: specify init no longer auto-installs the git extension.
Use `specify extension add git` to install it explicitly.
The --no-git flag has been removed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(scripts): remove git operations from core scripts

Git functionality is now entirely managed by the git extension.
Core scripts only handle directory-based feature creation and numbering.

- Remove has_git(), check_feature_branch(), git branch creation from core
- Simplify number detection to use only spec directory scanning
- Remove HAS_GIT output from get_feature_paths()
- Remove git remote fetching and branch querying
- Keep BRANCH_NAME output key for backward compatibility

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: remove all git operations from core

- Remove is_git_repo() and init_git_repo() dead code from _utils.py
- Remove --branch-numbering from init command
- Remove git from 'specify check' (now extension-only)
- Update docs: git is optional prerequisite, check command description
- Fix tests to reflect no-git-in-core reality (fallback to main)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(scripts): remove directory scanning and branch fallback from core

Core scripts now resolve feature context exclusively from:
1. SPECIFY_FEATURE env var (set by git extension)
2. .specify/feature.json (persisted by specify command)

Removed find_feature_dir_by_prefix() and directory scanning heuristics —
these are the git extension's responsibility. Scripts error clearly when
no feature context is available.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: introduce feature_numbering, deprecate branch_numbering in init-options

- specify command template now reads feature_numbering (preferred) with
  fallback to branch_numbering (deprecated) from init-options.json
- Git extension reads git-config.yml > feature_numbering > branch_numbering
- init now writes feature_numbering: sequential to init-options.json
- Deprecation warning emitted when branch_numbering is used as fallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: remove trailing whitespace in common.ps1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(scripts): persist SPECIFY_FEATURE_DIRECTORY env var to feature.json

When SPECIFY_FEATURE_DIRECTORY is set, get_feature_paths() now writes the
value to .specify/feature.json so future sessions without the env var can
still resolve the feature directory. The write is idempotent — it skips
when the file already contains the same value.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: address review feedback — error messages and docs

- Update error messages in common.sh and common.ps1 to reference
  SPECIFY_FEATURE_DIRECTORY instead of SPECIFY_FEATURE (which no longer
  resolves feature directories)
- Fix get_current_branch comment (returns empty string, not error)
- Update upgrade.md to reference SPECIFY_FEATURE_DIRECTORY with correct
  example paths
- Update local-development.md troubleshooting: replace stale 'Git step
  skipped' row with actionable git extension guidance

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(scripts): harden feature.json persistence

- Use json_escape in printf fallback when jq is unavailable (common.sh)
- Replace utf8NoBOM encoding with UTF8Encoding($false) for PowerShell
  5.1 compatibility (common.ps1)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(scripts): remove dead feature_json_matches_feature_dir functions

These guards are no longer needed since the branch-name validation they
protected against has been removed from check-prerequisites.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor(git-ext): rename create-new-feature to create-new-feature-branch

The git extension's script only creates the git branch — rename it to
reflect that responsibility. The core create-new-feature.sh/.ps1 handles
feature directory creation and feature.json persistence.

Also includes fixes from review feedback:
- common.sh: _persist_feature_json uses json_escape fallback
- common.ps1: Save-FeatureJson uses UTF8Encoding for PS 5.1 compat
- common.ps1: case-sensitive path stripping on non-Windows
- create-new-feature.sh/ps1: output both SPECIFY_FEATURE and
  SPECIFY_FEATURE_DIRECTORY
- setup-tasks.sh: fix stale 'Validate branch' comment

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(tests): update references to renamed git extension scripts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(tests): remove duplicate EXT_CREATE_FEATURE assignments

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>
2026-06-09 06:13:07 -05:00
Pascal THUET 4028c50af8 fix: render script command hints with active agent separator (#2649)
* fix script command hints for agent separators

* Address command hint review feedback

* chore: remove whitespace-only PR churn

* test: fix PowerShell command hint invocation

* fix: preserve hyphens in script command hints

* fix: render managed script command hints
2026-06-03 16:24:13 -05:00
WOLIKIMCHENG 57a518a583 Fix shared script command hints for integration separators (#2627)
* fix shared script command refs for integration separators

* Fix integration use shared infra refresh hint

* Clarify shared infrastructure force wording

---------

Co-authored-by: root <1647273252@qq.com>
Co-authored-by: root <kinsonnee@gmail.com>
2026-05-28 10:02:27 -05:00
Manfred Riem 3227b9660e fix: paths-only skips branch validation, setup-plan preserves existing plan (#2672)
* fix: paths-only skips branch validation, setup-plan preserves existing plan (#2653)

- check-prerequisites.sh/ps1: move branch validation after --paths-only
  early exit so --paths-only returns paths without requiring a spec branch
- setup-plan.sh/ps1: skip template copy when plan.md already exists to
  prevent overwriting user-authored plans on reruns
- setup-plan.sh: send status messages to stderr in --json mode so stdout
  remains parseable JSON
- Add tests for both fixes (bash + PowerShell)

* fix: remove trailing whitespace in PowerShell scripts

* fix: route PS skip message to stderr in -Json mode, add PS JSON assertions

Address review: setup-plan.ps1 Write-Output polluted stdout in -Json
mode when plan.md already existed. Use [Console]::Error.WriteLine()
when -Json is set. Add json.loads + stderr assertions to the PS rerun
test to catch regressions.

* fix: use Test-Path -PathType Leaf for plan existence check

Bare Test-Path matches directories too, which would silently skip plan
creation if a directory existed at the plan.md path.
2026-05-27 07:17:34 -05:00
den (work) 36ff7e6505 Update files 2025-10-14 11:52:26 -07:00
den (work) 15e5572a1d Update command reference 2025-10-06 22:16:48 -07:00
Den Delimarsky 🌺 c7b61f4bfd Update with extra commands 2025-09-22 19:35:45 -07:00
Den Delimarsky b03bba37ce Update scripts/powershell/check-prerequisites.ps1
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-21 01:48:21 -07:00
Den Delimarsky 84ec4611c4 Update scripts/powershell/check-prerequisites.ps1
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-20 13:56:20 -07:00
Den Delimarsky 826c3a6102 Update scripts/powershell/check-prerequisites.ps1
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-20 12:01:05 -07:00
Den Delimarsky 🌺 7c0f0a4627 Consolidate scripts 2025-09-20 11:19:47 -07:00