superkit-mcp-server 1.1.4 → 1.2.0

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 (160) 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/tools/todoTools.js +39 -39
  20. package/build/tools/validators/__tests__/apiSchema.test.js +23 -23
  21. package/build/tools/validators/__tests__/convertRules.test.js +5 -5
  22. package/build/tools/validators/__tests__/frontendDesign.test.js +12 -12
  23. package/build/tools/validators/__tests__/geoChecker.test.js +19 -19
  24. package/build/tools/validators/__tests__/mobileAudit.test.js +12 -12
  25. package/build/tools/validators/__tests__/reactPerformanceChecker.test.js +17 -17
  26. package/build/tools/validators/__tests__/securityScan.test.js +6 -6
  27. package/build/tools/validators/__tests__/seoChecker.test.js +16 -16
  28. package/build/tools/validators/__tests__/typeCoverage.test.js +14 -14
  29. package/commands/README.md +122 -122
  30. package/commands/ask.toml +72 -72
  31. package/commands/brainstorm.toml +119 -119
  32. package/commands/chat.toml +73 -73
  33. package/commands/code-preview.toml +37 -37
  34. package/commands/code.toml +28 -28
  35. package/commands/content.toml +200 -200
  36. package/commands/cook.toml +77 -77
  37. package/commands/copywrite.toml +131 -131
  38. package/commands/db.toml +192 -192
  39. package/commands/debug.toml +166 -166
  40. package/commands/design.toml +158 -158
  41. package/commands/dev-rules.toml +14 -14
  42. package/commands/do.toml +117 -117
  43. package/commands/doc-rules.toml +14 -14
  44. package/commands/docs.toml +148 -148
  45. package/commands/fix.toml +440 -440
  46. package/commands/fullstack.toml +175 -175
  47. package/commands/git.toml +235 -235
  48. package/commands/help.toml +84 -84
  49. package/commands/integrate.toml +127 -127
  50. package/commands/journal.toml +136 -136
  51. package/commands/kit-setup.toml +40 -40
  52. package/commands/mcp.toml +183 -183
  53. package/commands/orchestration.toml +15 -15
  54. package/commands/plan.toml +171 -171
  55. package/commands/pm.toml +148 -148
  56. package/commands/pr.toml +50 -50
  57. package/commands/project.toml +32 -32
  58. package/commands/research.toml +117 -117
  59. package/commands/review-pr.toml +63 -63
  60. package/commands/review.toml +190 -190
  61. package/commands/scout-ext.toml +97 -97
  62. package/commands/scout.toml +79 -79
  63. package/commands/screenshot.toml +65 -65
  64. package/commands/session.toml +98 -98
  65. package/commands/skill.toml +384 -384
  66. package/commands/status.toml +22 -22
  67. package/commands/team.toml +56 -56
  68. package/commands/test.toml +164 -164
  69. package/commands/ticket.toml +70 -70
  70. package/commands/use.toml +106 -106
  71. package/commands/video.toml +83 -83
  72. package/commands/watzup.toml +71 -71
  73. package/commands/workflow.toml +14 -14
  74. package/package.json +35 -35
  75. package/skills/meta/README.md +30 -30
  76. package/skills/meta/api-design/SKILL.md +134 -134
  77. package/skills/meta/code-review/SKILL.md +44 -44
  78. package/skills/meta/code-review/checklists/pre-merge.md +25 -25
  79. package/skills/meta/code-review/workflows/architecture-pass.md +26 -26
  80. package/skills/meta/code-review/workflows/performance-pass.md +27 -27
  81. package/skills/meta/code-review/workflows/security-pass.md +29 -29
  82. package/skills/meta/compound-docs/SKILL.md +133 -133
  83. package/skills/meta/debug/SKILL.md +40 -40
  84. package/skills/meta/debug/templates/bug-report.template.md +31 -31
  85. package/skills/meta/debug/workflows/reproduce-issue.md +20 -20
  86. package/skills/meta/docker/SKILL.md +126 -126
  87. package/skills/meta/examples/supabase/SKILL.md +46 -46
  88. package/skills/meta/examples/supabase/references/best-practices.md +319 -319
  89. package/skills/meta/examples/supabase/references/common-patterns.md +373 -373
  90. package/skills/meta/examples/supabase/templates/migration-template.sql +49 -49
  91. package/skills/meta/examples/supabase/templates/rls-policy-template.sql +77 -77
  92. package/skills/meta/examples/supabase/workflows/debugging.md +260 -260
  93. package/skills/meta/examples/supabase/workflows/migration-workflow.md +211 -211
  94. package/skills/meta/examples/supabase/workflows/rls-policies.md +244 -244
  95. package/skills/meta/examples/supabase/workflows/schema-design.md +321 -321
  96. package/skills/meta/file-todos/SKILL.md +88 -88
  97. package/skills/meta/mobile/SKILL.md +140 -140
  98. package/skills/meta/nextjs/SKILL.md +101 -101
  99. package/skills/meta/performance/SKILL.md +130 -130
  100. package/skills/meta/react-patterns/SKILL.md +83 -83
  101. package/skills/meta/security/SKILL.md +114 -114
  102. package/skills/meta/session-resume/SKILL.md +96 -96
  103. package/skills/meta/tailwind/SKILL.md +139 -139
  104. package/skills/meta/testing/SKILL.md +43 -43
  105. package/skills/meta/testing/references/vitest-patterns.md +45 -45
  106. package/skills/meta/testing/templates/component-test.template.tsx +37 -37
  107. package/skills/tech/alpha-vantage/SKILL.md +142 -142
  108. package/skills/tech/alpha-vantage/references/commodities.md +153 -153
  109. package/skills/tech/alpha-vantage/references/economic-indicators.md +158 -158
  110. package/skills/tech/alpha-vantage/references/forex-crypto.md +154 -154
  111. package/skills/tech/alpha-vantage/references/fundamentals.md +223 -223
  112. package/skills/tech/alpha-vantage/references/intelligence.md +138 -138
  113. package/skills/tech/alpha-vantage/references/options.md +93 -93
  114. package/skills/tech/alpha-vantage/references/technical-indicators.md +374 -374
  115. package/skills/tech/alpha-vantage/references/time-series.md +157 -157
  116. package/skills/tech/financial-modeling/SKILL.md +18 -18
  117. package/skills/tech/financial-modeling/skills/3-statements/SKILL.md +368 -368
  118. package/skills/tech/financial-modeling/skills/3-statements/references/formatting.md +118 -118
  119. package/skills/tech/financial-modeling/skills/3-statements/references/formulas.md +292 -292
  120. package/skills/tech/financial-modeling/skills/3-statements/references/sec-filings.md +125 -125
  121. package/skills/tech/financial-modeling/skills/dcf-model/SKILL.md +1210 -1210
  122. package/skills/tech/financial-modeling/skills/dcf-model/TROUBLESHOOTING.md +40 -40
  123. package/skills/tech/financial-modeling/skills/dcf-model/requirements.txt +8 -8
  124. package/skills/tech/financial-modeling/skills/dcf-model/scripts/validate_dcf.py +292 -292
  125. package/skills/tech/financial-modeling/skills/lbo-model/SKILL.md +236 -236
  126. package/skills/tech/financial-modeling/skills/merger-model/SKILL.md +108 -108
  127. package/skills/workflows/README.md +203 -203
  128. package/skills/workflows/adr.md +174 -174
  129. package/skills/workflows/changelog.md +74 -74
  130. package/skills/workflows/compound.md +323 -323
  131. package/skills/workflows/compound_health.md +74 -74
  132. package/skills/workflows/create-agent-skill.md +139 -139
  133. package/skills/workflows/cycle.md +144 -144
  134. package/skills/workflows/deploy-docs.md +84 -84
  135. package/skills/workflows/development-rules.md +42 -42
  136. package/skills/workflows/doc.md +95 -95
  137. package/skills/workflows/documentation-management.md +34 -34
  138. package/skills/workflows/explore.md +146 -146
  139. package/skills/workflows/generate_command.md +106 -106
  140. package/skills/workflows/heal-skill.md +97 -97
  141. package/skills/workflows/housekeeping.md +229 -229
  142. package/skills/workflows/kit-setup.md +102 -102
  143. package/skills/workflows/map-codebase.md +78 -78
  144. package/skills/workflows/orchestration-protocol.md +43 -43
  145. package/skills/workflows/plan-compound.md +439 -439
  146. package/skills/workflows/plan_review.md +269 -269
  147. package/skills/workflows/primary-workflow.md +37 -37
  148. package/skills/workflows/promote_pattern.md +86 -86
  149. package/skills/workflows/release-docs.md +82 -82
  150. package/skills/workflows/report-bug.md +135 -135
  151. package/skills/workflows/reproduce-bug.md +118 -118
  152. package/skills/workflows/resolve_pr.md +133 -133
  153. package/skills/workflows/resolve_todo.md +128 -128
  154. package/skills/workflows/review-compound.md +376 -376
  155. package/skills/workflows/skill-review.md +127 -127
  156. package/skills/workflows/specs.md +257 -257
  157. package/skills/workflows/triage-sprint.md +102 -102
  158. package/skills/workflows/triage.md +152 -152
  159. package/skills/workflows/work.md +399 -399
  160. 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()