devforgeai 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/CLAUDE.md +120 -0
  2. package/bin/devforgeai.js +0 -0
  3. package/package.json +9 -1
  4. package/src/CLAUDE.md +699 -0
  5. package/src/claude/hooks/phase-completion-gate.sh +0 -0
  6. package/src/claude/scripts/README.md +396 -0
  7. package/src/claude/scripts/audit-command-skill-overlap.sh +67 -0
  8. package/src/claude/scripts/check-hooks-fast.sh +70 -0
  9. package/src/claude/scripts/devforgeai-validate +6 -0
  10. package/src/claude/scripts/devforgeai_cli/README.md +531 -0
  11. package/src/claude/scripts/devforgeai_cli/__init__.py +12 -0
  12. package/src/claude/scripts/devforgeai_cli/cli.py +716 -0
  13. package/src/claude/scripts/devforgeai_cli/commands/__init__.py +1 -0
  14. package/src/claude/scripts/devforgeai_cli/commands/check_hooks.py +384 -0
  15. package/src/claude/scripts/devforgeai_cli/commands/invoke_hooks.py +149 -0
  16. package/src/claude/scripts/devforgeai_cli/commands/phase_commands.py +731 -0
  17. package/src/claude/scripts/devforgeai_cli/commands/validate_installation.py +412 -0
  18. package/src/claude/scripts/devforgeai_cli/context_extraction.py +426 -0
  19. package/src/claude/scripts/devforgeai_cli/feedback/AC_TO_TEST_MAPPING.md +636 -0
  20. package/src/claude/scripts/devforgeai_cli/feedback/DELIVERY_SUMMARY.txt +329 -0
  21. package/src/claude/scripts/devforgeai_cli/feedback/README_TEST_SPECS.md +486 -0
  22. package/src/claude/scripts/devforgeai_cli/feedback/TEST_IMPLEMENTATION_GUIDE.md +529 -0
  23. package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECIFICATIONS.md +2652 -0
  24. package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECS_INDEX.md +398 -0
  25. package/src/claude/scripts/devforgeai_cli/feedback/__init__.py +34 -0
  26. package/src/claude/scripts/devforgeai_cli/feedback/adaptive_questioning_engine.py +581 -0
  27. package/src/claude/scripts/devforgeai_cli/feedback/aggregation.py +179 -0
  28. package/src/claude/scripts/devforgeai_cli/feedback/commands.py +535 -0
  29. package/src/claude/scripts/devforgeai_cli/feedback/config_defaults.py +58 -0
  30. package/src/claude/scripts/devforgeai_cli/feedback/config_manager.py +423 -0
  31. package/src/claude/scripts/devforgeai_cli/feedback/config_models.py +192 -0
  32. package/src/claude/scripts/devforgeai_cli/feedback/config_schema.py +140 -0
  33. package/src/claude/scripts/devforgeai_cli/feedback/coverage.json +1 -0
  34. package/src/claude/scripts/devforgeai_cli/feedback/feature_flag.py +152 -0
  35. package/src/claude/scripts/devforgeai_cli/feedback/feedback_indexer.py +394 -0
  36. package/src/claude/scripts/devforgeai_cli/feedback/hot_reload.py +226 -0
  37. package/src/claude/scripts/devforgeai_cli/feedback/longitudinal.py +115 -0
  38. package/src/claude/scripts/devforgeai_cli/feedback/models.py +67 -0
  39. package/src/claude/scripts/devforgeai_cli/feedback/question_router.py +236 -0
  40. package/src/claude/scripts/devforgeai_cli/feedback/retrospective.py +233 -0
  41. package/src/claude/scripts/devforgeai_cli/feedback/skip_tracker.py +177 -0
  42. package/src/claude/scripts/devforgeai_cli/feedback/skip_tracking.py +221 -0
  43. package/src/claude/scripts/devforgeai_cli/feedback/template_engine.py +549 -0
  44. package/src/claude/scripts/devforgeai_cli/feedback/validation.py +163 -0
  45. package/src/claude/scripts/devforgeai_cli/headless/__init__.py +30 -0
  46. package/src/claude/scripts/devforgeai_cli/headless/answer_models.py +206 -0
  47. package/src/claude/scripts/devforgeai_cli/headless/answer_resolver.py +204 -0
  48. package/src/claude/scripts/devforgeai_cli/headless/exceptions.py +36 -0
  49. package/src/claude/scripts/devforgeai_cli/headless/pattern_matcher.py +156 -0
  50. package/src/claude/scripts/devforgeai_cli/hooks.py +313 -0
  51. package/src/claude/scripts/devforgeai_cli/metrics/__init__.py +46 -0
  52. package/src/claude/scripts/devforgeai_cli/metrics/command_metrics.py +142 -0
  53. package/src/claude/scripts/devforgeai_cli/metrics/failure_modes.py +152 -0
  54. package/src/claude/scripts/devforgeai_cli/metrics/story_segmentation.py +181 -0
  55. package/src/claude/scripts/devforgeai_cli/orchestrate_hooks.py +780 -0
  56. package/src/claude/scripts/devforgeai_cli/phase_state.py +1229 -0
  57. package/src/claude/scripts/devforgeai_cli/session/__init__.py +30 -0
  58. package/src/claude/scripts/devforgeai_cli/session/checkpoint.py +268 -0
  59. package/src/claude/scripts/devforgeai_cli/tests/__init__.py +1 -0
  60. package/src/claude/scripts/devforgeai_cli/tests/conftest.py +29 -0
  61. package/src/claude/scripts/devforgeai_cli/tests/feedback/TEST_EXECUTION_GUIDE.md +298 -0
  62. package/src/claude/scripts/devforgeai_cli/tests/feedback/__init__.py +3 -0
  63. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_adaptive_questioning_engine.py +2171 -0
  64. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_aggregation.py +476 -0
  65. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_defaults.py +133 -0
  66. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_manager.py +592 -0
  67. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_models.py +373 -0
  68. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_schema.py +130 -0
  69. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_configuration_management.py +1355 -0
  70. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_edge_cases.py +308 -0
  71. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feature_flag.py +307 -0
  72. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feedback_indexer.py +384 -0
  73. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_hot_reload.py +580 -0
  74. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_integration.py +402 -0
  75. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_models.py +105 -0
  76. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_question_routing.py +262 -0
  77. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_retrospective.py +333 -0
  78. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracker.py +410 -0
  79. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking.py +159 -0
  80. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking_integration.py +1155 -0
  81. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_template_engine.py +1389 -0
  82. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_validation_comprehensive.py +210 -0
  83. package/src/claude/scripts/devforgeai_cli/tests/fixtures/autonomous-deferral-story.md +46 -0
  84. package/src/claude/scripts/devforgeai_cli/tests/fixtures/missing-impl-notes.md +31 -0
  85. package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-deferral-story.md +46 -0
  86. package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-story-complete.md +48 -0
  87. package/src/claude/scripts/devforgeai_cli/tests/manual_test_invoke_hooks.sh +200 -0
  88. package/src/claude/scripts/devforgeai_cli/tests/session/DELIVERABLES.md +518 -0
  89. package/src/claude/scripts/devforgeai_cli/tests/session/TEST_SUMMARY.md +468 -0
  90. package/src/claude/scripts/devforgeai_cli/tests/session/__init__.py +6 -0
  91. package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/corrupted-checkpoint.json +1 -0
  92. package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/missing-fields-checkpoint.json +4 -0
  93. package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/valid-checkpoint.json +15 -0
  94. package/src/claude/scripts/devforgeai_cli/tests/session/test_checkpoint.py +851 -0
  95. package/src/claude/scripts/devforgeai_cli/tests/test_check_hooks.py +1886 -0
  96. package/src/claude/scripts/devforgeai_cli/tests/test_depends_on_normalizer.py +171 -0
  97. package/src/claude/scripts/devforgeai_cli/tests/test_dod_validator.py +97 -0
  98. package/src/claude/scripts/devforgeai_cli/tests/test_invoke_hooks.py +1902 -0
  99. package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands.py +320 -0
  100. package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_error_handling.py +1021 -0
  101. package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_import.py +697 -0
  102. package/src/claude/scripts/devforgeai_cli/tests/test_phase_state.py +2187 -0
  103. package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking.py +2141 -0
  104. package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking_coverage_gap.py +195 -0
  105. package/src/claude/scripts/devforgeai_cli/tests/test_subagent_enforcement.py +539 -0
  106. package/src/claude/scripts/devforgeai_cli/tests/test_validate_installation.py +361 -0
  107. package/src/claude/scripts/devforgeai_cli/utils/__init__.py +11 -0
  108. package/src/claude/scripts/devforgeai_cli/utils/depends_on_normalizer.py +149 -0
  109. package/src/claude/scripts/devforgeai_cli/utils/markdown_parser.py +219 -0
  110. package/src/claude/scripts/devforgeai_cli/utils/story_analyzer.py +249 -0
  111. package/src/claude/scripts/devforgeai_cli/utils/yaml_parser.py +152 -0
  112. package/src/claude/scripts/devforgeai_cli/validators/__init__.py +27 -0
  113. package/src/claude/scripts/devforgeai_cli/validators/ast_grep_validator.py +373 -0
  114. package/src/claude/scripts/devforgeai_cli/validators/context_validator.py +180 -0
  115. package/src/claude/scripts/devforgeai_cli/validators/dod_validator.py +309 -0
  116. package/src/claude/scripts/devforgeai_cli/validators/git_validator.py +107 -0
  117. package/src/claude/scripts/devforgeai_cli/validators/grep_fallback.py +300 -0
  118. package/src/claude/scripts/install_hooks.sh +186 -0
  119. package/src/claude/scripts/invoke_feedback_hooks.sh +59 -0
  120. package/src/claude/scripts/migrate-ac-headers.sh +122 -0
  121. package/src/claude/scripts/plan_file_kb.sh +704 -0
  122. package/src/claude/scripts/requirements.txt +8 -0
  123. package/src/claude/scripts/session_catalog.sh +543 -0
  124. package/src/claude/scripts/setup.py +55 -0
  125. package/src/claude/scripts/start-devforgeai.sh +16 -0
  126. package/src/claude/scripts/statusline.sh +27 -0
  127. package/src/claude/scripts/validate_deferrals.py +344 -0
  128. package/src/claude/skills/designing-systems/scripts/__pycache__/detect_anti_patterns.cpython-312.pyc +0 -0
  129. package/src/claude/skills/designing-systems/scripts/__pycache__/validate_all_context.cpython-312.pyc +0 -0
  130. package/src/claude/skills/designing-systems/scripts/__pycache__/validate_architecture.cpython-312.pyc +0 -0
  131. package/src/claude/skills/designing-systems/scripts/__pycache__/validate_dependencies.cpython-312.pyc +0 -0
  132. package/src/claude/skills/devforgeai-story-creation/scripts/__pycache__/migrate_story_v1_to_v2.cpython-312.pyc +0 -0
  133. package/src/claude/skills/devforgeai-story-creation/scripts/tests/__pycache__/measure_accuracy.cpython-312.pyc +0 -0
