sdd-jc-methodology 0.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.
- package/.claude/commands/sdd-archive.md +122 -0
- package/.claude/commands/sdd-constitution.md +240 -0
- package/.claude/commands/sdd-execute.md +132 -0
- package/.claude/commands/sdd-propose.md +149 -0
- package/.claude/commands/sdd-seo.md +251 -0
- package/.claude/commands/sdd-specify.md +264 -0
- package/.claude/commands/sdd-test.md +128 -0
- package/.claude/commands/sdd-validate.md +165 -0
- package/.claude/skills/api-design-principles/SKILL.md +528 -0
- package/.claude/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/.claude/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/.claude/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/.claude/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/.claude/skills/aws-serverless/SKILL.md +323 -0
- package/.claude/skills/brainstorming/SKILL.md +96 -0
- package/.claude/skills/error-handling-patterns/SKILL.md +641 -0
- package/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/.claude/skills/frontend-design/SKILL.md +272 -0
- package/.claude/skills/nestjs-expert/SKILL.md +552 -0
- package/.claude/skills/product-manager-toolkit/SKILL.md +351 -0
- package/.claude/skills/product-manager-toolkit/references/prd_templates.md +317 -0
- package/.claude/skills/product-manager-toolkit/scripts/customer_interview_analyzer.py +441 -0
- package/.claude/skills/product-manager-toolkit/scripts/rice_prioritizer.py +296 -0
- package/.claude/skills/react-doctor/AGENTS.md +15 -0
- package/.claude/skills/react-doctor/SKILL.md +19 -0
- package/.claude/skills/shadcn-ui/SKILL.md +1677 -0
- package/.claude/skills/shadcn-ui/references/learn.md +145 -0
- package/.claude/skills/shadcn-ui/references/official-ui-reference.md +1725 -0
- package/.claude/skills/shadcn-ui/references/reference.md +586 -0
- package/.claude/skills/shadcn-ui/references/ui-reference.md +1578 -0
- package/.claude/skills/stitch-design/README.md +50 -0
- package/.claude/skills/stitch-design/SKILL.md +84 -0
- package/.claude/skills/stitch-design/examples/DESIGN.md +22 -0
- package/.claude/skills/stitch-design/examples/enhanced-prompt.md +28 -0
- package/.claude/skills/stitch-design/references/design-mappings.md +45 -0
- package/.claude/skills/stitch-design/references/prompt-keywords.md +114 -0
- package/.claude/skills/stitch-design/references/tool-schemas.md +76 -0
- package/.claude/skills/stitch-design/workflows/edit-design.md +44 -0
- package/.claude/skills/stitch-design/workflows/generate-design-md.md +63 -0
- package/.claude/skills/stitch-design/workflows/text-to-design.md +47 -0
- package/.claude/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/.claude/skills/systematic-debugging/SKILL.md +296 -0
- package/.claude/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/.claude/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/.claude/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/.claude/skills/systematic-debugging/find-polluter.sh +63 -0
- package/.claude/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/.claude/skills/systematic-debugging/test-academic.md +14 -0
- package/.claude/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/.claude/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/.claude/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/.claude/skills/tailwind-design-system/SKILL.md +874 -0
- package/.claude/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/.claude/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/.claude/skills/vercel-react-best-practices/AGENTS.md +2934 -0
- package/.claude/skills/vercel-react-best-practices/SKILL.md +136 -0
- package/.claude/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.claude/skills/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/.claude/skills/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/.claude/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.claude/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.claude/skills/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/.claude/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.claude/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.claude/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.claude/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.claude/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.claude/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.claude/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.claude/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.claude/skills/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/.claude/skills/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/.claude/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.claude/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.claude/skills/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.claude/skills/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/.claude/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.claude/skills/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/.claude/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.claude/skills/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/.claude/skills/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/.claude/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/.claude/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.mcp.json.example +12 -0
- package/CHANGELOG.md +61 -0
- package/LICENSE +21 -0
- package/README.md +571 -0
- package/assets/jc-fox-mark.svg +10 -0
- package/assets/jc-methodology-badge.png +0 -0
- package/bin/sdd-jc.js +379 -0
- package/package.json +43 -0
- package/scripts/gsc_verify.py +162 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
RICE Prioritization Framework
|
|
4
|
+
Calculates RICE scores for feature prioritization
|
|
5
|
+
RICE = (Reach x Impact x Confidence) / Effort
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import csv
|
|
10
|
+
from typing import List, Dict, Tuple
|
|
11
|
+
import argparse
|
|
12
|
+
|
|
13
|
+
class RICECalculator:
|
|
14
|
+
"""Calculate RICE scores for feature prioritization"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.impact_map = {
|
|
18
|
+
'massive': 3.0,
|
|
19
|
+
'high': 2.0,
|
|
20
|
+
'medium': 1.0,
|
|
21
|
+
'low': 0.5,
|
|
22
|
+
'minimal': 0.25
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
self.confidence_map = {
|
|
26
|
+
'high': 100,
|
|
27
|
+
'medium': 80,
|
|
28
|
+
'low': 50
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
self.effort_map = {
|
|
32
|
+
'xl': 13,
|
|
33
|
+
'l': 8,
|
|
34
|
+
'm': 5,
|
|
35
|
+
's': 3,
|
|
36
|
+
'xs': 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def calculate_rice(self, reach: int, impact: str, confidence: str, effort: str) -> float:
|
|
40
|
+
"""
|
|
41
|
+
Calculate RICE score
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
reach: Number of users/customers affected per quarter
|
|
45
|
+
impact: massive/high/medium/low/minimal
|
|
46
|
+
confidence: high/medium/low (percentage)
|
|
47
|
+
effort: xl/l/m/s/xs (person-months)
|
|
48
|
+
"""
|
|
49
|
+
impact_score = self.impact_map.get(impact.lower(), 1.0)
|
|
50
|
+
confidence_score = self.confidence_map.get(confidence.lower(), 50) / 100
|
|
51
|
+
effort_score = self.effort_map.get(effort.lower(), 5)
|
|
52
|
+
|
|
53
|
+
if effort_score == 0:
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
rice_score = (reach * impact_score * confidence_score) / effort_score
|
|
57
|
+
return round(rice_score, 2)
|
|
58
|
+
|
|
59
|
+
def prioritize_features(self, features: List[Dict]) -> List[Dict]:
|
|
60
|
+
"""
|
|
61
|
+
Calculate RICE scores and rank features
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
features: List of feature dictionaries with RICE components
|
|
65
|
+
"""
|
|
66
|
+
for feature in features:
|
|
67
|
+
feature['rice_score'] = self.calculate_rice(
|
|
68
|
+
feature.get('reach', 0),
|
|
69
|
+
feature.get('impact', 'medium'),
|
|
70
|
+
feature.get('confidence', 'medium'),
|
|
71
|
+
feature.get('effort', 'm')
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Sort by RICE score descending
|
|
75
|
+
return sorted(features, key=lambda x: x['rice_score'], reverse=True)
|
|
76
|
+
|
|
77
|
+
def analyze_portfolio(self, features: List[Dict]) -> Dict:
|
|
78
|
+
"""
|
|
79
|
+
Analyze the feature portfolio for balance and insights
|
|
80
|
+
"""
|
|
81
|
+
if not features:
|
|
82
|
+
return {}
|
|
83
|
+
|
|
84
|
+
total_effort = sum(
|
|
85
|
+
self.effort_map.get(f.get('effort', 'm').lower(), 5)
|
|
86
|
+
for f in features
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
total_reach = sum(f.get('reach', 0) for f in features)
|
|
90
|
+
|
|
91
|
+
effort_distribution = {}
|
|
92
|
+
impact_distribution = {}
|
|
93
|
+
|
|
94
|
+
for feature in features:
|
|
95
|
+
effort = feature.get('effort', 'm').lower()
|
|
96
|
+
impact = feature.get('impact', 'medium').lower()
|
|
97
|
+
|
|
98
|
+
effort_distribution[effort] = effort_distribution.get(effort, 0) + 1
|
|
99
|
+
impact_distribution[impact] = impact_distribution.get(impact, 0) + 1
|
|
100
|
+
|
|
101
|
+
# Calculate quick wins (high impact, low effort)
|
|
102
|
+
quick_wins = [
|
|
103
|
+
f for f in features
|
|
104
|
+
if f.get('impact', '').lower() in ['massive', 'high']
|
|
105
|
+
and f.get('effort', '').lower() in ['xs', 's']
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
# Calculate big bets (high impact, high effort)
|
|
109
|
+
big_bets = [
|
|
110
|
+
f for f in features
|
|
111
|
+
if f.get('impact', '').lower() in ['massive', 'high']
|
|
112
|
+
and f.get('effort', '').lower() in ['l', 'xl']
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
'total_features': len(features),
|
|
117
|
+
'total_effort_months': total_effort,
|
|
118
|
+
'total_reach': total_reach,
|
|
119
|
+
'average_rice': round(sum(f['rice_score'] for f in features) / len(features), 2),
|
|
120
|
+
'effort_distribution': effort_distribution,
|
|
121
|
+
'impact_distribution': impact_distribution,
|
|
122
|
+
'quick_wins': len(quick_wins),
|
|
123
|
+
'big_bets': len(big_bets),
|
|
124
|
+
'quick_wins_list': quick_wins[:3], # Top 3 quick wins
|
|
125
|
+
'big_bets_list': big_bets[:3] # Top 3 big bets
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
def generate_roadmap(self, features: List[Dict], team_capacity: int = 10) -> List[Dict]:
|
|
129
|
+
"""
|
|
130
|
+
Generate a quarterly roadmap based on team capacity
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
features: Prioritized feature list
|
|
134
|
+
team_capacity: Person-months available per quarter
|
|
135
|
+
"""
|
|
136
|
+
quarters = []
|
|
137
|
+
current_quarter = {
|
|
138
|
+
'quarter': 1,
|
|
139
|
+
'features': [],
|
|
140
|
+
'capacity_used': 0,
|
|
141
|
+
'capacity_available': team_capacity
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for feature in features:
|
|
145
|
+
effort = self.effort_map.get(feature.get('effort', 'm').lower(), 5)
|
|
146
|
+
|
|
147
|
+
if current_quarter['capacity_used'] + effort <= team_capacity:
|
|
148
|
+
current_quarter['features'].append(feature)
|
|
149
|
+
current_quarter['capacity_used'] += effort
|
|
150
|
+
else:
|
|
151
|
+
# Move to next quarter
|
|
152
|
+
current_quarter['capacity_available'] = team_capacity - current_quarter['capacity_used']
|
|
153
|
+
quarters.append(current_quarter)
|
|
154
|
+
|
|
155
|
+
current_quarter = {
|
|
156
|
+
'quarter': len(quarters) + 1,
|
|
157
|
+
'features': [feature],
|
|
158
|
+
'capacity_used': effort,
|
|
159
|
+
'capacity_available': team_capacity - effort
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if current_quarter['features']:
|
|
163
|
+
current_quarter['capacity_available'] = team_capacity - current_quarter['capacity_used']
|
|
164
|
+
quarters.append(current_quarter)
|
|
165
|
+
|
|
166
|
+
return quarters
|
|
167
|
+
|
|
168
|
+
def format_output(features: List[Dict], analysis: Dict, roadmap: List[Dict]) -> str:
|
|
169
|
+
"""Format the results for display"""
|
|
170
|
+
output = ["=" * 60]
|
|
171
|
+
output.append("RICE PRIORITIZATION RESULTS")
|
|
172
|
+
output.append("=" * 60)
|
|
173
|
+
|
|
174
|
+
# Top prioritized features
|
|
175
|
+
output.append("\nš TOP PRIORITIZED FEATURES\n")
|
|
176
|
+
for i, feature in enumerate(features[:10], 1):
|
|
177
|
+
output.append(f"{i}. {feature.get('name', 'Unnamed')}")
|
|
178
|
+
output.append(f" RICE Score: {feature['rice_score']}")
|
|
179
|
+
output.append(f" Reach: {feature.get('reach', 0)} | Impact: {feature.get('impact', 'medium')} | "
|
|
180
|
+
f"Confidence: {feature.get('confidence', 'medium')} | Effort: {feature.get('effort', 'm')}")
|
|
181
|
+
output.append("")
|
|
182
|
+
|
|
183
|
+
# Portfolio analysis
|
|
184
|
+
output.append("\nš PORTFOLIO ANALYSIS\n")
|
|
185
|
+
output.append(f"Total Features: {analysis.get('total_features', 0)}")
|
|
186
|
+
output.append(f"Total Effort: {analysis.get('total_effort_months', 0)} person-months")
|
|
187
|
+
output.append(f"Total Reach: {analysis.get('total_reach', 0):,} users")
|
|
188
|
+
output.append(f"Average RICE Score: {analysis.get('average_rice', 0)}")
|
|
189
|
+
|
|
190
|
+
output.append(f"\nšÆ Quick Wins: {analysis.get('quick_wins', 0)} features")
|
|
191
|
+
for qw in analysis.get('quick_wins_list', []):
|
|
192
|
+
output.append(f" ⢠{qw.get('name', 'Unnamed')} (RICE: {qw['rice_score']})")
|
|
193
|
+
|
|
194
|
+
output.append(f"\nš Big Bets: {analysis.get('big_bets', 0)} features")
|
|
195
|
+
for bb in analysis.get('big_bets_list', []):
|
|
196
|
+
output.append(f" ⢠{bb.get('name', 'Unnamed')} (RICE: {bb['rice_score']})")
|
|
197
|
+
|
|
198
|
+
# Roadmap
|
|
199
|
+
output.append("\n\nš
SUGGESTED ROADMAP\n")
|
|
200
|
+
for quarter in roadmap:
|
|
201
|
+
output.append(f"\nQ{quarter['quarter']} - Capacity: {quarter['capacity_used']}/{quarter['capacity_used'] + quarter['capacity_available']} person-months")
|
|
202
|
+
for feature in quarter['features']:
|
|
203
|
+
output.append(f" ⢠{feature.get('name', 'Unnamed')} (RICE: {feature['rice_score']})")
|
|
204
|
+
|
|
205
|
+
return "\n".join(output)
|
|
206
|
+
|
|
207
|
+
def load_features_from_csv(filepath: str) -> List[Dict]:
|
|
208
|
+
"""Load features from CSV file"""
|
|
209
|
+
features = []
|
|
210
|
+
with open(filepath, 'r') as f:
|
|
211
|
+
reader = csv.DictReader(f)
|
|
212
|
+
for row in reader:
|
|
213
|
+
feature = {
|
|
214
|
+
'name': row.get('name', ''),
|
|
215
|
+
'reach': int(row.get('reach', 0)),
|
|
216
|
+
'impact': row.get('impact', 'medium'),
|
|
217
|
+
'confidence': row.get('confidence', 'medium'),
|
|
218
|
+
'effort': row.get('effort', 'm'),
|
|
219
|
+
'description': row.get('description', '')
|
|
220
|
+
}
|
|
221
|
+
features.append(feature)
|
|
222
|
+
return features
|
|
223
|
+
|
|
224
|
+
def create_sample_csv(filepath: str):
|
|
225
|
+
"""Create a sample CSV file for testing"""
|
|
226
|
+
sample_features = [
|
|
227
|
+
['name', 'reach', 'impact', 'confidence', 'effort', 'description'],
|
|
228
|
+
['User Dashboard Redesign', '5000', 'high', 'high', 'l', 'Complete redesign of user dashboard'],
|
|
229
|
+
['Mobile Push Notifications', '10000', 'massive', 'medium', 'm', 'Add push notification support'],
|
|
230
|
+
['Dark Mode', '8000', 'medium', 'high', 's', 'Implement dark mode theme'],
|
|
231
|
+
['API Rate Limiting', '2000', 'low', 'high', 'xs', 'Add rate limiting to API'],
|
|
232
|
+
['Social Login', '12000', 'high', 'medium', 'm', 'Add Google/Facebook login'],
|
|
233
|
+
['Export to PDF', '3000', 'medium', 'low', 's', 'Export reports as PDF'],
|
|
234
|
+
['Team Collaboration', '4000', 'massive', 'low', 'xl', 'Real-time collaboration features'],
|
|
235
|
+
['Search Improvements', '15000', 'high', 'high', 'm', 'Enhance search functionality'],
|
|
236
|
+
['Onboarding Flow', '20000', 'massive', 'high', 's', 'Improve new user onboarding'],
|
|
237
|
+
['Analytics Dashboard', '6000', 'high', 'medium', 'l', 'Advanced analytics for users'],
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
with open(filepath, 'w', newline='') as f:
|
|
241
|
+
writer = csv.writer(f)
|
|
242
|
+
writer.writerows(sample_features)
|
|
243
|
+
|
|
244
|
+
print(f"Sample CSV created at: {filepath}")
|
|
245
|
+
|
|
246
|
+
def main():
|
|
247
|
+
parser = argparse.ArgumentParser(description='RICE Framework for Feature Prioritization')
|
|
248
|
+
parser.add_argument('input', nargs='?', help='CSV file with features or "sample" to create sample')
|
|
249
|
+
parser.add_argument('--capacity', type=int, default=10, help='Team capacity per quarter (person-months)')
|
|
250
|
+
parser.add_argument('--output', choices=['text', 'json', 'csv'], default='text', help='Output format')
|
|
251
|
+
|
|
252
|
+
args = parser.parse_args()
|
|
253
|
+
|
|
254
|
+
# Create sample if requested
|
|
255
|
+
if args.input == 'sample':
|
|
256
|
+
create_sample_csv('sample_features.csv')
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
# Use sample data if no input provided
|
|
260
|
+
if not args.input:
|
|
261
|
+
features = [
|
|
262
|
+
{'name': 'User Dashboard', 'reach': 5000, 'impact': 'high', 'confidence': 'high', 'effort': 'l'},
|
|
263
|
+
{'name': 'Push Notifications', 'reach': 10000, 'impact': 'massive', 'confidence': 'medium', 'effort': 'm'},
|
|
264
|
+
{'name': 'Dark Mode', 'reach': 8000, 'impact': 'medium', 'confidence': 'high', 'effort': 's'},
|
|
265
|
+
{'name': 'API Rate Limiting', 'reach': 2000, 'impact': 'low', 'confidence': 'high', 'effort': 'xs'},
|
|
266
|
+
{'name': 'Social Login', 'reach': 12000, 'impact': 'high', 'confidence': 'medium', 'effort': 'm'},
|
|
267
|
+
]
|
|
268
|
+
else:
|
|
269
|
+
features = load_features_from_csv(args.input)
|
|
270
|
+
|
|
271
|
+
# Calculate RICE scores
|
|
272
|
+
calculator = RICECalculator()
|
|
273
|
+
prioritized = calculator.prioritize_features(features)
|
|
274
|
+
analysis = calculator.analyze_portfolio(prioritized)
|
|
275
|
+
roadmap = calculator.generate_roadmap(prioritized, args.capacity)
|
|
276
|
+
|
|
277
|
+
# Output results
|
|
278
|
+
if args.output == 'json':
|
|
279
|
+
result = {
|
|
280
|
+
'features': prioritized,
|
|
281
|
+
'analysis': analysis,
|
|
282
|
+
'roadmap': roadmap
|
|
283
|
+
}
|
|
284
|
+
print(json.dumps(result, indent=2))
|
|
285
|
+
elif args.output == 'csv':
|
|
286
|
+
# Output prioritized features as CSV
|
|
287
|
+
if prioritized:
|
|
288
|
+
keys = prioritized[0].keys()
|
|
289
|
+
print(','.join(keys))
|
|
290
|
+
for feature in prioritized:
|
|
291
|
+
print(','.join(str(feature.get(k, '')) for k in keys))
|
|
292
|
+
else:
|
|
293
|
+
print(format_output(prioritized, analysis, roadmap))
|
|
294
|
+
|
|
295
|
+
if __name__ == "__main__":
|
|
296
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# React Doctor
|
|
2
|
+
|
|
3
|
+
Run after making React changes to catch issues early. Use when reviewing code, finishing a feature, or fixing bugs in a React project.
|
|
4
|
+
|
|
5
|
+
Scans your React codebase for security, performance, correctness, and architecture issues. Outputs a 0-100 score with actionable diagnostics.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx -y react-doctor@latest . --verbose --diff
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Workflow
|
|
14
|
+
|
|
15
|
+
Run after making changes to catch issues early. Fix errors first, then re-run to verify the score improved.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-doctor
|
|
3
|
+
description: Run after making React changes to catch issues early. Use when reviewing code, finishing a feature, or fixing bugs in a React project.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Doctor
|
|
8
|
+
|
|
9
|
+
Scans your React codebase for security, performance, correctness, and architecture issues. Outputs a 0-100 score with actionable diagnostics.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx -y react-doctor@latest . --verbose --diff
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Workflow
|
|
18
|
+
|
|
19
|
+
Run after making changes to catch issues early. Fix errors first, then re-run to verify the score improved.
|