devforgeai 1.0.4 → 1.0.6

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 (134) hide show
  1. package/CLAUDE.md +120 -0
  2. package/package.json +9 -1
  3. package/src/CLAUDE.md +699 -0
  4. package/src/claude/scripts/README.md +396 -0
  5. package/src/claude/scripts/audit-command-skill-overlap.sh +67 -0
  6. package/src/claude/scripts/check-hooks-fast.sh +70 -0
  7. package/src/claude/scripts/devforgeai-validate +6 -0
  8. package/src/claude/scripts/devforgeai_cli/README.md +531 -0
  9. package/src/claude/scripts/devforgeai_cli/__init__.py +12 -0
  10. package/src/claude/scripts/devforgeai_cli/cli.py +716 -0
  11. package/src/claude/scripts/devforgeai_cli/commands/__init__.py +1 -0
  12. package/src/claude/scripts/devforgeai_cli/commands/check_hooks.py +384 -0
  13. package/src/claude/scripts/devforgeai_cli/commands/invoke_hooks.py +149 -0
  14. package/src/claude/scripts/devforgeai_cli/commands/phase_commands.py +731 -0
  15. package/src/claude/scripts/devforgeai_cli/commands/validate_installation.py +412 -0
  16. package/src/claude/scripts/devforgeai_cli/context_extraction.py +426 -0
  17. package/src/claude/scripts/devforgeai_cli/feedback/AC_TO_TEST_MAPPING.md +636 -0
  18. package/src/claude/scripts/devforgeai_cli/feedback/DELIVERY_SUMMARY.txt +329 -0
  19. package/src/claude/scripts/devforgeai_cli/feedback/README_TEST_SPECS.md +486 -0
  20. package/src/claude/scripts/devforgeai_cli/feedback/TEST_IMPLEMENTATION_GUIDE.md +529 -0
  21. package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECIFICATIONS.md +2652 -0
  22. package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECS_INDEX.md +398 -0
  23. package/src/claude/scripts/devforgeai_cli/feedback/__init__.py +34 -0
  24. package/src/claude/scripts/devforgeai_cli/feedback/adaptive_questioning_engine.py +581 -0
  25. package/src/claude/scripts/devforgeai_cli/feedback/aggregation.py +179 -0
  26. package/src/claude/scripts/devforgeai_cli/feedback/commands.py +535 -0
  27. package/src/claude/scripts/devforgeai_cli/feedback/config_defaults.py +58 -0
  28. package/src/claude/scripts/devforgeai_cli/feedback/config_manager.py +423 -0
  29. package/src/claude/scripts/devforgeai_cli/feedback/config_models.py +192 -0
  30. package/src/claude/scripts/devforgeai_cli/feedback/config_schema.py +140 -0
  31. package/src/claude/scripts/devforgeai_cli/feedback/coverage.json +1 -0
  32. package/src/claude/scripts/devforgeai_cli/feedback/feature_flag.py +152 -0
  33. package/src/claude/scripts/devforgeai_cli/feedback/feedback_indexer.py +394 -0
  34. package/src/claude/scripts/devforgeai_cli/feedback/hot_reload.py +226 -0
  35. package/src/claude/scripts/devforgeai_cli/feedback/longitudinal.py +115 -0
  36. package/src/claude/scripts/devforgeai_cli/feedback/models.py +67 -0
  37. package/src/claude/scripts/devforgeai_cli/feedback/question_router.py +236 -0
  38. package/src/claude/scripts/devforgeai_cli/feedback/retrospective.py +233 -0
  39. package/src/claude/scripts/devforgeai_cli/feedback/skip_tracker.py +177 -0
  40. package/src/claude/scripts/devforgeai_cli/feedback/skip_tracking.py +221 -0
  41. package/src/claude/scripts/devforgeai_cli/feedback/template_engine.py +549 -0
  42. package/src/claude/scripts/devforgeai_cli/feedback/validation.py +163 -0
  43. package/src/claude/scripts/devforgeai_cli/headless/__init__.py +30 -0
  44. package/src/claude/scripts/devforgeai_cli/headless/answer_models.py +206 -0
  45. package/src/claude/scripts/devforgeai_cli/headless/answer_resolver.py +204 -0
  46. package/src/claude/scripts/devforgeai_cli/headless/exceptions.py +36 -0
  47. package/src/claude/scripts/devforgeai_cli/headless/pattern_matcher.py +156 -0
  48. package/src/claude/scripts/devforgeai_cli/hooks.py +313 -0
  49. package/src/claude/scripts/devforgeai_cli/metrics/__init__.py +46 -0
  50. package/src/claude/scripts/devforgeai_cli/metrics/command_metrics.py +142 -0
  51. package/src/claude/scripts/devforgeai_cli/metrics/failure_modes.py +152 -0
  52. package/src/claude/scripts/devforgeai_cli/metrics/story_segmentation.py +181 -0
  53. package/src/claude/scripts/devforgeai_cli/orchestrate_hooks.py +780 -0
  54. package/src/claude/scripts/devforgeai_cli/phase_state.py +1229 -0
  55. package/src/claude/scripts/devforgeai_cli/session/__init__.py +30 -0
  56. package/src/claude/scripts/devforgeai_cli/session/checkpoint.py +268 -0
  57. package/src/claude/scripts/devforgeai_cli/tests/__init__.py +1 -0
  58. package/src/claude/scripts/devforgeai_cli/tests/conftest.py +29 -0
  59. package/src/claude/scripts/devforgeai_cli/tests/feedback/TEST_EXECUTION_GUIDE.md +298 -0
  60. package/src/claude/scripts/devforgeai_cli/tests/feedback/__init__.py +3 -0
  61. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_adaptive_questioning_engine.py +2171 -0
  62. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_aggregation.py +476 -0
  63. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_defaults.py +133 -0
  64. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_manager.py +592 -0
  65. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_models.py +373 -0
  66. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_schema.py +130 -0
  67. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_configuration_management.py +1355 -0
  68. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_edge_cases.py +308 -0
  69. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feature_flag.py +307 -0
  70. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feedback_indexer.py +384 -0
  71. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_hot_reload.py +580 -0
  72. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_integration.py +402 -0
  73. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_models.py +105 -0
  74. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_question_routing.py +262 -0
  75. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_retrospective.py +333 -0
  76. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracker.py +410 -0
  77. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking.py +159 -0
  78. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking_integration.py +1155 -0
  79. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_template_engine.py +1389 -0
  80. package/src/claude/scripts/devforgeai_cli/tests/feedback/test_validation_comprehensive.py +210 -0
  81. package/src/claude/scripts/devforgeai_cli/tests/fixtures/autonomous-deferral-story.md +46 -0
  82. package/src/claude/scripts/devforgeai_cli/tests/fixtures/missing-impl-notes.md +31 -0
  83. package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-deferral-story.md +46 -0
  84. package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-story-complete.md +48 -0
  85. package/src/claude/scripts/devforgeai_cli/tests/manual_test_invoke_hooks.sh +200 -0
  86. package/src/claude/scripts/devforgeai_cli/tests/session/DELIVERABLES.md +518 -0
  87. package/src/claude/scripts/devforgeai_cli/tests/session/TEST_SUMMARY.md +468 -0
  88. package/src/claude/scripts/devforgeai_cli/tests/session/__init__.py +6 -0
  89. package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/corrupted-checkpoint.json +1 -0
  90. package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/missing-fields-checkpoint.json +4 -0
  91. package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/valid-checkpoint.json +15 -0
  92. package/src/claude/scripts/devforgeai_cli/tests/session/test_checkpoint.py +851 -0
  93. package/src/claude/scripts/devforgeai_cli/tests/test_check_hooks.py +1886 -0
  94. package/src/claude/scripts/devforgeai_cli/tests/test_depends_on_normalizer.py +171 -0
  95. package/src/claude/scripts/devforgeai_cli/tests/test_dod_validator.py +97 -0
  96. package/src/claude/scripts/devforgeai_cli/tests/test_invoke_hooks.py +1902 -0
  97. package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands.py +320 -0
  98. package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_error_handling.py +1021 -0
  99. package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_import.py +697 -0
  100. package/src/claude/scripts/devforgeai_cli/tests/test_phase_state.py +2187 -0
  101. package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking.py +2141 -0
  102. package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking_coverage_gap.py +195 -0
  103. package/src/claude/scripts/devforgeai_cli/tests/test_subagent_enforcement.py +539 -0
  104. package/src/claude/scripts/devforgeai_cli/tests/test_validate_installation.py +361 -0
  105. package/src/claude/scripts/devforgeai_cli/utils/__init__.py +11 -0
  106. package/src/claude/scripts/devforgeai_cli/utils/depends_on_normalizer.py +149 -0
  107. package/src/claude/scripts/devforgeai_cli/utils/markdown_parser.py +219 -0
  108. package/src/claude/scripts/devforgeai_cli/utils/story_analyzer.py +249 -0
  109. package/src/claude/scripts/devforgeai_cli/utils/yaml_parser.py +152 -0
  110. package/src/claude/scripts/devforgeai_cli/validators/__init__.py +27 -0
  111. package/src/claude/scripts/devforgeai_cli/validators/ast_grep_validator.py +373 -0
  112. package/src/claude/scripts/devforgeai_cli/validators/context_validator.py +180 -0
  113. package/src/claude/scripts/devforgeai_cli/validators/dod_validator.py +309 -0
  114. package/src/claude/scripts/devforgeai_cli/validators/git_validator.py +107 -0
  115. package/src/claude/scripts/devforgeai_cli/validators/grep_fallback.py +300 -0
  116. package/src/claude/scripts/install_hooks.sh +186 -0
  117. package/src/claude/scripts/invoke_feedback_hooks.sh +59 -0
  118. package/src/claude/scripts/migrate-ac-headers.sh +122 -0
  119. package/src/claude/scripts/plan_file_kb.sh +704 -0
  120. package/src/claude/scripts/requirements.txt +8 -0
  121. package/src/claude/scripts/session_catalog.sh +543 -0
  122. package/src/claude/scripts/setup.py +55 -0
  123. package/src/claude/scripts/start-devforgeai.sh +16 -0
  124. package/src/claude/scripts/statusline.sh +27 -0
  125. package/src/claude/scripts/validate_deferrals.py +344 -0
  126. package/src/claude/skills/devforgeai-qa/SKILL.md +1 -1
  127. package/src/claude/skills/researching-market/SKILL.md +2 -1
  128. package/src/cli/lib/copier.js +13 -1
  129. package/src/claude/skills/designing-systems/scripts/__pycache__/detect_anti_patterns.cpython-312.pyc +0 -0
  130. package/src/claude/skills/designing-systems/scripts/__pycache__/validate_all_context.cpython-312.pyc +0 -0
  131. package/src/claude/skills/designing-systems/scripts/__pycache__/validate_architecture.cpython-312.pyc +0 -0
  132. package/src/claude/skills/designing-systems/scripts/__pycache__/validate_dependencies.cpython-312.pyc +0 -0
  133. package/src/claude/skills/devforgeai-story-creation/scripts/__pycache__/migrate_story_v1_to_v2.cpython-312.pyc +0 -0
  134. package/src/claude/skills/devforgeai-story-creation/scripts/tests/__pycache__/measure_accuracy.cpython-312.pyc +0 -0
