bbdata-cli 0.1.0 → 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.
Files changed (35) hide show
  1. package/dist/bin/bbdata.js +128 -16
  2. package/dist/bin/bbdata.js.map +1 -1
  3. package/dist/src/index.d.ts +3 -1
  4. package/dist/src/index.js +122 -12
  5. package/dist/src/index.js.map +1 -1
  6. package/dist/templates/queries/hitter-batted-ball.ts +66 -0
  7. package/dist/templates/queries/hitter-hot-cold-zones.ts +81 -0
  8. package/dist/templates/queries/hitter-vs-pitch-type.ts +78 -0
  9. package/dist/templates/queries/index.ts +24 -0
  10. package/dist/templates/queries/leaderboard-comparison.ts +72 -0
  11. package/dist/templates/queries/leaderboard-custom.ts +90 -0
  12. package/dist/templates/queries/matchup-pitcher-vs-hitter.ts +81 -0
  13. package/dist/templates/queries/matchup-situational.ts +68 -0
  14. package/dist/templates/queries/pitcher-arsenal.ts +89 -0
  15. package/dist/templates/queries/pitcher-handedness-splits.ts +81 -0
  16. package/dist/templates/queries/pitcher-velocity-trend.ts +73 -0
  17. package/dist/templates/queries/registry.ts +73 -0
  18. package/dist/templates/queries/trend-rolling-average.ts +86 -0
  19. package/dist/templates/queries/trend-year-over-year.ts +73 -0
  20. package/dist/templates/reports/advance-lineup.hbs +29 -0
  21. package/dist/templates/reports/advance-sp.hbs +60 -0
  22. package/dist/templates/reports/college-hitter-draft.hbs +49 -0
  23. package/dist/templates/reports/college-pitcher-draft.hbs +48 -0
  24. package/dist/templates/reports/dev-progress.hbs +29 -0
  25. package/dist/templates/reports/draft-board-card.hbs +35 -0
  26. package/dist/templates/reports/hs-prospect.hbs +48 -0
  27. package/dist/templates/reports/partials/footer.hbs +7 -0
  28. package/dist/templates/reports/partials/header.hbs +12 -0
  29. package/dist/templates/reports/post-promotion.hbs +25 -0
  30. package/dist/templates/reports/pro-hitter-eval.hbs +65 -0
  31. package/dist/templates/reports/pro-pitcher-eval.hbs +69 -0
  32. package/dist/templates/reports/registry.ts +215 -0
  33. package/dist/templates/reports/relief-pitcher-quick.hbs +29 -0
  34. package/dist/templates/reports/trade-target-onepager.hbs +45 -0
  35. package/package.json +1 -1
