Skip to content

Commit d072f76

Browse files
snooclaude
andcommitted
feat: v3.4.0 - skills.sh integration
- Add SKILL.md for skills.sh registration - Add `codesyncer skills` command to browse leaderboard - Add `codesyncer skills add <repo>` to install skills - Update READMEs with new skills command Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7eeeea5 commit d072f76

File tree

7 files changed

+342
-3
lines changed

7 files changed

+342
-3
lines changed

README.ko.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ codesyncer update # 템플릿 업데이트
125125
codesyncer validate # 설정 검증
126126
codesyncer watch # 실시간 모니터링
127127
codesyncer add-repo # 워크스페이스에 레포 추가
128+
codesyncer skills # skills.sh 리더보드 보기
128129
```
129130

130131
---

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ codesyncer update # Update templates
125125
codesyncer validate # Check setup
126126
codesyncer watch # Real-time monitoring
127127
codesyncer add-repo # Add repo to workspace
128+
codesyncer skills # Browse skills.sh leaderboard
128129
```
129130

130131
---

SKILL.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
---
2+
name: codesyncer
3+
description: AI context persistence - tags in code = permanent memory across sessions
4+
---
5+
6+
# CodeSyncer
7+
8+
> Claude forgets everything when the session ends. CodeSyncer makes it remember.
9+
10+
## When to use
11+
12+
Use this skill on ANY project where you want AI to:
13+
- Remember decisions across sessions
14+
- Track why code was written a certain way
15+
- Auto-pause before touching payment/security/API code
16+
- Never lose context again
17+
18+
## Core Rules
19+
20+
### 1. Always Add Tags
21+
22+
| Situation | Tag | Example |
23+
|-----------|-----|---------|
24+
| You inferred something | `@codesyncer-inference` | `// @codesyncer-inference: Page size 20 (standard UX)` |
25+
| Decision after discussion | `@codesyncer-decision` | `// @codesyncer-decision: [2024-01-15] Using JWT` |
26+
| Non-standard implementation | `@codesyncer-rule` | `// @codesyncer-rule: any type here (no types available)` |
27+
| Needs user confirmation | `@codesyncer-todo` | `// @codesyncer-todo: Confirm API endpoint` |
28+
| Business context | `@codesyncer-context` | `// @codesyncer-context: GDPR requires 30-day retention` |
29+
30+
### 2. Never Infer Critical Values
31+
32+
**ALWAYS ASK the user about:**
33+
- 💰 Prices, fees, discounts, billing
34+
- 🔐 Auth methods, token expiry, encryption
35+
- 🔌 API endpoints, external service URLs
36+
- 🗄️ Database schemas, migrations
37+
38+
```
39+
❌ Bad: "I set the shipping fee to $5"
40+
✅ Good: "What should the shipping fee be?"
41+
```
42+
43+
### 3. Auto-Pause Keywords
44+
45+
When you detect these keywords, **STOP and discuss** before coding:
46+
- payment, billing, subscription, charge, refund
47+
- authentication, login, permission, encrypt, token
48+
- delete, remove, drop, migrate, schema change
49+
- personal data, GDPR, privacy
50+
51+
## Tag Examples
52+
53+
```typescript
54+
// @codesyncer-decision: [2024-01-15] Using Stripe for payments (international support)
55+
// @codesyncer-context: Supports USD, EUR, KRW currencies
56+
const paymentProvider = 'stripe';
57+
58+
// @codesyncer-inference: 5 second timeout (typical API response time)
59+
// @codesyncer-rule: Retry max 3 times (rate limit protection)
60+
const apiConfig = {
61+
timeout: 5000,
62+
retries: 3,
63+
};
64+
65+
// @codesyncer-todo: Confirm discount percentage with business team
66+
const DISCOUNT_RATE = 0.1; // 10%
67+
```
68+
69+
## Why This Matters
70+
71+
Next session, when AI reads your code:
72+
1. Sees `@codesyncer-decision` → Knows WHY you chose Stripe
73+
2. Sees `@codesyncer-inference` → Knows the reasoning behind values
74+
3. Sees `@codesyncer-todo` → Knows what still needs confirmation
75+
76+
**Context survives across sessions. No more "why did we do this?"**
77+
78+
## Quick Reference
79+
80+
```
81+
Inferred something? → @codesyncer-inference: [reason]
82+
Made a decision? → @codesyncer-decision: [date] [what] [why]
83+
Special rule? → @codesyncer-rule: [explanation]
84+
Need to confirm? → @codesyncer-todo: [what to confirm]
85+
Business context? → @codesyncer-context: [domain knowledge]
86+
```
87+
88+
## Learn More
89+
90+
- [Full Documentation](https://github.com/bitjaru/codesyncer)
91+
- [Tag System Guide](https://github.com/bitjaru/codesyncer/blob/main/docs/TAGS.md)
92+
- [Hooks Guide](https://github.com/bitjaru/codesyncer/blob/main/docs/HOOKS.md)

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codesyncer",
3-
"version": "3.3.0",
3+
"version": "3.4.0",
44
"description": "Claude forgets everything when the session ends. CodeSyncer makes it remember.",
55
"keywords": [
66
"ai-collaboration",

src/commands/skills.ts

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/**
2+
* CodeSyncer Skills Command
3+
*
4+
* Integration with skills.sh for skill discovery and installation.
5+
*
6+
* @codesyncer-context skills.sh API: https://skills.sh/api/skills
7+
* @codesyncer-decision [2026-01-23] skills.sh 연동으로 생태계 확장
8+
*/
9+
10+
import chalk from 'chalk';
11+
import ora from 'ora';
12+
import { spawn } from 'child_process';
13+
14+
const SKILLS_API_URL = 'https://skills.sh/api/skills';
15+
16+
interface Skill {
17+
id: string;
18+
name: string;
19+
installs: number;
20+
topSource: string;
21+
}
22+
23+
interface SkillsApiResponse {
24+
skills: Skill[];
25+
}
26+
27+
/**
28+
* Fetch skills from skills.sh API
29+
*/
30+
async function fetchSkills(): Promise<Skill[]> {
31+
try {
32+
const response = await fetch(SKILLS_API_URL);
33+
if (!response.ok) {
34+
throw new Error(`HTTP ${response.status}`);
35+
}
36+
const data = await response.json() as SkillsApiResponse;
37+
return data.skills || [];
38+
} catch (error) {
39+
throw new Error(`Failed to fetch skills: ${error instanceof Error ? error.message : 'Unknown error'}`);
40+
}
41+
}
42+
43+
/**
44+
* Display skills leaderboard
45+
*/
46+
function displayLeaderboard(skills: Skill[], isKo: boolean): void {
47+
console.log();
48+
console.log(chalk.bold.cyan(isKo ? '🏆 Skills.sh 리더보드' : '🏆 Skills.sh Leaderboard'));
49+
console.log(chalk.gray('─'.repeat(60)));
50+
console.log();
51+
52+
// Sort by installs (descending)
53+
const sorted = [...skills].sort((a, b) => b.installs - a.installs);
54+
55+
// Find codesyncer position
56+
const codesyncerIndex = sorted.findIndex(s =>
57+
s.name.toLowerCase() === 'codesyncer' ||
58+
s.topSource.includes('bitjaru/codesyncer')
59+
);
60+
61+
// Display top 10
62+
const top10 = sorted.slice(0, 10);
63+
64+
console.log(chalk.gray(
65+
` ${isKo ? '순위' : 'Rank'} ${(isKo ? '이름' : 'Name').padEnd(25)} ${(isKo ? '설치수' : 'Installs').padStart(10)}`
66+
));
67+
console.log(chalk.gray(' ' + '─'.repeat(45)));
68+
69+
top10.forEach((skill, index) => {
70+
const rank = index + 1;
71+
const isCodesyncer = skill.name.toLowerCase() === 'codesyncer' ||
72+
skill.topSource.includes('bitjaru/codesyncer');
73+
74+
const rankStr = rank <= 3
75+
? ['🥇', '🥈', '🥉'][rank - 1]
76+
: `${rank}.`.padStart(3);
77+
78+
const nameStr = skill.name.padEnd(25);
79+
const installsStr = skill.installs.toLocaleString().padStart(10);
80+
81+
if (isCodesyncer) {
82+
console.log(chalk.bold.green(` ${rankStr} ${nameStr} ${installsStr} ← You are here!`));
83+
} else {
84+
console.log(chalk.white(` ${rankStr} ${nameStr} ${installsStr}`));
85+
}
86+
});
87+
88+
// If codesyncer is not in top 10, show its position
89+
if (codesyncerIndex >= 10) {
90+
const codesyncer = sorted[codesyncerIndex];
91+
console.log(chalk.gray(' ...'));
92+
console.log(chalk.bold.yellow(
93+
` ${(codesyncerIndex + 1).toString().padStart(3)}. ${codesyncer.name.padEnd(25)} ${codesyncer.installs.toLocaleString().padStart(10)}`
94+
));
95+
}
96+
97+
console.log();
98+
console.log(chalk.gray('─'.repeat(60)));
99+
console.log(chalk.gray(
100+
isKo
101+
? ` 총 ${skills.length}개 스킬 | 데이터: skills.sh`
102+
: ` Total ${skills.length} skills | Data: skills.sh`
103+
));
104+
console.log();
105+
106+
// Installation guide
107+
console.log(chalk.bold(isKo ? '📦 스킬 설치하기' : '📦 Install a Skill'));
108+
console.log(chalk.gray(
109+
isKo
110+
? ' npx skills add <owner/repo>'
111+
: ' npx skills add <owner/repo>'
112+
));
113+
console.log();
114+
console.log(chalk.cyan(' npx skills add bitjaru/codesyncer'));
115+
console.log();
116+
}
117+
118+
/**
119+
* Install a skill using npx skills add
120+
*/
121+
async function installSkill(skillName: string, isKo: boolean): Promise<void> {
122+
const spinner = ora(
123+
isKo
124+
? `${skillName} 설치 중...`
125+
: `Installing ${skillName}...`
126+
).start();
127+
128+
return new Promise((resolve, reject) => {
129+
const child = spawn('npx', ['skills', 'add', skillName], {
130+
stdio: 'inherit',
131+
shell: true,
132+
});
133+
134+
spinner.stop();
135+
136+
child.on('close', (code) => {
137+
if (code === 0) {
138+
console.log();
139+
console.log(chalk.green(
140+
isKo
141+
? `✅ ${skillName} 설치 완료!`
142+
: `✅ ${skillName} installed successfully!`
143+
));
144+
resolve();
145+
} else {
146+
reject(new Error(
147+
isKo
148+
? `설치 실패 (exit code: ${code})`
149+
: `Installation failed (exit code: ${code})`
150+
));
151+
}
152+
});
153+
154+
child.on('error', (error) => {
155+
spinner.fail(
156+
isKo
157+
? '설치 실패'
158+
: 'Installation failed'
159+
);
160+
reject(error);
161+
});
162+
});
163+
}
164+
165+
export interface SkillsOptions {
166+
// Reserved for future options
167+
}
168+
169+
/**
170+
* Main skills command
171+
*/
172+
export async function skillsCommand(subcommand?: string, skillName?: string): Promise<void> {
173+
// Detect language from environment or default to English
174+
const isKo = process.env.LANG?.startsWith('ko') || false;
175+
176+
console.log(chalk.bold.cyan('\n🎯 CodeSyncer - Skills\n'));
177+
178+
// Handle subcommands
179+
if (subcommand === 'add') {
180+
if (!skillName) {
181+
console.log(chalk.red(
182+
isKo
183+
? '❌ 스킬 이름을 지정해주세요'
184+
: '❌ Please specify a skill name'
185+
));
186+
console.log();
187+
console.log(chalk.gray(isKo ? '사용법:' : 'Usage:'));
188+
console.log(chalk.cyan(' codesyncer skills add <owner/repo>'));
189+
console.log(chalk.cyan(' codesyncer skills add bitjaru/codesyncer'));
190+
return;
191+
}
192+
193+
try {
194+
await installSkill(skillName, isKo);
195+
} catch (error) {
196+
console.log(chalk.red(
197+
isKo
198+
? `❌ 설치 실패: ${error instanceof Error ? error.message : 'Unknown error'}`
199+
: `❌ Installation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
200+
));
201+
}
202+
return;
203+
}
204+
205+
// Default: show leaderboard
206+
const spinner = ora(
207+
isKo
208+
? 'skills.sh에서 데이터 가져오는 중...'
209+
: 'Fetching data from skills.sh...'
210+
).start();
211+
212+
try {
213+
const skills = await fetchSkills();
214+
spinner.succeed(
215+
isKo
216+
? '데이터 로드 완료'
217+
: 'Data loaded'
218+
);
219+
displayLeaderboard(skills, isKo);
220+
} catch (error) {
221+
spinner.fail(
222+
isKo
223+
? 'skills.sh 연결 실패'
224+
: 'Failed to connect to skills.sh'
225+
);
226+
console.log();
227+
console.log(chalk.red(
228+
error instanceof Error ? error.message : 'Unknown error'
229+
));
230+
console.log();
231+
console.log(chalk.gray(
232+
isKo
233+
? '인터넷 연결을 확인하거나, 나중에 다시 시도해주세요.'
234+
: 'Check your internet connection or try again later.'
235+
));
236+
}
237+
}

src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { updateCommand } from './commands/update';
77
import { addRepoCommand } from './commands/add-repo';
88
import { watchCommand } from './commands/watch';
99
import { validateCommand } from './commands/validate';
10+
import { skillsCommand } from './commands/skills';
1011
import { VERSION } from './utils/version';
1112
import * as os from 'os';
1213

@@ -56,6 +57,8 @@ ${chalk.bold('Examples:')}
5657
$ codesyncer watch ${chalk.gray('# Real-time file monitoring')}
5758
$ codesyncer validate ${chalk.gray('# Validate setup and report issues')}
5859
$ codesyncer add-repo ${chalk.gray('# Add new repository to workspace')}
60+
$ codesyncer skills ${chalk.gray('# Browse skills.sh leaderboard')}
61+
$ codesyncer skills add <repo> ${chalk.gray('# Install a skill')}
5962
`);
6063

6164
program
@@ -92,4 +95,9 @@ program
9295
.option('--verbose', 'Show detailed output including file paths')
9396
.action(validateCommand);
9497

98+
program
99+
.command('skills [subcommand] [name]')
100+
.description('Browse skills.sh leaderboard or install skills')
101+
.action(skillsCommand);
102+
95103
program.parse(process.argv);

0 commit comments

Comments
 (0)