Skip to content

Commit 7c86106

Browse files
ericyangpanclaude
andcommitted
feat(skills): add manifest-automation skill
Add an advanced Claude Code skill for creating and updating manifest files with Playwright automation, intelligent retry logic, and smart merge capabilities. Features: - Dual mode operation: CREATE new manifests or UPDATE existing ones - Advanced web automation using Playwright MCP for dynamic content - Intelligent 3-attempt retry logic per field with graceful degradation - Smart merge strategy preserving user-curated data - Type-optimized workflows for CLI, Extension, IDE, Model, Provider, and Vendor - Cross-reference validation using Wikipedia and LinkedIn - i18n consistency checking and drift detection Components: - scripts/automate.mjs: Main automation script with retry logic - scripts/lib/config.mjs: Manifest type configurations - scripts/lib/field-tracker.mjs: Field-level retry tracking - scripts/lib/merge-strategies.mjs: Smart merge categorization - scripts/workflows/*.md: Type-specific extraction workflows (6 files) - SKILL.md: Complete documentation and best practices This skill extends beyond the basic manifest-creator by adding Playwright support for complex web interactions, intelligent retry strategies, and sophisticated merge logic for updates. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8c00ff5 commit 7c86106

File tree

12 files changed

+2525
-1
lines changed

12 files changed

+2525
-1
lines changed

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

Lines changed: 489 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Manifest Automation - Entry Point
5+
* Usage:
6+
* node automate.mjs create cli cursor-cli https://cursor.com/cli
7+
* node automate.mjs update extension claude-code https://code.anthropic.com
8+
*/
9+
10+
import fs from 'node:fs'
11+
import path from 'node:path'
12+
import { fileURLToPath } from 'node:url'
13+
import {
14+
MANIFEST_PATHS,
15+
MANIFEST_TYPES,
16+
RETRY_CONFIG,
17+
SCHEMA_PATHS,
18+
WORKFLOW_PATHS,
19+
} from './lib/config.mjs'
20+
21+
const __filename = fileURLToPath(import.meta.url)
22+
const __dirname = path.dirname(__filename)
23+
24+
// Parse command-line arguments
25+
const [, , mode, type, name, url] = process.argv
26+
27+
// Validation
28+
function validate() {
29+
const errors = []
30+
31+
if (!mode || !['create', 'update'].includes(mode)) {
32+
errors.push('Mode must be "create" or "update"')
33+
}
34+
35+
if (!type || !MANIFEST_TYPES.includes(type)) {
36+
errors.push(`Type must be one of: ${MANIFEST_TYPES.join(', ')}`)
37+
}
38+
39+
if (!name) {
40+
errors.push('Name is required')
41+
} else if (!/^[a-z0-9-]+$/.test(name)) {
42+
errors.push('Name must be lowercase with hyphens (e.g., "cursor-cli")')
43+
}
44+
45+
if (mode === 'create' && !url) {
46+
errors.push('URL is required for CREATE mode')
47+
}
48+
49+
if (url && !url.startsWith('https://')) {
50+
errors.push('URL must start with https://')
51+
}
52+
53+
return errors
54+
}
55+
56+
const errors = validate()
57+
58+
if (errors.length > 0) {
59+
console.error('❌ Validation Errors:\n')
60+
for (const error of errors) {
61+
console.error(` • ${error}`)
62+
}
63+
console.error('\nUsage:')
64+
console.error(' node automate.mjs create <type> <name> <url>')
65+
console.error(' node automate.mjs update <type> <name> [url]')
66+
console.error('\nExamples:')
67+
console.error(' node automate.mjs create cli cursor-cli https://cursor.com/cli')
68+
console.error(' node automate.mjs update extension claude-code https://code.anthropic.com')
69+
process.exit(1)
70+
}
71+
72+
// Load schema
73+
const schemaPath = SCHEMA_PATHS[type]
74+
const manifestPath = MANIFEST_PATHS[type](name)
75+
const workflowPath = WORKFLOW_PATHS[type]
76+
77+
// Check if files exist
78+
const projectRoot = path.resolve(__dirname, '../../../..')
79+
const schemaFullPath = path.join(projectRoot, schemaPath)
80+
const manifestFullPath = path.join(projectRoot, manifestPath)
81+
const workflowFullPath = path.join(projectRoot, workflowPath)
82+
83+
if (!fs.existsSync(schemaFullPath)) {
84+
console.error(`❌ Schema not found: ${schemaPath}`)
85+
process.exit(1)
86+
}
87+
88+
if (!fs.existsSync(workflowFullPath)) {
89+
console.error(`❌ Workflow not found: ${workflowPath}`)
90+
process.exit(1)
91+
}
92+
93+
// For UPDATE mode, check if manifest exists
94+
let _existingManifest = null
95+
if (mode === 'update') {
96+
if (!fs.existsSync(manifestFullPath)) {
97+
console.error(`❌ Manifest not found for update: ${manifestPath}`)
98+
console.error(' Use "create" mode for new manifests')
99+
process.exit(1)
100+
}
101+
102+
try {
103+
const manifestContent = fs.readFileSync(manifestFullPath, 'utf-8')
104+
_existingManifest = JSON.parse(manifestContent)
105+
} catch (error) {
106+
console.error(`❌ Failed to parse existing manifest: ${error.message}`)
107+
process.exit(1)
108+
}
109+
}
110+
111+
// Load workflow
112+
const workflowContent = fs.readFileSync(workflowFullPath, 'utf-8')
113+
114+
// Output instructions
115+
console.log('🤖 Manifest Automation Skill')
116+
console.log('━'.repeat(60))
117+
console.log('')
118+
console.log(`Mode: ${mode.toUpperCase()}`)
119+
console.log(`Type: ${type}`)
120+
console.log(`Name: ${name}`)
121+
if (url) console.log(`URL: ${url}`)
122+
console.log('')
123+
console.log(`Schema: ${schemaPath}`)
124+
console.log(`Output: ${manifestPath}`)
125+
console.log('')
126+
console.log('━'.repeat(60))
127+
console.log('📋 Workflow Instructions')
128+
console.log('━'.repeat(60))
129+
console.log('')
130+
131+
if (mode === 'update') {
132+
console.log('## UPDATE Mode - Smart Merge\n')
133+
console.log('You are updating an existing manifest. Follow these rules:\n')
134+
console.log('1. **Load existing manifest** from:', manifestPath)
135+
console.log('2. **Follow the workflow below** to extract fresh data')
136+
console.log('3. **Apply smart merge** using merge-strategies.mjs:')
137+
console.log(' - AUTO_UPDATE fields: Replace with new values')
138+
console.log(' - PRESERVE fields: Keep existing (id, name, verified, i18n, relatedProducts)')
139+
console.log(' - MERGE_ADDITIVE fields: Add new items to arrays/objects')
140+
console.log(' - CONDITIONAL fields: Present both for review')
141+
console.log('4. **Generate change report** showing what was updated/added/preserved')
142+
console.log('5. **Write updated manifest** back to the same path\n')
143+
console.log('━'.repeat(60))
144+
console.log('')
145+
}
146+
147+
console.log(workflowContent)
148+
console.log('')
149+
console.log('━'.repeat(60))
150+
console.log('🎯 Retry & Error Handling Rules')
151+
console.log('━'.repeat(60))
152+
console.log('')
153+
console.log(`• Maximum ${RETRY_CONFIG.maxAttempts} attempts per field`)
154+
console.log(`• After ${RETRY_CONFIG.maxAttempts} failures: Add TODO comment`)
155+
console.log('• Use Playwright MCP for dynamic content')
156+
console.log('• Use WebSearch for discovery (GitHub, social, platforms)')
157+
console.log('• Save draft even with missing fields')
158+
console.log('• Generate completion report at end\n')
159+
160+
if (mode === 'create') {
161+
console.log('TODO Comment Format:')
162+
console.log(
163+
' "discord": null, // TODO: Could not auto-discover after 3 attempts. Not found in footer or search results.\n'
164+
)
165+
} else {
166+
console.log('Change Tracking:')
167+
console.log(' Track all changes using merge-strategies.mjs')
168+
console.log(' Generate report with updated/added/preserved/needsReview fields\n')
169+
}
170+
171+
console.log('━'.repeat(60))
172+
console.log('')
173+
console.log('✅ Ready! Execute workflow above.')
174+
console.log('')
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Configuration and constants for manifest automation
5+
*/
6+
7+
// Supported manifest types
8+
export const MANIFEST_TYPES = ['cli', 'extension', 'ide', 'model', 'provider', 'vendor']
9+
10+
// Schema path mapping
11+
export const SCHEMA_PATHS = {
12+
cli: 'manifests/$schemas/cli.schema.json',
13+
extension: 'manifests/$schemas/extension.schema.json',
14+
ide: 'manifests/$schemas/ide.schema.json',
15+
model: 'manifests/$schemas/model.schema.json',
16+
provider: 'manifests/$schemas/provider.schema.json',
17+
vendor: 'manifests/$schemas/vendor.schema.json',
18+
}
19+
20+
// Manifest output path mapping
21+
export const MANIFEST_PATHS = {
22+
cli: name => `manifests/clis/${name}.json`,
23+
extension: name => `manifests/extensions/${name}.json`,
24+
ide: name => `manifests/ides/${name}.json`,
25+
model: name => `manifests/models/${name}.json`,
26+
provider: name => `manifests/providers/${name}.json`,
27+
vendor: name => `manifests/vendors/${name}.json`,
28+
}
29+
30+
// Workflow file mapping
31+
export const WORKFLOW_PATHS = {
32+
cli: '.claude/skills/manifest-automation/scripts/workflows/cli-workflow.md',
33+
extension: '.claude/skills/manifest-automation/scripts/workflows/extension-workflow.md',
34+
ide: '.claude/skills/manifest-automation/scripts/workflows/ide-workflow.md',
35+
model: '.claude/skills/manifest-automation/scripts/workflows/model-workflow.md',
36+
provider: '.claude/skills/manifest-automation/scripts/workflows/provider-workflow.md',
37+
vendor: '.claude/skills/manifest-automation/scripts/workflows/vendor-workflow.md',
38+
}
39+
40+
// Retry configuration
41+
export const RETRY_CONFIG = {
42+
maxAttempts: 3,
43+
defaultTimeout: 10000, // 10 seconds
44+
}
45+
46+
// Field categories for smart merge
47+
export const FIELD_CATEGORIES = {
48+
// Always update from official source
49+
AUTO_UPDATE: [
50+
'latestVersion',
51+
'description',
52+
'websiteUrl',
53+
'docsUrl',
54+
'resourceUrls.changelog',
55+
'resourceUrls.download',
56+
'tokenPricing',
57+
'size',
58+
'totalContext',
59+
'maxOutput',
60+
],
61+
62+
// Never update (user-curated)
63+
PRESERVE: ['id', 'name', 'verified', 'i18n', 'relatedProducts'],
64+
65+
// Smart merge: add new, keep existing
66+
MERGE_ADDITIVE: ['communityUrls', 'platformUrls', 'supportedIdes', 'platforms', 'pricing'],
67+
68+
// Present both for Claude to decide
69+
CONDITIONAL: ['license', 'vendor'],
70+
}
71+
72+
// Platform OS enum values
73+
export const PLATFORM_OS = ['macos', 'windows', 'linux', 'web']
74+
75+
// IDE enum values for extensions
76+
export const IDE_TYPES = ['vscode', 'cursor', 'windsurf', 'trae', 'zed', 'jetbrains']
77+
78+
// Common URL patterns for community links
79+
export const COMMUNITY_URL_PATTERNS = {
80+
linkedin: ['linkedin.com/company/', 'linkedin.com/in/'],
81+
twitter: ['twitter.com/', 'x.com/'],
82+
github: ['github.com/'],
83+
youtube: ['youtube.com/@', 'youtube.com/c/', 'youtube.com/channel/'],
84+
discord: ['discord.gg/', 'discord.com/invite/'],
85+
reddit: ['reddit.com/r/'],
86+
blog: [
87+
// Generic patterns - need validation
88+
'/blog',
89+
'blog.',
90+
'medium.com/',
91+
],
92+
}
93+
94+
// URL validation patterns
95+
export const URL_PATTERNS = {
96+
https: /^https:\/\/.+/,
97+
github: /^https:\/\/github\.com\/[^/]+\/[^/]+\/?$/,
98+
vscodeMarketplace: /^https:\/\/marketplace\.visualstudio\.com\/items\?itemName=.+/,
99+
jetbrainsPlugins: /^https:\/\/plugins\.jetbrains\.com\/plugin\/.+/,
100+
openVsx: /^https:\/\/open-vsx\.org\/extension\/.+/,
101+
huggingface: /^https:\/\/huggingface\.co\/.+/,
102+
artificialAnalysis: /^https:\/\/artificialanalysis\.ai\/.+/,
103+
openrouter: /^https:\/\/openrouter\.ai\/.+/,
104+
}
105+
106+
// Common documentation URL patterns
107+
export const DOCS_URL_PATTERNS = ['/docs', '/documentation', '/api', '/guides', 'docs.']
108+
109+
// Common pricing URL patterns
110+
export const PRICING_URL_PATTERNS = ['/pricing', '/plans', '/buy', '/subscribe', 'pricing.']
111+
112+
// Common download URL patterns
113+
export const DOWNLOAD_URL_PATTERNS = ['/download', '/downloads', '/get', '/install', 'download.']

0 commit comments

Comments
 (0)