sumulige-claude 1.0.6 → 1.0.8

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.
Files changed (53) hide show
  1. package/.claude/.kickoff-hint.txt +51 -0
  2. package/.claude/ANCHORS.md +40 -0
  3. package/.claude/MEMORY.md +34 -0
  4. package/.claude/PROJECT_LOG.md +101 -0
  5. package/.claude/THINKING_CHAIN_GUIDE.md +287 -0
  6. package/.claude/commands/commit-push-pr.md +59 -0
  7. package/.claude/commands/commit.md +53 -0
  8. package/.claude/commands/pr.md +76 -0
  9. package/.claude/commands/review.md +61 -0
  10. package/.claude/commands/sessions.md +62 -0
  11. package/.claude/commands/skill-create.md +131 -0
  12. package/.claude/commands/test.md +56 -0
  13. package/.claude/commands/todos.md +99 -0
  14. package/.claude/commands/verify-work.md +63 -0
  15. package/.claude/hooks/code-formatter.cjs +187 -0
  16. package/.claude/hooks/code-tracer.cjs +331 -0
  17. package/.claude/hooks/conversation-recorder.cjs +340 -0
  18. package/.claude/hooks/decision-tracker.cjs +398 -0
  19. package/.claude/hooks/export.cjs +329 -0
  20. package/.claude/hooks/multi-session.cjs +181 -0
  21. package/.claude/hooks/privacy-filter.js +224 -0
  22. package/.claude/hooks/project-kickoff.cjs +114 -0
  23. package/.claude/hooks/rag-skill-loader.cjs +159 -0
  24. package/.claude/hooks/session-end.sh +61 -0
  25. package/.claude/hooks/sync-to-log.sh +83 -0
  26. package/.claude/hooks/thinking-silent.cjs +145 -0
  27. package/.claude/hooks/tl-summary.sh +54 -0
  28. package/.claude/hooks/todo-manager.cjs +248 -0
  29. package/.claude/hooks/verify-work.cjs +134 -0
  30. package/.claude/sessions/active-sessions.json +359 -0
  31. package/.claude/settings.local.json +43 -2
  32. package/.claude/thinking-routes/.last-sync +1 -0
  33. package/.claude/thinking-routes/QUICKREF.md +98 -0
  34. package/.claude-plugin/marketplace.json +71 -0
  35. package/.github/workflows/sync-skills.yml +74 -0
  36. package/AGENTS.md +81 -22
  37. package/DEV_TOOLS_GUIDE.md +190 -0
  38. package/PROJECT_STRUCTURE.md +10 -1
  39. package/README.md +142 -708
  40. package/cli.js +116 -822
  41. package/config/defaults.json +34 -0
  42. package/config/skill-categories.json +40 -0
  43. package/development/todos/INDEX.md +14 -58
  44. package/docs/DEVELOPMENT.md +423 -0
  45. package/docs/MARKETPLACE.md +352 -0
  46. package/lib/commands.js +698 -0
  47. package/lib/config.js +71 -0
  48. package/lib/marketplace.js +486 -0
  49. package/lib/utils.js +62 -0
  50. package/package.json +11 -5
  51. package/scripts/sync-external.mjs +298 -0
  52. package/scripts/update-registry.mjs +325 -0
  53. package/sources.yaml +83 -0
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Sync External Skills
4
+ *
5
+ * This script reads sources.yaml and syncs skills from external repositories.
6
+ * It clones external repos, copies specified files, and generates source metadata.
7
+ *
8
+ * Usage: node scripts/sync-external.mjs
9
+ */
10
+
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import { execSync } from 'child_process';
15
+ import yaml from 'yaml';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+ const ROOT_DIR = path.join(__dirname, '..');
20
+ const SOURCES_FILE = path.join(ROOT_DIR, 'sources.yaml');
21
+
22
+ // ============================================================================
23
+ // Logging Utilities
24
+ // ============================================================================
25
+
26
+ const COLORS = {
27
+ reset: '\x1b[0m',
28
+ green: '\x1b[32m',
29
+ blue: '\x1b[34m',
30
+ yellow: '\x1b[33m',
31
+ red: '\x1b[31m',
32
+ gray: '\x1b[90m'
33
+ };
34
+
35
+ function log(message, color = 'reset') {
36
+ console.log(`${COLORS[color]}${message}${COLORS.reset}`);
37
+ }
38
+
39
+ function logStep(num, total, message) {
40
+ log(`[${num}/${total}] ${message}`, 'blue');
41
+ }
42
+
43
+ // ============================================================================
44
+ // File System Utilities
45
+ // ============================================================================
46
+
47
+ /**
48
+ * Ensure a directory exists, create if not
49
+ */
50
+ function ensureDir(dirPath) {
51
+ if (!fs.existsSync(dirPath)) {
52
+ fs.mkdirSync(dirPath, { recursive: true });
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Recursively copy directory contents
58
+ */
59
+ function copyRecursive(src, dest, include = [], exclude = []) {
60
+ let count = 0;
61
+
62
+ if (!fs.existsSync(src)) {
63
+ return count;
64
+ }
65
+
66
+ const stat = fs.statSync(src);
67
+
68
+ if (stat.isDirectory()) {
69
+ ensureDir(dest);
70
+
71
+ const entries = fs.readdirSync(src);
72
+
73
+ for (const entry of entries) {
74
+ // Check exclude patterns
75
+ const excluded = exclude.some(pattern => {
76
+ const regex = new RegExp(pattern.replace('*', '.*'));
77
+ return regex.test(entry);
78
+ });
79
+
80
+ if (excluded) {
81
+ continue;
82
+ }
83
+
84
+ // Check include patterns (if specified)
85
+ if (include.length > 0) {
86
+ const included = include.some(pattern => {
87
+ const regex = new RegExp(pattern.replace('*', '.*'));
88
+ return regex.test(entry);
89
+ });
90
+ if (!included) {
91
+ continue;
92
+ }
93
+ }
94
+
95
+ const srcPath = path.join(src, entry);
96
+ const destPath = path.join(dest, entry);
97
+ count += copyRecursive(srcPath, destPath, [], exclude);
98
+ }
99
+ } else {
100
+ fs.copyFileSync(src, dest);
101
+ count = 1;
102
+ }
103
+
104
+ return count;
105
+ }
106
+
107
+ /**
108
+ * Remove directory recursively
109
+ */
110
+ function removeDir(dirPath) {
111
+ if (fs.existsSync(dirPath)) {
112
+ fs.rmSync(dirPath, { recursive: true, force: true });
113
+ }
114
+ }
115
+
116
+ // ============================================================================
117
+ // Git Utilities
118
+ // ============================================================================
119
+
120
+ /**
121
+ * Clone a Git repository to a temporary directory
122
+ */
123
+ function cloneRepo(repo, ref, targetDir) {
124
+ const url = `https://github.com/${repo}.git`;
125
+ const command = `git clone --depth 1 --branch ${ref} ${url} ${targetDir}`;
126
+
127
+ try {
128
+ execSync(command, { stdio: 'pipe' });
129
+ return true;
130
+ } catch (error) {
131
+ log(`Failed to clone ${repo}: ${error.message}`, 'red');
132
+ return false;
133
+ }
134
+ }
135
+
136
+ // ============================================================================
137
+ // Source Metadata
138
+ // ============================================================================
139
+
140
+ /**
141
+ * Write .source.json file for a synced skill
142
+ */
143
+ function writeSourceJson(skill, targetPath) {
144
+ const sourceJson = {
145
+ name: skill.name,
146
+ description: skill.description,
147
+ source: {
148
+ repo: skill.source?.repo,
149
+ path: skill.source?.path,
150
+ ref: skill.source?.ref || 'main',
151
+ synced_at: new Date().toISOString()
152
+ },
153
+ author: skill.author,
154
+ license: skill.license,
155
+ homepage: skill.homepage,
156
+ verified: skill.verified || false
157
+ };
158
+
159
+ const sourceJsonPath = path.join(targetPath, '.source.json');
160
+ fs.writeFileSync(sourceJsonPath, JSON.stringify(sourceJson, null, 2));
161
+ }
162
+
163
+ // ============================================================================
164
+ // Main Sync Logic
165
+ // ============================================================================
166
+
167
+ /**
168
+ * Parse sources.yaml file
169
+ */
170
+ function parseSources() {
171
+ if (!fs.existsSync(SOURCES_FILE)) {
172
+ log('sources.yaml not found!', 'red');
173
+ return null;
174
+ }
175
+
176
+ const content = fs.readFileSync(SOURCES_FILE, 'utf8');
177
+ return yaml.parse(content);
178
+ }
179
+
180
+ /**
181
+ * Sync a single external skill
182
+ */
183
+ function syncSkill(skill, index, total) {
184
+ logStep(index, total, `Syncing: ${skill.name}`);
185
+
186
+ // Skip native skills (maintained directly in repo)
187
+ if (skill.native) {
188
+ log(` → Skipped (native skill)`, 'gray');
189
+ return { success: true, skipped: true };
190
+ }
191
+
192
+ // Validate required fields
193
+ if (!skill.source?.repo) {
194
+ log(` → Skipped (no source.repo)`, 'yellow');
195
+ return { success: false, error: 'Missing source.repo' };
196
+ }
197
+
198
+ const targetPath = path.join(ROOT_DIR, skill.target.path);
199
+ const tmpDir = path.join(ROOT_DIR, '.tmp', skill.name);
200
+
201
+ try {
202
+ // Clone repository
203
+ log(` → Cloning ${skill.source.repo}...`, 'gray');
204
+ if (!cloneRepo(skill.source.repo, skill.source.ref || 'main', tmpDir)) {
205
+ return { success: false, error: 'Clone failed' };
206
+ }
207
+
208
+ // Determine source path
209
+ const sourcePath = path.join(tmpDir, skill.source.path || '');
210
+
211
+ if (!fs.existsSync(sourcePath)) {
212
+ log(` → Source path not found: ${skill.source.path}`, 'yellow');
213
+ removeDir(tmpDir);
214
+ return { success: false, error: 'Source path not found' };
215
+ }
216
+
217
+ // Clear target directory
218
+ removeDir(targetPath);
219
+ ensureDir(targetPath);
220
+
221
+ // Copy files
222
+ const include = skill.sync?.include || [];
223
+ const exclude = skill.sync?.exclude || ['node_modules', '*.lock'];
224
+
225
+ log(` → Copying files...`, 'gray');
226
+ const count = copyRecursive(sourcePath, targetPath, include, exclude);
227
+
228
+ // Write source metadata
229
+ writeSourceJson(skill, targetPath);
230
+
231
+ // Cleanup
232
+ removeDir(tmpDir);
233
+
234
+ log(` → Synced ${count} files`, 'green');
235
+ return { success: true, fileCount: count };
236
+
237
+ } catch (error) {
238
+ removeDir(tmpDir);
239
+ log(` → Error: ${error.message}`, 'red');
240
+ return { success: false, error: error.message };
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Main sync function
246
+ */
247
+ function syncAll() {
248
+ log('🔄 Syncing External Skills', 'blue');
249
+ log('=====================================', 'gray');
250
+ log('');
251
+
252
+ const sources = parseSources();
253
+ if (!sources || !sources.skills) {
254
+ log('No skills found in sources.yaml', 'yellow');
255
+ return;
256
+ }
257
+
258
+ const skills = sources.skills;
259
+ const total = skills.length;
260
+
261
+ log(`Found ${total} skill(s) in sources.yaml`, 'gray');
262
+ log('');
263
+
264
+ let synced = 0;
265
+ let skipped = 0;
266
+ let failed = 0;
267
+
268
+ for (let i = 0; i < total; i++) {
269
+ const result = syncSkill(skills[i], i + 1, total);
270
+
271
+ if (result.success) {
272
+ if (result.skipped) {
273
+ skipped++;
274
+ } else {
275
+ synced++;
276
+ }
277
+ } else {
278
+ failed++;
279
+ }
280
+
281
+ log('');
282
+ }
283
+
284
+ // Summary
285
+ log('=====================================', 'gray');
286
+ log(`✅ Synced: ${synced}`, 'green');
287
+ log(`⏭️ Skipped: ${skipped}`, 'yellow');
288
+ if (failed > 0) {
289
+ log(`❌ Failed: ${failed}`, 'red');
290
+ }
291
+ log('=====================================', 'gray');
292
+ }
293
+
294
+ // ============================================================================
295
+ // Run
296
+ // ============================================================================
297
+
298
+ syncAll();
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Update Marketplace Registry
4
+ *
5
+ * This script scans the skills directory and generates the marketplace.json
6
+ * registry file for Claude Code's native plugin system.
7
+ *
8
+ * Usage: node scripts/update-registry.mjs
9
+ */
10
+
11
+ import fs from 'fs';
12
+ import path from 'path';
13
+ import { fileURLToPath } from 'url';
14
+ import { execSync } from 'child_process';
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+ const ROOT_DIR = path.join(__dirname, '..');
19
+ const MARKETPLACE_FILE = path.join(ROOT_DIR, '.claude-plugin', 'marketplace.json');
20
+ const SKILLS_DIR = path.join(ROOT_DIR, 'template', '.claude', 'skills');
21
+ const CATEGORIES_FILE = path.join(ROOT_DIR, 'config', 'skill-categories.json');
22
+
23
+ // ============================================================================
24
+ // Logging Utilities
25
+ // ============================================================================
26
+
27
+ const COLORS = {
28
+ reset: '\x1b[0m',
29
+ green: '\x1b[32m',
30
+ blue: '\x1b[34m',
31
+ yellow: '\x1b[33m',
32
+ gray: '\x1b[90m'
33
+ };
34
+
35
+ function log(message, color = 'reset') {
36
+ console.log(`${COLORS[color]}${message}${COLORS.reset}`);
37
+ }
38
+
39
+ // ============================================================================
40
+ // File System Utilities
41
+ // ============================================================================
42
+
43
+ /**
44
+ * Recursively find all skill directories
45
+ */
46
+ function findSkillDirs(dir, basePath = dir) {
47
+ if (!fs.existsSync(dir)) {
48
+ return [];
49
+ }
50
+
51
+ const skills = [];
52
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
53
+
54
+ for (const entry of entries) {
55
+ if (entry.isDirectory()) {
56
+ const fullPath = path.join(dir, entry.name);
57
+
58
+ // Skip template and examples directories
59
+ if (['template', 'examples', '__tests__'].includes(entry.name)) {
60
+ continue;
61
+ }
62
+
63
+ // Check if this is a skill directory (contains SKILL.md or metadata.yaml)
64
+ const hasSkillFile = fs.existsSync(path.join(fullPath, 'SKILL.md'));
65
+ const hasMetadata = fs.existsSync(path.join(fullPath, 'metadata.yaml'));
66
+ const hasSourceJson = fs.existsSync(path.join(fullPath, '.source.json'));
67
+
68
+ if (hasSkillFile || hasMetadata || hasSourceJson) {
69
+ const relativePath = path.relative(basePath, fullPath);
70
+ skills.push({
71
+ name: entry.name,
72
+ path: fullPath,
73
+ relativePath: relativePath,
74
+ hasSourceJson
75
+ });
76
+ } else {
77
+ // Recurse into subdirectories
78
+ skills.push(...findSkillDirs(fullPath, basePath));
79
+ }
80
+ }
81
+ }
82
+
83
+ return skills;
84
+ }
85
+
86
+ /**
87
+ * Parse YAML metadata file
88
+ */
89
+ function parseMetadata(skillDir) {
90
+ const metadataPath = path.join(skillDir, 'metadata.yaml');
91
+
92
+ if (!fs.existsSync(metadataPath)) {
93
+ return null;
94
+ }
95
+
96
+ try {
97
+ const content = fs.readFileSync(metadataPath, 'utf8');
98
+ // Simple YAML parser for basic key: value format
99
+ const result = {};
100
+ let currentKey = null;
101
+
102
+ content.split('\n').forEach(line => {
103
+ const trimmed = line.trim();
104
+
105
+ // Skip comments and empty lines
106
+ if (!trimmed || trimmed.startsWith('#')) {
107
+ return;
108
+ }
109
+
110
+ // Key: Value pair
111
+ const match = trimmed.match(/^([^:]+):\s*(.*)$/);
112
+ if (match) {
113
+ currentKey = match[1];
114
+ let value = match[2];
115
+
116
+ // Handle array values
117
+ if (value.startsWith('[')) {
118
+ try {
119
+ value = JSON.parse(value.replace(/'/g, '"'));
120
+ } catch (e) {
121
+ value = [];
122
+ }
123
+ } else if (value === '[]') {
124
+ value = [];
125
+ } else if (value === 'true') {
126
+ value = true;
127
+ } else if (value === 'false') {
128
+ value = false;
129
+ }
130
+
131
+ result[currentKey] = value;
132
+ } else if (currentKey && trimmed.startsWith('-')) {
133
+ // Array item continuation
134
+ if (!Array.isArray(result[currentKey])) {
135
+ result[currentKey] = [];
136
+ }
137
+ result[currentKey].push(trimmed.slice(1).trim());
138
+ }
139
+ });
140
+
141
+ return result;
142
+ } catch (error) {
143
+ log(`Warning: Failed to parse ${metadataPath}`, 'yellow');
144
+ return null;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Parse .source.json file
150
+ */
151
+ function parseSourceJson(skillDir) {
152
+ const sourcePath = path.join(skillDir, '.source.json');
153
+
154
+ if (!fs.existsSync(sourcePath)) {
155
+ return null;
156
+ }
157
+
158
+ try {
159
+ const content = fs.readFileSync(sourcePath, 'utf-8');
160
+ return JSON.parse(content);
161
+ } catch (error) {
162
+ return null;
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Get skill description from SKILL.md
168
+ */
169
+ function getSkillDescription(skillDir) {
170
+ const skillPath = path.join(skillDir, 'SKILL.md');
171
+
172
+ if (!fs.existsSync(skillPath)) {
173
+ return null;
174
+ }
175
+
176
+ try {
177
+ const content = fs.readFileSync(skillPath, 'utf-8');
178
+ // Extract first heading or first paragraph
179
+ const lines = content.split('\n');
180
+
181
+ for (const line of lines) {
182
+ const trimmed = line.trim();
183
+ // Skip title
184
+ if (trimmed.startsWith('#')) {
185
+ continue;
186
+ }
187
+ // Return first non-empty, non-comment line
188
+ if (trimmed && !trimmed.startsWith('>') && !trimmed.startsWith('<!--')) {
189
+ return trimmed.replace(/^>/, '').trim();
190
+ }
191
+ }
192
+
193
+ return null;
194
+ } catch (error) {
195
+ return null;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Determine skill category
201
+ */
202
+ function getSkillCategory(skillPath, categories) {
203
+ // Check if path contains a category
204
+ const pathParts = skillPath.split(path.sep);
205
+
206
+ for (const part of pathParts) {
207
+ if (categories[part]) {
208
+ return part;
209
+ }
210
+ }
211
+
212
+ return 'tools'; // Default category
213
+ }
214
+
215
+ // ============================================================================
216
+ // Registry Generation
217
+ // ============================================================================
218
+
219
+ /**
220
+ * Generate the marketplace.json registry
221
+ */
222
+ function generateRegistry() {
223
+ log('📋 Generating Marketplace Registry', 'blue');
224
+ log('=====================================', 'gray');
225
+ log('');
226
+
227
+ // Load categories
228
+ let categories = {};
229
+ if (fs.existsSync(CATEGORIES_FILE)) {
230
+ categories = JSON.parse(fs.readFileSync(CATEGORIES_FILE, 'utf-8')).categories || {};
231
+ }
232
+
233
+ // Find all skill directories
234
+ log('Scanning skills directory...', 'gray');
235
+ const skills = findSkillDirs(SKILLS_DIR);
236
+ log(`Found ${skills.length} skill(s)`, 'gray');
237
+ log('');
238
+
239
+ if (skills.length === 0) {
240
+ log('No skills found. Registry will be empty.', 'yellow');
241
+ return;
242
+ }
243
+
244
+ // Build plugin entries
245
+ const pluginSkills = [];
246
+
247
+ for (const skill of skills) {
248
+ log(`Processing: ${skill.name}`, 'gray');
249
+
250
+ // Get metadata
251
+ const metadata = parseMetadata(skill.path);
252
+ const sourceJson = parseSourceJson(skill.path);
253
+ const description = metadata?.description || getSkillDescription(skill.path) || 'No description';
254
+
255
+ // Determine category
256
+ const category = metadata?.category || getSkillCategory(skill.relativePath, categories);
257
+
258
+ // Build relative path from .claude-plugin
259
+ const relativeFromPlugin = path.join('..', 'template', '.claude', 'skills', skill.relativePath);
260
+
261
+ pluginSkills.push({
262
+ name: skill.name,
263
+ description: description,
264
+ path: `./${relativeFromPlugin}`,
265
+ category: category,
266
+ external: !!sourceJson
267
+ });
268
+ }
269
+
270
+ // Build the full registry
271
+ const registry = {
272
+ name: 'smc-skills',
273
+ description: 'Sumulige Claude Agent Harness - Curated skill collection for AI coding agents',
274
+ homepage: 'https://github.com/sumulige/sumulige-claude',
275
+ owner: {
276
+ name: 'sumulige',
277
+ email: 'sumulige@example.com'
278
+ },
279
+ plugins: [
280
+ {
281
+ name: 'smc-skills',
282
+ description: 'Multi-agent orchestration harness with curated skills. Includes Conductor, Architect, Builder, Reviewer, Librarian agents plus RAG-based skill discovery system.',
283
+ source: './',
284
+ skills: pluginSkills.map(s => s.path),
285
+ strict: false,
286
+ skill_list: pluginSkills
287
+ }
288
+ ],
289
+ metadata: {
290
+ version: getPackageVersion(),
291
+ generated_at: new Date().toISOString(),
292
+ skill_count: skills.length,
293
+ categories: categories
294
+ }
295
+ };
296
+
297
+ // Write registry file
298
+ log('', 'gray');
299
+ log(`Writing registry to: ${MARKETPLACE_FILE}`, 'gray');
300
+ fs.writeFileSync(MARKETPLACE_FILE, JSON.stringify(registry, null, 2));
301
+
302
+ log('', 'gray');
303
+ log('=====================================', 'gray');
304
+ log(`✅ Registry generated with ${skills.length} skills`, 'green');
305
+ log('=====================================', 'gray');
306
+ }
307
+
308
+ /**
309
+ * Get package version
310
+ */
311
+ function getPackageVersion() {
312
+ try {
313
+ const pkgPath = path.join(ROOT_DIR, 'package.json');
314
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
315
+ return pkg.version || '1.0.0';
316
+ } catch (error) {
317
+ return '1.0.0';
318
+ }
319
+ }
320
+
321
+ // ============================================================================
322
+ // Run
323
+ // ============================================================================
324
+
325
+ generateRegistry();
package/sources.yaml ADDED
@@ -0,0 +1,83 @@
1
+ # SMC External Skills Sources
2
+ # ===========================
3
+ # 此清单定义了从外部仓库同步到本项目的技能。
4
+ # GitHub Actions 工作流会读取此文件并每日自动同步上游技能。
5
+ #
6
+ # 添加技能:提交 PR 添加条目到此处。参考 CONTRIBUTING.md。
7
+
8
+ version: 1
9
+ skills:
10
+ # ─────────────────────────────────────────────────────────────────────────────
11
+ # manus-kickoff - Native skill (maintained directly in this repo)
12
+ # ─────────────────────────────────────────────────────────────────────────────
13
+ - name: manus-kickoff
14
+ description: Manus-style project kickoff workflow for AI autonomous development
15
+ native: true # Maintained directly in this repo
16
+ target:
17
+ category: workflow
18
+ path: template/.claude/skills/manus-kickoff
19
+ author:
20
+ name: sumulige
21
+ github: sumulige
22
+ license: MIT
23
+ homepage: https://github.com/sumulige/sumulige-claude
24
+
25
+ # ─────────────────────────────────────────────────────────────────────────────
26
+ # Example: Adding an external skill (commented out)
27
+ # ─────────────────────────────────────────────────────────────────────────────
28
+ # - name: dev-browser
29
+ # description: Browser automation with persistent page state
30
+ # source:
31
+ # repo: SawyerHood/dev-browser
32
+ # path: skills/dev-browser
33
+ # ref: main
34
+ # target:
35
+ # category: automation
36
+ # path: template/.claude/skills/automation/dev-browser
37
+ # author:
38
+ # name: Sawyer Hood
39
+ # github: SawyerHood
40
+ # license: MIT
41
+ # homepage: https://github.com/SawyerHood/dev-browser
42
+ # verified: false
43
+ # sync:
44
+ # include:
45
+ # - SKILL.md
46
+ # - references/
47
+ # - scripts/
48
+ # - src/
49
+ # - package.json
50
+ # exclude:
51
+ # - node_modules/
52
+ # - "*.lock"
53
+
54
+ # ─────────────────────────────────────────────────────────────────────────────
55
+ # Schema Reference
56
+ # ─────────────────────────────────────────────────────────────────────────────
57
+ #
58
+ # skills[]:
59
+ # name: Required. Unique skill identifier (lowercase, hyphens ok)
60
+ # description: Required. One-line description for marketplace listings
61
+ #
62
+ # native: Optional. Set true if skill is maintained directly in this repo
63
+ #
64
+ # source: Required for external skills
65
+ # repo: GitHub owner/repo (e.g., "SawyerHood/dev-browser")
66
+ # path: Path to skill folder within repo
67
+ # ref: Branch, tag, or commit SHA (default: main)
68
+ #
69
+ # target: Required
70
+ # category: One of: tools, development, productivity, automation, data, documentation, workflow
71
+ # path: Target path in smc-skills repo
72
+ #
73
+ # author: Required
74
+ # name: Display name
75
+ # github: GitHub username
76
+ #
77
+ # license: Required. SPDX identifier (MIT, Apache-2.0, etc.)
78
+ # homepage: Required. Link to project homepage or repo
79
+ # verified: Optional. Set true after manual review (default: false)
80
+ #
81
+ # sync: Optional. Fine-grained sync control
82
+ # include: List of files/folders to copy
83
+ # exclude: Glob patterns to skip