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,344 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Lightweight Deferral Format Validator (Hybrid Approach - Layer 1)
4
+
5
+ Quick format validation for deferred Definition of Done items.
6
+ Part of three-layer defense architecture:
7
+ - Layer 1 (this script): Fast format validation (~200 tokens, <100ms)
8
+ - Layer 2 (task file): Interactive user approval checkpoint
9
+ - Layer 3 (subagent): Comprehensive AI analysis (feasibility, circular deps)
10
+
11
+ This script provides FAST FEEDBACK by catching basic format errors instantly,
12
+ allowing comprehensive validation to be handled by AI subagent (deferral-validator).
13
+
14
+ Usage:
15
+ # Format-only validation (non-blocking warnings)
16
+ python validate_deferrals.py --story-file STORY-006.story.md --format-only
17
+
18
+ # Quiet mode (for automation)
19
+ python validate_deferrals.py --story-file STORY-006.story.md --quiet
20
+
21
+ Exit Codes:
22
+ 0 - Format valid OR warnings only (format-only mode)
23
+ 1 - Format invalid (strict mode, not used in hybrid approach)
24
+ 2 - Configuration error (file not found, parse error)
25
+
26
+ Design Philosophy:
27
+ - Simple and fast (regex pattern matching only)
28
+ - Non-blocking in hybrid architecture (warnings, not errors)
29
+ - Delegates complex analysis to deferral-validator subagent
30
+ - Follows DevForgeAI validation script patterns (Color class, argparse)
31
+
32
+ Related:
33
+ - RCA-006 Recommendation 3 (automated validation)
34
+ - Native Tools Efficiency Analysis (40-73% token savings)
35
+ - .claude/agents/deferral-validator.md (comprehensive validation)
36
+ """
37
+
38
+ import argparse
39
+ import re
40
+ import sys
41
+ from pathlib import Path
42
+ from typing import List, Tuple
43
+ from dataclasses import dataclass
44
+
45
+
46
+ class Color:
47
+ """
48
+ ANSI color codes for terminal output.
49
+
50
+ Pattern from DevForgeAI framework validation scripts:
51
+ - .claude/skills/designing-systems/scripts/validate_all_context.py
52
+ - .claude/skills/devforgeai-qa/scripts/security_scan.py
53
+ """
54
+ RED = '\033[91m'
55
+ GREEN = '\033[92m'
56
+ YELLOW = '\033[93m'
57
+ BLUE = '\033[94m'
58
+ CYAN = '\033[96m'
59
+ BOLD = '\033[1m'
60
+ END = '\033[0m'
61
+
62
+
63
+ @dataclass
64
+ class DoDItem:
65
+ """
66
+ Definition of Done item from story file.
67
+
68
+ Attributes:
69
+ text: Item description text
70
+ line_num: Line number in DoD section (1-indexed)
71
+ context: Next 3 lines after item (for justification detection)
72
+ """
73
+ text: str
74
+ line_num: int
75
+ context: str
76
+
77
+
78
+ class FormatValidator:
79
+ """
80
+ Lightweight format validator for deferral justifications.
81
+
82
+ Validates ONLY format compliance:
83
+ - Incomplete items have justification text
84
+ - Justification matches expected patterns
85
+
86
+ Does NOT validate (delegated to deferral-validator subagent):
87
+ - Story references exist (AI checks file system)
88
+ - ADR references exist (AI checks file system)
89
+ - Circular deferrals (AI analyzes chains)
90
+ - Implementation feasibility (AI analyzes tech spec)
91
+ """
92
+
93
+ # Valid deferral patterns (case-insensitive)
94
+ VALID_PATTERNS = [
95
+ r'Deferred to STORY-\d+:', # Story split deferral
96
+ r'Blocked by:', # External blocker
97
+ r'Out of scope: ADR-\d+' # Scope change with ADR
98
+ ]
99
+
100
+ def __init__(self, story_file: Path):
101
+ """
102
+ Initialize validator.
103
+
104
+ Args:
105
+ story_file: Path to story markdown file
106
+ """
107
+ self.story_file = story_file
108
+
109
+ def validate(self) -> Tuple[bool, List[str]]:
110
+ """
111
+ Run format validation.
112
+
113
+ Returns:
114
+ Tuple of (is_valid, violations_list)
115
+ - is_valid: True if all incomplete items have valid format
116
+ - violations_list: List of violation messages (empty if valid)
117
+ """
118
+ # Step 1: Read story file
119
+ story_content = self._read_story()
120
+
121
+ # Step 2: Extract DoD section
122
+ dod_section = self._extract_dod_section(story_content)
123
+
124
+ if not dod_section:
125
+ # No DoD section found - unusual but not an error for this script
126
+ # (Comprehensive validation in subagent will catch this)
127
+ return True, []
128
+
129
+ # Step 3: Extract incomplete items
130
+ incomplete_items = self._extract_incomplete_items(dod_section)
131
+
132
+ if not incomplete_items:
133
+ # All items complete - valid
134
+ return True, []
135
+
136
+ # Step 4: Check format for each incomplete item
137
+ violations = []
138
+ for item in incomplete_items:
139
+ if not self._has_valid_format(item):
140
+ violations.append(
141
+ f"Line {item.line_num}: '{self._truncate(item.text, 60)}'\n"
142
+ f" Missing justification. Expected one of:\n"
143
+ f" - Deferred to STORY-XXX: [reason]\n"
144
+ f" - Blocked by: [external dependency]\n"
145
+ f" - Out of scope: ADR-XXX"
146
+ )
147
+
148
+ is_valid = len(violations) == 0
149
+ return is_valid, violations
150
+
151
+ def _read_story(self) -> str:
152
+ """
153
+ Read story file content using Python stdlib.
154
+
155
+ Note: Uses Python file I/O (not Bash cat) per native tools efficiency guidance.
156
+ While this script is invoked via Bash, it uses Python stdlib internally,
157
+ which is equivalent to native tool efficiency.
158
+
159
+ Returns:
160
+ Story file content as string
161
+
162
+ Raises:
163
+ SystemExit(2): If file not found or read error
164
+ """
165
+ try:
166
+ with open(self.story_file, 'r', encoding='utf-8') as f:
167
+ return f.read()
168
+ except FileNotFoundError:
169
+ print(
170
+ f"{Color.RED}ERROR: Story file not found: {self.story_file}{Color.END}",
171
+ file=sys.stderr
172
+ )
173
+ sys.exit(2)
174
+ except Exception as e:
175
+ print(
176
+ f"{Color.RED}ERROR reading file: {e}{Color.END}",
177
+ file=sys.stderr
178
+ )
179
+ sys.exit(2)
180
+
181
+ def _extract_dod_section(self, content: str) -> str:
182
+ """
183
+ Extract Definition of Done section from story content.
184
+
185
+ Searches for markdown section between:
186
+ - Start: ## Definition of Done
187
+ - End: Next ## heading (or end of file)
188
+
189
+ Args:
190
+ content: Full story file content
191
+
192
+ Returns:
193
+ DoD section content (empty string if not found)
194
+ """
195
+ # Match DoD section (until next ## heading)
196
+ match = re.search(
197
+ r'## Definition of Done\n(.*?)(?:\n## |\Z)',
198
+ content,
199
+ re.DOTALL
200
+ )
201
+ return match.group(1).strip() if match else ""
202
+
203
+ def _extract_incomplete_items(self, dod_section: str) -> List[DoDItem]:
204
+ """
205
+ Extract all incomplete (- [ ]) items with context.
206
+
207
+ Each item includes the next 3 lines as context for justification detection.
208
+
209
+ Args:
210
+ dod_section: Definition of Done section content
211
+
212
+ Returns:
213
+ List of DoDItem objects for incomplete items
214
+ """
215
+ lines = dod_section.split('\n')
216
+ items = []
217
+
218
+ for i, line in enumerate(lines):
219
+ if line.strip().startswith('- [ ]'):
220
+ # Extract item text (after "- [ ]" marker)
221
+ text = line.strip()[6:].strip()
222
+
223
+ # Get next 3 lines as context (for justification detection)
224
+ context_lines = lines[i+1:min(i+4, len(lines))]
225
+ context = '\n'.join(context_lines)
226
+
227
+ items.append(DoDItem(
228
+ text=text,
229
+ line_num=i+1,
230
+ context=context
231
+ ))
232
+
233
+ return items
234
+
235
+ def _has_valid_format(self, item: DoDItem) -> bool:
236
+ """
237
+ Check if item has valid deferral justification format.
238
+
239
+ Valid formats (in context lines following item):
240
+ - "Deferred to STORY-XXX: [reason]"
241
+ - "Blocked by: [external dependency]"
242
+ - "Out of scope: ADR-XXX"
243
+
244
+ Args:
245
+ item: DoDItem to validate
246
+
247
+ Returns:
248
+ True if valid format found in context, False otherwise
249
+ """
250
+ for pattern in self.VALID_PATTERNS:
251
+ if re.search(pattern, item.context, re.IGNORECASE):
252
+ return True
253
+ return False
254
+
255
+ @staticmethod
256
+ def _truncate(text: str, max_len: int) -> str:
257
+ """Truncate text to max length with ellipsis."""
258
+ return text if len(text) <= max_len else text[:max_len] + "..."
259
+
260
+
261
+ def main():
262
+ """Main entry point for validation script."""
263
+ parser = argparse.ArgumentParser(
264
+ description="Lightweight deferral format validator (Hybrid Approach - Layer 1)",
265
+ formatter_class=argparse.RawDescriptionHelpFormatter,
266
+ epilog="""
267
+ Examples:
268
+ # Format-only validation (non-blocking)
269
+ python validate_deferrals.py --story-file devforgeai/specs/Stories/STORY-006.story.md --format-only
270
+
271
+ # Quiet mode (automation/git hooks)
272
+ python validate_deferrals.py --story-file devforgeai/specs/Stories/STORY-006.story.md --quiet
273
+
274
+ Exit Codes:
275
+ 0 - Validation passed (or warnings only in format-only mode)
276
+ 1 - Validation failed (strict mode, not used in hybrid architecture)
277
+ 2 - Configuration error (file not found, parse error)
278
+
279
+ Integration:
280
+ - Invoked by /dev command Phase 2.5a (Layer 1 quick check)
281
+ - Followed by interactive checkpoint (Layer 2)
282
+ - Followed by deferral-validator subagent (Layer 3)
283
+ """
284
+ )
285
+
286
+ parser.add_argument(
287
+ "--story-file",
288
+ type=Path,
289
+ required=True,
290
+ help="Path to story file to validate (devforgeai/specs/Stories/STORY-XXX.story.md)"
291
+ )
292
+
293
+ parser.add_argument(
294
+ "--format-only",
295
+ action="store_true",
296
+ help="Only validate format (non-blocking, always exit 0 with warnings)"
297
+ )
298
+
299
+ parser.add_argument(
300
+ "--quiet",
301
+ action="store_true",
302
+ help="Suppress output (only return exit code)"
303
+ )
304
+
305
+ args = parser.parse_args()
306
+
307
+ # Validate story file exists (early check)
308
+ if not args.story_file.exists():
309
+ if not args.quiet:
310
+ print(
311
+ f"{Color.RED}ERROR: Story file not found: {args.story_file}{Color.END}",
312
+ file=sys.stderr
313
+ )
314
+ sys.exit(2)
315
+
316
+ # Run validation
317
+ validator = FormatValidator(args.story_file)
318
+ is_valid, violations = validator.validate()
319
+
320
+ # Display results (unless quiet mode)
321
+ if not args.quiet:
322
+ if is_valid:
323
+ print(f"{Color.GREEN}✓ Deferral format validation PASSED{Color.END}")
324
+ print(f" All incomplete DoD items have basic justification format")
325
+ else:
326
+ print(f"{Color.YELLOW}⚠️ Format issues detected ({len(violations)} items){Color.END}\n")
327
+ for violation in violations:
328
+ print(f"{Color.YELLOW}{violation}{Color.END}\n")
329
+
330
+ if args.format_only:
331
+ print(f"{Color.CYAN}Note: Format-only mode - warnings only (non-blocking){Color.END}")
332
+ print(f"{Color.CYAN}Interactive checkpoint will guide you through resolution.{Color.END}")
333
+
334
+ # Exit code handling
335
+ if args.format_only:
336
+ # Format-only mode: Always exit 0 (warnings only, non-blocking)
337
+ sys.exit(0)
338
+ else:
339
+ # Strict mode: Exit 1 on violations (not used in hybrid approach)
340
+ sys.exit(0 if is_valid else 1)
341
+
342
+
343
+ if __name__ == "__main__":
344
+ main()
@@ -364,7 +364,7 @@ Show phase-by-phase completion status with timing and token usage.
364
364
 
365
365
  **6.5: Marker Cleanup (PASSED only)**
366
366
  - Preserve qa-phase-state.json as permanent audit trail
367
- - Delete legacy .qa-phase-N.marker files (superseded)
367
+ - Delete legacy QA marker files if present (superseded by qa-phase-state.json)
368
368
 
369
369
  **CLI gate:**
370
370
  ```
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: researching-market
3
3
  description: Guided market research workflow covering TAM/SAM/SOM market sizing, competitive landscape analysis, and customer interview preparation
4
+ version: "1.0"
4
5
  tools: Read, Write, Edit, Glob, Grep, AskUserQuestion, Agent
5
6
  ---
6
7
 
@@ -334,7 +335,7 @@ After market sizing and competitive analysis (or independently when invoked for
334
335
  Load the customer interview guide reference for methodology guidance:
335
336
 
336
337
  ```
337
- Read(file_path="references/customer-interview-guide.md")
338
+ Read(file_path=".claude/skills/researching-market/references/customer-interview-guide.md")
338
339
  ```
339
340
 
340
341
  For detailed interviewing methodology, see: [customer-interview-guide.md](references/customer-interview-guide.md) (Open-Ended Question Techniques)
@@ -78,7 +78,19 @@ class Copier {
78
78
  // Preserve execute permissions from source (important for .sh scripts)
79
79
  try {
80
80
  const srcStat = await fsp.stat(srcFile);
81
- await fsp.chmod(destFile, srcStat.mode);
81
+ let mode = srcStat.mode;
82
+
83
+ // Ensure .sh files and shebanged scripts are always executable.
84
+ // NTFS/Windows doesn't preserve Unix execute bits, so npm tarballs
85
+ // packed on Windows will contain .sh files with mode 0o644.
86
+ // We force +x on known executable extensions to guarantee they work
87
+ // after installation on Linux/macOS.
88
+ const ext = path.extname(destFile).toLowerCase();
89
+ if (ext === '.sh' || ext === '.py' || destFile.endsWith('/devforgeai-validate')) {
90
+ mode = mode | 0o755;
91
+ }
92
+
93
+ await fsp.chmod(destFile, mode);
82
94
  } catch {
83
95
  // chmod may fail on Windows — non-fatal
84
96
  }