superkit-mcp-server 1.2.4 → 1.2.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 (162) hide show
  1. package/ARCHITECTURE.md +102 -102
  2. package/README.md +71 -71
  3. package/SUPERKIT.md +168 -168
  4. package/agents/code-archaeologist.md +106 -106
  5. package/agents/coder.md +90 -90
  6. package/agents/data-engineer.md +28 -28
  7. package/agents/devops-engineer.md +242 -242
  8. package/agents/git-manager.md +203 -203
  9. package/agents/orchestrator.md +420 -420
  10. package/agents/penetration-tester.md +188 -188
  11. package/agents/performance-optimizer.md +187 -187
  12. package/agents/planner.md +270 -270
  13. package/agents/qa-automation-engineer.md +103 -103
  14. package/agents/quant-developer.md +32 -32
  15. package/agents/reviewer.md +100 -100
  16. package/agents/scout.md +222 -222
  17. package/agents/tester.md +274 -274
  18. package/agents/ui-designer.md +208 -208
  19. package/build/__tests__/test_apply_prompt_args.js +104 -0
  20. package/build/index.js +106 -45
  21. package/build/tools/todoTools.js +39 -39
  22. package/build/tools/validators/__tests__/apiSchema.test.js +23 -23
  23. package/build/tools/validators/__tests__/convertRules.test.js +5 -5
  24. package/build/tools/validators/__tests__/frontendDesign.test.js +12 -12
  25. package/build/tools/validators/__tests__/geoChecker.test.js +19 -19
  26. package/build/tools/validators/__tests__/mobileAudit.test.js +12 -12
  27. package/build/tools/validators/__tests__/reactPerformanceChecker.test.js +17 -17
  28. package/build/tools/validators/__tests__/securityScan.test.js +6 -6
  29. package/build/tools/validators/__tests__/seoChecker.test.js +16 -16
  30. package/build/tools/validators/__tests__/typeCoverage.test.js +14 -14
  31. package/commands/README.md +122 -122
  32. package/commands/ask.toml +72 -72
  33. package/commands/brainstorm.toml +119 -119
  34. package/commands/chat.toml +77 -77
  35. package/commands/code-preview.toml +37 -37
  36. package/commands/code.toml +28 -28
  37. package/commands/content.toml +200 -200
  38. package/commands/cook.toml +77 -77
  39. package/commands/copywrite.toml +131 -131
  40. package/commands/db.toml +192 -192
  41. package/commands/debug.toml +166 -166
  42. package/commands/design.toml +158 -158
  43. package/commands/dev-rules.toml +14 -14
  44. package/commands/do.toml +117 -117
  45. package/commands/doc-rules.toml +14 -14
  46. package/commands/docs.toml +148 -148
  47. package/commands/fix.toml +440 -440
  48. package/commands/fullstack.toml +175 -175
  49. package/commands/git.toml +235 -235
  50. package/commands/help.toml +84 -84
  51. package/commands/integrate.toml +127 -127
  52. package/commands/journal.toml +136 -136
  53. package/commands/kit-setup.toml +40 -40
  54. package/commands/mcp.toml +183 -183
  55. package/commands/orchestration.toml +15 -15
  56. package/commands/plan.toml +206 -172
  57. package/commands/pm.toml +148 -148
  58. package/commands/pr.toml +50 -50
  59. package/commands/project.toml +32 -32
  60. package/commands/research.toml +117 -117
  61. package/commands/review-pr.toml +63 -63
  62. package/commands/review.toml +190 -190
  63. package/commands/scout-ext.toml +97 -97
  64. package/commands/scout.toml +79 -79
  65. package/commands/screenshot.toml +65 -65
  66. package/commands/session.toml +102 -102
  67. package/commands/skill.toml +384 -384
  68. package/commands/status.toml +22 -22
  69. package/commands/team.toml +56 -56
  70. package/commands/test.toml +164 -164
  71. package/commands/ticket.toml +70 -70
  72. package/commands/use.toml +106 -106
  73. package/commands/video.toml +83 -83
  74. package/commands/watzup.toml +71 -71
  75. package/commands/workflow.toml +14 -14
  76. package/package.json +35 -35
  77. package/skills/meta/README.md +30 -30
  78. package/skills/meta/api-design/SKILL.md +134 -134
  79. package/skills/meta/code-review/SKILL.md +44 -44
  80. package/skills/meta/code-review/checklists/pre-merge.md +25 -25
  81. package/skills/meta/code-review/workflows/architecture-pass.md +26 -26
  82. package/skills/meta/code-review/workflows/performance-pass.md +27 -27
  83. package/skills/meta/code-review/workflows/security-pass.md +29 -29
  84. package/skills/meta/compound-docs/SKILL.md +133 -133
  85. package/skills/meta/debug/SKILL.md +40 -40
  86. package/skills/meta/debug/templates/bug-report.template.md +31 -31
  87. package/skills/meta/debug/workflows/reproduce-issue.md +20 -20
  88. package/skills/meta/docker/SKILL.md +126 -126
  89. package/skills/meta/examples/supabase/SKILL.md +46 -46
  90. package/skills/meta/examples/supabase/references/best-practices.md +319 -319
  91. package/skills/meta/examples/supabase/references/common-patterns.md +373 -373
  92. package/skills/meta/examples/supabase/templates/migration-template.sql +49 -49
  93. package/skills/meta/examples/supabase/templates/rls-policy-template.sql +77 -77
  94. package/skills/meta/examples/supabase/workflows/debugging.md +260 -260
  95. package/skills/meta/examples/supabase/workflows/migration-workflow.md +211 -211
  96. package/skills/meta/examples/supabase/workflows/rls-policies.md +244 -244
  97. package/skills/meta/examples/supabase/workflows/schema-design.md +321 -321
  98. package/skills/meta/file-todos/SKILL.md +88 -88
  99. package/skills/meta/mobile/SKILL.md +140 -140
  100. package/skills/meta/nextjs/SKILL.md +101 -101
  101. package/skills/meta/performance/SKILL.md +130 -130
  102. package/skills/meta/react-patterns/SKILL.md +83 -83
  103. package/skills/meta/security/SKILL.md +114 -114
  104. package/skills/meta/session-resume/SKILL.md +96 -96
  105. package/skills/meta/tailwind/SKILL.md +139 -139
  106. package/skills/meta/testing/SKILL.md +43 -43
  107. package/skills/meta/testing/references/vitest-patterns.md +45 -45
  108. package/skills/meta/testing/templates/component-test.template.tsx +37 -37
  109. package/skills/tech/alpha-vantage/SKILL.md +142 -142
  110. package/skills/tech/alpha-vantage/references/commodities.md +153 -153
  111. package/skills/tech/alpha-vantage/references/economic-indicators.md +158 -158
  112. package/skills/tech/alpha-vantage/references/forex-crypto.md +154 -154
  113. package/skills/tech/alpha-vantage/references/fundamentals.md +223 -223
  114. package/skills/tech/alpha-vantage/references/intelligence.md +138 -138
  115. package/skills/tech/alpha-vantage/references/options.md +93 -93
  116. package/skills/tech/alpha-vantage/references/technical-indicators.md +374 -374
  117. package/skills/tech/alpha-vantage/references/time-series.md +157 -157
  118. package/skills/tech/financial-modeling/SKILL.md +18 -18
  119. package/skills/tech/financial-modeling/skills/3-statements/SKILL.md +368 -368
  120. package/skills/tech/financial-modeling/skills/3-statements/references/formatting.md +118 -118
  121. package/skills/tech/financial-modeling/skills/3-statements/references/formulas.md +292 -292
  122. package/skills/tech/financial-modeling/skills/3-statements/references/sec-filings.md +125 -125
  123. package/skills/tech/financial-modeling/skills/dcf-model/SKILL.md +1210 -1210
  124. package/skills/tech/financial-modeling/skills/dcf-model/TROUBLESHOOTING.md +40 -40
  125. package/skills/tech/financial-modeling/skills/dcf-model/requirements.txt +8 -8
  126. package/skills/tech/financial-modeling/skills/dcf-model/scripts/validate_dcf.py +292 -292
  127. package/skills/tech/financial-modeling/skills/lbo-model/SKILL.md +236 -236
  128. package/skills/tech/financial-modeling/skills/merger-model/SKILL.md +108 -108
  129. package/skills/workflows/README.md +203 -203
  130. package/skills/workflows/adr.md +174 -174
  131. package/skills/workflows/changelog.md +74 -74
  132. package/skills/workflows/compound.md +323 -323
  133. package/skills/workflows/compound_health.md +74 -74
  134. package/skills/workflows/create-agent-skill.md +138 -138
  135. package/skills/workflows/cycle.md +144 -144
  136. package/skills/workflows/deploy-docs.md +84 -84
  137. package/skills/workflows/development-rules.md +42 -42
  138. package/skills/workflows/doc.md +95 -95
  139. package/skills/workflows/documentation-management.md +34 -34
  140. package/skills/workflows/explore.md +146 -146
  141. package/skills/workflows/generate_command.md +106 -106
  142. package/skills/workflows/heal-skill.md +97 -97
  143. package/skills/workflows/housekeeping.md +229 -229
  144. package/skills/workflows/kit-setup.md +102 -102
  145. package/skills/workflows/map-codebase.md +78 -78
  146. package/skills/workflows/orchestration-protocol.md +43 -43
  147. package/skills/workflows/plan-compound.md +439 -439
  148. package/skills/workflows/plan_review.md +269 -269
  149. package/skills/workflows/primary-workflow.md +37 -37
  150. package/skills/workflows/promote_pattern.md +86 -86
  151. package/skills/workflows/release-docs.md +82 -82
  152. package/skills/workflows/report-bug.md +135 -135
  153. package/skills/workflows/reproduce-bug.md +118 -118
  154. package/skills/workflows/resolve_pr.md +133 -133
  155. package/skills/workflows/resolve_todo.md +128 -128
  156. package/skills/workflows/review-compound.md +376 -376
  157. package/skills/workflows/skill-review.md +127 -127
  158. package/skills/workflows/specs.md +257 -257
  159. package/skills/workflows/triage-sprint.md +102 -102
  160. package/skills/workflows/triage.md +152 -152
  161. package/skills/workflows/work.md +399 -399
  162. package/skills/workflows/xcode-test.md +93 -93