@@ -0,0 +1,81 @@
1
+ import { registerTemplate, type QueryTemplate } from './registry.js';
2
+ import type { PitchData } from '../../adapters/types.js';
3
+
4
+ const template: QueryTemplate = {
5
+ id: 'pitcher-handedness-splits',
6
+ name: 'Pitcher Handedness Splits',
7
+ category: 'pitcher',
8
+ description: 'Performance splits vs LHH and RHH — BA, SLG, K%, BB%, pitch mix, exit velocity',
9
+ preferredSources: ['savant', 'fangraphs'],
10
+ requiredParams: ['player'],
11
+ optionalParams: ['season'],
12
+ examples: [
13
+ 'bbdata query pitcher-handedness-splits --player "Blake Snell" --season 2025',
14
+ ],
15
+
16
+ buildQuery(params) {
17
+ return {
18
+ player_name: params.player,
19
+ season: params.season ?? new Date().getFullYear(),
20
+ stat_type: 'pitching',
21
+ };
22
+ },
23
+
24
+ columns() {
25
+ return ['vs', 'PA', 'AVG', 'SLG', 'K %', 'BB %', 'Avg EV', 'Whiff %'];
26
+ },
27
+
28
+ transform(data) {
29
+ const pitches = data as PitchData[];
30
+ if (pitches.length === 0) return [];
31
+
32
+ return (['L', 'R'] as const).map((hand) => {
33
+ const group = pitches.filter((p) => p.stand === hand);
34
+ if (group.length === 0) {
35
+ return { vs: `vs ${hand}HH`, PA: 0, AVG: '—', SLG: '—', 'K %': '—', 'BB %': '—', 'Avg EV': '—', 'Whiff %': '—' };
36
+ }
37
+
38
+ // Plate appearances (approximate: count events)
39
+ const pas = group.filter((p) => p.events !== null);
40
+ const hits = pas.filter((p) =>
41
+ ['single', 'double', 'triple', 'home_run'].includes(p.events ?? ''),
42
+ );
43
+ const strikeouts = pas.filter((p) => p.events === 'strikeout');
44
+ const walks = pas.filter((p) => ['walk', 'hit_by_pitch'].includes(p.events ?? ''));
45
+
46
+ // Exit velocity on contact
47
+ const contacted = group.filter((p) => p.launch_speed !== null && p.launch_speed > 0);
48
+ const avgEv = contacted.length > 0
49
+ ? contacted.reduce((s, p) => s + (p.launch_speed ?? 0), 0) / contacted.length
50
+ : null;
51
+
52
+ // Total bases for SLG
53
+ const totalBases = pas.reduce((sum, p) => {
54
+ if (p.events === 'single') return sum + 1;
55
+ if (p.events === 'double') return sum + 2;
56
+ if (p.events === 'triple') return sum + 3;
57
+ if (p.events === 'home_run') return sum + 4;
58
+ return sum;
59
+ }, 0);
60
+
61
+ // Whiff rate
62
+ const swings = group.filter((p) =>
63
+ p.description.includes('swing') || p.description.includes('foul'),
64
+ );
65
+ const whiffs = group.filter((p) => p.description.includes('swinging_strike'));
66
+
67
+ return {
68
+ vs: `vs ${hand}HH`,
69
+ PA: pas.length,
70
+ AVG: pas.length > 0 ? (hits.length / pas.length).toFixed(3) : '—',
71
+ SLG: pas.length > 0 ? (totalBases / pas.length).toFixed(3) : '—',
72
+ 'K %': pas.length > 0 ? ((strikeouts.length / pas.length) * 100).toFixed(1) + '%' : '—',
73
+ 'BB %': pas.length > 0 ? ((walks.length / pas.length) * 100).toFixed(1) + '%' : '—',
74
+ 'Avg EV': avgEv !== null ? avgEv.toFixed(1) + ' mph' : '—',
75
+ 'Whiff %': swings.length > 0 ? ((whiffs.length / swings.length) * 100).toFixed(1) + '%' : '—',
76
+ };
77
+ });
78
+ },
79
+ };
80
+
81
+ registerTemplate(template);
@@ -0,0 +1,73 @@
1
+ import { registerTemplate, type QueryTemplate } from './registry.js';
2
+ import type { PitchData } from '../../adapters/types.js';
3
+ import { pitchTypeName } from '../../adapters/types.js';
4
+
5
+ const template: QueryTemplate = {
6
+ id: 'pitcher-velocity-trend',
7
+ name: 'Pitcher Velocity Trend',
8
+ category: 'pitcher',
9
+ description: 'Month-by-month fastball velocity tracking — flags drops > 0.5 mph',
10
+ preferredSources: ['savant'],
11
+ requiredParams: ['player'],
12
+ optionalParams: ['season'],
13
+ examples: [
14
+ 'bbdata query pitcher-velocity-trend --player "Gerrit Cole" --season 2025',
15
+ ],
16
+
17
+ buildQuery(params) {
18
+ return {
19
+ player_name: params.player,
20
+ season: params.season ?? new Date().getFullYear(),
21
+ stat_type: 'pitching',
22
+ };
23
+ },
24
+
25
+ columns() {
26
+ return ['Month', 'Avg Velo', 'Max Velo', 'Min Velo', 'Δ vs Prior', 'Pitches', 'Flag'];
27
+ },
28
+
29
+ transform(data) {
30
+ const pitches = (data as PitchData[]).filter(
31
+ (p) => ['FF', 'SI', 'FC'].includes(p.pitch_type) && p.release_speed > 0,
32
+ );
33
+
34
+ if (pitches.length === 0) return [];
35
+
36
+ // Group by month
37
+ const byMonth = new Map<string, PitchData[]>();
38
+ for (const pitch of pitches) {
39
+ const month = pitch.game_date.slice(0, 7); // YYYY-MM
40
+ const group = byMonth.get(month) ?? [];
41
+ group.push(pitch);
42
+ byMonth.set(month, group);
43
+ }
44
+
45
+ const months = Array.from(byMonth.entries()).sort(([a], [b]) => a.localeCompare(b));
46
+ let prevAvg: number | null = null;
47
+
48
+ return months.map(([month, group]) => {
49
+ const velos = group.map((p) => p.release_speed);
50
+ const avg = velos.reduce((s, v) => s + v, 0) / velos.length;
51
+ const max = Math.max(...velos);
52
+ const min = Math.min(...velos);
53
+
54
+ const delta = prevAvg !== null ? avg - prevAvg : 0;
55
+ const flag = prevAvg !== null && delta < -0.5 ? '⚠ DROP' : '';
56
+ prevAvg = avg;
57
+
58
+ return {
59
+ Month: month,
60
+ 'Avg Velo': avg.toFixed(1) + ' mph',
61
+ 'Max Velo': max.toFixed(1) + ' mph',
62
+ 'Min Velo': min.toFixed(1) + ' mph',
63
+ 'Δ vs Prior': prevAvg !== null && delta !== 0
64
+ ? (delta > 0 ? '+' : '') + delta.toFixed(1) + ' mph'
65
+ : '—',
66
+ Pitches: group.length,
67
+ Flag: flag,
68
+ };
69
+ });
70
+ },
71
+ };
72
+
73
+ registerTemplate(template);
@@ -0,0 +1,73 @@
1
+ import type { DataSource, AdapterQuery, PitchData, PlayerStats } from '../../adapters/types.js';
2
+
3
+ export type QueryCategory = 'pitcher' | 'hitter' | 'matchup' | 'leaderboard' | 'trend';
4
+
5
+ export interface QueryTemplateParams {
6
+ player?: string;
7
+ players?: string[];
8
+ team?: string;
9
+ season?: number;
10
+ stat?: string;
11
+ pitchType?: string;
12
+ minPa?: number;
13
+ minIp?: number;
14
+ top?: number;
15
+ seasons?: string; // "2023-2025"
16
+ }
17
+
18
+ export interface QueryResult {
19
+ rows: Record<string, unknown>[];
20
+ columns: string[];
21
+ title: string;
22
+ description: string;
23
+ source: DataSource;
24
+ cached: boolean;
25
+ }
26
+
27
+ export interface QueryTemplate {
28
+ id: string;
29
+ name: string;
30
+ category: QueryCategory;
31
+ description: string;
32
+ preferredSources: DataSource[];
33
+ requiredParams: (keyof QueryTemplateParams)[];
34
+ optionalParams: (keyof QueryTemplateParams)[];
35
+ examples: string[];
36
+
37
+ /** Build the adapter query from user-provided params */
38
+ buildQuery(params: QueryTemplateParams): AdapterQuery;
39
+
40
+ /** Transform raw adapter data into display rows */
41
+ transform(data: PitchData[] | PlayerStats[], params: QueryTemplateParams): Record<string, unknown>[];
42
+
43
+ /** Column names for display */
44
+ columns(params: QueryTemplateParams): string[];
45
+ }
46
+
47
+ // Template registry
48
+ const templates = new Map<string, QueryTemplate>();
49
+
50
+ export function registerTemplate(template: QueryTemplate): void {
51
+ templates.set(template.id, template);
52
+ }
53
+
54
+ export function getTemplate(id: string): QueryTemplate | undefined {
55
+ return templates.get(id);
56
+ }
57
+
58
+ export function getAllTemplates(): QueryTemplate[] {
59
+ return Array.from(templates.values());
60
+ }
61
+
62
+ export function getTemplatesByCategory(category: QueryCategory): QueryTemplate[] {
63
+ return getAllTemplates().filter((t) => t.category === category);
64
+ }
65
+
66
+ export function listTemplates(): { id: string; name: string; category: string; description: string }[] {
67
+ return getAllTemplates().map((t) => ({
68
+ id: t.id,
69
+ name: t.name,
70
+ category: t.category,
71
+ description: t.description,
72
+ }));
73
+ }
@@ -0,0 +1,86 @@
1
+ import { registerTemplate, type QueryTemplate } from './registry.js';
2
+ import type { PitchData } from '../../adapters/types.js';
3
+
4
+ const template: QueryTemplate = {
5
+ id: 'trend-rolling-average',
6
+ name: 'Season Trend (Rolling Average)',
7
+ category: 'trend',
8
+ description: '15-game (hitters) or 5-start (pitchers) rolling averages — identifies sustained trends',
9
+ preferredSources: ['savant'],
10
+ requiredParams: ['player'],
11
+ optionalParams: ['season'],
12
+ examples: [
13
+ 'bbdata query trend-rolling-average --player "Freddie Freeman" --season 2025',
14
+ ],
15
+
16
+ buildQuery(params) {
17
+ return {
18
+ player_name: params.player,
19
+ season: params.season ?? new Date().getFullYear(),
20
+ stat_type: 'batting',
21
+ };
22
+ },
23
+
24
+ columns() {
25
+ return ['Window', 'Games', 'AVG', 'SLG', 'K %', 'Avg EV', 'Hard Hit %'];
26
+ },
27
+
28
+ transform(data) {
29
+ const pitches = data as PitchData[];
30
+ if (pitches.length === 0) return [];
31
+
32
+ // Group by game date
33
+ const byDate = new Map<string, PitchData[]>();
34
+ for (const p of pitches) {
35
+ const group = byDate.get(p.game_date) ?? [];
36
+ group.push(p);
37
+ byDate.set(p.game_date, group);
38
+ }
39
+
40
+ const dates = Array.from(byDate.keys()).sort();
41
+ const windowSize = 15;
42
+
43
+ if (dates.length < windowSize) {
44
+ return [{ Window: 'Insufficient data', Games: dates.length, AVG: '—', SLG: '—', 'K %': '—', 'Avg EV': '—', 'Hard Hit %': '—' }];
45
+ }
46
+
47
+ // Calculate rolling windows
48
+ const results: Record<string, unknown>[] = [];
49
+
50
+ for (let i = 0; i <= dates.length - windowSize; i += Math.max(1, Math.floor(windowSize / 3))) {
51
+ const windowDates = dates.slice(i, i + windowSize);
52
+ const windowPitches = windowDates.flatMap((d) => byDate.get(d) ?? []);
53
+
54
+ const pas = windowPitches.filter((p) => p.events !== null);
55
+ const hits = pas.filter((p) => ['single', 'double', 'triple', 'home_run'].includes(p.events ?? ''));
56
+ const ks = pas.filter((p) => p.events === 'strikeout');
57
+ const totalBases = pas.reduce((sum, p) => {
58
+ if (p.events === 'single') return sum + 1;
59
+ if (p.events === 'double') return sum + 2;
60
+ if (p.events === 'triple') return sum + 3;
61
+ if (p.events === 'home_run') return sum + 4;
62
+ return sum;
63
+ }, 0);
64
+
65
+ const batted = windowPitches.filter((p) => p.launch_speed !== null && p.launch_speed > 0);
66
+ const avgEv = batted.length > 0
67
+ ? batted.reduce((s, p) => s + p.launch_speed!, 0) / batted.length
68
+ : null;
69
+ const hardHit = batted.filter((p) => p.launch_speed! >= 95).length;
70
+
71
+ results.push({
72
+ Window: `${windowDates[0]} → ${windowDates[windowDates.length - 1]}`,
73
+ Games: windowDates.length,
74
+ AVG: pas.length > 0 ? (hits.length / pas.length).toFixed(3) : '—',
75
+ SLG: pas.length > 0 ? (totalBases / pas.length).toFixed(3) : '—',
76
+ 'K %': pas.length > 0 ? ((ks.length / pas.length) * 100).toFixed(1) + '%' : '—',
77
+ 'Avg EV': avgEv !== null ? avgEv.toFixed(1) + ' mph' : '—',
78
+ 'Hard Hit %': batted.length > 0 ? ((hardHit / batted.length) * 100).toFixed(1) + '%' : '—',
79
+ });
80
+ }
81
+
82
+ return results;
83
+ },
84
+ };
85
+
86
+ registerTemplate(template);
@@ -0,0 +1,73 @@
1
+ import { registerTemplate, type QueryTemplate } from './registry.js';
2
+ import type { PlayerStats } from '../../adapters/types.js';
3
+
4
+ const template: QueryTemplate = {
5
+ id: 'trend-year-over-year',
6
+ name: 'Year-over-Year Comparison',
7
+ category: 'trend',
8
+ description: 'Compare metric changes year to year — flags changes greater than 10%',
9
+ preferredSources: ['fangraphs', 'mlb-stats-api'],
10
+ requiredParams: ['player'],
11
+ optionalParams: ['seasons'], // e.g. "2023-2025"
12
+ examples: [
13
+ 'bbdata query trend-year-over-year --player "Julio Rodriguez" --seasons 2023-2025',
14
+ ],
15
+
16
+ buildQuery(params) {
17
+ // We'll need multiple seasons — use the most recent by default
18
+ const season = params.season ?? new Date().getFullYear();
19
+ return {
20
+ player_name: params.player,
21
+ season,
22
+ stat_type: 'batting',
23
+ };
24
+ },
25
+
26
+ columns() {
27
+ return ['Metric', 'Prior', 'Current', 'Change', 'Flag'];
28
+ },
29
+
30
+ transform(data, params) {
31
+ const stats = data as PlayerStats[];
32
+ if (stats.length === 0) return [];
33
+
34
+ // If we have multi-season data, compare last two
35
+ // With single-season data, we show what we have
36
+ const player = stats[0];
37
+ const s = player.stats;
38
+
39
+ // Key metrics to compare
40
+ const metrics = ['AVG', 'OBP', 'SLG', 'HR', 'wRC+', 'K%', 'BB%', 'WAR', 'ISO', 'BABIP'];
41
+
42
+ return metrics.map((metric) => {
43
+ const current = findStat(s, metric);
44
+ // Without multi-season support in a single query, we show current only
45
+ // Full YoY requires querying multiple seasons (future enhancement)
46
+ return {
47
+ Metric: metric,
48
+ Prior: '—',
49
+ Current: current !== null ? formatVal(current) : '—',
50
+ Change: '—',
51
+ Flag: '',
52
+ };
53
+ });
54
+ },
55
+ };
56
+
57
+ function findStat(stats: Record<string, unknown>, key: string): number | null {
58
+ const lower = key.toLowerCase().replace(/[+%]/g, '');
59
+ for (const [k, v] of Object.entries(stats)) {
60
+ if (k.toLowerCase().replace(/[+%]/g, '') === lower) {
61
+ const n = Number(v);
62
+ return isNaN(n) ? null : n;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+
68
+ function formatVal(n: number): string {
69
+ if (n < 1 && n > 0) return n.toFixed(3);
70
+ return n % 1 === 0 ? String(n) : n.toFixed(1);
71
+ }
72
+
73
+ registerTemplate(template);
@@ -0,0 +1,29 @@
1
+ # Advance Report: {{team}} Lineup
2
+
3
+ **Season:** {{season}} | **For:** {{audience}} | **Generated:** {{date}}
4
+
5
+ ---
6
+
7
+ ## Header
8
+
9
+ | Field | Value |
10
+ |-------|-------|
11
+ | Opponent | {{team}} |
12
+ | Season | {{season}} |
13
+
14
+ ## Lineup Overview
15
+
16
+ *Projected lineup, team tendencies, overall approach — to be populated*
17
+
18
+ ## Hitter Breakdowns
19
+
20
+ *Individual hitter cards — to be generated per player:*
21
+ *Strengths, weaknesses, pitch approach, hot/cold zones*
22
+
23
+ ## Key Matchups
24
+
25
+ *Critical at-bats to plan for — to be filled by coaching staff*
26
+
27
+ ---
28
+
29
+ *Generated by bbdata CLI · 1-page advance format*
@@ -0,0 +1,60 @@
1
+ # Advance Report: {{player}} (SP)
2
+
3
+ **Season:** {{season}} | **For:** {{audience}} | **Generated:** {{date}}
4
+
5
+ ---
6
+
7
+ ## Recent Form
8
+
9
+ *Last 5 starts — to be populated with game log data*
10
+
11
+ ## Pitch Mix & Sequencing
12
+
13
+ {{#if data.pitcher-arsenal}}
14
+ ### Arsenal Overview
15
+
16
+ | Pitch | Usage | Velo | Spin | Whiff % |
17
+ |-------|-------|------|------|---------|
18
+ {{#each data.pitcher-arsenal}}
19
+ | {{this.[Pitch Type]}} | {{this.[Usage %]}} | {{this.[Avg Velo]}} | {{this.[Avg Spin]}} | {{this.[Whiff %]}} |
20
+ {{/each}}
21
+
22
+ ### By Count (to be populated)
23
+ - **Ahead in count:** Primary pitch selection
24
+ - **Behind in count:** Go-to pitch
25
+ - **Two-strike:** Put-away pitch
26
+ {{else}}
27
+ *Arsenal data not available*
28
+ {{/if}}
29
+
30
+ ## Times Through the Order
31
+
32
+ *3rd time adjustment, velocity drop patterns — to be analyzed from pitch-level data*
33
+
34
+ ## Platoon Vulnerabilities
35
+
36
+ {{#if data.pitcher-handedness-splits}}
37
+ | Split | PA | AVG | SLG | K % | BB % |
38
+ |-------|-----|-----|-----|-----|------|
39
+ {{#each data.pitcher-handedness-splits}}
40
+ | {{this.vs}} | {{this.PA}} | {{this.AVG}} | {{this.SLG}} | {{this.[K %]}} | {{this.[BB %]}} |
41
+ {{/each}}
42
+ {{else}}
43
+ *Splits data not available*
44
+ {{/if}}
45
+
46
+ ## How to Attack
47
+
48
+ ### Early in Game (1st time through)
49
+ - *To be filled: patient approach? aggressive?*
50
+
51
+ ### Late in Game (3rd+ time through)
52
+ - *To be filled: exploit fatigue patterns?*
53
+
54
+ ### Key Weaknesses
55
+ - *To be filled by evaluator*
56
+
57
+ ---
58
+
59
+ *Generated by bbdata CLI · Data sources: {{sources}}*
60
+ *This is a 1-page advance report designed for in-game use.*
@@ -0,0 +1,49 @@
1
+ # College Hitter Draft Report: {{player}}
2
+
3
+ **Season:** {{season}} | **Generated:** {{date}}
4
+
5
+ ---
6
+
7
+ ## Header
8
+
9
+ | Field | Value |
10
+ |-------|-------|
11
+ | Player | {{player}} |
12
+ | School | *To be filled* |
13
+ | Bats/Throws | *To be filled* |
14
+ | Position | *To be filled* |
15
+ | Age | *To be filled* |
16
+
17
+ ## Physical
18
+
19
+ *Body type, athleticism, strength projection — to be filled*
20
+
21
+ ## Tool Grades (20-80 Scale)
22
+
23
+ | Tool | Present | Future | Notes |
24
+ |------|---------|--------|-------|
25
+ | Hit | — | — | *Bat speed, plate coverage* |
26
+ | Power | — | — | *Raw, game power, pull/oppo* |
27
+ | Speed | — | — | *Run times, baserunning IQ* |
28
+ | Field | — | — | *Actions, arm, range* |
29
+ | Arm | — | — | *Strength, accuracy* |
30
+
31
+ ## Performance
32
+
33
+ *College slash line, K%, BB%, wood bat league stats — to be filled*
34
+
35
+ ## Projection
36
+
37
+ *Ceiling, floor, likely outcome — to be filled*
38
+
39
+ ## Risk
40
+
41
+ *Swing concerns, defensive fit, signability — to be filled*
42
+
43
+ ## Recommendation
44
+
45
+ *Draft round range, comparison — to be filled*
46
+
47
+ ---
48
+
49
+ *Generated by bbdata CLI*
@@ -0,0 +1,48 @@
1
+ # College Pitcher Draft Report: {{player}}
2
+
3
+ **Season:** {{season}} | **Generated:** {{date}}
4
+
5
+ ---
6
+
7
+ ## Header
8
+
9
+ | Field | Value |
10
+ |-------|-------|
11
+ | Player | {{player}} |
12
+ | School | *To be filled* |
13
+ | Throws | *To be filled* |
14
+ | Age | *To be filled* |
15
+ | Height/Weight | *To be filled* |
16
+
17
+ ## Physical
18
+
19
+ *Body type, athleticism, projectability — to be filled by evaluator*
20
+
21
+ ## Arsenal Grades (20-80 Scale)
22
+
23
+ | Tool | Present | Future | Notes |
24
+ |------|---------|--------|-------|
25
+ | Fastball | — | — | *Velo, life, command* |
26
+ | Breaking Ball | — | — | *Type, spin, depth* |
27
+ | Changeup | — | — | *Fade, deception* |
28
+ | Command | — | — | *Zone control, sequencing* |
29
+
30
+ ## Performance
31
+
32
+ *College stats: ERA, K/9, BB/9, WHIP — to be filled*
33
+
34
+ ## Projection
35
+
36
+ *Ceiling, floor, likely outcome — to be filled by evaluator*
37
+
38
+ ## Risk
39
+
40
+ *Injury history, workload, mechanics — to be filled by evaluator*
41
+
42
+ ## Recommendation
43
+
44
+ *Draft round range, comparison, acquire/pass — to be filled*
45
+
46
+ ---
47
+
48
+ *Generated by bbdata CLI*
@@ -0,0 +1,29 @@
1
+ # Development Progress Report: {{player}}
2
+
3
+ **Season:** {{season}} | **Generated:** {{date}}
4
+
5
+ ---
6
+
7
+ ## Current Stats
8
+
9
+ *Current level, slash line, K%, BB%, ISO — to be populated*
10
+
11
+ ## Trend Analysis
12
+
13
+ *Month-over-month progression, key metric changes — to be populated*
14
+
15
+ ## Mechanical Notes
16
+
17
+ *Changes, adjustments, coach observations — to be filled by evaluator*
18
+
19
+ ## Development Goals
20
+
21
+ *Target areas for improvement — to be filled by development staff*
22
+
23
+ ## Next Steps
24
+
25
+ *Promotion timeline, assignment recommendation — to be filled*
26
+
27
+ ---
28
+
29
+ *Generated by bbdata CLI*
@@ -0,0 +1,35 @@
1
+ # {{player}} — Draft Board Card
2
+
3
+ **Generated:** {{date}}
4
+
5
+ ---
6
+
7
+ | Field | Value |
8
+ |-------|-------|
9
+ | **Name** | {{player}} |
10
+ | **Position** | *To be filled* |
11
+ | **School** | *To be filled* |
12
+ | **B/T** | *To be filled* |
13
+ | **Age** | *To be filled* |
14
+
15
+ ### Tool Grades
16
+
17
+ | Hit | Power | Speed | Field | Arm | OFP |
18
+ |-----|-------|-------|-------|-----|-----|
19
+ | — | — | — | — | — | — |
20
+
21
+ ### Projection
22
+
23
+ *One-line ceiling* — *To be filled*
24
+
25
+ ### Comp
26
+
27
+ *Player comparison* — *To be filled*
28
+
29
+ ### Round Range
30
+
31
+ *Expected draft range* — *To be filled*
32
+
33
+ ---
34
+
35
+ *bbdata CLI · Draft room card format*
@@ -0,0 +1,48 @@
1
+ # High School Prospect Report: {{player}}
2
+
3
+ **Season:** {{season}} | **Generated:** {{date}}
4
+
5
+ ---
6
+
7
+ ## Header
8
+
9
+ | Field | Value |
10
+ |-------|-------|
11
+ | Player | {{player}} |
12
+ | School | *To be filled* |
13
+ | Position | *To be filled* |
14
+ | Commitment | *To be filled* |
15
+
16
+ ## Physical
17
+
18
+ *Body type, athleticism, projectability — to be filled*
19
+
20
+ ## Tool Grades (20-80 Scale)
21
+
22
+ | Tool | Present | Future | Notes |
23
+ |------|---------|--------|-------|
24
+ | Hit | — | — | |
25
+ | Power | — | — | |
26
+ | Speed | — | — | |
27
+ | Field | — | — | |
28
+ | Arm | — | — | |
29
+
30
+ ## Makeup
31
+
32
+ *Work ethic, competitiveness, coachability — to be filled*
33
+
34
+ ## Projection
35
+
36
+ *Development timeline, ceiling, floor — to be filled*
37
+
38
+ ## Signability
39
+
40
+ *Commitment strength, slot expectations — to be filled*
41
+
42
+ ## Recommendation
43
+
44
+ *Draft/follow recommendation — to be filled*
45
+
46
+ ---
47
+
48
+ *Generated by bbdata CLI*