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
9 changes: 8 additions & 1 deletion scripts/bash/check-prerequisites.sh
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,14 @@ if [[ ! -f "$IMPL_PLAN" ]]; then
exit 1
fi

if [[ ! -s "$IMPL_PLAN" ]]; then
# Read the file because opening it alone can succeed before a read error.
if ! cat -- "$IMPL_PLAN" >/dev/null 2>&1; then
echo "ERROR: plan.md is not readable in $FEATURE_DIR" >&2
echo "Check its file permissions and rerun the prerequisite check." >&2
exit 1
fi

if ! file_has_non_whitespace_content "$IMPL_PLAN"; then
echo "ERROR: plan.md is empty in $FEATURE_DIR" >&2
echo "Populate it, or remove the empty file and rerun $(format_speckit_command plan "$REPO_ROOT") with a valid plan template." >&2
exit 1
Expand Down
7 changes: 7 additions & 0 deletions scripts/bash/common.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#!/usr/bin/env bash
# Common functions and variables for all scripts

# Match Unicode White_Space, as PowerShell does, even under LC_ALL=C.
# Remove only a file-leading UTF-8 BOM for the check; never rewrite the file.
file_has_non_whitespace_content() {
LC_ALL=C sed $'1s/^\357\273\277//' "$1" |
LC_ALL=C grep -aEv $'^([[:space:]]|\302[\205\240]|\341\232\200|\342\200[\200-\212\250\251\257]|\342\201\237|\343\200\200)*$' >/dev/null
}

# Find repository root by searching upward for .specify directory
# This is the primary marker for spec-kit projects
find_specify_root() {
Expand Down
2 changes: 1 addition & 1 deletion scripts/bash/setup-plan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ else
template_file=$(mktemp)
trap 'rm -f "$template_file"' EXIT
if resolve_template_content "plan-template" "$REPO_ROOT" > "$template_file"; then
if [[ ! -s "$template_file" ]]; then
if ! file_has_non_whitespace_content "$template_file"; then
echo "ERROR: Plan template is empty; no new plan.md was created." >&2
echo "Restore a valid plan template and rerun plan setup." >&2
exit 1
Expand Down
10 changes: 9 additions & 1 deletion scripts/powershell/check-prerequisites.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,15 @@ if (-not (Test-Path -LiteralPath $paths.IMPL_PLAN -PathType Leaf)) {
exit 1
}

if ((Get-Item -LiteralPath $paths.IMPL_PLAN).Length -eq 0) {
try {
$planContent = [System.IO.File]::ReadAllText($paths.IMPL_PLAN)
} catch {
[Console]::Error.WriteLine("ERROR: plan.md is not readable in $($paths.FEATURE_DIR)")
[Console]::Error.WriteLine("Check its file permissions and rerun the prerequisite check.")
exit 1
}

if ([string]::IsNullOrWhiteSpace($planContent)) {
[Console]::Error.WriteLine("ERROR: plan.md is empty in $($paths.FEATURE_DIR)")
$planCommand = Format-SpecKitCommand -CommandName 'plan' -RepoRoot $paths.REPO_ROOT
[Console]::Error.WriteLine(
Expand Down
2 changes: 1 addition & 1 deletion scripts/powershell/setup-plan.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ if (Test-Path -LiteralPath $paths.IMPL_PLAN -PathType Leaf) {
} else {
$content = Resolve-TemplateContent -TemplateName 'plan-template' -RepoRoot $paths.REPO_ROOT
if ($null -ne $content) {
if ($content.Length -eq 0) {
if ([string]::IsNullOrWhiteSpace($content)) {
[Console]::Error.WriteLine(
"ERROR: Plan template is empty; no new plan.md was created."
)
Expand Down
14 changes: 13 additions & 1 deletion scripts/python/check_prerequisites.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
TemplateResolutionError,
format_speckit_command,
get_feature_paths,
has_non_whitespace_content,
resolve_template_content,
)
except ImportError: # pragma: no cover - direct execution from unusual cwd
Expand All @@ -23,6 +24,7 @@
TemplateResolutionError,
format_speckit_command,
get_feature_paths,
has_non_whitespace_content,
resolve_template_content,
)

Expand Down Expand Up @@ -236,7 +238,17 @@ def main(argv: list[str] | None = None) -> int:
)
return 1

if paths.impl_plan.stat().st_size == 0:
try:
plan_content = paths.impl_plan.read_text(encoding="utf-8", errors="replace")
except OSError:
print(f"ERROR: plan.md is not readable in {paths.feature_dir}", file=sys.stderr)
print(
"Check its file permissions and rerun the prerequisite check.",
file=sys.stderr,
)
return 1

if not has_non_whitespace_content(plan_content):
print(f"ERROR: plan.md is empty in {paths.feature_dir}", file=sys.stderr)
print(
"Populate it, or remove the empty file and rerun "
Expand Down
9 changes: 9 additions & 0 deletions scripts/python/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
from pathlib import Path


def has_non_whitespace_content(content: str) -> bool:
"""Check Unicode White_Space like PowerShell, ignoring one leading BOM."""
whitespace = (
"\t\n\v\f\r \u0085\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005"
"\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000"
)
return bool(content.removeprefix("\ufeff").strip(whitespace))


def _trim_trailing_separators(value: Path) -> str:
text = str(value)
while len(text) > 1 and text.endswith((os.sep, "/")):
Expand Down
4 changes: 3 additions & 1 deletion scripts/python/setup_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
from common import (
TemplateResolutionError,
get_feature_paths,
has_non_whitespace_content,
resolve_template_content,
)
except ImportError: # pragma: no cover - direct execution from unusual cwd
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import (
TemplateResolutionError,
get_feature_paths,
has_non_whitespace_content,
resolve_template_content,
)

Expand Down Expand Up @@ -70,7 +72,7 @@ def main(argv: list[str] | None = None) -> int:
print(f"Error: {exc}", file=sys.stderr)
return 1
if template_content is not None:
if template_content == "":
if not has_non_whitespace_content(template_content):
print(
"ERROR: Plan template is empty; no new plan.md was created.",
file=sys.stderr,
Expand Down
Loading