2929 DEFAULT_SKILLS_DIR ,
3030 SKILL_DESCRIPTIONS ,
3131 AGENT_CONFIG ,
32+ StepTracker ,
3233 app ,
34+ ensure_claude_md ,
3335)
3436
3537
@@ -693,30 +695,39 @@ class TestNewProjectCommandSkip:
693695 download_and_extract_template patched to create local fixtures.
694696 """
695697
696- @pytest .mark .skipif (
697- shutil .which ("bash" ) is None or shutil .which ("zip" ) is None ,
698- reason = "offline scaffolding requires bash + zip" ,
699- )
700698 def test_init_claude_creates_root_CLAUDE_md (self , tmp_path ):
701699 from typer .testing import CliRunner
702700
703701 runner = CliRunner ()
704702 target = tmp_path / "claude-proj"
705703
706- result = runner .invoke (
707- app ,
708- [
709- "init" ,
710- str (target ),
711- "--ai" ,
712- "claude" ,
713- "--offline" ,
714- "--ignore-agent-tools" ,
715- "--no-git" ,
716- "--script" ,
717- "sh" ,
718- ],
719- )
704+ def fake_download (project_path , * args , ** kwargs ):
705+ # Minimal scaffold required for ensure_constitution_from_template()
706+ # and ensure_claude_md() to succeed deterministically.
707+ templates_dir = project_path / ".specify" / "templates"
708+ templates_dir .mkdir (parents = True , exist_ok = True )
709+ (templates_dir / "constitution-template.md" ).write_text (
710+ "# Constitution\n \n Non-negotiable rules.\n " ,
711+ encoding = "utf-8" ,
712+ )
713+
714+ with patch ("specify_cli.download_and_extract_template" , side_effect = fake_download ), \
715+ patch ("specify_cli.ensure_executable_scripts" ), \
716+ patch ("specify_cli.is_git_repo" , return_value = False ), \
717+ patch ("specify_cli.shutil.which" , return_value = "/usr/bin/git" ):
718+ result = runner .invoke (
719+ app ,
720+ [
721+ "init" ,
722+ str (target ),
723+ "--ai" ,
724+ "claude" ,
725+ "--ignore-agent-tools" ,
726+ "--no-git" ,
727+ "--script" ,
728+ "sh" ,
729+ ],
730+ )
720731
721732 assert result .exit_code == 0 , result .output
722733
@@ -728,6 +739,18 @@ def test_init_claude_creates_root_CLAUDE_md(self, tmp_path):
728739 assert "`.specify/memory/constitution.md`" in content
729740 assert "/speckit.plan" in content
730741
742+ def test_ensure_claude_md_skips_when_constitution_missing (self , tmp_path ):
743+ project = tmp_path / "proj"
744+ project .mkdir ()
745+
746+ tracker = StepTracker ("t" )
747+ ensure_claude_md (project , tracker = tracker )
748+
749+ assert not (project / "CLAUDE.md" ).exists ()
750+ step = next (s for s in tracker .steps if s ["key" ] == "claude-md" )
751+ assert step ["status" ] == "skipped"
752+ assert "constitution missing" in step ["detail" ]
753+
731754 def _fake_extract (self , agent , project_path , ** _kwargs ):
732755 """Simulate template extraction: create agent commands dir."""
733756 agent_cfg = AGENT_CONFIG .get (agent , {})
0 commit comments