superkit-mcp-server 1.2.3 → 1.2.4

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