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.
- package/CLAUDE.md +120 -0
- package/bin/devforgeai.js +0 -0
- package/package.json +9 -1
- package/src/CLAUDE.md +699 -0
- package/src/claude/hooks/phase-completion-gate.sh +0 -0
- package/src/claude/scripts/README.md +396 -0
- package/src/claude/scripts/audit-command-skill-overlap.sh +67 -0
- package/src/claude/scripts/check-hooks-fast.sh +70 -0
- package/src/claude/scripts/devforgeai-validate +6 -0
- package/src/claude/scripts/devforgeai_cli/README.md +531 -0
- package/src/claude/scripts/devforgeai_cli/__init__.py +12 -0
- package/src/claude/scripts/devforgeai_cli/cli.py +716 -0
- package/src/claude/scripts/devforgeai_cli/commands/__init__.py +1 -0
- package/src/claude/scripts/devforgeai_cli/commands/check_hooks.py +384 -0
- package/src/claude/scripts/devforgeai_cli/commands/invoke_hooks.py +149 -0
- package/src/claude/scripts/devforgeai_cli/commands/phase_commands.py +731 -0
- package/src/claude/scripts/devforgeai_cli/commands/validate_installation.py +412 -0
- package/src/claude/scripts/devforgeai_cli/context_extraction.py +426 -0
- package/src/claude/scripts/devforgeai_cli/feedback/AC_TO_TEST_MAPPING.md +636 -0
- package/src/claude/scripts/devforgeai_cli/feedback/DELIVERY_SUMMARY.txt +329 -0
- package/src/claude/scripts/devforgeai_cli/feedback/README_TEST_SPECS.md +486 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_IMPLEMENTATION_GUIDE.md +529 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECIFICATIONS.md +2652 -0
- package/src/claude/scripts/devforgeai_cli/feedback/TEST_SPECS_INDEX.md +398 -0
- package/src/claude/scripts/devforgeai_cli/feedback/__init__.py +34 -0
- package/src/claude/scripts/devforgeai_cli/feedback/adaptive_questioning_engine.py +581 -0
- package/src/claude/scripts/devforgeai_cli/feedback/aggregation.py +179 -0
- package/src/claude/scripts/devforgeai_cli/feedback/commands.py +535 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_defaults.py +58 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_manager.py +423 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_models.py +192 -0
- package/src/claude/scripts/devforgeai_cli/feedback/config_schema.py +140 -0
- package/src/claude/scripts/devforgeai_cli/feedback/coverage.json +1 -0
- package/src/claude/scripts/devforgeai_cli/feedback/feature_flag.py +152 -0
- package/src/claude/scripts/devforgeai_cli/feedback/feedback_indexer.py +394 -0
- package/src/claude/scripts/devforgeai_cli/feedback/hot_reload.py +226 -0
- package/src/claude/scripts/devforgeai_cli/feedback/longitudinal.py +115 -0
- package/src/claude/scripts/devforgeai_cli/feedback/models.py +67 -0
- package/src/claude/scripts/devforgeai_cli/feedback/question_router.py +236 -0
- package/src/claude/scripts/devforgeai_cli/feedback/retrospective.py +233 -0
- package/src/claude/scripts/devforgeai_cli/feedback/skip_tracker.py +177 -0
- package/src/claude/scripts/devforgeai_cli/feedback/skip_tracking.py +221 -0
- package/src/claude/scripts/devforgeai_cli/feedback/template_engine.py +549 -0
- package/src/claude/scripts/devforgeai_cli/feedback/validation.py +163 -0
- package/src/claude/scripts/devforgeai_cli/headless/__init__.py +30 -0
- package/src/claude/scripts/devforgeai_cli/headless/answer_models.py +206 -0
- package/src/claude/scripts/devforgeai_cli/headless/answer_resolver.py +204 -0
- package/src/claude/scripts/devforgeai_cli/headless/exceptions.py +36 -0
- package/src/claude/scripts/devforgeai_cli/headless/pattern_matcher.py +156 -0
- package/src/claude/scripts/devforgeai_cli/hooks.py +313 -0
- package/src/claude/scripts/devforgeai_cli/metrics/__init__.py +46 -0
- package/src/claude/scripts/devforgeai_cli/metrics/command_metrics.py +142 -0
- package/src/claude/scripts/devforgeai_cli/metrics/failure_modes.py +152 -0
- package/src/claude/scripts/devforgeai_cli/metrics/story_segmentation.py +181 -0
- package/src/claude/scripts/devforgeai_cli/orchestrate_hooks.py +780 -0
- package/src/claude/scripts/devforgeai_cli/phase_state.py +1229 -0
- package/src/claude/scripts/devforgeai_cli/session/__init__.py +30 -0
- package/src/claude/scripts/devforgeai_cli/session/checkpoint.py +268 -0
- package/src/claude/scripts/devforgeai_cli/tests/__init__.py +1 -0
- package/src/claude/scripts/devforgeai_cli/tests/conftest.py +29 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/TEST_EXECUTION_GUIDE.md +298 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/__init__.py +3 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_adaptive_questioning_engine.py +2171 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_aggregation.py +476 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_defaults.py +133 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_manager.py +592 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_models.py +373 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_config_schema.py +130 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_configuration_management.py +1355 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_edge_cases.py +308 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feature_flag.py +307 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_feedback_indexer.py +384 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_hot_reload.py +580 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_integration.py +402 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_models.py +105 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_question_routing.py +262 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_retrospective.py +333 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracker.py +410 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking.py +159 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_skip_tracking_integration.py +1155 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_template_engine.py +1389 -0
- package/src/claude/scripts/devforgeai_cli/tests/feedback/test_validation_comprehensive.py +210 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/autonomous-deferral-story.md +46 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/missing-impl-notes.md +31 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-deferral-story.md +46 -0
- package/src/claude/scripts/devforgeai_cli/tests/fixtures/valid-story-complete.md +48 -0
- package/src/claude/scripts/devforgeai_cli/tests/manual_test_invoke_hooks.sh +200 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/DELIVERABLES.md +518 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/TEST_SUMMARY.md +468 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/__init__.py +6 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/corrupted-checkpoint.json +1 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/missing-fields-checkpoint.json +4 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/fixtures/valid-checkpoint.json +15 -0
- package/src/claude/scripts/devforgeai_cli/tests/session/test_checkpoint.py +851 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_check_hooks.py +1886 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_depends_on_normalizer.py +171 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_dod_validator.py +97 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_invoke_hooks.py +1902 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands.py +320 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_error_handling.py +1021 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_commands_import.py +697 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_phase_state.py +2187 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking.py +2141 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_skip_tracking_coverage_gap.py +195 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_subagent_enforcement.py +539 -0
- package/src/claude/scripts/devforgeai_cli/tests/test_validate_installation.py +361 -0
- package/src/claude/scripts/devforgeai_cli/utils/__init__.py +11 -0
- package/src/claude/scripts/devforgeai_cli/utils/depends_on_normalizer.py +149 -0
- package/src/claude/scripts/devforgeai_cli/utils/markdown_parser.py +219 -0
- package/src/claude/scripts/devforgeai_cli/utils/story_analyzer.py +249 -0
- package/src/claude/scripts/devforgeai_cli/utils/yaml_parser.py +152 -0
- package/src/claude/scripts/devforgeai_cli/validators/__init__.py +27 -0
- package/src/claude/scripts/devforgeai_cli/validators/ast_grep_validator.py +373 -0
- package/src/claude/scripts/devforgeai_cli/validators/context_validator.py +180 -0
- package/src/claude/scripts/devforgeai_cli/validators/dod_validator.py +309 -0
- package/src/claude/scripts/devforgeai_cli/validators/git_validator.py +107 -0
- package/src/claude/scripts/devforgeai_cli/validators/grep_fallback.py +300 -0
- package/src/claude/scripts/install_hooks.sh +186 -0
- package/src/claude/scripts/invoke_feedback_hooks.sh +59 -0
- package/src/claude/scripts/migrate-ac-headers.sh +122 -0
- package/src/claude/scripts/plan_file_kb.sh +704 -0
- package/src/claude/scripts/requirements.txt +8 -0
- package/src/claude/scripts/session_catalog.sh +543 -0
- package/src/claude/scripts/setup.py +55 -0
- package/src/claude/scripts/start-devforgeai.sh +16 -0
- package/src/claude/scripts/statusline.sh +27 -0
- package/src/claude/scripts/validate_deferrals.py +344 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/detect_anti_patterns.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_all_context.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_architecture.cpython-312.pyc +0 -0
- package/src/claude/skills/designing-systems/scripts/__pycache__/validate_dependencies.cpython-312.pyc +0 -0
- package/src/claude/skills/devforgeai-story-creation/scripts/__pycache__/migrate_story_v1_to_v2.cpython-312.pyc +0 -0
- 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())
|