mirror of
https://github.com/github/spec-kit.git
synced 2026-07-03 12:28:06 +08:00
refactor(presets): unify constitution template materialization
Address latest Copilot feedback on the constitution seeding path: - moved resolver/layer I/O behind the existing-memory fast path in init - corrected tracker output for composed materialization - deduplicated materialization logic shared by init and preset install seeding into presets._materialize_constitution_template() Behavior is unchanged for replace strategies (copy verbatim) and remains composed for prepend/append/wrap via resolve_content(). Assisted-by: GitHub Copilot (model: GPT-5.3-Codex, autonomous) Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -41,11 +40,9 @@ def ensure_constitution_from_template(
|
||||
constitution) can seed the memory file. When nothing overrides it, the
|
||||
resolver falls through to the core template.
|
||||
"""
|
||||
from ..presets import PresetResolver
|
||||
from ..presets import _materialize_constitution_template
|
||||
|
||||
memory_constitution = project_path / ".specify" / "memory" / "constitution.md"
|
||||
resolver = PresetResolver(project_path)
|
||||
layers = resolver.collect_all_layers("constitution-template", "template")
|
||||
|
||||
if memory_constitution.exists():
|
||||
if tracker:
|
||||
@@ -53,27 +50,21 @@ def ensure_constitution_from_template(
|
||||
tracker.skip("constitution", "existing file preserved")
|
||||
return
|
||||
|
||||
if not layers:
|
||||
if tracker:
|
||||
tracker.add("constitution", "Constitution setup")
|
||||
tracker.error("constitution", "template not found")
|
||||
return
|
||||
|
||||
try:
|
||||
memory_constitution.parent.mkdir(parents=True, exist_ok=True)
|
||||
top_layer = layers[0]
|
||||
if top_layer["strategy"] == "replace":
|
||||
shutil.copy2(top_layer["path"], memory_constitution)
|
||||
else:
|
||||
composed_content = resolver.resolve_content(
|
||||
"constitution-template", "template"
|
||||
)
|
||||
if composed_content is None:
|
||||
raise FileNotFoundError("constitution template not found")
|
||||
memory_constitution.write_text(composed_content, encoding="utf-8")
|
||||
materialization = _materialize_constitution_template(
|
||||
project_path, memory_constitution
|
||||
)
|
||||
if materialization is None:
|
||||
if tracker:
|
||||
tracker.add("constitution", "Constitution setup")
|
||||
tracker.error("constitution", "template not found")
|
||||
return
|
||||
if tracker:
|
||||
tracker.add("constitution", "Constitution setup")
|
||||
tracker.complete("constitution", "copied from template")
|
||||
if materialization == "copied":
|
||||
tracker.complete("constitution", "copied from template")
|
||||
else:
|
||||
tracker.complete("constitution", "composed from template")
|
||||
else:
|
||||
console.print("[cyan]Initialized constitution from template[/cyan]")
|
||||
except Exception as e:
|
||||
|
||||
@@ -45,6 +45,35 @@ def _constitution_is_placeholder(content: str) -> bool:
|
||||
return any(token in content for token in _CONSTITUTION_PLACEHOLDER_TOKENS)
|
||||
|
||||
|
||||
def _materialize_constitution_template(
|
||||
project_root: Path,
|
||||
memory_constitution: Path,
|
||||
) -> str | None:
|
||||
"""Materialize constitution-template content into memory/constitution.md.
|
||||
|
||||
Returns:
|
||||
"copied" when the winning layer is ``replace`` and the source file is
|
||||
copied verbatim; "composed" when a composing strategy is materialized
|
||||
via ``resolve_content``; ``None`` when no constitution template resolves.
|
||||
"""
|
||||
resolver = PresetResolver(project_root)
|
||||
layers = resolver.collect_all_layers("constitution-template", "template")
|
||||
if not layers:
|
||||
return None
|
||||
|
||||
memory_constitution.parent.mkdir(parents=True, exist_ok=True)
|
||||
top_layer = layers[0]
|
||||
if top_layer["strategy"] == "replace":
|
||||
shutil.copy2(top_layer["path"], memory_constitution)
|
||||
return "copied"
|
||||
|
||||
composed_content = resolver.resolve_content("constitution-template", "template")
|
||||
if composed_content is None:
|
||||
return None
|
||||
memory_constitution.write_text(composed_content, encoding="utf-8")
|
||||
return "composed"
|
||||
|
||||
|
||||
def _substitute_core_template(
|
||||
body: str,
|
||||
cmd_name: str,
|
||||
@@ -1664,23 +1693,12 @@ class PresetManager:
|
||||
# Legitimately authored constitution; leave it untouched.
|
||||
return
|
||||
|
||||
resolver = PresetResolver(self.project_root)
|
||||
layers = resolver.collect_all_layers("constitution-template", "template")
|
||||
if not layers:
|
||||
return
|
||||
|
||||
try:
|
||||
memory_constitution.parent.mkdir(parents=True, exist_ok=True)
|
||||
top_layer = layers[0]
|
||||
if top_layer["strategy"] == "replace":
|
||||
shutil.copy2(top_layer["path"], memory_constitution)
|
||||
else:
|
||||
composed_content = resolver.resolve_content(
|
||||
"constitution-template", "template"
|
||||
)
|
||||
if composed_content is None:
|
||||
return
|
||||
memory_constitution.write_text(composed_content, encoding="utf-8")
|
||||
result = _materialize_constitution_template(
|
||||
self.project_root, memory_constitution
|
||||
)
|
||||
if result is None:
|
||||
return
|
||||
except OSError as exc:
|
||||
import warnings
|
||||
|
||||
|
||||
Reference in New Issue
Block a user