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,716 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ DevForgeAI CLI - Main Entry Point
4
+
5
+ Command-line interface for DevForgeAI workflow validators.
6
+
7
+ Commands:
8
+ validate-dod Validate Definition of Done completion
9
+ check-git Check Git repository availability
10
+ validate-context Validate context files exist
11
+ check-hooks Check if hooks should trigger for an operation
12
+ invoke-hooks Invoke devforgeai-feedback skill for operation
13
+ phase-init Initialize phase state file for a story
14
+ phase-check Check if phase transition is allowed
15
+ phase-complete Mark a phase as complete
16
+ phase-status Display current phase status
17
+ phase-record Record subagent invocation for a phase
18
+ ast-grep scan Semantic code analysis with ast-grep or grep fallback
19
+
20
+ Based on industry research (SpecDriven AI, pre-commit patterns, DoD checkers).
21
+ """
22
+
23
+ import sys
24
+ import argparse
25
+ from pathlib import Path
26
+
27
+
28
+ def main():
29
+ """Main CLI entry point."""
30
+ parser = argparse.ArgumentParser(
31
+ prog='devforgeai-validate',
32
+ description='DevForgeAI Workflow Validators',
33
+ epilog='For detailed help: devforgeai-validate <command> --help'
34
+ )
35
+
36
+ parser.add_argument('--version', action='version', version='%(prog)s 0.1.0')
37
+
38
+ subparsers = parser.add_subparsers(dest='command', help='Available commands')
39
+
40
+ # ======================================================================
41
+ # validate-dod command
42
+ # ======================================================================
43
+ dod_parser = subparsers.add_parser(
44
+ 'validate-dod',
45
+ help='Validate Definition of Done completion',
46
+ description='Detects autonomous deferrals and validates user approval markers'
47
+ )
48
+ dod_parser.add_argument(
49
+ 'story_file',
50
+ help='Path to story file (.story.md)'
51
+ )
52
+ dod_parser.add_argument(
53
+ '--format',
54
+ choices=['text', 'json'],
55
+ default='text',
56
+ help='Output format (default: text)'
57
+ )
58
+ dod_parser.add_argument(
59
+ '--project-root',
60
+ default='.',
61
+ help='Project root directory (default: current directory)'
62
+ )
63
+
64
+ # ======================================================================
65
+ # check-git command
66
+ # ======================================================================
67
+ git_parser = subparsers.add_parser(
68
+ 'check-git',
69
+ help='Check if directory is a Git repository',
70
+ description='Validates Git availability for DevForgeAI workflows'
71
+ )
72
+ git_parser.add_argument(
73
+ '--directory',
74
+ default='.',
75
+ help='Directory to check (default: current directory)'
76
+ )
77
+ git_parser.add_argument(
78
+ '--format',
79
+ choices=['text', 'json'],
80
+ default='text',
81
+ help='Output format (default: text)'
82
+ )
83
+
84
+ # ======================================================================
85
+ # validate-context command
86
+ # ======================================================================
87
+ context_parser = subparsers.add_parser(
88
+ 'validate-context',
89
+ help='Validate context files exist',
90
+ description='Checks all 6 DevForgeAI context files are present and non-empty'
91
+ )
92
+ context_parser.add_argument(
93
+ '--directory',
94
+ default='.',
95
+ help='Project root directory (default: current directory)'
96
+ )
97
+ context_parser.add_argument(
98
+ '--format',
99
+ choices=['text', 'json'],
100
+ default='text',
101
+ help='Output format (default: text)'
102
+ )
103
+
104
+ # ======================================================================
105
+ # check-hooks command
106
+ # ======================================================================
107
+ hooks_parser = subparsers.add_parser(
108
+ 'check-hooks',
109
+ help='Check if hooks should trigger for an operation',
110
+ description='Validates hook configuration and determines trigger status'
111
+ )
112
+ hooks_parser.add_argument(
113
+ '--operation',
114
+ required=True,
115
+ help='Operation name (e.g., dev, qa, release)'
116
+ )
117
+ hooks_parser.add_argument(
118
+ '--status',
119
+ required=True,
120
+ choices=['success', 'failure', 'partial'],
121
+ help='Operation status'
122
+ )
123
+ hooks_parser.add_argument(
124
+ '--config',
125
+ default=None,
126
+ help='Path to hooks.yaml config file (default: devforgeai/config/hooks.yaml)'
127
+ )
128
+
129
+ # ======================================================================
130
+ # invoke-hooks command
131
+ # ======================================================================
132
+ invoke_hooks_parser = subparsers.add_parser(
133
+ 'invoke-hooks',
134
+ help='Invoke devforgeai-feedback skill for operation',
135
+ description='Extracts operation context and invokes devforgeai-feedback skill for retrospective feedback'
136
+ )
137
+ invoke_hooks_parser.add_argument(
138
+ '--operation',
139
+ required=True,
140
+ help='Operation name (e.g., dev, qa, release)'
141
+ )
142
+ invoke_hooks_parser.add_argument(
143
+ '--story',
144
+ default=None,
145
+ help='Story ID (format: STORY-NNN)'
146
+ )
147
+ invoke_hooks_parser.add_argument(
148
+ '--verbose',
149
+ action='store_true',
150
+ help='Enable verbose logging'
151
+ )
152
+
153
+ # ======================================================================
154
+ # phase-init command (STORY-148)
155
+ # ======================================================================
156
+ phase_init_parser = subparsers.add_parser(
157
+ 'phase-init',
158
+ help='Initialize phase state file for a story',
159
+ description='Creates a new phase state file to track TDD workflow execution'
160
+ )
161
+ phase_init_parser.add_argument(
162
+ 'story_id',
163
+ help='Story ID (format: STORY-XXX)'
164
+ )
165
+ phase_init_parser.add_argument(
166
+ '--project-root',
167
+ default='.',
168
+ help='Project root directory (default: current directory)'
169
+ )
170
+ phase_init_parser.add_argument(
171
+ '--format',
172
+ choices=['text', 'json'],
173
+ default='text',
174
+ help='Output format (default: text)'
175
+ )
176
+ phase_init_parser.add_argument(
177
+ '--workflow',
178
+ default='dev',
179
+ help='Workflow type (default: dev). Built-in: dev, qa. Extensible via WORKFLOW_SCHEMAS registry (STORY-521)'
180
+ )
181
+
182
+ # ======================================================================
183
+ # phase-check command (STORY-148)
184
+ # ======================================================================
185
+ phase_check_parser = subparsers.add_parser(
186
+ 'phase-check',
187
+ help='Check if phase transition is allowed',
188
+ description='Validates that phase transition follows sequential order and all subagents were invoked'
189
+ )
190
+ phase_check_parser.add_argument(
191
+ 'story_id',
192
+ help='Story ID (format: STORY-XXX)'
193
+ )
194
+ phase_check_parser.add_argument(
195
+ '--from',
196
+ dest='from_phase',
197
+ required=True,
198
+ help='Source phase (e.g., 01)'
199
+ )
200
+ phase_check_parser.add_argument(
201
+ '--to',
202
+ dest='to_phase',
203
+ required=True,
204
+ help='Target phase (e.g., 02)'
205
+ )
206
+ phase_check_parser.add_argument(
207
+ '--project-root',
208
+ default='.',
209
+ help='Project root directory'
210
+ )
211
+ phase_check_parser.add_argument(
212
+ '--format',
213
+ choices=['text', 'json'],
214
+ default='text',
215
+ help='Output format'
216
+ )
217
+
218
+ # ======================================================================
219
+ # phase-complete command (STORY-148)
220
+ # ======================================================================
221
+ phase_complete_parser = subparsers.add_parser(
222
+ 'phase-complete',
223
+ help='Mark a phase as complete',
224
+ description='Updates phase status to completed and advances current phase'
225
+ )
226
+ phase_complete_parser.add_argument(
227
+ 'story_id',
228
+ help='Story ID (format: STORY-XXX)'
229
+ )
230
+ phase_complete_parser.add_argument(
231
+ '--phase',
232
+ required=True,
233
+ help='Phase to complete (e.g., 02)'
234
+ )
235
+ phase_complete_parser.add_argument(
236
+ '--checkpoint-passed',
237
+ action='store_true',
238
+ default=True,
239
+ help='Whether checkpoint validation passed'
240
+ )
241
+ phase_complete_parser.add_argument(
242
+ '--checkpoint-failed',
243
+ action='store_true',
244
+ help='Mark checkpoint as failed'
245
+ )
246
+ phase_complete_parser.add_argument(
247
+ '--project-root',
248
+ default='.',
249
+ help='Project root directory'
250
+ )
251
+ phase_complete_parser.add_argument(
252
+ '--format',
253
+ choices=['text', 'json'],
254
+ default='text',
255
+ help='Output format'
256
+ )
257
+ phase_complete_parser.add_argument(
258
+ '--workflow',
259
+ default='dev',
260
+ help='Workflow type (default: dev). Built-in: dev, qa. Extensible via WORKFLOW_SCHEMAS registry (STORY-521)'
261
+ )
262
+
263
+ # ======================================================================
264
+ # phase-status command (STORY-148)
265
+ # ======================================================================
266
+ phase_status_parser = subparsers.add_parser(
267
+ 'phase-status',
268
+ help='Display current phase status',
269
+ description='Shows workflow progress and phase completion status'
270
+ )
271
+ phase_status_parser.add_argument(
272
+ 'story_id',
273
+ help='Story ID (format: STORY-XXX)'
274
+ )
275
+ phase_status_parser.add_argument(
276
+ '--project-root',
277
+ default='.',
278
+ help='Project root directory'
279
+ )
280
+ phase_status_parser.add_argument(
281
+ '--format',
282
+ choices=['text', 'json'],
283
+ default='text',
284
+ help='Output format'
285
+ )
286
+
287
+ # ======================================================================
288
+ # phase-record command (STORY-149 AC#4)
289
+ # ======================================================================
290
+ phase_record_parser = subparsers.add_parser(
291
+ 'phase-record',
292
+ help='Record subagent invocation for a phase',
293
+ description='Appends subagent to phase subagents_invoked list (idempotent)'
294
+ )
295
+ phase_record_parser.add_argument(
296
+ 'story_id',
297
+ help='Story ID (format: STORY-XXX)'
298
+ )
299
+ phase_record_parser.add_argument(
300
+ '--phase',
301
+ required=True,
302
+ help='Phase ID (01-10)'
303
+ )
304
+ phase_record_parser.add_argument(
305
+ '--subagent',
306
+ required=True,
307
+ help='Subagent name that was invoked'
308
+ )
309
+ phase_record_parser.add_argument(
310
+ '--project-root',
311
+ default='.',
312
+ help='Project root directory (default: current directory)'
313
+ )
314
+ phase_record_parser.add_argument(
315
+ '--format',
316
+ choices=['text', 'json'],
317
+ default='text',
318
+ help='Output format (default: text)'
319
+ )
320
+
321
+ # ======================================================================
322
+ # phase-observe command (STORY-188)
323
+ # ======================================================================
324
+ phase_observe_parser = subparsers.add_parser(
325
+ 'phase-observe',
326
+ help='Record workflow observation for a phase',
327
+ description='Captures friction, gaps, successes, and patterns during TDD workflow execution'
328
+ )
329
+ phase_observe_parser.add_argument(
330
+ 'story_id',
331
+ help='Story ID (format: STORY-XXX)'
332
+ )
333
+ phase_observe_parser.add_argument(
334
+ '--phase',
335
+ required=True,
336
+ help='Phase ID (01-10)'
337
+ )
338
+ phase_observe_parser.add_argument(
339
+ '--category',
340
+ required=True,
341
+ choices=['friction', 'gap', 'success', 'pattern'],
342
+ help='Observation category'
343
+ )
344
+ phase_observe_parser.add_argument(
345
+ '--note',
346
+ required=True,
347
+ help='Observation description'
348
+ )
349
+ phase_observe_parser.add_argument(
350
+ '--severity',
351
+ default='medium',
352
+ choices=['low', 'medium', 'high'],
353
+ help='Severity level (default: medium)'
354
+ )
355
+ phase_observe_parser.add_argument(
356
+ '--project-root',
357
+ default='.',
358
+ help='Project root directory (default: current directory)'
359
+ )
360
+ phase_observe_parser.add_argument(
361
+ '--format',
362
+ choices=['text', 'json'],
363
+ default='text',
364
+ help='Output format (default: text)'
365
+ )
366
+
367
+ # ======================================================================
368
+ # validate-installation command (STORY-314)
369
+ # ======================================================================
370
+ validate_install_parser = subparsers.add_parser(
371
+ 'validate-installation',
372
+ help='Validate DevForgeAI installation completeness',
373
+ description='Runs 6 checks to verify installation: CLI, context files, hooks, PYTHONPATH, Git, settings'
374
+ )
375
+ validate_install_parser.add_argument(
376
+ '--project-root',
377
+ default='.',
378
+ help='Project root directory (default: current directory)'
379
+ )
380
+ validate_install_parser.add_argument(
381
+ '--format',
382
+ choices=['text', 'json'],
383
+ default='text',
384
+ help='Output format (default: text)'
385
+ )
386
+
387
+ # ======================================================================
388
+ # ast-grep command (STORY-115)
389
+ # ======================================================================
390
+ astgrep_parser = subparsers.add_parser(
391
+ 'ast-grep',
392
+ help='Semantic code analysis with ast-grep',
393
+ description='Analyze code using ast-grep patterns or grep fallback'
394
+ )
395
+ astgrep_subparsers = astgrep_parser.add_subparsers(dest='ast_grep_subcommand')
396
+
397
+ # ast-grep scan subcommand
398
+ scan_parser = astgrep_subparsers.add_parser(
399
+ 'scan',
400
+ help='Scan directory for code violations'
401
+ )
402
+ scan_parser.add_argument(
403
+ 'path',
404
+ help='Directory to scan'
405
+ )
406
+ scan_parser.add_argument(
407
+ '--category',
408
+ choices=['security', 'anti-patterns', 'complexity', 'architecture'],
409
+ help='Filter by rule category'
410
+ )
411
+ scan_parser.add_argument(
412
+ '--language',
413
+ choices=['python', 'csharp', 'typescript', 'javascript'],
414
+ help='Filter by language'
415
+ )
416
+ scan_parser.add_argument(
417
+ '--format',
418
+ choices=['text', 'json', 'markdown'],
419
+ default='text',
420
+ help='Output format (default: text)'
421
+ )
422
+ scan_parser.add_argument(
423
+ '--fallback',
424
+ action='store_true',
425
+ help='Force grep fallback mode (skip ast-grep)'
426
+ )
427
+
428
+ # ast-grep init subcommand (STORY-116)
429
+ init_parser = astgrep_subparsers.add_parser(
430
+ 'init',
431
+ help='Initialize ast-grep configuration directory'
432
+ )
433
+ init_parser.add_argument(
434
+ '--force',
435
+ action='store_true',
436
+ help='Force overwrite existing configuration'
437
+ )
438
+ init_parser.add_argument(
439
+ '--project-root',
440
+ default='.',
441
+ help='Project root directory (default: current directory)'
442
+ )
443
+
444
+ # ast-grep validate-config subcommand (STORY-116)
445
+ validate_config_parser = astgrep_subparsers.add_parser(
446
+ 'validate-config',
447
+ help='Validate ast-grep configuration'
448
+ )
449
+ validate_config_parser.add_argument(
450
+ '--config',
451
+ default=None,
452
+ help='Path to sgconfig.yml (default: devforgeai/ast-grep/sgconfig.yml)'
453
+ )
454
+ validate_config_parser.add_argument(
455
+ '--format',
456
+ choices=['text', 'json'],
457
+ default='text',
458
+ help='Output format (default: text)'
459
+ )
460
+
461
+ # ======================================================================
462
+ # feedback-reindex command
463
+ # ======================================================================
464
+ feedback_reindex_parser = subparsers.add_parser(
465
+ 'feedback-reindex',
466
+ help='Rebuild feedback index from all feedback sources',
467
+ description='Scans ai-analysis, code-review, and report files to build unified index'
468
+ )
469
+ feedback_reindex_parser.add_argument(
470
+ '--project-root',
471
+ default='.',
472
+ help='Project root directory (default: current directory)'
473
+ )
474
+ feedback_reindex_parser.add_argument(
475
+ '--format',
476
+ choices=['text', 'json'],
477
+ default='text',
478
+ help='Output format (default: text)'
479
+ )
480
+
481
+ # Parse arguments
482
+ args = parser.parse_args()
483
+
484
+ # Show help if no command specified
485
+ if not args.command:
486
+ parser.print_help()
487
+ return 0
488
+
489
+ # Execute command
490
+ try:
491
+ if args.command == 'validate-dod':
492
+ from .validators.dod_validator import validate_dod
493
+ return validate_dod(args.story_file, args.format, args.project_root)
494
+
495
+ elif args.command == 'check-git':
496
+ from .validators.git_validator import validate_git
497
+ return validate_git(args.directory, args.format)
498
+
499
+ elif args.command == 'validate-context':
500
+ from .validators.context_validator import validate_context
501
+ return validate_context(args.directory, args.format)
502
+
503
+ elif args.command == 'check-hooks':
504
+ from .commands.check_hooks import check_hooks_command
505
+ return check_hooks_command(
506
+ operation=args.operation,
507
+ status=args.status,
508
+ config_path=args.config
509
+ )
510
+
511
+ elif args.command == 'invoke-hooks':
512
+ from .commands.invoke_hooks import invoke_hooks_command
513
+ return invoke_hooks_command(
514
+ operation=args.operation,
515
+ story_id=args.story,
516
+ verbose=args.verbose
517
+ )
518
+
519
+ elif args.command == 'phase-init':
520
+ from .commands.phase_commands import phase_init_command
521
+ return phase_init_command(
522
+ story_id=args.story_id,
523
+ project_root=args.project_root,
524
+ format=args.format,
525
+ workflow=getattr(args, 'workflow', 'dev')
526
+ )
527
+
528
+ elif args.command == 'phase-check':
529
+ from .commands.phase_commands import phase_check_command
530
+ return phase_check_command(
531
+ story_id=args.story_id,
532
+ from_phase=args.from_phase,
533
+ to_phase=args.to_phase,
534
+ project_root=args.project_root,
535
+ format=args.format
536
+ )
537
+
538
+ elif args.command == 'phase-complete':
539
+ from .commands.phase_commands import phase_complete_command
540
+ checkpoint = not args.checkpoint_failed if hasattr(args, 'checkpoint_failed') else True
541
+ return phase_complete_command(
542
+ story_id=args.story_id,
543
+ phase=args.phase,
544
+ checkpoint_passed=checkpoint,
545
+ project_root=args.project_root,
546
+ format=args.format,
547
+ workflow=getattr(args, 'workflow', 'dev')
548
+ )
549
+
550
+ elif args.command == 'phase-status':
551
+ from .commands.phase_commands import phase_status_command
552
+ return phase_status_command(
553
+ story_id=args.story_id,
554
+ project_root=args.project_root,
555
+ format=args.format
556
+ )
557
+
558
+ elif args.command == 'phase-record':
559
+ from .commands.phase_commands import phase_record_command
560
+ return phase_record_command(
561
+ story_id=args.story_id,
562
+ phase=args.phase,
563
+ subagent=args.subagent,
564
+ project_root=args.project_root,
565
+ format=args.format
566
+ )
567
+
568
+ elif args.command == 'phase-observe':
569
+ from .commands.phase_commands import phase_observe_command
570
+ return phase_observe_command(
571
+ story_id=args.story_id,
572
+ phase=args.phase,
573
+ category=args.category,
574
+ note=args.note,
575
+ severity=args.severity,
576
+ project_root=args.project_root,
577
+ format=args.format
578
+ )
579
+
580
+ elif args.command == 'validate-installation':
581
+ from .commands.validate_installation import main as validate_installation_main
582
+ return validate_installation_main(
583
+ project_root=args.project_root,
584
+ output_format=args.format
585
+ )
586
+
587
+ elif args.command == 'feedback-reindex':
588
+ from .feedback.feedback_indexer import reindex_all_feedback
589
+ return reindex_all_feedback(
590
+ project_root=args.project_root,
591
+ output_format=args.format
592
+ )
593
+
594
+ elif args.command == 'ast-grep':
595
+ if args.ast_grep_subcommand == 'scan':
596
+ from .validators.ast_grep_validator import AstGrepValidator
597
+ from .validators.grep_fallback import GrepFallbackAnalyzer, log_fallback_warning
598
+
599
+ validator = AstGrepValidator()
600
+
601
+ # Check if we should use fallback mode
602
+ use_fallback = args.fallback or validator.config.get('fallback_mode', False)
603
+
604
+ if use_fallback or not validator.is_installed():
605
+ # Use grep fallback
606
+ log_fallback_warning()
607
+ analyzer = GrepFallbackAnalyzer()
608
+ violations = analyzer.analyze_directory(
609
+ args.path,
610
+ category=args.category,
611
+ language=args.language
612
+ )
613
+ output = analyzer.format_results(violations, format=args.format)
614
+ print(output)
615
+ return 0 if not violations else 1
616
+ else:
617
+ # Validate installation and version
618
+ is_valid, violations = validator.validate(args.path)
619
+
620
+ # For now, just report validation status
621
+ # Full ast-grep integration will be in future stories
622
+ if args.format == 'json':
623
+ import json
624
+ print(json.dumps({"valid": is_valid, "violations": violations}, indent=2))
625
+ else:
626
+ if is_valid:
627
+ print("✓ ast-grep available and compatible")
628
+ else:
629
+ print("✗ ast-grep validation failed:")
630
+ for v in violations:
631
+ print(f" {v['severity']}: {v['error']}")
632
+
633
+ return 0 if is_valid else 1
634
+
635
+ elif args.ast_grep_subcommand == 'init':
636
+ # STORY-116: Initialize ast-grep configuration
637
+ from pathlib import Path
638
+ from .ast_grep.config_init import ConfigurationInitializer
639
+
640
+ project_root = Path(args.project_root).resolve()
641
+ initializer = ConfigurationInitializer(project_root)
642
+ result = initializer.initialize(force=args.force)
643
+
644
+ if result.success:
645
+ print(f"SUCCESS: ast-grep configuration initialized")
646
+ if result.created_paths:
647
+ print(f"Created directories:")
648
+ for p in result.created_paths:
649
+ print(f" - {p}")
650
+ print(f"Configuration file: {result.config_path}")
651
+ return 0
652
+ else:
653
+ print(f"ERROR: {result.error}", file=sys.stderr)
654
+ return 1
655
+
656
+ elif args.ast_grep_subcommand == 'validate-config':
657
+ # STORY-116: Validate ast-grep configuration
658
+ from pathlib import Path
659
+ import json as json_module
660
+ from .ast_grep.config_validator import ConfigurationValidator
661
+
662
+ if args.config:
663
+ config_path = Path(args.config)
664
+ else:
665
+ config_path = Path('devforgeai/ast-grep/sgconfig.yml')
666
+
667
+ validator = ConfigurationValidator(config_path)
668
+ result = validator.validate()
669
+
670
+ if args.format == 'json':
671
+ output = {
672
+ 'valid': result.valid,
673
+ 'errors': [
674
+ {
675
+ 'field': e.field,
676
+ 'message': e.message,
677
+ 'line': e.line
678
+ }
679
+ for e in result.errors
680
+ ],
681
+ 'warnings': result.warnings
682
+ }
683
+ print(json_module.dumps(output, indent=2))
684
+ else:
685
+ if result.valid:
686
+ print("VALID: Configuration is valid")
687
+ if result.warnings:
688
+ for warning in result.warnings:
689
+ print(f" WARNING: {warning}")
690
+ else:
691
+ print("INVALID: Configuration has errors:")
692
+ for error in result.errors:
693
+ line_info = f" (line {error.line})" if error.line else ""
694
+ print(f" {error.field.upper()}{line_info}: {error.message}")
695
+
696
+ return 0 if result.valid else 1
697
+
698
+ else:
699
+ print(f"Unknown ast-grep subcommand: {args.ast_grep_subcommand}", file=sys.stderr)
700
+ return 2
701
+
702
+ else:
703
+ print(f"Unknown command: {args.command}", file=sys.stderr)
704
+ return 2
705
+
706
+ except KeyboardInterrupt:
707
+ print("\nInterrupted by user", file=sys.stderr)
708
+ return 130
709
+
710
+ except Exception as e:
711
+ print(f"ERROR: {e}", file=sys.stderr)
712
+ return 2
713
+
714
+
715
+ if __name__ == '__main__':
716
+ sys.exit(main())