superkit-mcp-server 1.2.1 → 1.2.3

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