@@ -1,292 +1,292 @@
1
- #!/usr/bin/env python3
2
- """
3
- DCF Model Validation Script
4
- Validates Excel DCF models for formula errors and common DCF mistakes
5
- """
6
-
7
- import sys
8
- import json
9
- from pathlib import Path
10
- from typing import Optional
11
-
12
-
13
- class DCFModelValidator:
14
- """Validates DCF models for errors and quality issues"""
15
-
16
- def __init__(self, excel_path: str):
17
- try:
18
- import openpyxl
19
- except ImportError:
20
- raise ImportError("openpyxl not installed. Run: pip install openpyxl")
21
-
22
- self.excel_path = excel_path
23
- self.openpyxl = openpyxl
24
-
25
- if not Path(excel_path).exists():
26
- raise FileNotFoundError(f"File not found: {excel_path}")
27
-
28
- self.workbook_formulas = openpyxl.load_workbook(excel_path, data_only=False)
29
- self.workbook_values = openpyxl.load_workbook(excel_path, data_only=True)
30
- self.errors = []
31
- self.warnings = []
32
- self.info = []
33
-
34
- def validate_all(self) -> dict:
35
- """
36
- Run all validation checks
37
-
38
- Returns:
39
- Dict with validation results
40
- """
41
- from datetime import datetime
42
-
43
- self.check_sheet_structure()
44
- self.check_formula_errors()
45
- self.check_dcf_logic()
46
-
47
- results = {
48
- 'file': self.excel_path,
49
- 'validation_date': datetime.now().isoformat(),
50
- 'status': 'PASS' if len(self.errors) == 0 else 'FAIL',
51
- 'error_count': len(self.errors),
52
- 'warning_count': len(self.warnings),
53
- 'errors': self.errors,
54
- 'warnings': self.warnings,
55
- 'info': self.info
56
- }
57
-
58
- return results
59
-
60
- def check_sheet_structure(self):
61
- """Verify required sheets exist"""
62
- required_sheets = ['DCF', 'WACC', 'Sensitivity']
63
- sheet_names = self.workbook_values.sheetnames
64
-
65
- for sheet in required_sheets:
66
- if sheet not in sheet_names:
67
- self.warnings.append(f"Recommended sheet missing: {sheet}")
68
- else:
69
- self.info.append(f"Found sheet: {sheet}")
70
-
71
- def check_formula_errors(self):
72
- """Check for Excel formula errors in all sheets"""
73
- excel_errors = ['#VALUE!', '#DIV/0!', '#REF!', '#NAME?', '#NULL!', '#NUM!', '#N/A']
74
- error_details = {err: [] for err in excel_errors}
75
- total_errors = 0
76
- total_formulas = 0
77
-
78
- for sheet_name in self.workbook_values.sheetnames:
79
- ws_values = self.workbook_values[sheet_name]
80
- ws_formulas = self.workbook_formulas[sheet_name]
81
-
82
- for row in ws_values.iter_rows():
83
- for cell in row:
84
- formula_cell = ws_formulas[cell.coordinate]
85
-
86
- # Count formulas
87
- if formula_cell.value and isinstance(formula_cell.value, str) and formula_cell.value.startswith('='):
88
- total_formulas += 1
89
-
90
- # Check for errors
91
- if cell.value is not None and isinstance(cell.value, str):
92
- for err in excel_errors:
93
- if err in cell.value:
94
- location = f"{sheet_name}!{cell.coordinate}"
95
- error_details[err].append(location)
96
- total_errors += 1
97
- self.errors.append(f"{err} at {location}")
98
- break
99
-
100
- # Add summary info
101
- self.info.append(f"Total formulas: {total_formulas}")
102
- if total_errors == 0:
103
- self.info.append("✓ No formula errors found")
104
- else:
105
- self.errors.append(f"Total formula errors: {total_errors}")
106
-
107
- return error_details, total_errors
108
-
109
- def check_dcf_logic(self):
110
- """Validate DCF-specific logic and calculations"""
111
- self._check_terminal_growth_vs_wacc()
112
- self._check_wacc_range()
113
- self._check_terminal_value_proportion()
114
-
115
- def _check_terminal_growth_vs_wacc(self):
116
- """Critical check: Terminal growth must be less than WACC"""
117
- try:
118
- dcf_sheet = self.workbook_values['DCF']
119
-
120
- terminal_growth = None
121
- wacc = None
122
-
123
- # Search for terminal growth and WACC values
124
- for row in dcf_sheet.iter_rows(max_row=100, max_col=20):
125
- for cell in row:
126
- if cell.value and isinstance(cell.value, str):
127
- cell_str = cell.value.lower()
128
- if 'terminal' in cell_str and 'growth' in cell_str:
129
- # Look for value in adjacent cells
130
- for offset in range(1, 5):
131
- adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
132
- if isinstance(adjacent, (int, float)) and 0 < adjacent < 1:
133
- terminal_growth = adjacent
134
- break
135
- if 'wacc' in cell_str and wacc is None:
136
- for offset in range(1, 5):
137
- adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
138
- if isinstance(adjacent, (int, float)) and 0 < adjacent < 1:
139
- wacc = adjacent
140
- break
141
-
142
- if terminal_growth is not None and wacc is not None:
143
- if terminal_growth >= wacc:
144
- self.errors.append(
145
- f"CRITICAL: Terminal growth ({terminal_growth:.2%}) >= WACC ({wacc:.2%}). "
146
- "This creates infinite value and is mathematically invalid."
147
- )
148
- else:
149
- self.info.append(
150
- f"✓ Terminal growth ({terminal_growth:.2%}) < WACC ({wacc:.2%})"
151
- )
152
- else:
153
- self.warnings.append("Could not locate terminal growth and WACC values")
154
-
155
- except KeyError:
156
- self.warnings.append("DCF sheet not found")
157
- except Exception as e:
158
- self.warnings.append(f"Could not validate terminal growth vs WACC: {str(e)}")
159
-
160
- def _check_wacc_range(self):
161
- """Check if WACC is in reasonable range"""
162
- try:
163
- wacc_sheet = self.workbook_values.get('WACC') or self.workbook_values['DCF']
164
- wacc = None
165
-
166
- for row in wacc_sheet.iter_rows(max_row=100, max_col=20):
167
- for cell in row:
168
- if cell.value and isinstance(cell.value, str):
169
- if 'wacc' in cell.value.lower():
170
- for offset in range(1, 5):
171
- adjacent = wacc_sheet.cell(cell.row, cell.column + offset).value
172
- if isinstance(adjacent, (int, float)) and 0 < adjacent < 1:
173
- wacc = adjacent
174
- break
175
-
176
- if wacc is not None:
177
- if wacc < 0.05 or wacc > 0.20:
178
- self.warnings.append(
179
- f"WACC ({wacc:.2%}) is outside typical range (5%-20%). Verify calculation."
180
- )
181
- else:
182
- self.info.append(f"✓ WACC ({wacc:.2%}) in reasonable range")
183
- else:
184
- self.warnings.append("Could not locate WACC value")
185
-
186
- except Exception as e:
187
- self.warnings.append(f"Could not validate WACC range: {str(e)}")
188
-
189
- def _check_terminal_value_proportion(self):
190
- """Check if terminal value is reasonable proportion of enterprise value"""
191
- try:
192
- dcf_sheet = self.workbook_values['DCF']
193
-
194
- terminal_value = None
195
- enterprise_value = None
196
-
197
- for row in dcf_sheet.iter_rows(max_row=200, max_col=20):
198
- for cell in row:
199
- if cell.value and isinstance(cell.value, str):
200
- cell_str = cell.value.lower()
201
- if 'terminal' in cell_str and 'value' in cell_str and 'pv' in cell_str:
202
- for offset in range(1, 5):
203
- adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
204
- if isinstance(adjacent, (int, float)) and adjacent > 0:
205
- terminal_value = adjacent
206
- break
207
- if 'enterprise' in cell_str and 'value' in cell_str:
208
- for offset in range(1, 5):
209
- adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
210
- if isinstance(adjacent, (int, float)) and adjacent > 0:
211
- enterprise_value = adjacent
212
- break
213
-
214
- if terminal_value is not None and enterprise_value is not None and enterprise_value > 0:
215
- proportion = terminal_value / enterprise_value
216
- if proportion > 0.80:
217
- self.warnings.append(
218
- f"Terminal value is {proportion:.1%} of EV (typically should be 50-70%). "
219
- "Model may be over-reliant on terminal assumptions."
220
- )
221
- elif proportion < 0.40:
222
- self.warnings.append(
223
- f"Terminal value is {proportion:.1%} of EV (typically should be 50-70%). "
224
- "Check if terminal assumptions are too conservative."
225
- )
226
- else:
227
- self.info.append(f"✓ Terminal value is {proportion:.1%} of EV")
228
- else:
229
- self.warnings.append("Could not locate terminal value and enterprise value")
230
-
231
- except Exception as e:
232
- self.warnings.append(f"Could not validate terminal value proportion: {str(e)}")
233
-
234
-
235
-
236
- def validate_dcf_model(excel_path: str) -> dict:
237
- """
238
- Validate a DCF model Excel file
239
-
240
- Args:
241
- excel_path: Path to Excel DCF model
242
-
243
- Returns:
244
- Dict with validation results
245
- """
246
- validator = DCFModelValidator(excel_path)
247
- return validator.validate_all()
248
-
249
-
250
- def main():
251
- """Command-line interface"""
252
- if len(sys.argv) < 2:
253
- print("Usage: python validate_dcf.py <excel_file> [output.json]")
254
- print("\nValidates DCF model for:")
255
- print(" - Formula errors (#REF!, #DIV/0!, etc.)")
256
- print(" - Terminal growth < WACC (critical)")
257
- print(" - WACC in reasonable range (5-20%)")
258
- print(" - Terminal value proportion of EV (40-80%)")
259
- print("\nReturns JSON with errors, warnings, and info")
260
- print("\nExample: python validate_dcf.py model.xlsx")
261
- print("Example: python validate_dcf.py model.xlsx results.json")
262
- sys.exit(1)
263
-
264
- excel_file = sys.argv[1]
265
- output_file = sys.argv[2] if len(sys.argv) > 2 else None
266
-
267
- try:
268
- results = validate_dcf_model(excel_file)
269
-
270
- # Print results
271
- print(json.dumps(results, indent=2))
272
-
273
- # Save to file if requested
274
- if output_file:
275
- with open(output_file, 'w') as f:
276
- json.dump(results, f, indent=2)
277
-
278
- # Exit with error code if validation failed
279
- sys.exit(0 if results['status'] == 'PASS' else 1)
280
-
281
- except Exception as e:
282
- error_result = {
283
- 'file': excel_file,
284
- 'status': 'ERROR',
285
- 'error': str(e)
286
- }
287
- print(json.dumps(error_result, indent=2))
288
- sys.exit(1)
289
-
290
-
291
- if __name__ == "__main__":
292
- main()
1
+ #!/usr/bin/env python3
2
+ """
3
+ DCF Model Validation Script
4
+ Validates Excel DCF models for formula errors and common DCF mistakes
5
+ """
6
+
7
+ import sys
8
+ import json
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+
13
+ class DCFModelValidator:
14
+ """Validates DCF models for errors and quality issues"""
15
+
16
+ def __init__(self, excel_path: str):
17
+ try:
18
+ import openpyxl
19
+ except ImportError:
20
+ raise ImportError("openpyxl not installed. Run: pip install openpyxl")
21
+
22
+ self.excel_path = excel_path
23
+ self.openpyxl = openpyxl
24
+
25
+ if not Path(excel_path).exists():
26
+ raise FileNotFoundError(f"File not found: {excel_path}")
27
+
28
+ self.workbook_formulas = openpyxl.load_workbook(excel_path, data_only=False)
29
+ self.workbook_values = openpyxl.load_workbook(excel_path, data_only=True)
30
+ self.errors = []
31
+ self.warnings = []
32
+ self.info = []
33
+
34
+ def validate_all(self) -> dict:
35
+ """
36
+ Run all validation checks
37
+
38
+ Returns:
39
+ Dict with validation results
40
+ """
41
+ from datetime import datetime
42
+
43
+ self.check_sheet_structure()
44
+ self.check_formula_errors()
45
+ self.check_dcf_logic()
46
+
47
+ results = {
48
+ 'file': self.excel_path,
49
+ 'validation_date': datetime.now().isoformat(),
50
+ 'status': 'PASS' if len(self.errors) == 0 else 'FAIL',
51
+ 'error_count': len(self.errors),
52
+ 'warning_count': len(self.warnings),
53
+ 'errors': self.errors,
54
+ 'warnings': self.warnings,
55
+ 'info': self.info
56
+ }
57
+
58
+ return results
59
+
60
+ def check_sheet_structure(self):
61
+ """Verify required sheets exist"""
62
+ required_sheets = ['DCF', 'WACC', 'Sensitivity']
63
+ sheet_names = self.workbook_values.sheetnames
64
+
65
+ for sheet in required_sheets:
66
+ if sheet not in sheet_names:
67
+ self.warnings.append(f"Recommended sheet missing: {sheet}")
68
+ else:
69
+ self.info.append(f"Found sheet: {sheet}")
70
+
71
+ def check_formula_errors(self):
72
+ """Check for Excel formula errors in all sheets"""
73
+ excel_errors = ['#VALUE!', '#DIV/0!', '#REF!', '#NAME?', '#NULL!', '#NUM!', '#N/A']
74
+ error_details = {err: [] for err in excel_errors}
75
+ total_errors = 0
76
+ total_formulas = 0
77
+
78
+ for sheet_name in self.workbook_values.sheetnames:
79
+ ws_values = self.workbook_values[sheet_name]
80
+ ws_formulas = self.workbook_formulas[sheet_name]
81
+
82
+ for row in ws_values.iter_rows():
83
+ for cell in row:
84
+ formula_cell = ws_formulas[cell.coordinate]
85
+
86
+ # Count formulas
87
+ if formula_cell.value and isinstance(formula_cell.value, str) and formula_cell.value.startswith('='):
88
+ total_formulas += 1
89
+
90
+ # Check for errors
91
+ if cell.value is not None and isinstance(cell.value, str):
92
+ for err in excel_errors:
93
+ if err in cell.value:
94
+ location = f"{sheet_name}!{cell.coordinate}"
95
+ error_details[err].append(location)
96
+ total_errors += 1
97
+ self.errors.append(f"{err} at {location}")
98
+ break
99
+
100
+ # Add summary info
101
+ self.info.append(f"Total formulas: {total_formulas}")
102
+ if total_errors == 0:
103
+ self.info.append("✓ No formula errors found")
104
+ else:
105
+ self.errors.append(f"Total formula errors: {total_errors}")
106
+
107
+ return error_details, total_errors
108
+
109
+ def check_dcf_logic(self):
110
+ """Validate DCF-specific logic and calculations"""
111
+ self._check_terminal_growth_vs_wacc()
112
+ self._check_wacc_range()
113
+ self._check_terminal_value_proportion()
114
+
115
+ def _check_terminal_growth_vs_wacc(self):
116
+ """Critical check: Terminal growth must be less than WACC"""
117
+ try:
118
+ dcf_sheet = self.workbook_values['DCF']
119
+
120
+ terminal_growth = None
121
+ wacc = None
122
+
123
+ # Search for terminal growth and WACC values
124
+ for row in dcf_sheet.iter_rows(max_row=100, max_col=20):
125
+ for cell in row:
126
+ if cell.value and isinstance(cell.value, str):
127
+ cell_str = cell.value.lower()
128
+ if 'terminal' in cell_str and 'growth' in cell_str:
129
+ # Look for value in adjacent cells
130
+ for offset in range(1, 5):
131
+ adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
132
+ if isinstance(adjacent, (int, float)) and 0 < adjacent < 1:
133
+ terminal_growth = adjacent
134
+ break
135
+ if 'wacc' in cell_str and wacc is None:
136
+ for offset in range(1, 5):
137
+ adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
138
+ if isinstance(adjacent, (int, float)) and 0 < adjacent < 1:
139
+ wacc = adjacent
140
+ break
141
+
142
+ if terminal_growth is not None and wacc is not None:
143
+ if terminal_growth >= wacc:
144
+ self.errors.append(
145
+ f"CRITICAL: Terminal growth ({terminal_growth:.2%}) >= WACC ({wacc:.2%}). "
146
+ "This creates infinite value and is mathematically invalid."
147
+ )
148
+ else:
149
+ self.info.append(
150
+ f"✓ Terminal growth ({terminal_growth:.2%}) < WACC ({wacc:.2%})"
151
+ )
152
+ else:
153
+ self.warnings.append("Could not locate terminal growth and WACC values")
154
+
155
+ except KeyError:
156
+ self.warnings.append("DCF sheet not found")
157
+ except Exception as e:
158
+ self.warnings.append(f"Could not validate terminal growth vs WACC: {str(e)}")
159
+
160
+ def _check_wacc_range(self):
161
+ """Check if WACC is in reasonable range"""
162
+ try:
163
+ wacc_sheet = self.workbook_values.get('WACC') or self.workbook_values['DCF']
164
+ wacc = None
165
+
166
+ for row in wacc_sheet.iter_rows(max_row=100, max_col=20):
167
+ for cell in row:
168
+ if cell.value and isinstance(cell.value, str):
169
+ if 'wacc' in cell.value.lower():
170
+ for offset in range(1, 5):
171
+ adjacent = wacc_sheet.cell(cell.row, cell.column + offset).value
172
+ if isinstance(adjacent, (int, float)) and 0 < adjacent < 1:
173
+ wacc = adjacent
174
+ break
175
+
176
+ if wacc is not None:
177
+ if wacc < 0.05 or wacc > 0.20:
178
+ self.warnings.append(
179
+ f"WACC ({wacc:.2%}) is outside typical range (5%-20%). Verify calculation."
180
+ )
181
+ else:
182
+ self.info.append(f"✓ WACC ({wacc:.2%}) in reasonable range")
183
+ else:
184
+ self.warnings.append("Could not locate WACC value")
185
+
186
+ except Exception as e:
187
+ self.warnings.append(f"Could not validate WACC range: {str(e)}")
188
+
189
+ def _check_terminal_value_proportion(self):
190
+ """Check if terminal value is reasonable proportion of enterprise value"""
191
+ try:
192
+ dcf_sheet = self.workbook_values['DCF']
193
+
194
+ terminal_value = None
195
+ enterprise_value = None
196
+
197
+ for row in dcf_sheet.iter_rows(max_row=200, max_col=20):
198
+ for cell in row:
199
+ if cell.value and isinstance(cell.value, str):
200
+ cell_str = cell.value.lower()
201
+ if 'terminal' in cell_str and 'value' in cell_str and 'pv' in cell_str:
202
+ for offset in range(1, 5):
203
+ adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
204
+ if isinstance(adjacent, (int, float)) and adjacent > 0:
205
+ terminal_value = adjacent
206
+ break
207
+ if 'enterprise' in cell_str and 'value' in cell_str:
208
+ for offset in range(1, 5):
209
+ adjacent = dcf_sheet.cell(cell.row, cell.column + offset).value
210
+ if isinstance(adjacent, (int, float)) and adjacent > 0:
211
+ enterprise_value = adjacent
212
+ break
213
+
214
+ if terminal_value is not None and enterprise_value is not None and enterprise_value > 0:
215
+ proportion = terminal_value / enterprise_value
216
+ if proportion > 0.80:
217
+ self.warnings.append(
218
+ f"Terminal value is {proportion:.1%} of EV (typically should be 50-70%). "
219
+ "Model may be over-reliant on terminal assumptions."
220
+ )
221
+ elif proportion < 0.40:
222
+ self.warnings.append(
223
+ f"Terminal value is {proportion:.1%} of EV (typically should be 50-70%). "
224
+ "Check if terminal assumptions are too conservative."
225
+ )
226
+ else:
227
+ self.info.append(f"✓ Terminal value is {proportion:.1%} of EV")
228
+ else:
229
+ self.warnings.append("Could not locate terminal value and enterprise value")
230
+
231
+ except Exception as e:
232
+ self.warnings.append(f"Could not validate terminal value proportion: {str(e)}")
233
+
234
+
235
+
236
+ def validate_dcf_model(excel_path: str) -> dict:
237
+ """
238
+ Validate a DCF model Excel file
239
+
240
+ Args:
241
+ excel_path: Path to Excel DCF model
242
+
243
+ Returns:
244
+ Dict with validation results
245
+ """
246
+ validator = DCFModelValidator(excel_path)
247
+ return validator.validate_all()
248
+
249
+
250
+ def main():
251
+ """Command-line interface"""
252
+ if len(sys.argv) < 2:
253
+ print("Usage: python validate_dcf.py <excel_file> [output.json]")
254
+ print("\nValidates DCF model for:")
255
+ print(" - Formula errors (#REF!, #DIV/0!, etc.)")
256
+ print(" - Terminal growth < WACC (critical)")
257
+ print(" - WACC in reasonable range (5-20%)")
258
+ print(" - Terminal value proportion of EV (40-80%)")
259
+ print("\nReturns JSON with errors, warnings, and info")
260
+ print("\nExample: python validate_dcf.py model.xlsx")
261
+ print("Example: python validate_dcf.py model.xlsx results.json")
262
+ sys.exit(1)
263
+
264
+ excel_file = sys.argv[1]
265
+ output_file = sys.argv[2] if len(sys.argv) > 2 else None
266
+
267
+ try:
268
+ results = validate_dcf_model(excel_file)
269
+
270
+ # Print results
271
+ print(json.dumps(results, indent=2))
272
+
273
+ # Save to file if requested
274
+ if output_file:
275
+ with open(output_file, 'w') as f:
276
+ json.dump(results, f, indent=2)
277
+
278
+ # Exit with error code if validation failed
279
+ sys.exit(0 if results['status'] == 'PASS' else 1)
280
+
281
+ except Exception as e:
282
+ error_result = {
283
+ 'file': excel_file,
284
+ 'status': 'ERROR',
285
+ 'error': str(e)
286
+ }
287
+ print(json.dumps(error_result, indent=2))
288
+ sys.exit(1)
289
+
290
+
291
+ if __name__ == "__main__":
292
+ main()