binary-agents 1.0.2 → 1.0.3
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.
- package/README.md +76 -38
- package/bin/cli.js +13 -5
- package/commands/commit.md +53 -0
- package/package.json +4 -2
- package/src/sync.js +200 -53
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Binary Agents
|
|
2
2
|
|
|
3
|
-
Claude Code 서브에이전트 컬렉션
|
|
3
|
+
Claude Code 서브에이전트 및 슬래시 명령어 컬렉션 + 동기화 도구
|
|
4
4
|
|
|
5
5
|
## 소개
|
|
6
6
|
|
|
7
|
-
`binary-agents`는 Claude Code의 강력한
|
|
7
|
+
`binary-agents`는 Claude Code의 강력한 서브에이전트와 슬래시 명령어를 모아놓은 모노레포입니다. 코드 리뷰, 리팩토링 분석, 주니어 개발자 친화성 체크 등 다양한 서브에이전트와 자동 커밋 등의 슬래시 명령어를 제공하며, 이를 프로젝트에 쉽게 설치할 수 있는 CLI 도구를 포함합니다.
|
|
8
8
|
|
|
9
9
|
## 설치
|
|
10
10
|
|
|
@@ -32,39 +32,48 @@ npx binary-agents sync
|
|
|
32
32
|
|
|
33
33
|
## 사용법
|
|
34
34
|
|
|
35
|
-
###
|
|
35
|
+
### 기본 동기화
|
|
36
36
|
|
|
37
|
-
현재 프로젝트의 `.claude
|
|
37
|
+
현재 프로젝트의 `.claude/` 디렉토리에 agents와 commands 모두 설치:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
# 모든 서브에이전트 동기화
|
|
41
40
|
npx binary-agents sync
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 선택적 동기화
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# 서브에이전트만 동기화
|
|
47
|
+
npx binary-agents sync --agents
|
|
42
48
|
|
|
43
|
-
#
|
|
44
|
-
npx binary-agents sync --
|
|
49
|
+
# 슬래시 명령어만 동기화
|
|
50
|
+
npx binary-agents sync --commands
|
|
45
51
|
|
|
46
|
-
#
|
|
47
|
-
npx binary-agents sync --
|
|
52
|
+
# 기본 서브에이전트만 (Haiku 모델)
|
|
53
|
+
npx binary-agents sync --agents --basic
|
|
54
|
+
|
|
55
|
+
# 고급 서브에이전트만 (Opus 모델)
|
|
56
|
+
npx binary-agents sync --agents --advanced
|
|
48
57
|
```
|
|
49
58
|
|
|
50
59
|
### 전역 설치
|
|
51
60
|
|
|
52
|
-
홈 디렉토리의 `~/.claude
|
|
61
|
+
홈 디렉토리의 `~/.claude/`에 설치 (모든 프로젝트에서 사용 가능):
|
|
53
62
|
|
|
54
63
|
```bash
|
|
55
|
-
# 전역으로
|
|
64
|
+
# 전역으로 모두 설치
|
|
56
65
|
npx binary-agents sync --global
|
|
57
66
|
# 또는
|
|
58
67
|
npx binary-agents sync -g
|
|
59
68
|
|
|
60
|
-
# 전역 +
|
|
61
|
-
npx binary-agents sync
|
|
69
|
+
# 전역 + 서브에이전트만
|
|
70
|
+
npx binary-agents sync -g --agents
|
|
62
71
|
|
|
63
|
-
# 전역 +
|
|
64
|
-
npx binary-agents sync
|
|
72
|
+
# 전역 + 슬래시 명령어만
|
|
73
|
+
npx binary-agents sync -g --commands
|
|
65
74
|
```
|
|
66
75
|
|
|
67
|
-
###
|
|
76
|
+
### 목록 보기
|
|
68
77
|
|
|
69
78
|
```bash
|
|
70
79
|
npx binary-agents list
|
|
@@ -73,17 +82,43 @@ npx binary-agents list
|
|
|
73
82
|
## 서브에이전트 종류
|
|
74
83
|
|
|
75
84
|
### 기본 버전 (Haiku 모델)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
| 이름 | 설명 |
|
|
86
|
+
|------|------|
|
|
87
|
+
| `code-reviewer` | 코드 리뷰어 |
|
|
88
|
+
| `refactor-analyzer` | 리팩토링 분석기 |
|
|
89
|
+
| `junior-friendly-checker` | 주니어 친화성 체커 |
|
|
79
90
|
|
|
80
91
|
### 고급 버전 (Opus 모델)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
| 이름 | 설명 |
|
|
93
|
+
|------|------|
|
|
94
|
+
| `advanced-code-reviewer` | 고급 코드 리뷰어 (웹 검색 포함) |
|
|
95
|
+
| `advanced-refactor-analyzer` | 고급 리팩토링 분석기 (웹 검색 포함) |
|
|
96
|
+
| `advanced-junior-checker` | 고급 주니어 친화성 체커 (웹 검색 포함) |
|
|
97
|
+
| `react-performance-optimizer` | React 성능 최적화 |
|
|
98
|
+
| `toss-cohesion-analyzer` | Toss 팀 원칙 기반 응집도 분석기 |
|
|
99
|
+
| `subagent-builder` | 커스텀 서브에이전트 빌더 |
|
|
100
|
+
|
|
101
|
+
## 슬래시 명령어
|
|
102
|
+
|
|
103
|
+
| 명령어 | 설명 |
|
|
104
|
+
|--------|------|
|
|
105
|
+
| `/commit` | git log 분석 후 컨벤션에 맞는 커밋 메시지 자동 생성 및 커밋 |
|
|
106
|
+
|
|
107
|
+
### /commit 사용법
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# 1. 변경 사항 스테이징
|
|
111
|
+
git add .
|
|
112
|
+
|
|
113
|
+
# 2. Claude Code에서 /commit 실행
|
|
114
|
+
/commit
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Claude가 자동으로:
|
|
118
|
+
1. 최근 커밋 로그에서 컨벤션 분석
|
|
119
|
+
2. staged changes 확인
|
|
120
|
+
3. 컨벤션에 맞는 커밋 메시지 생성
|
|
121
|
+
4. 커밋 실행
|
|
87
122
|
|
|
88
123
|
## 저장소 구조
|
|
89
124
|
|
|
@@ -99,6 +134,8 @@ binary-agents/
|
|
|
99
134
|
│ ├── toss-cohesion-analyzer.md
|
|
100
135
|
│ ├── react-performance-optimizer.md
|
|
101
136
|
│ └── subagent-builder.md
|
|
137
|
+
├── commands/ # 슬래시 명령어 MD 파일들
|
|
138
|
+
│ └── commit.md
|
|
102
139
|
├── bin/ # CLI 실행 파일
|
|
103
140
|
├── src/ # CLI 소스 코드
|
|
104
141
|
├── docs/ # 문서
|
|
@@ -110,9 +147,9 @@ binary-agents/
|
|
|
110
147
|
|
|
111
148
|
## 작동 원리
|
|
112
149
|
|
|
113
|
-
1. 로컬 `agents/` 디렉토리에서
|
|
150
|
+
1. 로컬 `agents/`, `commands/` 디렉토리에서 파일 목록 읽기
|
|
114
151
|
2. YAML frontmatter 검증
|
|
115
|
-
3. 사용자 프로젝트의 `.claude/agents/` 디렉토리로 복사
|
|
152
|
+
3. 사용자 프로젝트의 `.claude/agents/`, `.claude/commands/` 디렉토리로 복사
|
|
116
153
|
|
|
117
154
|
## 요구사항
|
|
118
155
|
|
|
@@ -126,23 +163,24 @@ MIT
|
|
|
126
163
|
|
|
127
164
|
이슈 및 PR을 환영합니다!
|
|
128
165
|
|
|
129
|
-
##
|
|
166
|
+
## 직접 사용하기
|
|
167
|
+
|
|
168
|
+
NPM 패키지를 설치하지 않고 이 저장소의 파일을 직접 사용할 수도 있습니다:
|
|
130
169
|
|
|
131
|
-
|
|
170
|
+
```bash
|
|
171
|
+
# 저장소 클론
|
|
172
|
+
git clone https://github.com/01-binary/binary-agents.git
|
|
132
173
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
git clone https://github.com/01-binary/binary-agents.git
|
|
136
|
-
```
|
|
174
|
+
# 서브에이전트 복사
|
|
175
|
+
cp binary-agents/agents/*.md your-project/.claude/agents/
|
|
137
176
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
```
|
|
177
|
+
# 슬래시 명령어 복사
|
|
178
|
+
cp binary-agents/commands/*.md your-project/.claude/commands/
|
|
179
|
+
```
|
|
142
180
|
|
|
143
181
|
## 관련 링크
|
|
144
182
|
|
|
145
|
-
- [Claude Code Documentation](https://docs.
|
|
183
|
+
- [Claude Code Documentation](https://docs.anthropic.com/en/docs/claude-code)
|
|
146
184
|
- [서브에이전트 상세 가이드](docs/SUBAGENTS.md)
|
|
147
185
|
- [Basic vs Advanced 비교](docs/COMPARISON.md)
|
|
148
186
|
- [커스텀 서브에이전트 제작](docs/BUILDER_GUIDE.md)
|
package/bin/cli.js
CHANGED
|
@@ -13,10 +13,12 @@ program
|
|
|
13
13
|
|
|
14
14
|
program
|
|
15
15
|
.command('sync')
|
|
16
|
-
.description('Sync
|
|
16
|
+
.description('Sync agents and commands to .claude/ (local) or ~/.claude/ (global)')
|
|
17
17
|
.option('--basic', 'Sync only basic (Haiku) subagents')
|
|
18
|
-
.option('--advanced', 'Sync only advanced (
|
|
19
|
-
.option('-g, --global', 'Install to ~/.claude/
|
|
18
|
+
.option('--advanced', 'Sync only advanced (Opus) subagents')
|
|
19
|
+
.option('-g, --global', 'Install to ~/.claude/ instead of current directory')
|
|
20
|
+
.option('--agents', 'Sync only agents (subagents)')
|
|
21
|
+
.option('--commands', 'Sync only commands (slash commands)')
|
|
20
22
|
.action(async (options) => {
|
|
21
23
|
let filter = null;
|
|
22
24
|
|
|
@@ -26,9 +28,15 @@ program
|
|
|
26
28
|
filter = 'advanced';
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
// --agents 또는 --commands가 명시되지 않으면 둘 다 동기화
|
|
32
|
+
const syncAgents = options.agents || (!options.agents && !options.commands);
|
|
33
|
+
const syncCommands = options.commands || (!options.agents && !options.commands);
|
|
34
|
+
|
|
29
35
|
const result = await syncSubagents({
|
|
30
36
|
filter,
|
|
31
|
-
global: options.global || false
|
|
37
|
+
global: options.global || false,
|
|
38
|
+
agents: syncAgents,
|
|
39
|
+
commands: syncCommands
|
|
32
40
|
});
|
|
33
41
|
|
|
34
42
|
if (!result.success) {
|
|
@@ -38,7 +46,7 @@ program
|
|
|
38
46
|
|
|
39
47
|
program
|
|
40
48
|
.command('list')
|
|
41
|
-
.description('List available
|
|
49
|
+
.description('List available agents and commands')
|
|
42
50
|
.action(async () => {
|
|
43
51
|
await listSubagents();
|
|
44
52
|
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Analyze staged changes and recent commits to generate a conventional commit
|
|
3
|
+
allowed-tools: Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(git commit:*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Auto Commit Generator
|
|
7
|
+
|
|
8
|
+
You are a commit message generator that analyzes the project's commit conventions and staged changes to create appropriate commit messages.
|
|
9
|
+
|
|
10
|
+
## Context Information
|
|
11
|
+
|
|
12
|
+
**Recent commit history (for convention analysis):**
|
|
13
|
+
!`git log --oneline -20`
|
|
14
|
+
|
|
15
|
+
**Staged changes summary:**
|
|
16
|
+
!`git diff --cached --stat`
|
|
17
|
+
|
|
18
|
+
**Staged changes detail:**
|
|
19
|
+
!`git diff --cached`
|
|
20
|
+
|
|
21
|
+
**Unstaged files (for reference):**
|
|
22
|
+
!`git status --short`
|
|
23
|
+
|
|
24
|
+
## Your Task
|
|
25
|
+
|
|
26
|
+
1. **Analyze the commit convention** from the recent commit history above
|
|
27
|
+
- Identify the pattern (e.g., `type: message`, `type(scope): message`, `[type] message`, emoji usage, etc.)
|
|
28
|
+
- Note the language used (English/Korean/etc.)
|
|
29
|
+
- Observe the message style (imperative, past tense, etc.)
|
|
30
|
+
|
|
31
|
+
2. **Understand the staged changes**
|
|
32
|
+
- What files were modified/added/deleted?
|
|
33
|
+
- What is the main purpose of these changes?
|
|
34
|
+
- Are there multiple logical changes that should be separate commits?
|
|
35
|
+
|
|
36
|
+
3. **Generate the commit message**
|
|
37
|
+
- Follow the detected convention exactly
|
|
38
|
+
- Keep the subject line concise (50 chars recommended, 72 max)
|
|
39
|
+
- If the changes are complex, suggest a body with bullet points
|
|
40
|
+
|
|
41
|
+
4. **Execute the commit**
|
|
42
|
+
- Run `git commit -m "message"` with the generated message
|
|
43
|
+
- If a body is needed, use the multi-line format
|
|
44
|
+
|
|
45
|
+
## Output Format
|
|
46
|
+
|
|
47
|
+
First, briefly explain:
|
|
48
|
+
- Detected convention pattern
|
|
49
|
+
- Summary of changes
|
|
50
|
+
|
|
51
|
+
Then execute the commit command directly.
|
|
52
|
+
|
|
53
|
+
If there are no staged changes, inform the user and suggest using `git add` first.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "binary-agents",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Claude Code subagents collection with sync CLI tool",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Claude Code subagents and slash commands collection with sync CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/sync.js",
|
|
7
7
|
"bin": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"claude",
|
|
17
17
|
"claude-code",
|
|
18
18
|
"subagents",
|
|
19
|
+
"slash-commands",
|
|
19
20
|
"sync",
|
|
20
21
|
"cli",
|
|
21
22
|
"code-review",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
},
|
|
30
31
|
"files": [
|
|
31
32
|
"agents",
|
|
33
|
+
"commands",
|
|
32
34
|
"bin",
|
|
33
35
|
"src",
|
|
34
36
|
"docs",
|
package/src/sync.js
CHANGED
|
@@ -7,8 +7,9 @@ import ora from 'ora';
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
9
|
|
|
10
|
-
// 모노레포의 agents 디렉토리 경로
|
|
10
|
+
// 모노레포의 agents, commands 디렉토리 경로
|
|
11
11
|
const REPO_AGENTS_DIR = path.join(__dirname, '..', 'agents');
|
|
12
|
+
const REPO_COMMANDS_DIR = path.join(__dirname, '..', 'commands');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* 로컬 agents 디렉토리에서 서브에이전트 파일 목록 가져오기
|
|
@@ -29,6 +30,22 @@ async function fetchSubagentFiles() {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* 로컬 commands 디렉토리에서 슬래시 명령어 파일 목록 가져오기
|
|
35
|
+
*/
|
|
36
|
+
async function fetchCommandFiles() {
|
|
37
|
+
try {
|
|
38
|
+
const files = await fs.readdir(REPO_COMMANDS_DIR);
|
|
39
|
+
|
|
40
|
+
// .md 파일만 필터링
|
|
41
|
+
const commandFiles = files.filter(file => file.endsWith('.md'));
|
|
42
|
+
|
|
43
|
+
return commandFiles;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new Error(`Failed to read commands directory: ${error.message}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
32
49
|
/**
|
|
33
50
|
* YAML frontmatter가 있는지 검증
|
|
34
51
|
*/
|
|
@@ -38,7 +55,7 @@ function validateYamlFrontmatter(content) {
|
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
/**
|
|
41
|
-
* 로컬에서 파일 읽기
|
|
58
|
+
* 로컬에서 에이전트 파일 읽기
|
|
42
59
|
*/
|
|
43
60
|
async function readAgentFile(filename) {
|
|
44
61
|
const filePath = path.join(REPO_AGENTS_DIR, filename);
|
|
@@ -53,28 +70,44 @@ async function readAgentFile(filename) {
|
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
/**
|
|
56
|
-
*
|
|
57
|
-
* @param {boolean} isGlobal - true면 ~/.claude/agents, false면 현재 디렉토리의 .claude/agents
|
|
73
|
+
* 로컬에서 커맨드 파일 읽기
|
|
58
74
|
*/
|
|
59
|
-
async function
|
|
60
|
-
|
|
75
|
+
async function readCommandFile(filename) {
|
|
76
|
+
const filePath = path.join(REPO_COMMANDS_DIR, filename);
|
|
77
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
78
|
+
|
|
79
|
+
// YAML frontmatter 검증
|
|
80
|
+
if (!validateYamlFrontmatter(content)) {
|
|
81
|
+
throw new Error(`Invalid YAML frontmatter in ${filename}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return content;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* .claude 하위 디렉토리 생성
|
|
89
|
+
* @param {string} subdir - 하위 디렉토리 이름 ('agents' 또는 'commands')
|
|
90
|
+
* @param {boolean} isGlobal - true면 ~/.claude/{subdir}, false면 현재 디렉토리의 .claude/{subdir}
|
|
91
|
+
*/
|
|
92
|
+
async function ensureClaudeDirectory(subdir, isGlobal = false) {
|
|
93
|
+
let targetDir;
|
|
61
94
|
|
|
62
95
|
if (isGlobal) {
|
|
63
|
-
// 전역 설치: ~/.claude/
|
|
96
|
+
// 전역 설치: ~/.claude/{subdir}
|
|
64
97
|
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
65
|
-
|
|
98
|
+
targetDir = path.join(homeDir, '.claude', subdir);
|
|
66
99
|
} else {
|
|
67
|
-
// 로컬 설치: 현재 디렉토리의 .claude/
|
|
68
|
-
|
|
100
|
+
// 로컬 설치: 현재 디렉토리의 .claude/{subdir}
|
|
101
|
+
targetDir = path.join(process.cwd(), '.claude', subdir);
|
|
69
102
|
}
|
|
70
103
|
|
|
71
104
|
try {
|
|
72
|
-
await fs.access(
|
|
105
|
+
await fs.access(targetDir);
|
|
73
106
|
} catch {
|
|
74
|
-
await fs.mkdir(
|
|
107
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
75
108
|
}
|
|
76
109
|
|
|
77
|
-
return
|
|
110
|
+
return targetDir;
|
|
78
111
|
}
|
|
79
112
|
|
|
80
113
|
/**
|
|
@@ -87,19 +120,15 @@ async function saveFile(agentsDir, filename, content) {
|
|
|
87
120
|
}
|
|
88
121
|
|
|
89
122
|
/**
|
|
90
|
-
* 서브에이전트 동기화
|
|
123
|
+
* 서브에이전트 동기화
|
|
91
124
|
*/
|
|
92
|
-
|
|
93
|
-
const { filter = null, global = false } = options;
|
|
125
|
+
async function syncAgentsOnly(options = {}) {
|
|
126
|
+
const { filter = null, global: isGlobal = false } = options;
|
|
94
127
|
|
|
95
|
-
console.log(chalk.
|
|
96
|
-
|
|
97
|
-
if (global) {
|
|
98
|
-
console.log(chalk.cyan('📍 Global mode: Installing to ~/.claude/agents\n'));
|
|
99
|
-
}
|
|
128
|
+
console.log(chalk.yellow.bold('\n📦 Syncing Agents...\n'));
|
|
100
129
|
|
|
101
130
|
// 로컬 agents 디렉토리에서 파일 목록 가져오기
|
|
102
|
-
const fetchSpinner = ora('Reading subagent files
|
|
131
|
+
const fetchSpinner = ora('Reading subagent files...').start();
|
|
103
132
|
let allFiles;
|
|
104
133
|
|
|
105
134
|
try {
|
|
@@ -107,7 +136,7 @@ export async function syncSubagents(options = {}) {
|
|
|
107
136
|
fetchSpinner.succeed(chalk.green(`Found ${allFiles.length} subagent files`));
|
|
108
137
|
} catch (error) {
|
|
109
138
|
fetchSpinner.fail(chalk.red(`Failed to read file list: ${error.message}`));
|
|
110
|
-
return { success: false, error: error.message };
|
|
139
|
+
return { success: false, error: error.message, type: 'agents' };
|
|
111
140
|
}
|
|
112
141
|
|
|
113
142
|
// 필터링된 파일 목록
|
|
@@ -122,17 +151,17 @@ export async function syncSubagents(options = {}) {
|
|
|
122
151
|
}
|
|
123
152
|
|
|
124
153
|
// .claude/agents 디렉토리 생성
|
|
125
|
-
const dirMessage =
|
|
154
|
+
const dirMessage = isGlobal ? 'Creating ~/.claude/agents directory...' : 'Creating .claude/agents directory...';
|
|
126
155
|
const dirSpinner = ora(dirMessage).start();
|
|
127
156
|
let agentsDir;
|
|
128
157
|
|
|
129
158
|
try {
|
|
130
|
-
agentsDir = await
|
|
131
|
-
const successMessage =
|
|
159
|
+
agentsDir = await ensureClaudeDirectory('agents', isGlobal);
|
|
160
|
+
const successMessage = isGlobal ? 'Created ~/.claude/agents directory' : 'Created .claude/agents directory';
|
|
132
161
|
dirSpinner.succeed(chalk.green(successMessage));
|
|
133
162
|
} catch (error) {
|
|
134
163
|
dirSpinner.fail(chalk.red(`Failed to create directory: ${error.message}`));
|
|
135
|
-
return { success: false, error: error.message };
|
|
164
|
+
return { success: false, error: error.message, type: 'agents' };
|
|
136
165
|
}
|
|
137
166
|
|
|
138
167
|
// 각 파일 복사
|
|
@@ -145,12 +174,71 @@ export async function syncSubagents(options = {}) {
|
|
|
145
174
|
const fileSpinner = ora(`Copying ${filename}...`).start();
|
|
146
175
|
|
|
147
176
|
try {
|
|
148
|
-
// 로컬 파일 읽기
|
|
149
177
|
const content = await readAgentFile(filename);
|
|
178
|
+
await saveFile(agentsDir, filename, content);
|
|
179
|
+
fileSpinner.succeed(chalk.green(`✓ ${filename}`));
|
|
180
|
+
results.success.push(filename);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
fileSpinner.fail(chalk.red(`✗ ${filename}: ${error.message}`));
|
|
183
|
+
results.failed.push({ filename, error: error.message });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
success: results.failed.length === 0,
|
|
189
|
+
results,
|
|
190
|
+
type: 'agents',
|
|
191
|
+
dir: agentsDir,
|
|
192
|
+
total: filesToSync.length
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* 슬래시 명령어 동기화
|
|
198
|
+
*/
|
|
199
|
+
async function syncCommandsOnly(options = {}) {
|
|
200
|
+
const { global: isGlobal = false } = options;
|
|
201
|
+
|
|
202
|
+
console.log(chalk.yellow.bold('\n⚡ Syncing Commands...\n'));
|
|
203
|
+
|
|
204
|
+
// 로컬 commands 디렉토리에서 파일 목록 가져오기
|
|
205
|
+
const fetchSpinner = ora('Reading command files...').start();
|
|
206
|
+
let allFiles;
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
allFiles = await fetchCommandFiles();
|
|
210
|
+
fetchSpinner.succeed(chalk.green(`Found ${allFiles.length} command files`));
|
|
211
|
+
} catch (error) {
|
|
212
|
+
fetchSpinner.fail(chalk.red(`Failed to read file list: ${error.message}`));
|
|
213
|
+
return { success: false, error: error.message, type: 'commands' };
|
|
214
|
+
}
|
|
150
215
|
|
|
151
|
-
|
|
152
|
-
|
|
216
|
+
// .claude/commands 디렉토리 생성
|
|
217
|
+
const dirMessage = isGlobal ? 'Creating ~/.claude/commands directory...' : 'Creating .claude/commands directory...';
|
|
218
|
+
const dirSpinner = ora(dirMessage).start();
|
|
219
|
+
let commandsDir;
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
commandsDir = await ensureClaudeDirectory('commands', isGlobal);
|
|
223
|
+
const successMessage = isGlobal ? 'Created ~/.claude/commands directory' : 'Created .claude/commands directory';
|
|
224
|
+
dirSpinner.succeed(chalk.green(successMessage));
|
|
225
|
+
} catch (error) {
|
|
226
|
+
dirSpinner.fail(chalk.red(`Failed to create directory: ${error.message}`));
|
|
227
|
+
return { success: false, error: error.message, type: 'commands' };
|
|
228
|
+
}
|
|
153
229
|
|
|
230
|
+
// 각 파일 복사
|
|
231
|
+
const results = {
|
|
232
|
+
success: [],
|
|
233
|
+
failed: []
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
for (const filename of allFiles) {
|
|
237
|
+
const fileSpinner = ora(`Copying ${filename}...`).start();
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const content = await readCommandFile(filename);
|
|
241
|
+
await saveFile(commandsDir, filename, content);
|
|
154
242
|
fileSpinner.succeed(chalk.green(`✓ ${filename}`));
|
|
155
243
|
results.success.push(filename);
|
|
156
244
|
} catch (error) {
|
|
@@ -159,49 +247,108 @@ export async function syncSubagents(options = {}) {
|
|
|
159
247
|
}
|
|
160
248
|
}
|
|
161
249
|
|
|
250
|
+
return {
|
|
251
|
+
success: results.failed.length === 0,
|
|
252
|
+
results,
|
|
253
|
+
type: 'commands',
|
|
254
|
+
dir: commandsDir,
|
|
255
|
+
total: allFiles.length
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* 메인 동기화 함수
|
|
261
|
+
*/
|
|
262
|
+
export async function syncSubagents(options = {}) {
|
|
263
|
+
const { filter = null, global: isGlobal = false, agents = true, commands = true } = options;
|
|
264
|
+
|
|
265
|
+
console.log(chalk.blue.bold('\n🤖 Binary Agents Sync\n'));
|
|
266
|
+
|
|
267
|
+
if (isGlobal) {
|
|
268
|
+
console.log(chalk.cyan('📍 Global mode: Installing to ~/.claude/\n'));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const syncResults = [];
|
|
272
|
+
|
|
273
|
+
// Agents 동기화
|
|
274
|
+
if (agents) {
|
|
275
|
+
const agentResult = await syncAgentsOnly({ filter, global: isGlobal });
|
|
276
|
+
syncResults.push(agentResult);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Commands 동기화
|
|
280
|
+
if (commands) {
|
|
281
|
+
const commandResult = await syncCommandsOnly({ global: isGlobal });
|
|
282
|
+
syncResults.push(commandResult);
|
|
283
|
+
}
|
|
284
|
+
|
|
162
285
|
// 결과 요약
|
|
163
286
|
console.log(chalk.blue.bold('\n📊 Sync Summary\n'));
|
|
164
|
-
console.log(chalk.green(`✓ Successful: ${results.success.length}/${filesToSync.length}`));
|
|
165
287
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
288
|
+
for (const result of syncResults) {
|
|
289
|
+
if (result.error) {
|
|
290
|
+
console.log(chalk.red(`✗ ${result.type}: Failed - ${result.error}`));
|
|
291
|
+
} else {
|
|
292
|
+
const icon = result.type === 'agents' ? '🤖' : '⚡';
|
|
293
|
+
console.log(chalk.green(`${icon} ${result.type}: ${result.results.success.length}/${result.total} successful`));
|
|
294
|
+
|
|
295
|
+
if (result.results.failed.length > 0) {
|
|
296
|
+
console.log(chalk.red(` Failed files:`));
|
|
297
|
+
result.results.failed.forEach(({ filename, error }) => {
|
|
298
|
+
console.log(chalk.red(` - ${filename}: ${error}`));
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.log(chalk.cyan(` 📁 Location: ${result.dir}`));
|
|
303
|
+
}
|
|
172
304
|
}
|
|
173
305
|
|
|
174
|
-
console.log(
|
|
306
|
+
console.log('');
|
|
175
307
|
|
|
308
|
+
const allSuccess = syncResults.every(r => r.success);
|
|
176
309
|
return {
|
|
177
|
-
success:
|
|
178
|
-
results
|
|
310
|
+
success: allSuccess,
|
|
311
|
+
results: syncResults
|
|
179
312
|
};
|
|
180
313
|
}
|
|
181
314
|
|
|
182
315
|
/**
|
|
183
|
-
* 사용 가능한 서브에이전트 목록 표시
|
|
316
|
+
* 사용 가능한 서브에이전트 및 명령어 목록 표시
|
|
184
317
|
*/
|
|
185
318
|
export async function listSubagents() {
|
|
186
|
-
console.log(chalk.blue.bold('\n🤖 Available
|
|
319
|
+
console.log(chalk.blue.bold('\n🤖 Binary Agents - Available Items\n'));
|
|
187
320
|
|
|
188
|
-
|
|
321
|
+
// Agents 목록
|
|
322
|
+
const agentSpinner = ora('Reading subagent files...').start();
|
|
189
323
|
|
|
190
324
|
try {
|
|
191
|
-
const
|
|
192
|
-
|
|
325
|
+
const agentFiles = await fetchSubagentFiles();
|
|
326
|
+
agentSpinner.succeed(chalk.green(`Found ${agentFiles.length} subagent files`));
|
|
193
327
|
|
|
194
|
-
const basic =
|
|
195
|
-
const advanced =
|
|
328
|
+
const basic = agentFiles.filter(f => !f.startsWith('advanced-'));
|
|
329
|
+
const advanced = agentFiles.filter(f => f.startsWith('advanced-'));
|
|
196
330
|
|
|
197
|
-
console.log(chalk.yellow('\
|
|
198
|
-
basic.forEach(f => console.log(chalk.white(` • ${f}`)));
|
|
331
|
+
console.log(chalk.yellow('\n📦 Agents - Basic (Haiku model):'));
|
|
332
|
+
basic.forEach(f => console.log(chalk.white(` • ${f.replace('.md', '')}`)));
|
|
199
333
|
|
|
200
|
-
console.log(chalk.yellow('\
|
|
201
|
-
advanced.forEach(f => console.log(chalk.white(` • ${f}`)));
|
|
334
|
+
console.log(chalk.yellow('\n📦 Agents - Advanced (Opus model):'));
|
|
335
|
+
advanced.forEach(f => console.log(chalk.white(` • ${f.replace('.md', '')}`)));
|
|
336
|
+
} catch (error) {
|
|
337
|
+
agentSpinner.fail(chalk.red(`Failed to read agents: ${error.message}`));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Commands 목록
|
|
341
|
+
const commandSpinner = ora('Reading command files...').start();
|
|
202
342
|
|
|
203
|
-
|
|
343
|
+
try {
|
|
344
|
+
const commandFiles = await fetchCommandFiles();
|
|
345
|
+
commandSpinner.succeed(chalk.green(`Found ${commandFiles.length} command files`));
|
|
346
|
+
|
|
347
|
+
console.log(chalk.yellow('\n⚡ Commands (Slash commands):'));
|
|
348
|
+
commandFiles.forEach(f => console.log(chalk.white(` • /${f.replace('.md', '')}`)));
|
|
204
349
|
} catch (error) {
|
|
205
|
-
|
|
350
|
+
commandSpinner.fail(chalk.red(`Failed to read commands: ${error.message}`));
|
|
206
351
|
}
|
|
352
|
+
|
|
353
|
+
console.log('');
|
|
207
354
|
}
|