antikit 1.15.0 → 1.16.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/README.md CHANGED
@@ -204,6 +204,42 @@ antikit config set-token <new_token>
204
204
  antikit config remove-token
205
205
  ```
206
206
 
207
+ ### šŸ“Š Statistics & Analytics
208
+
209
+ View insights about your installed skills.
210
+
211
+ ```bash
212
+ # Show comprehensive statistics
213
+ antikit stats
214
+ ```
215
+
216
+ **What you'll see:**
217
+ - šŸ“ˆ **Overview** - Total skills, sources, metadata coverage
218
+ - šŸ“¦ **Source Distribution** - Skills grouped by source with percentages
219
+ - šŸ”¢ **Version Stats** - Version tracking information
220
+ - 🌟 **Top Skills** - Recently installed or most used skills
221
+
222
+ **Example Output:**
223
+ ```
224
+ šŸ“Š Antikit Statistics
225
+
226
+ Metric Value
227
+ ────────────────────────────────────
228
+ Total Skills Installed 12
229
+ Total Sources Configured 3
230
+ Skills with Metadata 10
231
+
232
+ šŸ“¦ Skills by Source:
233
+
234
+ Source Skills Percentage
235
+ ───────────────────────────────────────────
236
+ antiskills 8 66.7% ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ
237
+ claudekit 3 25.0% ā–ˆā–ˆā–ˆā–ˆā–ˆ
238
+ local 1 8.3% ā–ˆā–ˆ
239
+ ```
240
+
241
+ ---
242
+
207
243
  ### šŸ”„ Tool Maintenance
208
244
 
209
245
  **Update CLI**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antikit",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "CLI tool to manage AI agent skills from Anti Gravity skills repository",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -12,6 +12,7 @@
12
12
  "test:watch": "vitest",
13
13
  "test:coverage": "vitest run --coverage",
14
14
  "release": "commit-and-tag-version",
15
+ "release:push": "./scripts/release.sh",
15
16
  "prepare": "husky",
16
17
  "format": "prettier --write ."
17
18
  },
@@ -0,0 +1,196 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ import { existsSync, readFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { getLocalSkills, findLocalSkillsDir } from '../utils/local.js';
6
+ import { getSources } from '../utils/configManager.js';
7
+ import { METADATA_FILE } from '../utils/constants.js';
8
+
9
+ export async function showStats() {
10
+ const skillsDir = findLocalSkillsDir();
11
+
12
+ if (!skillsDir) {
13
+ console.log(chalk.yellow('No .agent/skills directory found in current path.'));
14
+ console.log(chalk.dim('Run this command from a project with .agent/skills folder.'));
15
+ return;
16
+ }
17
+
18
+ const skills = getLocalSkills();
19
+ const sources = getSources();
20
+
21
+ console.log(chalk.bold.cyan('\nšŸ“Š Antikit Statistics\n'));
22
+ console.log(chalk.dim(`Skills directory: ${skillsDir}\n`));
23
+
24
+ // 1. Overview Stats
25
+ displayOverview(skills, sources);
26
+
27
+ if (skills.length > 0) {
28
+ // 2. Source Distribution
29
+ displaySourceDistribution(skills);
30
+
31
+ // 3. Version Stats
32
+ displayVersionStats(skills);
33
+
34
+ // 4. Top Skills (by size or complexity)
35
+ displayTopSkills(skills);
36
+ }
37
+
38
+ console.log();
39
+ }
40
+
41
+ function displayOverview(skills, sources) {
42
+ const overview = new Table({
43
+ head: [chalk.cyan('Metric'), chalk.cyan('Value')],
44
+ colWidths: [30, 20],
45
+ style: { head: [], border: [] }
46
+ });
47
+
48
+ overview.push(
49
+ [chalk.bold('Total Skills Installed'), chalk.green.bold(skills.length.toString())],
50
+ [chalk.bold('Total Sources Configured'), chalk.blue(sources.length.toString())],
51
+ [
52
+ chalk.bold('Skills with Metadata'),
53
+ chalk.yellow(
54
+ skills.filter(s => {
55
+ const metaPath = join(s.path, METADATA_FILE);
56
+ return existsSync(metaPath);
57
+ }).length.toString()
58
+ )
59
+ ]
60
+ );
61
+
62
+ console.log(overview.toString());
63
+ console.log();
64
+ }
65
+
66
+ function displaySourceDistribution(skills) {
67
+ const sourceMap = new Map();
68
+
69
+ skills.forEach(skill => {
70
+ const metaPath = join(skill.path, METADATA_FILE);
71
+ let sourceName = 'local';
72
+
73
+ if (existsSync(metaPath)) {
74
+ try {
75
+ const meta = JSON.parse(readFileSync(metaPath, 'utf-8'));
76
+ if (meta.source && meta.source.owner && meta.source.repo) {
77
+ // Use source name if available, otherwise owner/repo
78
+ sourceName = meta.sourceName || `${meta.source.owner}/${meta.source.repo}`;
79
+ }
80
+ } catch { }
81
+ }
82
+
83
+ sourceMap.set(sourceName, (sourceMap.get(sourceName) || 0) + 1);
84
+ });
85
+
86
+ // Sort by count descending
87
+ const sortedSources = Array.from(sourceMap.entries()).sort((a, b) => b[1] - a[1]);
88
+
89
+ console.log(chalk.bold.cyan('šŸ“¦ Skills by Source:\n'));
90
+
91
+ const sourceTable = new Table({
92
+ head: [chalk.cyan('Source'), chalk.cyan('Skills'), chalk.cyan('Percentage')],
93
+ colWidths: [35, 12, 15],
94
+ style: { head: [], border: [] }
95
+ });
96
+
97
+ const total = skills.length;
98
+ sortedSources.forEach(([source, count]) => {
99
+ const percentage = ((count / total) * 100).toFixed(1);
100
+ const bar = 'ā–ˆ'.repeat(Math.ceil((count / total) * 20));
101
+
102
+ sourceTable.push([
103
+ chalk.magenta(source),
104
+ chalk.green(count.toString()),
105
+ chalk.yellow(`${percentage}%`) + ' ' + chalk.dim(bar)
106
+ ]);
107
+ });
108
+
109
+ console.log(sourceTable.toString());
110
+ console.log();
111
+ }
112
+
113
+ function displayVersionStats(skills) {
114
+ let withVersion = 0;
115
+ let withoutVersion = 0;
116
+ const versions = new Map();
117
+
118
+ skills.forEach(skill => {
119
+ const metaPath = join(skill.path, METADATA_FILE);
120
+
121
+ if (existsSync(metaPath)) {
122
+ try {
123
+ const meta = JSON.parse(readFileSync(metaPath, 'utf-8'));
124
+ if (meta.version) {
125
+ withVersion++;
126
+ // Track version patterns (major.minor.patch)
127
+ const major = meta.version.split('.')[0];
128
+ versions.set(major, (versions.get(major) || 0) + 1);
129
+ } else {
130
+ withoutVersion++;
131
+ }
132
+ } catch {
133
+ withoutVersion++;
134
+ }
135
+ } else {
136
+ withoutVersion++;
137
+ }
138
+ });
139
+
140
+ console.log(chalk.bold.cyan('šŸ”¢ Version Information:\n'));
141
+
142
+ const versionTable = new Table({
143
+ head: [chalk.cyan('Status'), chalk.cyan('Count')],
144
+ colWidths: [35, 15],
145
+ style: { head: [], border: [] }
146
+ });
147
+
148
+ versionTable.push(
149
+ ['Skills with version info', chalk.green(withVersion.toString())],
150
+ ['Skills without version info', chalk.dim(withoutVersion.toString())]
151
+ );
152
+
153
+ console.log(versionTable.toString());
154
+ console.log();
155
+ }
156
+
157
+ function displayTopSkills(skills) {
158
+ // Show skills sorted by name with their basic info
159
+ const topCount = Math.min(5, skills.length);
160
+
161
+ console.log(chalk.bold.cyan(`🌟 Installed Skills (showing ${topCount} of ${skills.length}):\n`));
162
+
163
+ const topTable = new Table({
164
+ head: [chalk.cyan('Skill Name'), chalk.cyan('Version'), chalk.cyan('Source')],
165
+ colWidths: [30, 15, 25],
166
+ wordWrap: true,
167
+ style: { head: [], border: [] }
168
+ });
169
+
170
+ // Sort alphabetically and take top N
171
+ const sortedSkills = [...skills].sort((a, b) => a.name.localeCompare(b.name)).slice(0, topCount);
172
+
173
+ sortedSkills.forEach(skill => {
174
+ const metaPath = join(skill.path, METADATA_FILE);
175
+ let version = chalk.dim('local');
176
+ let source = chalk.dim('local');
177
+
178
+ if (existsSync(metaPath)) {
179
+ try {
180
+ const meta = JSON.parse(readFileSync(metaPath, 'utf-8'));
181
+ if (meta.version) version = chalk.yellow(meta.version);
182
+ if (meta.source && meta.source.owner && meta.source.repo) {
183
+ source = chalk.magenta(meta.sourceName || `${meta.source.owner}/${meta.source.repo}`);
184
+ }
185
+ } catch { }
186
+ }
187
+
188
+ topTable.push([chalk.bold.cyan(skill.name), version, source]);
189
+ });
190
+
191
+ console.log(topTable.toString());
192
+
193
+ if (skills.length > topCount) {
194
+ console.log(chalk.dim(`\n... and ${skills.length - topCount} more. Use 'antikit local' to see all.`));
195
+ }
196
+ }
package/src/index.js CHANGED
@@ -11,6 +11,7 @@ import { showSkillInfo } from './commands/info.js';
11
11
  import { validateSkill } from './commands/validate.js';
12
12
  import { updateCli } from './commands/update.js';
13
13
  import { upgradeSkills } from './commands/upgrade.js';
14
+ import { showStats } from './commands/stats.js';
14
15
  import { listSources, addNewSource, removeExistingSource, setDefault } from './commands/source.js';
15
16
  import { listConfig, setGitHubToken, removeGitHubToken } from './commands/config.js';
16
17
  import { checkForUpdates } from './utils/updateNotifier.js';
@@ -89,6 +90,11 @@ program
89
90
  .option('-y, --yes', 'Skip confirmation')
90
91
  .action(upgradeSkills);
91
92
 
93
+ program
94
+ .command('stats')
95
+ .description('Show statistics about installed skills')
96
+ .action(showStats);
97
+
92
98
  program
93
99
  .command('completion')
94
100
  .description('Setup autocomplete for your shell (zsh/bash)')