Remove template version info from CLI, fix Claude user-invocable, cleanup dead code (#2081)

* Remove Template Version and Released from version output

Templates are now bundled with the CLI, so showing them as separate
artifacts with their own version and release date is no longer accurate.
This also removes the GitHub API call that fetched the latest release,
making the version command faster and eliminating a network dependency.

* Remove unused datetime import

* fix: inject user-invocable: true into Claude skill frontmatter

The SkillsIntegration.setup() builds frontmatter manually without
user-invocable. Add post-processing injection in ClaudeIntegration.setup(),
matching the existing pattern for disable-model-invocation.

* refactor: address review feedback

- Factor _inject_user_invocable and _inject_disable_model_invocation
  into a shared _inject_frontmatter_flag(key, value) helper
- Remove unused httpx, ssl, truststore imports and globals
- Remove unused _github_token and _github_auth_headers helpers
- Update setup() docstring to mention user-invocable

* chore: remove httpx and truststore from dependencies

Both are no longer used after removing the GitHub API call from the
version command. Removes from PEP 723 script header and pyproject.toml.

* fix: match EOL detection style in _inject_frontmatter_flag

Handle \r\n, \n, and no-newline cases consistently with
inject_argument_hint's pattern.
This commit is contained in:
Manfred Riem
2026-04-03 10:48:39 -05:00
committed by GitHub
parent 535ddbe0d2
commit e1ab4f0486
3 changed files with 15 additions and 60 deletions

View File

@@ -6,7 +6,6 @@
# "rich",
# "platformdirs",
# "readchar",
# "httpx",
# "json5",
# ]
# ///
@@ -39,7 +38,6 @@ from pathlib import Path
from typing import Any, Optional, Tuple
import typer
import httpx
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
@@ -51,21 +49,6 @@ from typer.core import TyperGroup
# For cross-platform keyboard input
import readchar
import ssl
import truststore
from datetime import datetime
ssl_context = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
client = httpx.Client(verify=ssl_context)
def _github_token(cli_token: str | None = None) -> str | None:
"""Return sanitized GitHub token (cli arg takes precedence) or None."""
return ((cli_token or os.getenv("GH_TOKEN") or os.getenv("GITHUB_TOKEN") or "").strip()) or None
def _github_auth_headers(cli_token: str | None = None) -> dict:
"""Return Authorization header dict only when a non-empty token exists."""
token = _github_token(cli_token)
return {"Authorization": f"Bearer {token}"} if token else {}
def _build_agent_config() -> dict[str, dict[str, Any]]:
"""Derive AGENT_CONFIG from INTEGRATION_REGISTRY."""
@@ -1429,45 +1412,11 @@ def version():
except Exception:
pass
# Fetch latest template release version
repo_owner = "github"
repo_name = "spec-kit"
api_url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases/latest"
template_version = "unknown"
release_date = "unknown"
try:
response = client.get(
api_url,
timeout=10,
follow_redirects=True,
headers=_github_auth_headers(),
)
if response.status_code == 200:
release_data = response.json()
template_version = release_data.get("tag_name", "unknown")
# Remove 'v' prefix if present
if template_version.startswith("v"):
template_version = template_version[1:]
release_date = release_data.get("published_at", "unknown")
if release_date != "unknown":
# Format the date nicely
try:
dt = datetime.fromisoformat(release_date.replace('Z', '+00:00'))
release_date = dt.strftime("%Y-%m-%d")
except Exception:
pass
except Exception:
pass
info_table = Table(show_header=False, box=None, padding=(0, 2))
info_table.add_column("Key", style="cyan", justify="right")
info_table.add_column("Value", style="white")
info_table.add_row("CLI Version", cli_version)
info_table.add_row("Template Version", template_version)
info_table.add_row("Released", release_date)
info_table.add_row("", "")
info_table.add_row("Python", platform.python_version())
info_table.add_row("Platform", platform.system())

View File

@@ -112,8 +112,8 @@ class ClaudeIntegration(SkillsIntegration):
)
@staticmethod
def _inject_disable_model_invocation(content: str) -> str:
"""Insert ``disable-model-invocation: true`` before the closing ``---``."""
def _inject_frontmatter_flag(content: str, key: str, value: str = "true") -> str:
"""Insert ``key: value`` before the closing ``---`` if not already present."""
lines = content.splitlines(keepends=True)
# Pre-scan: bail out if already present in frontmatter
@@ -125,7 +125,7 @@ class ClaudeIntegration(SkillsIntegration):
if dash_count == 2:
break
continue
if dash_count == 1 and stripped.startswith("disable-model-invocation:"):
if dash_count == 1 and stripped.startswith(f"{key}:"):
return content
# Inject before the closing --- of frontmatter
@@ -137,8 +137,13 @@ class ClaudeIntegration(SkillsIntegration):
if stripped == "---":
dash_count += 1
if dash_count == 2 and not injected:
eol = "\r\n" if line.endswith("\r\n") else "\n"
out.append(f"disable-model-invocation: true{eol}")
if line.endswith("\r\n"):
eol = "\r\n"
elif line.endswith("\n"):
eol = "\n"
else:
eol = ""
out.append(f"{key}: {value}{eol}")
injected = True
out.append(line)
return "".join(out)
@@ -150,7 +155,7 @@ class ClaudeIntegration(SkillsIntegration):
parsed_options: dict[str, Any] | None = None,
**opts: Any,
) -> list[Path]:
"""Install Claude skills, then inject argument-hint and disable-model-invocation."""
"""Install Claude skills, then inject user-invocable, disable-model-invocation, and argument-hint."""
created = super().setup(project_root, manifest, parsed_options, **opts)
# Post-process generated skill files
@@ -168,8 +173,11 @@ class ClaudeIntegration(SkillsIntegration):
content_bytes = path.read_bytes()
content = content_bytes.decode("utf-8")
# Inject user-invocable: true (Claude skills are accessible via /command)
updated = self._inject_frontmatter_flag(content, "user-invocable")
# Inject disable-model-invocation: true (Claude skills run only when invoked)
updated = self._inject_disable_model_invocation(content)
updated = self._inject_frontmatter_flag(updated, "disable-model-invocation")
# Inject argument-hint if available for this skill
skill_dir_name = path.parent.name # e.g. "speckit-plan"