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,171 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tests for Depends On Normalizer - STORY-090 AC#5
4
+
5
+ TDD tests for input normalization logic.
6
+ Run: pytest tests/test_depends_on_normalizer.py -v
7
+ """
8
+
9
+ import pytest
10
+ from devforgeai_cli.utils.depends_on_normalizer import (
11
+ normalize_depends_on,
12
+ is_valid_story_id,
13
+ validate_depends_on_input,
14
+ STORY_ID_PATTERN
15
+ )
16
+
17
+
18
+ class TestIsValidStoryId:
19
+ """Test STORY-ID format validation."""
20
+
21
+ def test_valid_three_digit_story_id(self):
22
+ assert is_valid_story_id("STORY-044") == True
23
+
24
+ def test_valid_four_digit_story_id(self):
25
+ assert is_valid_story_id("STORY-1234") == True
26
+
27
+ def test_invalid_lowercase_story_id(self):
28
+ assert is_valid_story_id("story-044") == False
29
+
30
+ def test_invalid_insufficient_digits(self):
31
+ assert is_valid_story_id("STORY-44") == False
32
+
33
+ def test_invalid_missing_prefix(self):
34
+ assert is_valid_story_id("044") == False
35
+
36
+ def test_invalid_wrong_prefix(self):
37
+ assert is_valid_story_id("EPIC-044") == False
38
+
39
+ def test_invalid_extra_characters(self):
40
+ assert is_valid_story_id("STORY-044a") == False
41
+
42
+ def test_invalid_none(self):
43
+ assert is_valid_story_id(None) == False
44
+
45
+ def test_invalid_empty_string(self):
46
+ assert is_valid_story_id("") == False
47
+
48
+
49
+ class TestNormalizeDependsOn:
50
+ """Test normalize_depends_on function."""
51
+
52
+ # Null/Empty handling
53
+ def test_null_to_empty_array(self):
54
+ assert normalize_depends_on(None) == []
55
+
56
+ def test_empty_string_to_empty_array(self):
57
+ assert normalize_depends_on("") == []
58
+
59
+ def test_none_string_to_empty_array(self):
60
+ assert normalize_depends_on("none") == []
61
+
62
+ def test_none_uppercase_to_empty_array(self):
63
+ assert normalize_depends_on("NONE") == []
64
+
65
+ def test_null_string_to_empty_array(self):
66
+ assert normalize_depends_on("null") == []
67
+
68
+ def test_empty_brackets_to_empty_array(self):
69
+ assert normalize_depends_on("[]") == []
70
+
71
+ # Single string normalization
72
+ def test_single_story_string_to_array(self):
73
+ assert normalize_depends_on("STORY-044") == ["STORY-044"]
74
+
75
+ def test_single_lowercase_converted_to_uppercase(self):
76
+ assert normalize_depends_on("story-044") == ["STORY-044"]
77
+
78
+ # Multiple items - comma separated
79
+ def test_comma_separated_to_array(self):
80
+ result = normalize_depends_on("STORY-044, STORY-045")
81
+ assert result == ["STORY-044", "STORY-045"]
82
+
83
+ def test_comma_no_space_to_array(self):
84
+ result = normalize_depends_on("STORY-044,STORY-045")
85
+ assert result == ["STORY-044", "STORY-045"]
86
+
87
+ # Multiple items - space separated
88
+ def test_space_separated_to_array(self):
89
+ result = normalize_depends_on("STORY-044 STORY-045")
90
+ assert result == ["STORY-044", "STORY-045"]
91
+
92
+ # Array input
93
+ def test_array_passthrough(self):
94
+ result = normalize_depends_on(["STORY-044", "STORY-045"])
95
+ assert result == ["STORY-044", "STORY-045"]
96
+
97
+ def test_array_filters_invalid(self):
98
+ result = normalize_depends_on(["STORY-044", "invalid", "STORY-045"])
99
+ assert result == ["STORY-044", "STORY-045"]
100
+
101
+ def test_empty_array_stays_empty(self):
102
+ assert normalize_depends_on([]) == []
103
+
104
+ # Edge cases
105
+ def test_whitespace_trimmed(self):
106
+ result = normalize_depends_on(" STORY-044 , STORY-045 ")
107
+ assert result == ["STORY-044", "STORY-045"]
108
+
109
+ def test_mixed_case_normalized(self):
110
+ result = normalize_depends_on("Story-044, story-045, STORY-046")
111
+ assert result == ["STORY-044", "STORY-045", "STORY-046"]
112
+
113
+
114
+ class TestValidateDependsOnInput:
115
+ """Test validate_depends_on_input function."""
116
+
117
+ def test_mixed_input_returns_both(self):
118
+ valid, invalid = validate_depends_on_input("STORY-044, invalid, STORY-045")
119
+ assert valid == ["STORY-044", "STORY-045"]
120
+ assert invalid == ["invalid"]
121
+
122
+ def test_all_valid_returns_empty_invalid(self):
123
+ valid, invalid = validate_depends_on_input("STORY-044, STORY-045")
124
+ assert valid == ["STORY-044", "STORY-045"]
125
+ assert invalid == []
126
+
127
+ def test_all_invalid_returns_empty_valid(self):
128
+ valid, invalid = validate_depends_on_input("invalid, bad")
129
+ assert valid == []
130
+ assert invalid == ["invalid", "bad"]
131
+
132
+ def test_none_returns_empty_lists(self):
133
+ valid, invalid = validate_depends_on_input(None)
134
+ assert valid == []
135
+ assert invalid == []
136
+
137
+ def test_empty_string_returns_empty_lists(self):
138
+ valid, invalid = validate_depends_on_input("")
139
+ assert valid == []
140
+ assert invalid == []
141
+
142
+ def test_list_with_mixed_entries(self):
143
+ valid, invalid = validate_depends_on_input(["STORY-044", "bad", "STORY-045", ""])
144
+ assert valid == ["STORY-044", "STORY-045"]
145
+ assert invalid == ["bad"]
146
+
147
+
148
+ class TestStoryIdPattern:
149
+ """Test the regex pattern directly."""
150
+
151
+ def test_pattern_matches_three_digits(self):
152
+ assert STORY_ID_PATTERN.match("STORY-044") is not None
153
+
154
+ def test_pattern_matches_four_digits(self):
155
+ assert STORY_ID_PATTERN.match("STORY-1234") is not None
156
+
157
+ def test_pattern_rejects_two_digits(self):
158
+ assert STORY_ID_PATTERN.match("STORY-44") is None
159
+
160
+ def test_pattern_rejects_five_digits(self):
161
+ assert STORY_ID_PATTERN.match("STORY-12345") is None
162
+
163
+ def test_pattern_rejects_lowercase(self):
164
+ assert STORY_ID_PATTERN.match("story-044") is None
165
+
166
+ def test_pattern_rejects_no_hyphen(self):
167
+ assert STORY_ID_PATTERN.match("STORY044") is None
168
+
169
+
170
+ if __name__ == '__main__':
171
+ pytest.main([__file__, '-v'])
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tests for DoD Validator
4
+
5
+ Tests the core autonomous deferral detection logic.
6
+ """
7
+
8
+ import pytest
9
+ from pathlib import Path
10
+
11
+ from devforgeai_cli.validators.dod_validator import DoDValidator
12
+
13
+
14
+ # Test fixtures directory
15
+ FIXTURES_DIR = Path(__file__).parent / 'fixtures'
16
+
17
+
18
+ class TestDoDValidator:
19
+ """Test DoD validation logic."""
20
+
21
+ def setup_method(self):
22
+ """Setup test validator."""
23
+ # Use parent directory as project root for testing
24
+ self.validator = DoDValidator(project_root=FIXTURES_DIR.parent.parent.parent.parent)
25
+
26
+ def test_valid_story_passes(self):
27
+ """Test that story with all DoD complete passes validation."""
28
+ story_file = FIXTURES_DIR / 'valid-story-complete.md'
29
+
30
+ is_valid, violations = self.validator.validate(str(story_file))
31
+
32
+ assert is_valid == True, f"Expected valid, got violations: {violations}"
33
+ assert len(violations) == 0, "Expected no violations"
34
+
35
+ def test_autonomous_deferral_detected(self):
36
+ """Test detection of DoD [x] but Impl [ ] without user approval."""
37
+ story_file = FIXTURES_DIR / 'autonomous-deferral-story.md'
38
+
39
+ is_valid, violations = self.validator.validate(str(story_file))
40
+
41
+ assert is_valid == False, "Expected validation to fail for autonomous deferral"
42
+
43
+ # Should have CRITICAL violation
44
+ critical_violations = [v for v in violations if v['severity'] == 'CRITICAL']
45
+ assert len(critical_violations) > 0, "Expected CRITICAL violation"
46
+
47
+ # Check violation details
48
+ deferral_violation = next(
49
+ (v for v in critical_violations if 'AUTONOMOUS DEFERRAL' in v['error']),
50
+ None
51
+ )
52
+ assert deferral_violation is not None, "Expected autonomous deferral violation"
53
+ assert deferral_violation['violation_type'] == 'autonomous_deferral'
54
+ assert 'Deadlock retry' in deferral_violation['item']
55
+
56
+ def test_valid_deferral_with_user_approval_passes(self):
57
+ """Test that deferral with 'User approved:' marker passes."""
58
+ story_file = FIXTURES_DIR / 'valid-deferral-story.md'
59
+
60
+ is_valid, violations = self.validator.validate(str(story_file))
61
+
62
+ # Should pass (no CRITICAL/HIGH violations)
63
+ critical_high = [v for v in violations if v['severity'] in ['CRITICAL', 'HIGH']]
64
+ assert len(critical_high) == 0, f"Expected no critical/high violations, got: {critical_high}"
65
+
66
+ def test_missing_implementation_notes_fails(self):
67
+ """Test that story without Implementation Notes fails."""
68
+ story_file = FIXTURES_DIR / 'missing-impl-notes.md'
69
+
70
+ is_valid, violations = self.validator.validate(str(story_file))
71
+
72
+ assert is_valid == False, "Expected validation to fail"
73
+
74
+ # Should have HIGH violation for missing section
75
+ high_violations = [v for v in violations if v['severity'] == 'HIGH']
76
+ assert len(high_violations) > 0, "Expected HIGH violation"
77
+
78
+ impl_notes_violation = next(
79
+ (v for v in high_violations if 'Implementation Notes' in v['error']),
80
+ None
81
+ )
82
+ assert impl_notes_violation is not None, "Expected Implementation Notes violation"
83
+
84
+ def test_story_file_not_found(self):
85
+ """Test handling of non-existent story file."""
86
+ story_file = FIXTURES_DIR / 'nonexistent.md'
87
+
88
+ is_valid, violations = self.validator.validate(str(story_file))
89
+
90
+ assert is_valid == False, "Expected validation to fail for missing file"
91
+ assert len(violations) > 0, "Expected violations"
92
+ assert violations[0]['severity'] == 'CRITICAL'
93
+
94
+
95
+ # Run tests if executed directly
96
+ if __name__ == '__main__':
97
+ pytest.main([__file__, '-v'])