@@ -0,0 +1,300 @@
1
+ """
2
+ GrepFallbackAnalyzer - Grep-based pattern matching fallback.
3
+
4
+ Used when ast-grep is unavailable. Provides ~60-75% accuracy
5
+ compared to ast-grep's 90-95% accuracy.
6
+
7
+ Output format matches ast-grep schema for interoperability.
8
+ """
9
+
10
+ import re
11
+ import json
12
+ import logging
13
+ from pathlib import Path
14
+ from typing import List, Dict, Tuple, Optional
15
+ from dataclasses import dataclass, field, asdict
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ # =============================================================================
21
+ # Data Classes
22
+ # =============================================================================
23
+
24
+ @dataclass
25
+ class GrepPattern:
26
+ """Pattern definition for grep-based detection."""
27
+ id: str
28
+ pattern: str # Regex pattern
29
+ severity: str # CRITICAL, HIGH, MEDIUM, LOW
30
+ message: str
31
+ category: str # security, anti-pattern, etc.
32
+ languages: List[str] = field(default_factory=list)
33
+
34
+
35
+ @dataclass
36
+ class Violation:
37
+ """Standardized violation result."""
38
+ file: str
39
+ line: int
40
+ column: int
41
+ rule_id: str
42
+ severity: str
43
+ message: str
44
+ evidence: str
45
+ analysis_method: str = "grep-fallback"
46
+ category: str = "security"
47
+
48
+ def to_dict(self) -> Dict:
49
+ """Convert to dictionary for JSON serialization."""
50
+ return asdict(self)
51
+
52
+
53
+ # =============================================================================
54
+ # Utility Functions
55
+ # =============================================================================
56
+
57
+ def log_fallback_warning() -> None:
58
+ """Log warning about reduced accuracy in fallback mode."""
59
+ logger.warning(
60
+ "Using grep-based fallback for code analysis. "
61
+ "Accuracy: 60-75% (vs 90-95% with ast-grep). "
62
+ "Install ast-grep-cli for better results: pip install ast-grep-cli"
63
+ )
64
+
65
+
66
+ # =============================================================================
67
+ # Main Analyzer Class
68
+ # =============================================================================
69
+
70
+ class GrepFallbackAnalyzer:
71
+ """Grep-based pattern matching for code analysis."""
72
+
73
+ # Built-in security patterns
74
+ SECURITY_PATTERNS: List[GrepPattern] = [
75
+ GrepPattern(
76
+ id="SEC-001",
77
+ pattern=r'(SELECT|INSERT|UPDATE|DELETE).*WHERE.*[\+\{]|[\+\{].*WHERE|execute\(["\']SELECT.*[\+\{]|f["\']SELECT.*\{|\.format\(.*SELECT',
78
+ severity="CRITICAL",
79
+ message="Potential SQL injection via string concatenation",
80
+ category="security",
81
+ languages=["python", "py", "csharp", "cs"]
82
+ ),
83
+ GrepPattern(
84
+ id="SEC-002",
85
+ pattern=r'(API_KEY|SECRET|PASSWORD|TOKEN|ACCESS_KEY)\s*=\s*["\']\w{8,}["\']',
86
+ severity="HIGH",
87
+ message="Hardcoded secret detected",
88
+ category="security",
89
+ languages=["python", "py", "javascript", "js", "typescript", "ts", "csharp", "cs"]
90
+ ),
91
+ GrepPattern(
92
+ id="SEC-003",
93
+ pattern=r'(AWS_ACCESS_KEY|AWS_SECRET|AKIA[0-9A-Z]{16})',
94
+ severity="CRITICAL",
95
+ message="AWS credentials detected",
96
+ category="security",
97
+ languages=["python", "py", "javascript", "js", "typescript", "ts", "csharp", "cs"]
98
+ ),
99
+ ]
100
+
101
+ def __init__(self, custom_patterns: Optional[List[GrepPattern]] = None):
102
+ """
103
+ Initialize analyzer with patterns.
104
+
105
+ Args:
106
+ custom_patterns: Additional patterns to use
107
+ """
108
+ self.patterns = self.SECURITY_PATTERNS.copy()
109
+ if custom_patterns:
110
+ self.patterns.extend(custom_patterns)
111
+
112
+ def analyze_file(self, file_path: str) -> List[Dict]:
113
+ """
114
+ Analyze single file with grep patterns.
115
+
116
+ Args:
117
+ file_path: Path to file to analyze
118
+
119
+ Returns:
120
+ List of violations found (as dicts for compatibility)
121
+ """
122
+ violations = []
123
+ file_path_obj = Path(file_path)
124
+
125
+ if not file_path_obj.exists():
126
+ return violations
127
+
128
+ if not file_path_obj.is_file():
129
+ return violations
130
+
131
+ # Skip binary files
132
+ try:
133
+ with open(file_path_obj, 'r', encoding='utf-8') as f:
134
+ content = f.read()
135
+ except (UnicodeDecodeError, PermissionError):
136
+ # Binary file or permission denied - skip gracefully
137
+ return violations
138
+
139
+ # Apply each pattern
140
+ for pattern in self.patterns:
141
+ # Check if pattern applies to this file type
142
+ if pattern.languages:
143
+ file_ext = file_path_obj.suffix.lstrip('.')
144
+ if file_ext not in pattern.languages and not any(lang in file_ext for lang in pattern.languages):
145
+ continue
146
+
147
+ # Search for pattern in content
148
+ matches = re.finditer(pattern.pattern, content, re.MULTILINE | re.IGNORECASE)
149
+
150
+ for match in matches:
151
+ # Find line number and column
152
+ line_num = content[:match.start()].count('\n') + 1
153
+ line_start = content.rfind('\n', 0, match.start()) + 1
154
+ column = match.start() - line_start + 1
155
+
156
+ # Extract evidence (the matching line)
157
+ line_end = content.find('\n', match.start())
158
+ if line_end == -1:
159
+ line_end = len(content)
160
+ evidence = content[line_start:line_end].strip()
161
+
162
+ violation = Violation(
163
+ file=str(file_path_obj),
164
+ line=line_num,
165
+ column=column,
166
+ rule_id=pattern.id,
167
+ severity=pattern.severity,
168
+ message=pattern.message,
169
+ evidence=evidence,
170
+ analysis_method="grep-fallback",
171
+ category=pattern.category
172
+ )
173
+
174
+ violations.append(violation.to_dict())
175
+
176
+ return violations
177
+
178
+ def analyze_directory(self, directory: str,
179
+ category: Optional[str] = None,
180
+ language: Optional[str] = None) -> List[Dict]:
181
+ """
182
+ Analyze directory recursively.
183
+
184
+ Args:
185
+ directory: Directory to scan
186
+ category: Filter by category (security, anti-patterns)
187
+ language: Filter by language (python, typescript, csharp)
188
+
189
+ Returns:
190
+ List of all violations found
191
+ """
192
+ all_violations = []
193
+ directory_path = Path(directory)
194
+
195
+ if not directory_path.exists() or not directory_path.is_dir():
196
+ return all_violations
197
+
198
+ # Find all relevant files
199
+ if language:
200
+ # Map language to file extensions
201
+ ext_map = {
202
+ 'python': ['.py'],
203
+ 'typescript': ['.ts', '.tsx'],
204
+ 'javascript': ['.js', '.jsx'],
205
+ 'csharp': ['.cs']
206
+ }
207
+ extensions = ext_map.get(language, [])
208
+ else:
209
+ extensions = ['.py', '.ts', '.tsx', '.js', '.jsx', '.cs']
210
+
211
+ # Scan all files
212
+ for ext in extensions:
213
+ for file_path in directory_path.rglob(f'*{ext}'):
214
+ if file_path.is_file():
215
+ violations = self.analyze_file(str(file_path))
216
+
217
+ # Filter by category if specified
218
+ if category:
219
+ violations = [v for v in violations if v.get('category') == category]
220
+
221
+ all_violations.extend(violations)
222
+
223
+ return all_violations
224
+
225
+ def format_results(self, violations: List[Dict], format: str = "json") -> str:
226
+ """
227
+ Format results for output.
228
+
229
+ Args:
230
+ violations: List of violations
231
+ format: Output format (json, text, markdown)
232
+
233
+ Returns:
234
+ Formatted output string
235
+ """
236
+ if format == "json":
237
+ return self._format_json(violations)
238
+ elif format == "text":
239
+ return self._format_text(violations)
240
+ elif format == "markdown":
241
+ return self._format_markdown(violations)
242
+ else:
243
+ return self._format_json(violations)
244
+
245
+ def _format_json(self, violations: List[Dict]) -> str:
246
+ """Format as JSON matching ast-grep schema."""
247
+ # Calculate summary
248
+ by_severity = {}
249
+ for v in violations:
250
+ severity = v.get('severity', 'UNKNOWN')
251
+ by_severity[severity] = by_severity.get(severity, 0) + 1
252
+
253
+ result = {
254
+ "violations": violations,
255
+ "analysis_method": "grep-fallback",
256
+ "summary": {
257
+ "total_violations": len(violations),
258
+ "by_severity": by_severity,
259
+ "accuracy_note": "60-75% vs 90-95% with ast-grep"
260
+ }
261
+ }
262
+
263
+ return json.dumps(result, indent=2)
264
+
265
+ def _format_text(self, violations: List[Dict]) -> str:
266
+ """Format as human-readable text."""
267
+ if not violations:
268
+ return "No violations found.\n"
269
+
270
+ lines = []
271
+ lines.append(f"Found {len(violations)} violation(s):\n")
272
+
273
+ for v in violations:
274
+ lines.append(f"{v.get('severity', 'UNKNOWN')}: {v.get('file', 'unknown')}")
275
+ lines.append(f" Line {v.get('line', '?')}, Column {v.get('column', '?')}")
276
+ lines.append(f" {v.get('message', 'No message')}")
277
+ lines.append(f" Evidence: {v.get('evidence', 'N/A')}")
278
+ lines.append("")
279
+
280
+ return "\n".join(lines)
281
+
282
+ def _format_markdown(self, violations: List[Dict]) -> str:
283
+ """Format as Markdown."""
284
+ if not violations:
285
+ return "## Code Analysis Results\n\nNo violations found.\n"
286
+
287
+ lines = []
288
+ lines.append("## Code Analysis Results\n")
289
+ lines.append(f"**Total Violations:** {len(violations)}\n")
290
+ lines.append("**Analysis Method:** grep-fallback (60-75% accuracy)\n")
291
+ lines.append("### Violations\n")
292
+
293
+ for v in violations:
294
+ lines.append(f"#### {v.get('severity', 'UNKNOWN')}: {v.get('rule_id', 'UNKNOWN')}\n")
295
+ lines.append(f"**File:** `{v.get('file', 'unknown')}` ")
296
+ lines.append(f"**Line:** {v.get('line', '?')}, **Column:** {v.get('column', '?')}\n")
297
+ lines.append(f"**Message:** {v.get('message', 'No message')}\n")
298
+ lines.append(f"**Evidence:**\n```\n{v.get('evidence', 'N/A')}\n```\n")
299
+
300
+ return "\n".join(lines)
@@ -0,0 +1,186 @@
1
+ #!/bin/bash
2
+ #
3
+ # DevForgeAI Pre-Commit Hook Installer
4
+ #
5
+ # Installs pre-commit hook that validates:
6
+ # - DoD completion (prevents autonomous deferrals)
7
+ # - Story file format
8
+ # - User approval markers
9
+ #
10
+ # Based on industry patterns: pre-commit.com, SpecDriven AI
11
+ #
12
+
13
+ set -e
14
+
15
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
16
+ echo " DevForgeAI Pre-Commit Hook Installer"
17
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
18
+ echo ""
19
+
20
+ # Check if in Git repository
21
+ if [ ! -d ".git" ]; then
22
+ echo "❌ Error: Not in a Git repository"
23
+ echo ""
24
+ echo "Initialize Git first:"
25
+ echo " git init"
26
+ echo " git add ."
27
+ echo " git commit -m 'Initial commit'"
28
+ echo ""
29
+ exit 1
30
+ fi
31
+
32
+ # Check if Python available
33
+ if ! command -v python3 &> /dev/null; then
34
+ echo "❌ Error: Python 3 not found"
35
+ echo ""
36
+ echo "Install Python 3.8 or higher"
37
+ exit 1
38
+ fi
39
+
40
+ # Check if DevForgeAI CLI validators available
41
+ if [ ! -f ".claude/scripts/devforgeai_cli/validators/dod_validator.py" ]; then
42
+ echo "❌ Error: DevForgeAI CLI not found"
43
+ echo ""
44
+ echo "Expected location: .claude/scripts/devforgeai_cli/"
45
+ echo ""
46
+ echo "Install DevForgeAI CLI first:"
47
+ echo " pip install -e .claude/scripts/"
48
+ exit 1
49
+ fi
50
+
51
+ echo "Installing pre-commit hook..."
52
+ echo ""
53
+
54
+ # Create pre-commit hook
55
+ cat > .git/hooks/pre-commit <<'EOF'
56
+ #!/bin/bash
57
+ #
58
+ # DevForgeAI Pre-Commit Validation Hook
59
+ #
60
+ # Validates story files before commit to prevent:
61
+ # - Autonomous deferrals (DoD [x] but Impl [ ] without user approval)
62
+ # - Missing Implementation Notes
63
+ # - Invalid deferral justifications
64
+ #
65
+ # To bypass (NOT RECOMMENDED): git commit --no-verify
66
+ #
67
+
68
+ echo ""
69
+ echo "🔍 DevForgeAI Validators Running..."
70
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
71
+
72
+ VALIDATION_FAILED=0
73
+
74
+ # ============================================================================
75
+ # Registry Drift Detection (STORY-109)
76
+ # ============================================================================
77
+ AGENT_FILES=$(git diff --cached --name-only --diff-filter=d | grep '^\.claude/agents/.*\.md$' || true)
78
+ if [ -n "$AGENT_FILES" ]; then
79
+ echo " 📋 Checking subagent registry..."
80
+ if [ -f "scripts/generate-subagent-registry.sh" ]; then
81
+ if ! bash scripts/generate-subagent-registry.sh --check 2>/dev/null; then
82
+ echo " ❌ Registry out of date"
83
+ echo " Run: bash scripts/generate-subagent-registry.sh"
84
+ echo " Then: git add CLAUDE.md"
85
+ VALIDATION_FAILED=1
86
+ else
87
+ echo " ✅ Registry up to date"
88
+ fi
89
+ fi
90
+ fi
91
+
92
+ # ============================================================================
93
+ # Story-Scoped Validation (STORY-121)
94
+ # Set DEVFORGEAI_STORY=STORY-NNN to validate only that story
95
+ # ============================================================================
96
+
97
+ if [ -n "$DEVFORGEAI_STORY" ]; then
98
+ # Validate format: STORY-NNN (3+ digits, uppercase only)
99
+ if ! echo "$DEVFORGEAI_STORY" | grep -qE '^STORY-[0-9]{3,}$'; then
100
+ echo " WARNING: Invalid DEVFORGEAI_STORY format: $DEVFORGEAI_STORY"
101
+ echo " Expected: STORY-NNN (e.g., STORY-120)"
102
+ echo " Falling back to unscoped validation..."
103
+ DEVFORGEAI_STORY=""
104
+ fi
105
+ fi
106
+
107
+ if [ -n "$DEVFORGEAI_STORY" ]; then
108
+ # Scoped validation - only validate specific story
109
+ STORY_FILES=$(git diff --cached --name-only --diff-filter=d | grep "${DEVFORGEAI_STORY}" | grep -v '^tests/' || true)
110
+ echo " Scoped to: $DEVFORGEAI_STORY"
111
+ else
112
+ # Default behavior - validate all staged story files
113
+ STORY_FILES=$(git diff --cached --name-only --diff-filter=d | grep '\.story\.md$' | grep -v '^tests/' || true)
114
+ fi
115
+
116
+ if [ -z "$STORY_FILES" ]; then
117
+ echo " No story files to validate"
118
+ echo "✅ Pre-commit validation passed"
119
+ echo ""
120
+ exit 0
121
+ fi
122
+
123
+ # Validate each story file
124
+ VALIDATION_FAILED=0
125
+
126
+ for file in $STORY_FILES; do
127
+ # Skip if file doesn't exist (shouldn't happen with --diff-filter=d, but safety check)
128
+ if [ ! -f "$file" ]; then
129
+ echo " ⚠️ Skipping (file not found): $file"
130
+ continue
131
+ fi
132
+
133
+ echo " 📋 Validating: $file"
134
+
135
+ # Run DoD validator with PYTHONPATH set (fixes relative import issue)
136
+ # This allows the validator to import from parent package without pip install
137
+ if PYTHONPATH=".claude/scripts:$PYTHONPATH" python3 -m devforgeai_cli.validators.dod_validator "$file" --project-root .; then
138
+ echo " ✅ Passed"
139
+ else
140
+ echo " ❌ Failed"
141
+ VALIDATION_FAILED=1
142
+ fi
143
+ done
144
+
145
+ echo ""
146
+
147
+ if [ $VALIDATION_FAILED -eq 1 ]; then
148
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
149
+ echo "❌ COMMIT BLOCKED - Fix violations"
150
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
151
+ echo ""
152
+ echo "One or more story files have validation errors."
153
+ echo "Fix the violations shown above before committing."
154
+ echo ""
155
+ echo "To bypass validation (NOT RECOMMENDED):"
156
+ echo " git commit --no-verify"
157
+ echo ""
158
+ exit 1
159
+ fi
160
+
161
+ echo "✅ All validators passed - commit allowed"
162
+ echo ""
163
+ exit 0
164
+ EOF
165
+
166
+ # Make hook executable
167
+ chmod +x .git/hooks/pre-commit
168
+
169
+ echo "✅ Pre-commit hook installed successfully"
170
+ echo ""
171
+ echo "Location: .git/hooks/pre-commit"
172
+ echo ""
173
+ echo "The hook will automatically run on 'git commit' and validate:"
174
+ echo " • DoD completion status"
175
+ echo " • Autonomous deferral detection"
176
+ echo " • User approval markers for deferrals"
177
+ echo " • Story/ADR reference validation"
178
+ echo ""
179
+ echo "To test the hook:"
180
+ echo " git add devforgeai/specs/Stories/STORY-XXX.story.md"
181
+ echo " git commit -m 'Test commit'"
182
+ echo ""
183
+ echo "To bypass validation (not recommended):"
184
+ echo " git commit --no-verify"
185
+ echo ""
186
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -0,0 +1,59 @@
1
+ #!/bin/bash
2
+ # Reusable helper function for feedback hook integration
3
+ # Used by: /ideate, /create-story, /create-epic, /create-sprint, /create-context, /dev
4
+ # Pattern: STORY-023 pilot, implemented across STORY-027-031
5
+
6
+ set -euo pipefail
7
+
8
+ # Usage: invoke_feedback_hooks.sh <operation> <status> [additional-args...]
9
+ # Examples:
10
+ # invoke_feedback_hooks.sh ideate completed --artifacts='[epic1.md]' --complexity-score=42
11
+ # invoke_feedback_hooks.sh dev completed --story=STORY-001
12
+ # invoke_feedback_hooks.sh create-context completed
13
+
14
+ OPERATION="${1:-}"
15
+ STATUS="${2:-completed}"
16
+ shift 2 || true # Remove first 2 args, keep remaining as passthrough
17
+
18
+ # Validate required arguments
19
+ if [ -z "$OPERATION" ]; then
20
+ echo "❌ ERROR: Operation required" >&2
21
+ echo "Usage: invoke_feedback_hooks.sh <operation> <status> [additional-args...]" >&2
22
+ exit 1
23
+ fi
24
+
25
+ # Phase 1: Check Hook Eligibility
26
+ echo "Checking hook eligibility for operation: $OPERATION..."
27
+
28
+ devforgeai check-hooks --operation="$OPERATION" --status="$STATUS" 2>/dev/null
29
+ CHECK_EXIT=$?
30
+
31
+ # Interpret exit code
32
+ case $CHECK_EXIT in
33
+ 0)
34
+ # Eligible - proceed to invocation
35
+ echo "✓ Hooks eligible for $OPERATION"
36
+ ;;
37
+ 1)
38
+ # Not eligible (disabled/rate-limited) - silent exit
39
+ exit 0
40
+ ;;
41
+ *)
42
+ # Unexpected error - warn and continue
43
+ echo "⚠️ Hook eligibility check failed (exit code: $CHECK_EXIT), continuing..." >&2
44
+ exit 0
45
+ ;;
46
+ esac
47
+
48
+ # Phase 2: Invoke Feedback Hooks
49
+ echo "Invoking feedback hooks for $OPERATION..."
50
+
51
+ devforgeai invoke-hooks --operation="$OPERATION" "$@" 2>/dev/null || {
52
+ # Hook invocation failed - non-blocking
53
+ echo "⚠️ Post-$OPERATION feedback skipped (hook system unavailable)" >&2
54
+ exit 0
55
+ }
56
+
57
+ # Phase 3: Display Success
58
+ echo "✓ Post-$OPERATION feedback initiated"
59
+ exit 0
@@ -0,0 +1,122 @@
1
+ #!/bin/bash
2
+ # Migrate story template v2.0 → v2.1 (remove AC header checkboxes)
3
+ # Usage: migrate-ac-headers.sh <story-file-or-directory>
4
+
5
+ TARGET="$1"
6
+
7
+ if [[ -z "$TARGET" ]]; then
8
+ echo "Usage: migrate-ac-headers.sh <story-file-or-directory>"
9
+ echo ""
10
+ echo "Examples:"
11
+ echo " migrate-ac-headers.sh devforgeai/specs/Stories/STORY-052.story.md"
12
+ echo " migrate-ac-headers.sh devforgeai/specs/Stories/"
13
+ exit 1
14
+ fi
15
+
16
+ # Track if any file migration failed
17
+ MIGRATION_FAILED=0
18
+
19
+ # ============================================================================
20
+ # Helper: update_format_version(file)
21
+ # Updates format_version from 2.0 to 2.1 in YAML frontmatter
22
+ # Handles three quote formats: double quotes, single quotes, and unquoted
23
+ # ============================================================================
24
+ update_format_version() {
25
+ local file="$1"
26
+
27
+ # Handle double quotes: format_version: "2.0"
28
+ sed -i 's/format_version: "2.0"/format_version: "2.1"/' "$file"
29
+
30
+ # Handle single quotes: format_version: '2.0'
31
+ sed -i "s/format_version: '2.0'/format_version: \"2.1\"/" "$file"
32
+
33
+ # Handle unquoted: format_version: 2.0
34
+ sed -i 's/format_version: 2.0$/format_version: "2.1"/' "$file"
35
+ }
36
+
37
+ # ============================================================================
38
+ # Helper: migrate_ac_headers(file)
39
+ # Converts AC header format from v2.0 to v2.1
40
+ # Changes: ### N. [ ] Title → ### AC#N: Title
41
+ # ============================================================================
42
+ migrate_ac_headers() {
43
+ local file="$1"
44
+
45
+ sed -i 's/^### \([0-9]\+\)\. \[ \] /### AC#\1: /' "$file"
46
+ }
47
+
48
+ # ============================================================================
49
+ # Helper: backup_file(file)
50
+ # Creates backup copy of file with .backup extension
51
+ # ============================================================================
52
+ backup_file() {
53
+ local file="$1"
54
+
55
+ cp "$file" "$file.backup"
56
+ }
57
+
58
+ # ============================================================================
59
+ # Function: migrate_file(file)
60
+ # Main entry point for migrating a single story file
61
+ # Performs: backup → migrate AC headers → update format version
62
+ # ============================================================================
63
+ migrate_file() {
64
+ local file="$1"
65
+
66
+ if [[ ! -f "$file" ]]; then
67
+ echo "Error: File not found: $file"
68
+ return 1
69
+ fi
70
+
71
+ backup_file "$file"
72
+ migrate_ac_headers "$file"
73
+ update_format_version "$file"
74
+
75
+ echo "Migrated: $file"
76
+ echo " Backup: $file.backup"
77
+ }
78
+
79
+ # ============================================================================
80
+ # Main Logic: Process TARGET (file or directory)
81
+ # ============================================================================
82
+
83
+ if [[ -d "$TARGET" ]]; then
84
+ # Directory mode - migrate all story files
85
+ # Tracks failures and reports summary
86
+ FILES_PROCESSED=0
87
+ FILES_FAILED=0
88
+
89
+ for file in "$TARGET"/*.story.md; do
90
+ if [[ -f "$file" ]]; then
91
+ FILES_PROCESSED=$((FILES_PROCESSED + 1))
92
+ if ! migrate_file "$file"; then
93
+ FILES_FAILED=$((FILES_FAILED + 1))
94
+ MIGRATION_FAILED=1
95
+ fi
96
+ fi
97
+ done
98
+
99
+ # Report directory migration summary
100
+ if [[ $FILES_PROCESSED -gt 0 ]]; then
101
+ echo ""
102
+ echo "Directory migration complete:"
103
+ echo " Files processed: $FILES_PROCESSED"
104
+ if [[ $FILES_FAILED -gt 0 ]]; then
105
+ echo " Files failed: $FILES_FAILED"
106
+ fi
107
+ fi
108
+ else
109
+ # Single file mode
110
+ if ! migrate_file "$TARGET"; then
111
+ MIGRATION_FAILED=1
112
+ fi
113
+ fi
114
+
115
+ echo ""
116
+ echo "Migration complete!"
117
+ echo "To undo, restore from .backup files"
118
+
119
+ # Exit with error if migration failed
120
+ if [[ $MIGRATION_FAILED -eq 1 ]]; then
121
+ exit 1
122
+ fi