Compare commits

..

4 Commits

Author SHA1 Message Date
Den Delimarsky
fa3171ca6e Merge pull request #214 from github/update-cli
Saner approach to scripts
2025-09-12 14:06:34 -07:00
Den Delimarsky 🌺
117ec67e47 Saner approach to scripts 2025-09-12 14:05:55 -07:00
Den Delimarsky
5bd7027526 Merge pull request #213 from github/update-cli
Update packaging
2025-09-12 13:45:55 -07:00
Den Delimarsky 🌺
ec7d87f121 Update packaging 2025-09-12 13:45:28 -07:00
4 changed files with 44 additions and 47 deletions

View File

@@ -47,43 +47,36 @@ generate_commands() {
mkdir -p "$output_dir" mkdir -p "$output_dir"
for template in templates/commands/*.md; do for template in templates/commands/*.md; do
[[ -f "$template" ]] || continue [[ -f "$template" ]] || continue
local name description raw_body variant_line injected body file_norm delim_count local name description script_command body
name=$(basename "$template" .md) name=$(basename "$template" .md)
# Normalize line endings first (remove CR) for consistent regex matching
file_norm=$(tr -d '\r' < "$template") # Normalize line endings
# Extract description from frontmatter file_content=$(tr -d '\r' < "$template")
description=$(printf '%s\n' "$file_norm" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
# Count YAML frontmatter delimiter lines # Extract description and script command from YAML frontmatter
delim_count=$(printf '%s\n' "$file_norm" | grep -c '^---$' || true) description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
if [[ $delim_count -ge 2 ]]; then script_command=$(printf '%s\n' "$file_content" | awk -v sv="$script_variant" '/^[[:space:]]*'"$script_variant"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, ""); print; exit}')
# Grab everything after the second --- line
raw_body=$(printf '%s\n' "$file_norm" | awk '/^---$/ {if(++c==2){next}; if(c>=2){print}}') if [[ -z $script_command ]]; then
else echo "Warning: no script command found for $script_variant in $template" >&2
# Fallback: no proper frontmatter detected; use entire file content (still allowing variant parsing) script_command="(Missing script command for $script_variant)"
raw_body=$file_norm
fi fi
# If somehow still empty, fallback once more to whole normalized file
if [[ -z ${raw_body// /} ]]; then # Replace {SCRIPT} placeholder with the script command
echo "Warning: body extraction empty for $template; using full file" >&2 body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g")
raw_body=$file_norm
fi # Remove the scripts: section from frontmatter while preserving YAML structure
# Find single-line variant comment matching the variant: <!-- VARIANT:sh ... --> or <!-- VARIANT:ps ... --> body=$(printf '%s\n' "$body" | awk '
variant_line=$(printf '%s\n' "$raw_body" | awk -v sv="$script_variant" '/<!--[[:space:]]+VARIANT:'sv'/ {match($0, /VARIANT:'"sv"'[[:space:]]+(.*)-->/, m); if (m[1]!="") {print m[1]; exit}}') /^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next }
if [[ -z $variant_line ]]; then in_frontmatter && /^scripts:$/ { skip_scripts=1; next }
echo "Warning: no variant line found for $script_variant in $template" >&2 in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 }
variant_line="(Missing variant command for $script_variant)" in_frontmatter && skip_scripts && /^[[:space:]]/ { next }
fi { print }
# Replace the token VARIANT-INJECT with the selected variant line ')
injected=$(printf '%s\n' "$raw_body" | sed "s/VARIANT-INJECT/${variant_line//\//\/}/")
# Remove all single-line variant comments # Apply other substitutions
injected=$(printf '%s\n' "$injected" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d') body=$(printf '%s\n' "$body" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths)
# Guard: if after stripping variant lines and injection the body became empty, restore original (minus variant comments) to avoid empty prompt files
if [[ -z ${injected// /} ]]; then
echo "Warning: resulting injected body empty for $template; writing unmodified body" >&2
injected=$raw_body
fi
# Apply arg substitution and path rewrite
body=$(printf '%s\n' "$injected" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths)
case $ext in case $ext in
toml) toml)
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/$name.$ext" ;; { echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/$name.$ext" ;;
@@ -104,12 +97,13 @@ build_variant() {
# Inject variant into plan-template.md within .specify/templates if present # Inject variant into plan-template.md within .specify/templates if present
local plan_tpl="$base_dir/.specify/templates/plan-template.md" local plan_tpl="$base_dir/.specify/templates/plan-template.md"
if [[ -f "$plan_tpl" ]]; then if [[ -f "$plan_tpl" ]]; then
variant_line=$(awk -v sv="$script" '/<!--[[:space:]]*VARIANT:'"$script"'/ {match($0, /VARIANT:'"$script"'[[:space:]]+(.*)-->/, m); if(m[1]!=""){print m[1]; exit}}' "$plan_tpl") plan_norm=$(tr -d '\r' < "$plan_tpl")
variant_line=$(printf '%s\n' "$plan_norm" | grep -E "<!--[[:space:]]*VARIANT:$script" | head -1 | sed -E "s/.*VARIANT:$script[[:space:]]+//; s/-->.*//; s/^[[:space:]]+//; s/[[:space:]]+$//")
if [[ -n $variant_line ]]; then if [[ -n $variant_line ]]; then
tmp_file=$(mktemp) tmp_file=$(mktemp)
sed "s/VARIANT-INJECT/${variant_line//\//\/}/" "$plan_tpl" | sed "/__AGENT__/s//${agent}/g" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d' > "$tmp_file" && mv "$tmp_file" "$plan_tpl" sed "s|VARIANT-INJECT|${variant_line}|" "$plan_tpl" | tr -d '\r' | sed "s|__AGENT__|${agent}|g" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d' > "$tmp_file" && mv "$tmp_file" "$plan_tpl"
else else
echo "Warning: no plan-template variant for $script" >&2 echo "Warning: no plan-template variant for $script (pattern not matched)" >&2
fi fi
fi fi
case $agent in case $agent in

View File

@@ -1,12 +1,13 @@
--- ---
description: Execute the implementation planning workflow using the plan template to generate design artifacts. description: Execute the implementation planning workflow using the plan template to generate design artifacts.
scripts:
sh: scripts/bash/setup-plan.sh --json
ps: scripts/powershell/setup-plan.ps1 -Json
--- ---
<!-- VARIANT:sh 1. Run `scripts/bash/setup-plan.sh --json` from the repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. All future file paths must be absolute. -->
<!-- VARIANT:ps 1. Run `scripts/powershell/setup-plan.ps1 -Json` from the repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. All future file paths must be absolute. -->
Given the implementation details provided as an argument, do this: Given the implementation details provided as an argument, do this:
1. VARIANT-INJECT 1. Run `{SCRIPT}` from the repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. All future file paths must be absolute.
2. Read and analyze the feature specification to understand: 2. Read and analyze the feature specification to understand:
- The feature requirements and user stories - The feature requirements and user stories
- Functional and non-functional requirements - Functional and non-functional requirements

View File

@@ -1,12 +1,13 @@
--- ---
description: Create or update the feature specification from a natural language feature description. description: Create or update the feature specification from a natural language feature description.
scripts:
sh: scripts/bash/create-new-feature.sh --json "{ARGS}"
ps: scripts/powershell/create-new-feature.ps1 -Json "{ARGS}"
--- ---
<!-- VARIANT:sh 1. Run the script `scripts/bash/create-new-feature.sh --json "{ARGS}"` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute. -->
<!-- VARIANT:ps 1. Run the script `scripts/powershell/create-new-feature.ps1 -Json "{ARGS}"` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute. -->
Given the feature description provided as an argument, do this: Given the feature description provided as an argument, do this:
1. VARIANT-INJECT 1. Run the script `{SCRIPT}` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
2. Load `templates/spec-template.md` to understand required sections. 2. Load `templates/spec-template.md` to understand required sections.
3. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings. 3. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings.
4. Report completion with branch name, spec file path, and readiness for the next phase. 4. Report completion with branch name, spec file path, and readiness for the next phase.

View File

@@ -1,12 +1,13 @@
--- ---
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts. description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
scripts:
sh: scripts/bash/check-task-prerequisites.sh --json
ps: scripts/powershell/check-task-prerequisites.ps1 -Json
--- ---
<!-- VARIANT:sh 1. Run `scripts/bash/check-task-prerequisites.sh --json` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. -->
<!-- VARIANT:ps 1. Run `scripts/powershell/check-task-prerequisites.ps1 -Json` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. -->
Given the context provided as an argument, do this: Given the context provided as an argument, do this:
1. VARIANT-INJECT 1. Run `{SCRIPT}` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute.
2. Load and analyze available design documents: 2. Load and analyze available design documents:
- Always read plan.md for tech stack and libraries - Always read plan.md for tech stack and libraries
- IF EXISTS: Read data-model.md for entities - IF EXISTS: Read data-model.md for entities