@@ -0,0 +1,320 @@
1
+ """
2
+ Tests for phase CLI commands.
3
+
4
+ TDD Red Phase: These tests verify the phase-init, phase-check,
5
+ phase-complete, and phase-status CLI commands.
6
+
7
+ Exit Codes:
8
+ - phase-init: 0=created, 1=exists, 2=invalid ID
9
+ - phase-check: 0=allowed, 1=blocked, 2=missing subagents
10
+ - phase-complete: 0=success, 1=incomplete
11
+ - phase-status: 0=success, 1=not found
12
+ """
13
+
14
+ import json
15
+ import sys
16
+ import tempfile
17
+ from pathlib import Path
18
+ from unittest.mock import patch, MagicMock
19
+
20
+ import pytest
21
+
22
+ # Add parent directory to path
23
+ sys.path.insert(0, str(Path(__file__).parent.parent))
24
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent.parent))
25
+
26
+
27
+ @pytest.fixture
28
+ def temp_project_dir():
29
+ """Create a temporary project directory."""
30
+ with tempfile.TemporaryDirectory() as tmpdir:
31
+ project_root = Path(tmpdir)
32
+ # Create devforgeai/workflows directory
33
+ workflows_dir = project_root / "devforgeai" / "workflows"
34
+ workflows_dir.mkdir(parents=True, exist_ok=True)
35
+ yield project_root
36
+
37
+
38
+ @pytest.fixture
39
+ def existing_state(temp_project_dir):
40
+ """Create an existing phase state file."""
41
+ state = {
42
+ "story_id": "STORY-001",
43
+ "workflow_started": "2025-12-24T10:00:00Z",
44
+ "current_phase": "02",
45
+ "phases": {
46
+ "01": {
47
+ "status": "completed",
48
+ "started_at": "2025-12-24T10:00:00Z",
49
+ "completed_at": "2025-12-24T10:05:00Z",
50
+ "subagents_required": ["git-validator", "tech-stack-detector"],
51
+ "subagents_invoked": ["git-validator", "tech-stack-detector"],
52
+ "checkpoint_passed": True
53
+ },
54
+ "02": {"status": "pending", "subagents_required": ["test-automator"], "subagents_invoked": []},
55
+ "03": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
56
+ "04": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
57
+ "05": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
58
+ "06": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
59
+ "07": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
60
+ "08": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
61
+ "09": {"status": "pending", "subagents_required": [], "subagents_invoked": []},
62
+ "10": {"status": "pending", "subagents_required": [], "subagents_invoked": []}
63
+ },
64
+ "validation_errors": [],
65
+ "blocking_status": False
66
+ }
67
+
68
+ state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-001-phase-state.json"
69
+ state_file.write_text(json.dumps(state, indent=2))
70
+ return state_file
71
+
72
+
73
+ # =============================================================================
74
+ # phase-init Tests
75
+ # =============================================================================
76
+
77
+
78
+ class TestPhaseInitCommand:
79
+ """Tests for phase-init command."""
80
+
81
+ def test_phase_init_creates_state_file(self, temp_project_dir):
82
+ """Test phase-init creates a new state file."""
83
+ from commands.phase_commands import phase_init_command
84
+
85
+ exit_code = phase_init_command("STORY-100", str(temp_project_dir), format="text")
86
+
87
+ assert exit_code == 0
88
+ state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-100-phase-state.json"
89
+ assert state_file.exists()
90
+
91
+ def test_phase_init_returns_1_if_exists(self, temp_project_dir, existing_state):
92
+ """Test phase-init returns 1 if state file already exists."""
93
+ from commands.phase_commands import phase_init_command
94
+
95
+ exit_code = phase_init_command("STORY-001", str(temp_project_dir), format="text")
96
+
97
+ assert exit_code == 1
98
+
99
+ def test_phase_init_returns_2_for_invalid_id(self, temp_project_dir):
100
+ """Test phase-init returns 2 for invalid story ID."""
101
+ from commands.phase_commands import phase_init_command
102
+
103
+ exit_code = phase_init_command("INVALID-001", str(temp_project_dir), format="text")
104
+
105
+ assert exit_code == 2
106
+
107
+ def test_phase_init_json_output(self, temp_project_dir, capsys):
108
+ """Test phase-init with JSON output format."""
109
+ from commands.phase_commands import phase_init_command
110
+
111
+ exit_code = phase_init_command("STORY-100", str(temp_project_dir), format="json")
112
+
113
+ assert exit_code == 0
114
+ output = capsys.readouterr().out
115
+ result = json.loads(output)
116
+ assert result["success"] is True
117
+ assert result["story_id"] == "STORY-100"
118
+ assert "path" in result
119
+
120
+
121
+ # =============================================================================
122
+ # phase-check Tests
123
+ # =============================================================================
124
+
125
+
126
+ class TestPhaseCheckCommand:
127
+ """Tests for phase-check command."""
128
+
129
+ def test_phase_check_allows_valid_transition(self, temp_project_dir, existing_state):
130
+ """Test phase-check allows valid phase transition."""
131
+ from commands.phase_commands import phase_check_command
132
+
133
+ # Current phase is 02, should allow 01->02 check
134
+ exit_code = phase_check_command(
135
+ "STORY-001",
136
+ from_phase="01",
137
+ to_phase="02",
138
+ project_root=str(temp_project_dir),
139
+ format="text"
140
+ )
141
+
142
+ assert exit_code == 0
143
+
144
+ def test_phase_check_blocks_skip(self, temp_project_dir, existing_state):
145
+ """Test phase-check blocks phase skipping."""
146
+ from commands.phase_commands import phase_check_command
147
+
148
+ exit_code = phase_check_command(
149
+ "STORY-001",
150
+ from_phase="02",
151
+ to_phase="04", # Trying to skip phase 03
152
+ project_root=str(temp_project_dir),
153
+ format="text"
154
+ )
155
+
156
+ assert exit_code == 1
157
+
158
+ def test_phase_check_reports_missing_subagents(self, temp_project_dir):
159
+ """Test phase-check returns 2 when subagents are missing."""
160
+ from commands.phase_commands import phase_check_command
161
+
162
+ # Create state with phase 01 incomplete (missing subagents)
163
+ state = {
164
+ "story_id": "STORY-002",
165
+ "workflow_started": "2025-12-24T10:00:00Z",
166
+ "current_phase": "01",
167
+ "phases": {
168
+ "01": {
169
+ "status": "completed",
170
+ "subagents_required": ["git-validator", "tech-stack-detector"],
171
+ "subagents_invoked": ["git-validator"], # Missing tech-stack-detector
172
+ "checkpoint_passed": True
173
+ },
174
+ "02": {"status": "pending", "subagents_required": ["test-automator"], "subagents_invoked": []},
175
+ **{f"{i:02d}": {"status": "pending", "subagents_required": [], "subagents_invoked": []} for i in range(3, 11)}
176
+ },
177
+ "validation_errors": [],
178
+ "blocking_status": False
179
+ }
180
+
181
+ state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-002-phase-state.json"
182
+ state_file.write_text(json.dumps(state, indent=2))
183
+
184
+ exit_code = phase_check_command(
185
+ "STORY-002",
186
+ from_phase="01",
187
+ to_phase="02",
188
+ project_root=str(temp_project_dir),
189
+ format="text"
190
+ )
191
+
192
+ assert exit_code == 2
193
+
194
+ def test_phase_check_json_output(self, temp_project_dir, existing_state, capsys):
195
+ """Test phase-check with JSON output format."""
196
+ from commands.phase_commands import phase_check_command
197
+
198
+ exit_code = phase_check_command(
199
+ "STORY-001",
200
+ from_phase="01",
201
+ to_phase="02",
202
+ project_root=str(temp_project_dir),
203
+ format="json"
204
+ )
205
+
206
+ assert exit_code == 0
207
+ output = capsys.readouterr().out
208
+ result = json.loads(output)
209
+ assert result["allowed"] is True
210
+
211
+
212
+ # =============================================================================
213
+ # phase-complete Tests
214
+ # =============================================================================
215
+
216
+
217
+ class TestPhaseCompleteCommand:
218
+ """Tests for phase-complete command."""
219
+
220
+ def test_phase_complete_success(self, temp_project_dir, existing_state):
221
+ """Test phase-complete marks phase as complete."""
222
+ from commands.phase_commands import phase_complete_command
223
+
224
+ exit_code = phase_complete_command(
225
+ "STORY-001",
226
+ phase="02",
227
+ checkpoint_passed=True,
228
+ project_root=str(temp_project_dir),
229
+ format="text"
230
+ )
231
+
232
+ assert exit_code == 0
233
+
234
+ # Verify state was updated
235
+ state_file = temp_project_dir / "devforgeai" / "workflows" / "STORY-001-phase-state.json"
236
+ state = json.loads(state_file.read_text())
237
+ assert state["phases"]["02"]["status"] == "completed"
238
+ assert state["current_phase"] == "03"
239
+
240
+ def test_phase_complete_returns_1_for_wrong_phase(self, temp_project_dir, existing_state):
241
+ """Test phase-complete returns 1 when completing wrong phase."""
242
+ from commands.phase_commands import phase_complete_command
243
+
244
+ # Current phase is 02, can't complete 03
245
+ exit_code = phase_complete_command(
246
+ "STORY-001",
247
+ phase="03",
248
+ checkpoint_passed=True,
249
+ project_root=str(temp_project_dir),
250
+ format="text"
251
+ )
252
+
253
+ assert exit_code == 1
254
+
255
+ def test_phase_complete_not_found(self, temp_project_dir):
256
+ """Test phase-complete returns 1 for non-existent state."""
257
+ from commands.phase_commands import phase_complete_command
258
+
259
+ exit_code = phase_complete_command(
260
+ "STORY-999",
261
+ phase="01",
262
+ checkpoint_passed=True,
263
+ project_root=str(temp_project_dir),
264
+ format="text"
265
+ )
266
+
267
+ assert exit_code == 1
268
+
269
+
270
+ # =============================================================================
271
+ # phase-status Tests
272
+ # =============================================================================
273
+
274
+
275
+ class TestPhaseStatusCommand:
276
+ """Tests for phase-status command."""
277
+
278
+ def test_phase_status_success(self, temp_project_dir, existing_state, capsys):
279
+ """Test phase-status displays current state."""
280
+ from commands.phase_commands import phase_status_command
281
+
282
+ exit_code = phase_status_command(
283
+ "STORY-001",
284
+ project_root=str(temp_project_dir),
285
+ format="text"
286
+ )
287
+
288
+ assert exit_code == 0
289
+ output = capsys.readouterr().out
290
+ assert "STORY-001" in output
291
+ assert "Phase 02" in output or "02" in output
292
+
293
+ def test_phase_status_not_found(self, temp_project_dir):
294
+ """Test phase-status returns 1 for non-existent state."""
295
+ from commands.phase_commands import phase_status_command
296
+
297
+ exit_code = phase_status_command(
298
+ "STORY-999",
299
+ project_root=str(temp_project_dir),
300
+ format="text"
301
+ )
302
+
303
+ assert exit_code == 1
304
+
305
+ def test_phase_status_json_output(self, temp_project_dir, existing_state, capsys):
306
+ """Test phase-status with JSON output format."""
307
+ from commands.phase_commands import phase_status_command
308
+
309
+ exit_code = phase_status_command(
310
+ "STORY-001",
311
+ project_root=str(temp_project_dir),
312
+ format="json"
313
+ )
314
+
315
+ assert exit_code == 0
316
+ output = capsys.readouterr().out
317
+ result = json.loads(output)
318
+ assert result["story_id"] == "STORY-001"
319
+ assert result["current_phase"] == "02"
320
+ assert "phases" in result