Skip to content

Commit 58513bf

Browse files
ericyangpanclaude
andcommitted
feat(automation): add GitHub stars updater and enhance manifest workflow
Add new GitHub stars management system: - New github-stars-updater.mjs module with add/update/remove operations - Integration with automate.mjs for automatic updates - CLI support for manual github-stars.json maintenance - Enhanced SKILL.md documentation with usage examples Remove obsolete update-ides.mjs script. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2f099b7 commit 58513bf

File tree

4 files changed

+320
-127
lines changed

4 files changed

+320
-127
lines changed

.claude/skills/manifest-automation/SKILL.md

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,10 @@ Status: DRAFT (3 fields incomplete)
337337
📝 Next Steps:
338338
1. Review manifest file: manifests/clis/cursor-cli.json
339339
2. Manually fill TODO-marked fields if information available
340-
3. Update i18n translations if English content changed
341-
4. Update verified field once data confirmed accurate
342-
5. Run validation: node scripts/validate/validate-manifests.mjs
340+
3. **Update github-stars.json** with new entry
341+
4. Update i18n translations if English content changed
342+
5. Update verified field once data confirmed accurate
343+
6. Run validation: node scripts/validate/validate-manifests.mjs
343344
```
344345

345346
## Comparison with manifest-creator
@@ -430,18 +431,100 @@ After running this skill, expect:
430431
3. **Valid against schema**: `manifests/$schemas/<type>.schema.json`
431432
4. **Ready for validation**: Run `node scripts/validate/validate-manifests.mjs`
432433

434+
## GitHub Stars Update
435+
436+
After creating or updating a manifest, you **MUST** update the `data/github-stars.json` file:
437+
438+
### Why Update github-stars.json?
439+
440+
The `github-stars.json` file tracks GitHub star counts for all manifests. When you create or update a manifest:
441+
- A new entry must be added for tracking
442+
- The entry is initialized with `null` (stars not yet fetched)
443+
- A scheduled job will automatically fetch the actual star count later
444+
445+
### How to Update
446+
447+
Use the `updateGithubStarsEntry()` function from `github-stars-updater.mjs`:
448+
449+
```javascript
450+
import { updateGithubStarsEntry } from './lib/github-stars-updater.mjs'
451+
452+
// After creating a new manifest
453+
const result = updateGithubStarsEntry('cli', 'cursor-cli', { isNew: true })
454+
455+
// After updating an existing manifest
456+
const result = updateGithubStarsEntry('extension', 'claude-code', { isNew: false })
457+
```
458+
459+
### CLI Usage
460+
461+
You can also use the CLI directly:
462+
463+
```bash
464+
# Add new entry
465+
node .claude/skills/manifest-automation/scripts/lib/github-stars-updater.mjs add cli cursor-cli
466+
467+
# Update existing entry
468+
node .claude/skills/manifest-automation/scripts/lib/github-stars-updater.mjs update extension claude-code
469+
470+
# Remove entry
471+
node .claude/skills/manifest-automation/scripts/lib/github-stars-updater.mjs remove ide windsurf
472+
```
473+
474+
### What It Does
475+
476+
The updater will:
477+
1. Load the current `github-stars.json` file
478+
2. Add or update the entry: `<category>["<id>"] = null`
479+
3. Sort entries alphabetically within the category
480+
4. Save the updated file back to disk
481+
482+
### Example Result
483+
484+
For a new CLI manifest `cursor-cli`:
485+
486+
```json
487+
{
488+
"clis": {
489+
"claude-code-cli": 43.5,
490+
"cursor-cli": null, // ← newly added
491+
"kode": 3.6
492+
}
493+
}
494+
```
495+
496+
### Integration with Workflow
497+
498+
The `automate.mjs` script exports the necessary information for you to call the updater:
499+
500+
```javascript
501+
// After workflow completes and manifest is saved:
502+
import {
503+
updateGithubStarsEntry,
504+
manifestType,
505+
manifestName,
506+
operationMode
507+
} from './automate.mjs'
508+
509+
updateGithubStarsEntry(manifestType, manifestName, { isNew: operationMode === 'create' })
510+
```
511+
433512
## Next Steps After Creation
434513

435514
1. **Review manifest**: Check all extracted values for accuracy
436515
2. **Fill TODOs**: Manually add fields that couldn't be auto-discovered
437-
3. **Add translations**: Populate `i18n` object with localized content
516+
3. **Update github-stars.json**: Add entry for the new/updated manifest
517+
- Use: `updateGithubStarsEntry(type, id, { isNew: mode === 'create' })`
518+
- This adds an entry like `clis["cursor-cli"] = null`
519+
- Stars will be automatically fetched in the next scheduled update
520+
4. **Add translations**: Populate `i18n` object with localized content
438521
- **CRITICAL**: Ensure i18n content matches English default values
439522
- Validate all i18n fields are consistent across all supported languages
440523
- Check that no language has outdated or mismatched translations
441-
4. **Set verified**: Change `verified` to `true` if data is confirmed accurate
442-
5. **Add related products**: Manually curate `relatedProducts` array
443-
6. **Run validation**: Ensure schema compliance
444-
7. **Commit changes**: Add manifest to git repository
524+
5. **Set verified**: Change `verified` to `true` if data is confirmed accurate
525+
6. **Add related products**: Manually curate `relatedProducts` array
526+
7. **Run validation**: Ensure schema compliance
527+
8. **Commit changes**: Add manifest to git repository
445528

446529
## i18n Consistency Requirements
447530

.claude/skills/manifest-automation/scripts/automate.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
SCHEMA_PATHS,
1818
WORKFLOW_PATHS,
1919
} from './lib/config.mjs'
20+
import { updateGithubStarsEntry } from './lib/github-stars-updater.mjs'
2021

2122
const __filename = fileURLToPath(import.meta.url)
2223
const __dirname = path.dirname(__filename)
@@ -168,7 +169,23 @@ if (mode === 'create') {
168169
console.log(' Generate report with updated/added/preserved/needsReview fields\n')
169170
}
170171

172+
console.log('━'.repeat(60))
173+
console.log('📝 Post-Creation/Update Steps')
174+
console.log('━'.repeat(60))
175+
console.log('')
176+
console.log('After creating or updating the manifest:')
177+
console.log('1. Save the manifest file')
178+
console.log('2. Update github-stars.json automatically:')
179+
console.log(` - Will add entry: ${type}s["${name}"] = null`)
180+
console.log(' - Stars will be fetched in next scheduled update')
181+
console.log('3. Generate completion report')
182+
console.log('')
171183
console.log('━'.repeat(60))
172184
console.log('')
173185
console.log('✅ Ready! Execute workflow above.')
174186
console.log('')
187+
console.log('⚠️ IMPORTANT: After saving the manifest, remember to update github-stars.json')
188+
console.log('')
189+
190+
// Export helper function for Claude to use in manifest-automation skill
191+
export { updateGithubStarsEntry, type as manifestType, name as manifestName, mode as operationMode }
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* GitHub Stars Updater
5+
* Updates github-stars.json with new manifest entries
6+
*/
7+
8+
import fs from 'node:fs'
9+
import path from 'node:path'
10+
import { fileURLToPath } from 'node:url'
11+
12+
const __filename = fileURLToPath(import.meta.url)
13+
const __dirname = path.dirname(__filename)
14+
15+
/**
16+
* Get project root directory
17+
*/
18+
function getProjectRoot() {
19+
return path.resolve(__dirname, '../../../../..')
20+
}
21+
22+
/**
23+
* Get the path to github-stars.json
24+
*/
25+
function getGithubStarsPath() {
26+
return path.join(getProjectRoot(), 'data/github-stars.json')
27+
}
28+
29+
/**
30+
* Load github-stars.json
31+
* @returns {Object} The current github-stars data
32+
*/
33+
export function loadGithubStars() {
34+
const filePath = getGithubStarsPath()
35+
36+
if (!fs.existsSync(filePath)) {
37+
throw new Error(`github-stars.json not found at: ${filePath}`)
38+
}
39+
40+
const content = fs.readFileSync(filePath, 'utf-8')
41+
return JSON.parse(content)
42+
}
43+
44+
/**
45+
* Save github-stars.json
46+
* @param {Object} data - The github-stars data to save
47+
*/
48+
export function saveGithubStars(data) {
49+
const filePath = getGithubStarsPath()
50+
const content = `${JSON.stringify(data, null, 2)}\n`
51+
fs.writeFileSync(filePath, content, 'utf-8')
52+
}
53+
54+
/**
55+
* Get the category name (plural form) from manifest type
56+
* @param {string} type - Manifest type (cli, extension, ide, model, provider, vendor)
57+
* @returns {string} Category name for github-stars.json
58+
*/
59+
function getCategoryName(type) {
60+
const mapping = {
61+
cli: 'clis',
62+
extension: 'extensions',
63+
ide: 'ides',
64+
model: 'models',
65+
provider: 'providers',
66+
vendor: 'vendors',
67+
}
68+
69+
return mapping[type] || `${type}s`
70+
}
71+
72+
/**
73+
* Update github-stars.json with a new or updated manifest entry
74+
* @param {string} type - Manifest type (cli, extension, ide, etc.)
75+
* @param {string} id - Manifest id
76+
* @param {Object} options - Options
77+
* @param {boolean} options.isNew - Whether this is a new entry (true) or update (false)
78+
* @returns {Object} Result with status and message
79+
*/
80+
export function updateGithubStarsEntry(type, id, options = {}) {
81+
const { isNew = false } = options
82+
83+
try {
84+
// Load current data
85+
const githubStars = loadGithubStars()
86+
const category = getCategoryName(type)
87+
88+
// Ensure category exists
89+
if (!githubStars[category]) {
90+
githubStars[category] = {}
91+
}
92+
93+
// Check if entry already exists
94+
const exists = id in githubStars[category]
95+
96+
if (isNew && exists) {
97+
return {
98+
status: 'skipped',
99+
message: `Entry "${id}" already exists in github-stars.json under "${category}"`,
100+
}
101+
}
102+
103+
if (!isNew && !exists) {
104+
return {
105+
status: 'warning',
106+
message: `Entry "${id}" does not exist in github-stars.json under "${category}", adding as new`,
107+
}
108+
}
109+
110+
// Add or update entry with null (stars will be fetched later)
111+
githubStars[category][id] = null
112+
113+
// Sort entries alphabetically within category
114+
const sortedCategory = Object.keys(githubStars[category])
115+
.sort()
116+
.reduce((acc, key) => {
117+
acc[key] = githubStars[category][key]
118+
return acc
119+
}, {})
120+
121+
githubStars[category] = sortedCategory
122+
123+
// Save updated data
124+
saveGithubStars(githubStars)
125+
126+
return {
127+
status: 'success',
128+
message: `Updated github-stars.json: ${category}["${id}"] = null`,
129+
action: exists ? 'updated' : 'added',
130+
}
131+
} catch (error) {
132+
return {
133+
status: 'error',
134+
message: `Failed to update github-stars.json: ${error.message}`,
135+
error,
136+
}
137+
}
138+
}
139+
140+
/**
141+
* Remove an entry from github-stars.json
142+
* @param {string} type - Manifest type
143+
* @param {string} id - Manifest id
144+
* @returns {Object} Result with status and message
145+
*/
146+
export function removeGithubStarsEntry(type, id) {
147+
try {
148+
const githubStars = loadGithubStars()
149+
const category = getCategoryName(type)
150+
151+
if (!githubStars[category] || !(id in githubStars[category])) {
152+
return {
153+
status: 'skipped',
154+
message: `Entry "${id}" not found in github-stars.json under "${category}"`,
155+
}
156+
}
157+
158+
delete githubStars[category][id]
159+
saveGithubStars(githubStars)
160+
161+
return {
162+
status: 'success',
163+
message: `Removed "${id}" from github-stars.json under "${category}"`,
164+
}
165+
} catch (error) {
166+
return {
167+
status: 'error',
168+
message: `Failed to remove entry from github-stars.json: ${error.message}`,
169+
error,
170+
}
171+
}
172+
}
173+
174+
/**
175+
* CLI entry point for testing
176+
*/
177+
if (import.meta.url === `file://${process.argv[1]}`) {
178+
const [, , command, type, id] = process.argv
179+
180+
if (!command || !['add', 'update', 'remove'].includes(command)) {
181+
console.error('Usage:')
182+
console.error(' node github-stars-updater.mjs add <type> <id>')
183+
console.error(' node github-stars-updater.mjs update <type> <id>')
184+
console.error(' node github-stars-updater.mjs remove <type> <id>')
185+
console.error('')
186+
console.error('Examples:')
187+
console.error(' node github-stars-updater.mjs add cli cursor-cli')
188+
console.error(' node github-stars-updater.mjs update extension claude-code')
189+
console.error(' node github-stars-updater.mjs remove ide windsurf')
190+
process.exit(1)
191+
}
192+
193+
if (!type || !id) {
194+
console.error('Error: type and id are required')
195+
process.exit(1)
196+
}
197+
198+
let result
199+
200+
if (command === 'add' || command === 'update') {
201+
result = updateGithubStarsEntry(type, id, { isNew: command === 'add' })
202+
} else {
203+
result = removeGithubStarsEntry(type, id)
204+
}
205+
206+
console.log(`Status: ${result.status}`)
207+
console.log(`Message: ${result.message}`)
208+
209+
if (result.status === 'error') {
210+
process.exit(1)
211+
}
212+
}

0 commit comments

Comments
 (0)