MCPcopy Create free account
hub / github.com/garrytan/gstack / formatReport

Function formatReport

scripts/analytics.ts:69–153  ·  view source on GitHub ↗
(events: AnalyticsEvent[], period: string = 'all')

Source from the content-addressed store, hash-verified

67 * Format a report string from a list of events.
68 */
69export function formatReport(events: AnalyticsEvent[], period: string = 'all'): string {
70 const skillEvents = events.filter(e => e.event !== 'hook_fire');
71 const hookEvents = events.filter(e => e.event === 'hook_fire');
72
73 const lines: string[] = [];
74 lines.push('gstack skill usage analytics');
75 lines.push('\u2550'.repeat(39));
76 lines.push('');
77
78 const periodLabel = period === 'all' ? 'all time' : `last ${period.replace('d', ' days')}`;
79 lines.push(`Period: ${periodLabel}`);
80
81 // Top Skills
82 const skillCounts = new Map<string, number>();
83 for (const e of skillEvents) {
84 skillCounts.set(e.skill, (skillCounts.get(e.skill) || 0) + 1);
85 }
86
87 if (skillCounts.size > 0) {
88 lines.push('');
89 lines.push('Top Skills');
90
91 const sorted = [...skillCounts.entries()].sort((a, b) => b[1] - a[1]);
92 const maxName = Math.max(...sorted.map(([name]) => name.length + 1)); // +1 for /
93 const maxCount = Math.max(...sorted.map(([, count]) => String(count).length));
94
95 for (const [name, count] of sorted) {
96 const label = `/${name}`;
97 const suffix = `${count} invocation${count === 1 ? '' : 's'}`;
98 const dotLen = Math.max(2, 25 - label.length - suffix.length);
99 const dots = ' ' + '.'.repeat(dotLen) + ' ';
100 lines.push(` ${label}${dots}${suffix}`);
101 }
102 }
103
104 // By Repo
105 const repoSkills = new Map<string, Map<string, number>>();
106 for (const e of skillEvents) {
107 if (!repoSkills.has(e.repo)) repoSkills.set(e.repo, new Map());
108 const m = repoSkills.get(e.repo)!;
109 m.set(e.skill, (m.get(e.skill) || 0) + 1);
110 }
111
112 if (repoSkills.size > 0) {
113 lines.push('');
114 lines.push('By Repo');
115
116 const sortedRepos = [...repoSkills.entries()].sort((a, b) => a[0].localeCompare(b[0]));
117 for (const [repo, skills] of sortedRepos) {
118 const parts = [...skills.entries()]
119 .sort((a, b) => b[1] - a[1])
120 .map(([s, c]) => `${s}(${c})`);
121 lines.push(` ${repo}: ${parts.join(' ')}`);
122 }
123 }
124
125 // Safety Hook Events
126 const hookCounts = new Map<string, number>();

Callers 3

runScriptFunction · 0.90
analytics.test.tsFile · 0.90
mainFunction · 0.85

Calls 3

setMethod · 0.80
pushMethod · 0.45
getMethod · 0.45

Tested by 1

runScriptFunction · 0.72