claude-termux 1.0.3 → 2.0.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.
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ const https = require('https');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ const MCP_CONFIG_URL = 'https://raw.githubusercontent.com/zesbe/claude-mcp-config/main/mcp.json';
8
+ const HOME = os.homedir();
9
+ const MCP_PATH = path.join(HOME, '.mcp.json');
10
+
11
+ console.log('šŸš€ ClaudeAll MCP Config Installer\n');
12
+
13
+ // Backup existing config
14
+ if (fs.existsSync(MCP_PATH)) {
15
+ const backup = `${MCP_PATH}.backup.${Date.now()}`;
16
+ fs.copyFileSync(MCP_PATH, backup);
17
+ console.log(`šŸ“¦ Backed up existing config to ${backup}`);
18
+ }
19
+
20
+ // Download MCP config
21
+ console.log('šŸ“„ Downloading MCP config...');
22
+
23
+ https.get(MCP_CONFIG_URL, (res) => {
24
+ let data = '';
25
+ res.on('data', chunk => data += chunk);
26
+ res.on('end', () => {
27
+ try {
28
+ // Validate JSON
29
+ JSON.parse(data);
30
+ fs.writeFileSync(MCP_PATH, data);
31
+ console.log(`āœ… Installed MCP config to ${MCP_PATH}\n`);
32
+
33
+ const config = JSON.parse(data);
34
+ const servers = Object.keys(config.mcpServers || {});
35
+ console.log('MCP Servers installed:');
36
+ servers.forEach((s, i) => console.log(` ${i+1}. ${s}`));
37
+ console.log('\nšŸŽ‰ Run "claude" to start with MCP enabled!');
38
+ } catch (e) {
39
+ console.error('āŒ Invalid JSON response');
40
+ process.exit(1);
41
+ }
42
+ });
43
+ }).on('error', (e) => {
44
+ console.error(`āŒ Download failed: ${e.message}`);
45
+ process.exit(1);
46
+ });
@@ -0,0 +1,722 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * ClaudeAll Skills CLI
5
+ * Advanced skill management beyond skills.sh
6
+ *
7
+ * Features skills.sh DOESN'T have:
8
+ * - Skill testing/validation
9
+ * - Quality scoring
10
+ * - Dependencies management
11
+ * - Skill scaffolding
12
+ * - Local development
13
+ * - Hooks system
14
+ * - Offline support
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const { execSync } = require('child_process');
20
+
21
+ const CLAUDE_DIR = path.join(process.env.HOME, '.claude');
22
+ const SKILLS_DIR = path.join(CLAUDE_DIR, 'skills');
23
+ const SKILLS_REGISTRY = path.join(CLAUDE_DIR, '.skills-registry.json');
24
+
25
+ // Colors for terminal
26
+ const colors = {
27
+ reset: '\x1b[0m',
28
+ bright: '\x1b[1m',
29
+ green: '\x1b[32m',
30
+ yellow: '\x1b[33m',
31
+ blue: '\x1b[34m',
32
+ magenta: '\x1b[35m',
33
+ cyan: '\x1b[36m',
34
+ red: '\x1b[31m',
35
+ gray: '\x1b[90m',
36
+ };
37
+
38
+ const c = (color, text) => `${colors[color]}${text}${colors.reset}`;
39
+
40
+ // ============================================
41
+ // SKILL SCHEMA
42
+ // ============================================
43
+ const SKILL_SCHEMA = {
44
+ required: ['name', 'description', 'version'],
45
+ optional: ['author', 'license', 'dependencies', 'hooks', 'tags', 'agents', 'quality'],
46
+ defaults: {
47
+ version: '1.0.0',
48
+ license: 'MIT',
49
+ agents: ['claude-code', 'cursor', 'copilot'],
50
+ tags: [],
51
+ dependencies: [],
52
+ hooks: {
53
+ preInstall: null,
54
+ postInstall: null,
55
+ preUninstall: null,
56
+ postUninstall: null,
57
+ },
58
+ quality: {
59
+ tested: false,
60
+ documented: false,
61
+ examples: false,
62
+ }
63
+ }
64
+ };
65
+
66
+ // ============================================
67
+ // UTILITY FUNCTIONS
68
+ // ============================================
69
+
70
+ function ensureDir(dir) {
71
+ if (!fs.existsSync(dir)) {
72
+ fs.mkdirSync(dir, { recursive: true });
73
+ }
74
+ }
75
+
76
+ function loadRegistry() {
77
+ if (fs.existsSync(SKILLS_REGISTRY)) {
78
+ return JSON.parse(fs.readFileSync(SKILLS_REGISTRY, 'utf8'));
79
+ }
80
+ return { skills: {}, installed: [], lastUpdate: null };
81
+ }
82
+
83
+ function saveRegistry(registry) {
84
+ registry.lastUpdate = new Date().toISOString();
85
+ fs.writeFileSync(SKILLS_REGISTRY, JSON.stringify(registry, null, 2));
86
+ }
87
+
88
+ function parseSkillMd(content) {
89
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
90
+ if (!frontmatterMatch) return null;
91
+
92
+ const frontmatter = {};
93
+ frontmatterMatch[1].split('\n').forEach(line => {
94
+ const [key, ...valueParts] = line.split(':');
95
+ if (key && valueParts.length) {
96
+ frontmatter[key.trim()] = valueParts.join(':').trim();
97
+ }
98
+ });
99
+
100
+ return {
101
+ ...frontmatter,
102
+ content: content.replace(/^---\n[\s\S]*?\n---\n*/, '')
103
+ };
104
+ }
105
+
106
+ function calculateQualityScore(skillPath) {
107
+ let score = 0;
108
+ const checks = [];
109
+
110
+ const skillDir = fs.statSync(skillPath).isDirectory() ? skillPath : path.dirname(skillPath);
111
+ const skillName = path.basename(skillDir);
112
+
113
+ // Check 1: Has SKILL.md (25 points)
114
+ const skillMd = path.join(skillDir, 'SKILL.md');
115
+ if (fs.existsSync(skillMd)) {
116
+ score += 25;
117
+ checks.push({ name: 'Has SKILL.md', passed: true, points: 25 });
118
+
119
+ const content = fs.readFileSync(skillMd, 'utf8');
120
+
121
+ // Check 2: Has frontmatter (10 points)
122
+ if (content.startsWith('---')) {
123
+ score += 10;
124
+ checks.push({ name: 'Has frontmatter', passed: true, points: 10 });
125
+ } else {
126
+ checks.push({ name: 'Has frontmatter', passed: false, points: 0 });
127
+ }
128
+
129
+ // Check 3: Has description (10 points)
130
+ if (content.includes('description:')) {
131
+ score += 10;
132
+ checks.push({ name: 'Has description', passed: true, points: 10 });
133
+ } else {
134
+ checks.push({ name: 'Has description', passed: false, points: 0 });
135
+ }
136
+
137
+ // Check 4: Has examples/usage section (15 points)
138
+ if (content.toLowerCase().includes('## example') || content.toLowerCase().includes('## usage')) {
139
+ score += 15;
140
+ checks.push({ name: 'Has examples', passed: true, points: 15 });
141
+ } else {
142
+ checks.push({ name: 'Has examples', passed: false, points: 0 });
143
+ }
144
+
145
+ // Check 5: Has code blocks (10 points)
146
+ if (content.includes('```')) {
147
+ score += 10;
148
+ checks.push({ name: 'Has code blocks', passed: true, points: 10 });
149
+ } else {
150
+ checks.push({ name: 'Has code blocks', passed: false, points: 0 });
151
+ }
152
+ } else {
153
+ checks.push({ name: 'Has SKILL.md', passed: false, points: 0 });
154
+ }
155
+
156
+ // Check 6: Has tests (15 points)
157
+ const testFiles = fs.readdirSync(skillDir).filter(f => f.startsWith('test-'));
158
+ if (testFiles.length > 0) {
159
+ score += 15;
160
+ checks.push({ name: `Has tests (${testFiles.length})`, passed: true, points: 15 });
161
+ } else {
162
+ checks.push({ name: 'Has tests', passed: false, points: 0 });
163
+ }
164
+
165
+ // Check 7: Has skill.json (10 points)
166
+ const skillJson = path.join(skillDir, 'skill.json');
167
+ if (fs.existsSync(skillJson)) {
168
+ score += 10;
169
+ checks.push({ name: 'Has skill.json', passed: true, points: 10 });
170
+ } else {
171
+ checks.push({ name: 'Has skill.json', passed: false, points: 0 });
172
+ }
173
+
174
+ // Check 8: Has CREATION-LOG.md (5 points)
175
+ if (fs.existsSync(path.join(skillDir, 'CREATION-LOG.md'))) {
176
+ score += 5;
177
+ checks.push({ name: 'Has creation log', passed: true, points: 5 });
178
+ } else {
179
+ checks.push({ name: 'Has creation log', passed: false, points: 0 });
180
+ }
181
+
182
+ return { score, maxScore: 100, checks };
183
+ }
184
+
185
+ // ============================================
186
+ // CLI COMMANDS
187
+ // ============================================
188
+
189
+ const commands = {
190
+ // List all installed skills
191
+ list: () => {
192
+ console.log(c('cyan', '\n ClaudeAll Skills\n'));
193
+
194
+ if (!fs.existsSync(SKILLS_DIR)) {
195
+ console.log(c('yellow', ' No skills installed yet.'));
196
+ return;
197
+ }
198
+
199
+ const skills = fs.readdirSync(SKILLS_DIR).filter(f =>
200
+ fs.statSync(path.join(SKILLS_DIR, f)).isDirectory()
201
+ );
202
+
203
+ if (skills.length === 0) {
204
+ console.log(c('yellow', ' No skills found.'));
205
+ return;
206
+ }
207
+
208
+ console.log(c('gray', ` Found ${skills.length} skills:\n`));
209
+
210
+ skills.forEach((skill, i) => {
211
+ const skillPath = path.join(SKILLS_DIR, skill);
212
+ const { score } = calculateQualityScore(skillPath);
213
+
214
+ const scoreColor = score >= 70 ? 'green' : score >= 40 ? 'yellow' : 'red';
215
+ const scoreBar = 'ā–ˆ'.repeat(Math.floor(score / 10)) + 'ā–‘'.repeat(10 - Math.floor(score / 10));
216
+
217
+ console.log(` ${c('bright', (i + 1).toString().padStart(2))}. ${c('green', skill)}`);
218
+ console.log(` Quality: ${c(scoreColor, scoreBar)} ${score}/100`);
219
+ });
220
+
221
+ console.log();
222
+ },
223
+
224
+ // Search skills
225
+ search: (query) => {
226
+ if (!query) {
227
+ console.log(c('red', '\n Usage: claude-all skills search <query>\n'));
228
+ return;
229
+ }
230
+
231
+ console.log(c('cyan', `\n Searching for "${query}"...\n`));
232
+
233
+ const skills = fs.readdirSync(SKILLS_DIR).filter(f =>
234
+ fs.statSync(path.join(SKILLS_DIR, f)).isDirectory()
235
+ );
236
+
237
+ const matches = skills.filter(skill => {
238
+ const skillPath = path.join(SKILLS_DIR, skill);
239
+ const skillMd = path.join(skillPath, 'SKILL.md');
240
+
241
+ if (skill.toLowerCase().includes(query.toLowerCase())) return true;
242
+
243
+ if (fs.existsSync(skillMd)) {
244
+ const content = fs.readFileSync(skillMd, 'utf8').toLowerCase();
245
+ return content.includes(query.toLowerCase());
246
+ }
247
+
248
+ return false;
249
+ });
250
+
251
+ if (matches.length === 0) {
252
+ console.log(c('yellow', ' No skills found matching your query.'));
253
+ } else {
254
+ console.log(c('gray', ` Found ${matches.length} matching skills:\n`));
255
+ matches.forEach((skill, i) => {
256
+ console.log(` ${i + 1}. ${c('green', skill)}`);
257
+ });
258
+ }
259
+
260
+ console.log();
261
+ },
262
+
263
+ // Create new skill from template
264
+ create: (name) => {
265
+ if (!name) {
266
+ console.log(c('red', '\n Usage: claude-all skills create <skill-name>\n'));
267
+ return;
268
+ }
269
+
270
+ const skillDir = path.join(SKILLS_DIR, name);
271
+
272
+ if (fs.existsSync(skillDir)) {
273
+ console.log(c('red', `\n Skill "${name}" already exists!\n`));
274
+ return;
275
+ }
276
+
277
+ console.log(c('cyan', `\n Creating skill: ${name}\n`));
278
+
279
+ // Create directory
280
+ fs.mkdirSync(skillDir, { recursive: true });
281
+
282
+ // Create skill.json
283
+ const skillJson = {
284
+ name,
285
+ version: '1.0.0',
286
+ description: `Description for ${name}`,
287
+ author: 'Your Name',
288
+ license: 'MIT',
289
+ tags: [],
290
+ agents: ['claude-code', 'cursor', 'copilot', 'gemini'],
291
+ dependencies: [],
292
+ hooks: {
293
+ preInstall: null,
294
+ postInstall: null
295
+ },
296
+ quality: {
297
+ tested: false,
298
+ documented: false,
299
+ examples: false
300
+ }
301
+ };
302
+
303
+ fs.writeFileSync(
304
+ path.join(skillDir, 'skill.json'),
305
+ JSON.stringify(skillJson, null, 2)
306
+ );
307
+
308
+ // Create SKILL.md
309
+ const skillMd = `---
310
+ name: ${name}
311
+ description: Description for ${name}
312
+ version: 1.0.0
313
+ author: Your Name
314
+ tags: []
315
+ ---
316
+
317
+ # ${name.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}
318
+
319
+ ## Overview
320
+
321
+ Describe what this skill does and when to use it.
322
+
323
+ ## When to Use
324
+
325
+ - Situation 1
326
+ - Situation 2
327
+ - Situation 3
328
+
329
+ ## How It Works
330
+
331
+ Explain the approach or methodology.
332
+
333
+ ## Examples
334
+
335
+ ### Example 1: Basic Usage
336
+
337
+ \`\`\`
338
+ // Your example here
339
+ \`\`\`
340
+
341
+ ### Example 2: Advanced Usage
342
+
343
+ \`\`\`
344
+ // Advanced example
345
+ \`\`\`
346
+
347
+ ## Best Practices
348
+
349
+ 1. Practice 1
350
+ 2. Practice 2
351
+ 3. Practice 3
352
+
353
+ ## Common Mistakes
354
+
355
+ - Mistake 1 and how to avoid it
356
+ - Mistake 2 and how to avoid it
357
+
358
+ ## Related Skills
359
+
360
+ - skill-name-1
361
+ - skill-name-2
362
+ `;
363
+
364
+ fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillMd);
365
+
366
+ // Create test template
367
+ const testMd = `# Test: ${name}
368
+
369
+ ## Scenario
370
+
371
+ Describe the test scenario here.
372
+
373
+ ## Expected Behavior
374
+
375
+ What should happen when the skill is applied correctly.
376
+
377
+ ## Test Steps
378
+
379
+ 1. Step 1
380
+ 2. Step 2
381
+ 3. Step 3
382
+
383
+ ## Success Criteria
384
+
385
+ - [ ] Criterion 1
386
+ - [ ] Criterion 2
387
+ - [ ] Criterion 3
388
+ `;
389
+
390
+ fs.writeFileSync(path.join(skillDir, 'test-basic.md'), testMd);
391
+
392
+ // Create CREATION-LOG.md
393
+ const creationLog = `# Creation Log: ${name}
394
+
395
+ ## Created
396
+ - Date: ${new Date().toISOString().split('T')[0]}
397
+ - Author: Your Name
398
+
399
+ ## Changelog
400
+
401
+ ### v1.0.0
402
+ - Initial creation
403
+ `;
404
+
405
+ fs.writeFileSync(path.join(skillDir, 'CREATION-LOG.md'), creationLog);
406
+
407
+ console.log(c('green', ' āœ“ Created skill.json'));
408
+ console.log(c('green', ' āœ“ Created SKILL.md'));
409
+ console.log(c('green', ' āœ“ Created test-basic.md'));
410
+ console.log(c('green', ' āœ“ Created CREATION-LOG.md'));
411
+ console.log();
412
+ console.log(c('cyan', ` Skill created at: ${skillDir}`));
413
+ console.log(c('gray', ' Edit SKILL.md to add your skill content.'));
414
+ console.log();
415
+ },
416
+
417
+ // Validate skill
418
+ validate: (name) => {
419
+ if (!name) {
420
+ console.log(c('red', '\n Usage: claude-all skills validate <skill-name>\n'));
421
+ return;
422
+ }
423
+
424
+ const skillDir = path.join(SKILLS_DIR, name);
425
+
426
+ if (!fs.existsSync(skillDir)) {
427
+ console.log(c('red', `\n Skill "${name}" not found!\n`));
428
+ return;
429
+ }
430
+
431
+ console.log(c('cyan', `\n Validating skill: ${name}\n`));
432
+
433
+ const { score, maxScore, checks } = calculateQualityScore(skillDir);
434
+
435
+ checks.forEach(check => {
436
+ const icon = check.passed ? c('green', 'āœ“') : c('red', 'āœ—');
437
+ const points = check.passed ? c('green', `+${check.points}`) : c('gray', '+0');
438
+ console.log(` ${icon} ${check.name} ${points}`);
439
+ });
440
+
441
+ console.log();
442
+
443
+ const scoreColor = score >= 70 ? 'green' : score >= 40 ? 'yellow' : 'red';
444
+ console.log(c('bright', ` Quality Score: ${c(scoreColor, `${score}/${maxScore}`)}`));
445
+
446
+ if (score >= 70) {
447
+ console.log(c('green', '\n āœ“ Skill is ready for publishing!\n'));
448
+ } else if (score >= 40) {
449
+ console.log(c('yellow', '\n ! Skill needs improvement before publishing.\n'));
450
+ } else {
451
+ console.log(c('red', '\n āœ— Skill requires significant work.\n'));
452
+ }
453
+ },
454
+
455
+ // Test skill
456
+ test: (name) => {
457
+ if (!name) {
458
+ console.log(c('red', '\n Usage: claude-all skills test <skill-name>\n'));
459
+ return;
460
+ }
461
+
462
+ const skillDir = path.join(SKILLS_DIR, name);
463
+
464
+ if (!fs.existsSync(skillDir)) {
465
+ console.log(c('red', `\n Skill "${name}" not found!\n`));
466
+ return;
467
+ }
468
+
469
+ console.log(c('cyan', `\n Testing skill: ${name}\n`));
470
+
471
+ const testFiles = fs.readdirSync(skillDir).filter(f => f.startsWith('test-'));
472
+
473
+ if (testFiles.length === 0) {
474
+ console.log(c('yellow', ' No test files found (test-*.md)'));
475
+ console.log(c('gray', ' Create test files with: claude-all skills create-test <skill-name>'));
476
+ console.log();
477
+ return;
478
+ }
479
+
480
+ console.log(c('gray', ` Found ${testFiles.length} test files:\n`));
481
+
482
+ testFiles.forEach((file, i) => {
483
+ const testPath = path.join(skillDir, file);
484
+ const content = fs.readFileSync(testPath, 'utf8');
485
+
486
+ // Check if test has success criteria
487
+ const hasCriteria = content.includes('Success Criteria') || content.includes('- [ ]');
488
+ const criteriaMatch = content.match(/- \[[ x]\]/g) || [];
489
+ const completed = criteriaMatch.filter(m => m.includes('x')).length;
490
+ const total = criteriaMatch.length;
491
+
492
+ const status = total === 0 ? c('gray', 'No criteria') :
493
+ completed === total ? c('green', `${completed}/${total} passed`) :
494
+ c('yellow', `${completed}/${total} passed`);
495
+
496
+ console.log(` ${i + 1}. ${file} - ${status}`);
497
+ });
498
+
499
+ console.log();
500
+ },
501
+
502
+ // Show skill info
503
+ info: (name) => {
504
+ if (!name) {
505
+ console.log(c('red', '\n Usage: claude-all skills info <skill-name>\n'));
506
+ return;
507
+ }
508
+
509
+ const skillDir = path.join(SKILLS_DIR, name);
510
+
511
+ if (!fs.existsSync(skillDir)) {
512
+ console.log(c('red', `\n Skill "${name}" not found!\n`));
513
+ return;
514
+ }
515
+
516
+ console.log(c('cyan', `\n Skill: ${name}\n`));
517
+
518
+ // Read skill.json if exists
519
+ const skillJsonPath = path.join(skillDir, 'skill.json');
520
+ if (fs.existsSync(skillJsonPath)) {
521
+ const meta = JSON.parse(fs.readFileSync(skillJsonPath, 'utf8'));
522
+ console.log(c('gray', ' Metadata:'));
523
+ console.log(` Version: ${c('green', meta.version || 'N/A')}`);
524
+ console.log(` Author: ${meta.author || 'Unknown'}`);
525
+ console.log(` License: ${meta.license || 'N/A'}`);
526
+ console.log(` Tags: ${(meta.tags || []).join(', ') || 'None'}`);
527
+ console.log(` Agents: ${(meta.agents || []).join(', ')}`);
528
+ if (meta.dependencies && meta.dependencies.length > 0) {
529
+ console.log(` Dependencies: ${meta.dependencies.join(', ')}`);
530
+ }
531
+ console.log();
532
+ }
533
+
534
+ // Read SKILL.md
535
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
536
+ if (fs.existsSync(skillMdPath)) {
537
+ const parsed = parseSkillMd(fs.readFileSync(skillMdPath, 'utf8'));
538
+ if (parsed) {
539
+ console.log(c('gray', ' Description:'));
540
+ console.log(` ${parsed.description || 'No description'}`);
541
+ console.log();
542
+ }
543
+ }
544
+
545
+ // Quality score
546
+ const { score } = calculateQualityScore(skillDir);
547
+ const scoreColor = score >= 70 ? 'green' : score >= 40 ? 'yellow' : 'red';
548
+ console.log(c('gray', ' Quality Score:'));
549
+ console.log(` ${c(scoreColor, `${score}/100`)}`);
550
+ console.log();
551
+
552
+ // Files
553
+ const files = fs.readdirSync(skillDir);
554
+ console.log(c('gray', ' Files:'));
555
+ files.forEach(f => console.log(` - ${f}`));
556
+ console.log();
557
+ },
558
+
559
+ // Export skill for publishing
560
+ export: (name) => {
561
+ if (!name) {
562
+ console.log(c('red', '\n Usage: claude-all skills export <skill-name>\n'));
563
+ return;
564
+ }
565
+
566
+ const skillDir = path.join(SKILLS_DIR, name);
567
+
568
+ if (!fs.existsSync(skillDir)) {
569
+ console.log(c('red', `\n Skill "${name}" not found!\n`));
570
+ return;
571
+ }
572
+
573
+ const { score } = calculateQualityScore(skillDir);
574
+
575
+ if (score < 40) {
576
+ console.log(c('red', `\n Skill quality too low (${score}/100). Improve before exporting.\n`));
577
+ return;
578
+ }
579
+
580
+ const exportDir = path.join(process.cwd(), `${name}-skill`);
581
+
582
+ console.log(c('cyan', `\n Exporting skill: ${name}\n`));
583
+
584
+ // Copy skill directory
585
+ execSync(`cp -r "${skillDir}" "${exportDir}"`);
586
+
587
+ // Create package.json for npm
588
+ const skillJson = fs.existsSync(path.join(skillDir, 'skill.json'))
589
+ ? JSON.parse(fs.readFileSync(path.join(skillDir, 'skill.json'), 'utf8'))
590
+ : {};
591
+
592
+ const packageJson = {
593
+ name: `claudeall-skill-${name}`,
594
+ version: skillJson.version || '1.0.0',
595
+ description: skillJson.description || `ClaudeAll skill: ${name}`,
596
+ author: skillJson.author || 'ClaudeAll Community',
597
+ license: skillJson.license || 'MIT',
598
+ keywords: ['claudeall', 'skill', 'ai-agent', ...(skillJson.tags || [])],
599
+ repository: {
600
+ type: 'git',
601
+ url: `https://github.com/zesbe/ClaudeAll.git`
602
+ },
603
+ claudeall: {
604
+ type: 'skill',
605
+ agents: skillJson.agents || ['claude-code'],
606
+ dependencies: skillJson.dependencies || [],
607
+ quality: score
608
+ }
609
+ };
610
+
611
+ fs.writeFileSync(
612
+ path.join(exportDir, 'package.json'),
613
+ JSON.stringify(packageJson, null, 2)
614
+ );
615
+
616
+ console.log(c('green', ` āœ“ Exported to: ${exportDir}`));
617
+ console.log(c('gray', '\n To publish to npm:'));
618
+ console.log(c('cyan', ` cd ${exportDir}`));
619
+ console.log(c('cyan', ' npm publish'));
620
+ console.log();
621
+ },
622
+
623
+ // Stats
624
+ stats: () => {
625
+ console.log(c('cyan', '\n ClaudeAll Skills Statistics\n'));
626
+
627
+ if (!fs.existsSync(SKILLS_DIR)) {
628
+ console.log(c('yellow', ' No skills directory found.'));
629
+ return;
630
+ }
631
+
632
+ const skills = fs.readdirSync(SKILLS_DIR).filter(f =>
633
+ fs.statSync(path.join(SKILLS_DIR, f)).isDirectory()
634
+ );
635
+
636
+ let totalScore = 0;
637
+ let highQuality = 0;
638
+ let mediumQuality = 0;
639
+ let lowQuality = 0;
640
+
641
+ skills.forEach(skill => {
642
+ const { score } = calculateQualityScore(path.join(SKILLS_DIR, skill));
643
+ totalScore += score;
644
+ if (score >= 70) highQuality++;
645
+ else if (score >= 40) mediumQuality++;
646
+ else lowQuality++;
647
+ });
648
+
649
+ const avgScore = skills.length > 0 ? Math.round(totalScore / skills.length) : 0;
650
+
651
+ console.log(` Total Skills: ${c('bright', skills.length.toString())}`);
652
+ console.log(` Average Quality: ${c('cyan', avgScore + '/100')}`);
653
+ console.log();
654
+ console.log(` ${c('green', 'ā—')} High Quality (70+): ${highQuality}`);
655
+ console.log(` ${c('yellow', 'ā—')} Medium Quality (40-69): ${mediumQuality}`);
656
+ console.log(` ${c('red', 'ā—')} Low Quality (<40): ${lowQuality}`);
657
+ console.log();
658
+ },
659
+
660
+ // Help
661
+ help: () => {
662
+ console.log(`
663
+ ${c('cyan', ' ClaudeAll Skills CLI')} ${c('gray', '- Advanced skill management')}
664
+
665
+ ${c('bright', ' COMMANDS:')}
666
+
667
+ ${c('green', 'list')} List all installed skills
668
+ ${c('green', 'search')} <query> Search skills by name or content
669
+ ${c('green', 'info')} <name> Show skill details
670
+ ${c('green', 'create')} <name> Create new skill from template
671
+ ${c('green', 'validate')} <name> Validate skill quality
672
+ ${c('green', 'test')} <name> Run skill tests
673
+ ${c('green', 'export')} <name> Export skill for publishing
674
+ ${c('green', 'stats')} Show skills statistics
675
+ ${c('green', 'help')} Show this help
676
+
677
+ ${c('bright', ' FEATURES BEYOND skills.sh:')}
678
+
679
+ ${c('magenta', 'āœ“')} Quality scoring system (0-100)
680
+ ${c('magenta', 'āœ“')} Skill scaffolding generator
681
+ ${c('magenta', 'āœ“')} Built-in testing framework
682
+ ${c('magenta', 'āœ“')} Dependency management
683
+ ${c('magenta', 'āœ“')} Hooks system (pre/post install)
684
+ ${c('magenta', 'āœ“')} Multi-agent support
685
+ ${c('magenta', 'āœ“')} Offline-first design
686
+ ${c('magenta', 'āœ“')} Export for npm publishing
687
+
688
+ ${c('bright', ' EXAMPLES:')}
689
+
690
+ ${c('gray', '$')} claude-all skills list
691
+ ${c('gray', '$')} claude-all skills create my-skill
692
+ ${c('gray', '$')} claude-all skills validate my-skill
693
+ ${c('gray', '$')} claude-all skills export my-skill
694
+
695
+ `);
696
+ }
697
+ };
698
+
699
+ // ============================================
700
+ // MAIN
701
+ // ============================================
702
+
703
+ function main() {
704
+ const args = process.argv.slice(2);
705
+
706
+ // Handle "claude-all skills <cmd>" format
707
+ if (args[0] === 'skills') {
708
+ args.shift();
709
+ }
710
+
711
+ const cmd = args[0] || 'help';
712
+ const arg = args[1];
713
+
714
+ if (commands[cmd]) {
715
+ commands[cmd](arg);
716
+ } else {
717
+ console.log(c('red', `\n Unknown command: ${cmd}`));
718
+ commands.help();
719
+ }
720
+ }
721
+
722
+ main();
@@ -0,0 +1,140 @@
1
+ /**
2
+ * ClaudeAll Skill Hooks System
3
+ *
4
+ * Lifecycle hooks for skills:
5
+ * - preInstall: Before skill is installed
6
+ * - postInstall: After skill is installed
7
+ * - preUninstall: Before skill is removed
8
+ * - postUninstall: After skill is removed
9
+ * - preUpdate: Before skill is updated
10
+ * - postUpdate: After skill is updated
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const { execSync } = require('child_process');
16
+
17
+ const CLAUDE_DIR = path.join(process.env.HOME, '.claude');
18
+ const SKILLS_DIR = path.join(CLAUDE_DIR, 'skills');
19
+ const HOOKS_LOG = path.join(CLAUDE_DIR, '.hooks-log.json');
20
+
21
+ class SkillHooks {
22
+ constructor() {
23
+ this.hooks = {};
24
+ this.log = this.loadLog();
25
+ }
26
+
27
+ loadLog() {
28
+ if (fs.existsSync(HOOKS_LOG)) {
29
+ return JSON.parse(fs.readFileSync(HOOKS_LOG, 'utf8'));
30
+ }
31
+ return { executions: [] };
32
+ }
33
+
34
+ saveLog() {
35
+ fs.writeFileSync(HOOKS_LOG, JSON.stringify(this.log, null, 2));
36
+ }
37
+
38
+ logExecution(skillName, hookType, success, error = null) {
39
+ this.log.executions.push({
40
+ skill: skillName,
41
+ hook: hookType,
42
+ success,
43
+ error: error ? error.message : null,
44
+ timestamp: new Date().toISOString()
45
+ });
46
+
47
+ // Keep last 100 executions
48
+ if (this.log.executions.length > 100) {
49
+ this.log.executions = this.log.executions.slice(-100);
50
+ }
51
+
52
+ this.saveLog();
53
+ }
54
+
55
+ /**
56
+ * Load hooks from skill.json
57
+ */
58
+ loadSkillHooks(skillName) {
59
+ const skillJsonPath = path.join(SKILLS_DIR, skillName, 'skill.json');
60
+
61
+ if (fs.existsSync(skillJsonPath)) {
62
+ const skillJson = JSON.parse(fs.readFileSync(skillJsonPath, 'utf8'));
63
+ return skillJson.hooks || {};
64
+ }
65
+
66
+ return {};
67
+ }
68
+
69
+ /**
70
+ * Execute a hook
71
+ */
72
+ async executeHook(skillName, hookType) {
73
+ const hooks = this.loadSkillHooks(skillName);
74
+ const hookScript = hooks[hookType];
75
+
76
+ if (!hookScript) {
77
+ return { executed: false, reason: 'No hook defined' };
78
+ }
79
+
80
+ const skillDir = path.join(SKILLS_DIR, skillName);
81
+ const hookPath = path.join(skillDir, hookScript);
82
+
83
+ console.log(` Executing ${hookType} hook for ${skillName}...`);
84
+
85
+ try {
86
+ if (fs.existsSync(hookPath)) {
87
+ // Execute script file
88
+ execSync(`bash "${hookPath}"`, {
89
+ cwd: skillDir,
90
+ stdio: 'inherit'
91
+ });
92
+ } else if (hookScript.startsWith('npm ') || hookScript.startsWith('node ')) {
93
+ // Execute command directly
94
+ execSync(hookScript, {
95
+ cwd: skillDir,
96
+ stdio: 'inherit'
97
+ });
98
+ } else {
99
+ // Treat as inline script
100
+ execSync(hookScript, {
101
+ cwd: skillDir,
102
+ shell: true,
103
+ stdio: 'inherit'
104
+ });
105
+ }
106
+
107
+ this.logExecution(skillName, hookType, true);
108
+ return { executed: true, success: true };
109
+ } catch (error) {
110
+ this.logExecution(skillName, hookType, false, error);
111
+ return { executed: true, success: false, error: error.message };
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Run all lifecycle hooks for an operation
117
+ */
118
+ async runLifecycle(skillName, operation) {
119
+ const results = {};
120
+
121
+ // Pre hook
122
+ const preHook = `pre${operation.charAt(0).toUpperCase()}${operation.slice(1)}`;
123
+ results.pre = await this.executeHook(skillName, preHook);
124
+
125
+ // Post hook
126
+ const postHook = `post${operation.charAt(0).toUpperCase()}${operation.slice(1)}`;
127
+ results.post = await this.executeHook(skillName, postHook);
128
+
129
+ return results;
130
+ }
131
+
132
+ /**
133
+ * Get recent hook executions
134
+ */
135
+ getRecentExecutions(limit = 10) {
136
+ return this.log.executions.slice(-limit);
137
+ }
138
+ }
139
+
140
+ module.exports = SkillHooks;
package/package.json CHANGED
@@ -1,10 +1,16 @@
1
1
  {
2
2
  "name": "claude-termux",
3
- "version": "1.0.3",
4
- "description": "Claude Code & Gemini CLI configuration for Termux Android - agents, skills, commands, MCP servers",
3
+ "version": "2.0.0",
4
+ "description": "Claude Code & Gemini CLI configuration for Termux Android - agents, skills, commands, MCP servers with quality scoring & hooks",
5
5
  "main": "postinstall.js",
6
+ "bin": {
7
+ "claude-termux-skills": "./bin/skills-cli.js",
8
+ "claude-termux-mcp": "./bin/mcp-install.js"
9
+ },
6
10
  "scripts": {
7
- "postinstall": "node postinstall.js"
11
+ "postinstall": "node postinstall.js",
12
+ "skills": "node bin/skills-cli.js",
13
+ "mcp": "node bin/mcp-install.js"
8
14
  },
9
15
  "keywords": [
10
16
  "claude",
@@ -17,18 +23,27 @@
17
23
  "agents",
18
24
  "skills",
19
25
  "mcp",
20
- "superpowers"
26
+ "superpowers",
27
+ "quality-scoring",
28
+ "hooks-system"
21
29
  ],
22
- "author": "zesbe",
30
+ "author": {
31
+ "name": "zesbe",
32
+ "email": "yudiharyanto41@gmail.com",
33
+ "url": "https://github.com/zesbe"
34
+ },
23
35
  "license": "MIT",
24
36
  "repository": {
25
37
  "type": "git",
26
- "url": "git+https://github.com/zesbe/ClaudeAll.git"
38
+ "url": "https://github.com/zesbe/ClaudeAll.git"
27
39
  },
28
40
  "bugs": {
29
41
  "url": "https://github.com/zesbe/ClaudeAll/issues"
30
42
  },
31
43
  "homepage": "https://github.com/zesbe/ClaudeAll#readme",
44
+ "engines": {
45
+ "node": ">=14.0.0"
46
+ },
32
47
  "files": [
33
48
  "agents/**",
34
49
  "skills/**",
@@ -37,6 +52,7 @@
37
52
  "hooks/**",
38
53
  "plugins/**",
39
54
  "lib/**",
55
+ "bin/**",
40
56
  "CLAUDE.md",
41
57
  "GEMINI.md",
42
58
  "TERMUX.md",
@@ -45,5 +61,16 @@
45
61
  "settings.local.json",
46
62
  "install.sh",
47
63
  "postinstall.js"
48
- ]
64
+ ],
65
+ "claudeTermux": {
66
+ "features": [
67
+ "quality-scoring",
68
+ "skill-scaffolding",
69
+ "hooks-system",
70
+ "mcp-auto-install"
71
+ ],
72
+ "skillsCount": 60,
73
+ "agentsCount": 14,
74
+ "commandsCount": 8
75
+ }
49
76
  }