Skip to content

Commit a273957

Browse files
Revert "Add specify doctor command for project health diagnostics"
This reverts commit d5bd932.
1 parent 206e0ba commit a273957

1 file changed

Lines changed: 0 additions & 303 deletions

File tree

src/specify_cli/__init__.py

Lines changed: 0 additions & 303 deletions
Original file line numberDiff line numberDiff line change
@@ -1695,309 +1695,6 @@ def check():
16951695
if not any(agent_results.values()):
16961696
console.print("[dim]Tip: Install an AI assistant for the best experience[/dim]")
16971697

1698-
1699-
@app.command()
1700-
def doctor():
1701-
"""Diagnose a Specify project and report health issues."""
1702-
show_banner()
1703-
console.print("[bold]Running project diagnostics...[/bold]\n")
1704-
1705-
project_root = Path.cwd()
1706-
issues = [] # (severity, message) tuples: "error", "warning", "info"
1707-
1708-
# ── 1. Project structure ──────────────────────────────────────────
1709-
tracker = StepTracker("Project Structure")
1710-
1711-
specify_dir = project_root / ".specify"
1712-
tracker.add("specify_dir", ".specify/ directory")
1713-
if specify_dir.is_dir():
1714-
tracker.complete("specify_dir", "found")
1715-
else:
1716-
tracker.error("specify_dir", "missing")
1717-
issues.append(("error", "No .specify/ directory — run 'specify init --here' to initialize"))
1718-
1719-
specs_dir = project_root / "specs"
1720-
tracker.add("specs_dir", "specs/ directory")
1721-
if specs_dir.is_dir():
1722-
tracker.complete("specs_dir", "found")
1723-
else:
1724-
tracker.skip("specs_dir", "not created yet")
1725-
issues.append(("info", "No specs/ directory — created when you run /speckit.specify"))
1726-
1727-
scripts_dir = project_root / "scripts"
1728-
tracker.add("scripts_dir", "scripts/ directory")
1729-
if scripts_dir.is_dir():
1730-
tracker.complete("scripts_dir", "found")
1731-
else:
1732-
tracker.error("scripts_dir", "missing")
1733-
issues.append(("error", "No scripts/ directory — project may not be initialized"))
1734-
1735-
templates_dir = project_root / "templates"
1736-
tracker.add("templates_dir", "templates/ directory")
1737-
if templates_dir.is_dir():
1738-
tracker.complete("templates_dir", "found")
1739-
else:
1740-
tracker.error("templates_dir", "missing")
1741-
issues.append(("error", "No templates/ directory — project may not be initialized"))
1742-
1743-
memory_dir = project_root / "memory"
1744-
tracker.add("memory_dir", "memory/ directory")
1745-
if memory_dir.is_dir():
1746-
tracker.complete("memory_dir", "found")
1747-
constitution = memory_dir / "constitution.md"
1748-
tracker.add("constitution", "memory/constitution.md")
1749-
if constitution.is_file():
1750-
tracker.complete("constitution", "found")
1751-
else:
1752-
tracker.error("constitution", "missing")
1753-
issues.append(("warning", "No constitution.md in memory/ — project governance rules are missing"))
1754-
else:
1755-
tracker.error("memory_dir", "missing")
1756-
issues.append(("error", "No memory/ directory — project may not be initialized"))
1757-
1758-
console.print(tracker.render())
1759-
console.print()
1760-
1761-
# ── 2. AI agent detection ─────────────────────────────────────────
1762-
agent_tracker = StepTracker("AI Agent Configuration")
1763-
detected_agents = []
1764-
1765-
for agent_key, agent_config in AGENT_CONFIG.items():
1766-
if agent_key == "generic":
1767-
continue
1768-
agent_folder = agent_config["folder"]
1769-
if agent_folder and (project_root / agent_folder).is_dir():
1770-
detected_agents.append(agent_key)
1771-
agent_tracker.add(agent_key, agent_config["name"])
1772-
commands_dir = project_root / agent_folder / agent_config["commands_subdir"]
1773-
if commands_dir.is_dir() and any(commands_dir.iterdir()):
1774-
agent_tracker.complete(agent_key, f"commands in {agent_folder}{agent_config['commands_subdir']}/")
1775-
else:
1776-
agent_tracker.error(agent_key, f"folder exists but no commands in {agent_config['commands_subdir']}/")
1777-
issues.append(("warning", f"Agent '{agent_config['name']}' folder exists but commands directory is empty"))
1778-
1779-
if not detected_agents:
1780-
agent_tracker.add("none", "No AI agent configured")
1781-
agent_tracker.skip("none", "run 'specify init --here --ai <agent>' to set up")
1782-
issues.append(("info", "No AI agent folder detected — this is fine if you use IDE-based agents"))
1783-
1784-
console.print(agent_tracker.render())
1785-
console.print()
1786-
1787-
# ── 3. Feature specs ──────────────────────────────────────────────
1788-
feature_tracker = StepTracker("Feature Specifications")
1789-
1790-
if specs_dir.is_dir():
1791-
feature_dirs = sorted(
1792-
[d for d in specs_dir.iterdir() if d.is_dir()],
1793-
key=lambda d: d.name,
1794-
)
1795-
if not feature_dirs:
1796-
feature_tracker.add("empty", "No feature directories")
1797-
feature_tracker.skip("empty", "run /speckit.specify to create one")
1798-
else:
1799-
for fdir in feature_dirs:
1800-
key = fdir.name
1801-
feature_tracker.add(key, key)
1802-
1803-
spec_file = fdir / "spec.md"
1804-
plan_file = fdir / "plan.md"
1805-
tasks_file = fdir / "tasks.md"
1806-
1807-
artifacts = []
1808-
missing = []
1809-
for name, path in [("spec", spec_file), ("plan", plan_file), ("tasks", tasks_file)]:
1810-
if path.is_file():
1811-
artifacts.append(name)
1812-
else:
1813-
missing.append(name)
1814-
1815-
if missing:
1816-
detail = f"{', '.join(artifacts)} present; missing {', '.join(missing)}"
1817-
if "spec" in missing:
1818-
feature_tracker.error(key, detail)
1819-
issues.append(("error", f"Feature '{key}' is missing spec.md"))
1820-
else:
1821-
feature_tracker.complete(key, detail)
1822-
for m in missing:
1823-
issues.append(("info", f"Feature '{key}' has no {m}.md — run /speckit.{m} to generate"))
1824-
else:
1825-
feature_tracker.complete(key, "spec, plan, tasks all present")
1826-
else:
1827-
feature_tracker.add("none", "No specs/ directory")
1828-
feature_tracker.skip("none", "not applicable")
1829-
1830-
console.print(feature_tracker.render())
1831-
console.print()
1832-
1833-
# ── 4. Scripts health ─────────────────────────────────────────────
1834-
script_tracker = StepTracker("Scripts")
1835-
1836-
bash_dir = project_root / "scripts" / "bash"
1837-
ps_dir = project_root / "scripts" / "powershell"
1838-
1839-
expected_scripts = ["common", "check-prerequisites", "create-new-feature", "setup-plan", "update-agent-context"]
1840-
1841-
if bash_dir.is_dir():
1842-
for name in expected_scripts:
1843-
key = f"sh_{name}"
1844-
script_path = bash_dir / f"{name}.sh"
1845-
script_tracker.add(key, f"bash/{name}.sh")
1846-
if script_path.is_file():
1847-
if os.name != "nt" and not os.access(script_path, os.X_OK):
1848-
script_tracker.error(key, "not executable")
1849-
issues.append(("warning", f"scripts/bash/{name}.sh is not executable — run chmod +x"))
1850-
else:
1851-
script_tracker.complete(key, "ok")
1852-
else:
1853-
script_tracker.error(key, "missing")
1854-
issues.append(("error", f"scripts/bash/{name}.sh is missing"))
1855-
else:
1856-
script_tracker.add("no_bash", "scripts/bash/")
1857-
script_tracker.skip("no_bash", "not found")
1858-
1859-
if ps_dir.is_dir():
1860-
for name in expected_scripts:
1861-
key = f"ps_{name}"
1862-
script_path = ps_dir / f"{name}.ps1"
1863-
script_tracker.add(key, f"powershell/{name}.ps1")
1864-
if script_path.is_file():
1865-
script_tracker.complete(key, "ok")
1866-
else:
1867-
script_tracker.error(key, "missing")
1868-
issues.append(("error", f"scripts/powershell/{name}.ps1 is missing"))
1869-
else:
1870-
script_tracker.add("no_ps", "scripts/powershell/")
1871-
script_tracker.skip("no_ps", "not found")
1872-
1873-
console.print(script_tracker.render())
1874-
console.print()
1875-
1876-
# ── 5. Extensions health ──────────────────────────────────────────
1877-
ext_tracker = StepTracker("Extensions")
1878-
1879-
extensions_yml = specify_dir / "extensions.yml" if specify_dir.is_dir() else None
1880-
registry_json = specify_dir / "extensions" / "registry.json" if specify_dir.is_dir() else None
1881-
1882-
if extensions_yml and extensions_yml.is_file():
1883-
ext_tracker.add("config", "extensions.yml")
1884-
try:
1885-
with open(extensions_yml) as f:
1886-
ext_config = yaml.safe_load(f)
1887-
if ext_config and isinstance(ext_config, dict):
1888-
ext_tracker.complete("config", "valid YAML")
1889-
hooks = ext_config.get("hooks", {})
1890-
if hooks:
1891-
hook_count = sum(len(v) if isinstance(v, list) else 0 for v in hooks.values())
1892-
ext_tracker.add("hooks", "Hook registrations")
1893-
ext_tracker.complete("hooks", f"{hook_count} hook(s) registered")
1894-
else:
1895-
ext_tracker.complete("config", "empty or no hooks")
1896-
except Exception as e:
1897-
ext_tracker.error("config", f"invalid YAML: {e}")
1898-
issues.append(("warning", f"extensions.yml has invalid YAML: {e}"))
1899-
else:
1900-
ext_tracker.add("config", "extensions.yml")
1901-
ext_tracker.skip("config", "no extensions configured")
1902-
1903-
if registry_json and registry_json.is_file():
1904-
ext_tracker.add("registry", "Extension registry")
1905-
try:
1906-
with open(registry_json) as f:
1907-
registry = json.load(f)
1908-
installed = [k for k, v in registry.items() if isinstance(v, dict)]
1909-
enabled = [k for k, v in registry.items() if isinstance(v, dict) and v.get("enabled", True)]
1910-
ext_tracker.complete("registry", f"{len(installed)} installed, {len(enabled)} enabled")
1911-
except Exception as e:
1912-
ext_tracker.error("registry", f"corrupt: {e}")
1913-
issues.append(("error", f"Extension registry is corrupt: {e}"))
1914-
else:
1915-
ext_tracker.add("registry", "Extension registry")
1916-
ext_tracker.skip("registry", "no extensions installed")
1917-
1918-
console.print(ext_tracker.render())
1919-
console.print()
1920-
1921-
# ── 6. Git status ─────────────────────────────────────────────────
1922-
git_tracker = StepTracker("Git Repository")
1923-
git_tracker.add("git", "Git repository")
1924-
1925-
git_ok = shutil.which("git") is not None
1926-
in_git_repo = False
1927-
if git_ok:
1928-
try:
1929-
result = subprocess.run(
1930-
["git", "rev-parse", "--is-inside-work-tree"],
1931-
capture_output=True, text=True, cwd=str(project_root)
1932-
)
1933-
in_git_repo = result.returncode == 0
1934-
except Exception:
1935-
pass
1936-
1937-
if in_git_repo:
1938-
git_tracker.complete("git", "inside git repository")
1939-
try:
1940-
branch = subprocess.run(
1941-
["git", "rev-parse", "--abbrev-ref", "HEAD"],
1942-
capture_output=True, text=True, cwd=str(project_root)
1943-
).stdout.strip()
1944-
git_tracker.add("branch", "Current branch")
1945-
git_tracker.complete("branch", branch)
1946-
except Exception:
1947-
pass
1948-
elif git_ok:
1949-
git_tracker.skip("git", "not a git repository")
1950-
issues.append(("info", "Not inside a git repository — git features like branching won't work"))
1951-
else:
1952-
git_tracker.skip("git", "git not installed")
1953-
issues.append(("info", "Git is not installed — branching and version control unavailable"))
1954-
1955-
console.print(git_tracker.render())
1956-
console.print()
1957-
1958-
# ── Summary ───────────────────────────────────────────────────────
1959-
errors = [msg for sev, msg in issues if sev == "error"]
1960-
warnings = [msg for sev, msg in issues if sev == "warning"]
1961-
infos = [msg for sev, msg in issues if sev == "info"]
1962-
1963-
if not issues:
1964-
console.print(Panel(
1965-
"[bold green]All checks passed — project looks healthy![/bold green]",
1966-
border_style="green",
1967-
padding=(1, 2),
1968-
))
1969-
else:
1970-
summary_lines = []
1971-
1972-
if errors:
1973-
summary_lines.append(f"[bold red]{len(errors)} error(s)[/bold red]")
1974-
for msg in errors:
1975-
summary_lines.append(f" [red]●[/red] {msg}")
1976-
summary_lines.append("")
1977-
1978-
if warnings:
1979-
summary_lines.append(f"[bold yellow]{len(warnings)} warning(s)[/bold yellow]")
1980-
for msg in warnings:
1981-
summary_lines.append(f" [yellow]●[/yellow] {msg}")
1982-
summary_lines.append("")
1983-
1984-
if infos:
1985-
summary_lines.append(f"[bold blue]{len(infos)} note(s)[/bold blue]")
1986-
for msg in infos:
1987-
summary_lines.append(f" [blue]○[/blue] {msg}")
1988-
1989-
border = "red" if errors else "yellow" if warnings else "blue"
1990-
console.print(Panel(
1991-
"\n".join(summary_lines),
1992-
title="Diagnostic Summary",
1993-
border_style=border,
1994-
padding=(1, 2),
1995-
))
1996-
1997-
if errors:
1998-
raise typer.Exit(1)
1999-
2000-
20011698
@app.command()
20021699
def version():
20031700
"""Display version and system information."""

0 commit comments

Comments
 (0)