claudient 0.1.0 → 0.3.0
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/.claude-plugin/plugin.json +4 -2
- package/README.md +150 -103
- package/guides/agent-orchestration.md +1 -0
- package/guides/token-optimization.md +37 -0
- package/index.json +1745 -0
- package/package.json +5 -3
- package/scripts/build-index.js +139 -0
- package/scripts/cli.js +378 -66
- package/skills/backend/java/de/spring-boot.md +333 -0
- package/skills/backend/java/es/spring-boot.md +333 -0
- package/skills/backend/java/fr/spring-boot.md +333 -0
- package/skills/backend/java/nl/spring-boot.md +333 -0
- package/skills/backend/java/spring-boot.md +331 -0
- package/skills/productivity/caveman.md +70 -0
- package/skills/productivity/de/caveman.md +72 -0
- package/skills/productivity/es/caveman.md +72 -0
- package/skills/productivity/fr/caveman.md +72 -0
- package/skills/productivity/nl/caveman.md +72 -0
package/scripts/cli.js
CHANGED
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
const fs = require('fs')
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const os = require('os')
|
|
6
|
+
const { execSync } = require('child_process')
|
|
6
7
|
|
|
7
8
|
const REPO_ROOT = path.resolve(__dirname, '..')
|
|
8
9
|
const CLAUDE_DIR = path.join(os.homedir(), '.claude')
|
|
9
10
|
const SKILLS_DEST = path.join(CLAUDE_DIR, 'skills')
|
|
11
|
+
const AGENTS_DEST = path.join(CLAUDE_DIR, 'agents')
|
|
12
|
+
const HOOKS_DEST = path.join(CLAUDE_DIR, 'hooks')
|
|
13
|
+
const RULES_DEST = path.join(CLAUDE_DIR, 'rules')
|
|
10
14
|
|
|
11
|
-
const
|
|
15
|
+
const SKILL_CATEGORIES = [
|
|
12
16
|
'backend',
|
|
13
17
|
'devops-infra',
|
|
14
18
|
'data-ml',
|
|
@@ -17,23 +21,42 @@ const VALID_CATEGORIES = [
|
|
|
17
21
|
'ai-engineering',
|
|
18
22
|
]
|
|
19
23
|
|
|
24
|
+
const SUPPORTED_LANGS = ['en', 'fr', 'de', 'nl', 'es']
|
|
25
|
+
|
|
20
26
|
function usage() {
|
|
21
27
|
console.log(`
|
|
22
|
-
claudient — Claude Code knowledge system
|
|
28
|
+
claudient — Claude Code knowledge system
|
|
23
29
|
|
|
24
30
|
Usage:
|
|
25
|
-
npx claudient add
|
|
26
|
-
npx claudient add
|
|
27
|
-
npx claudient
|
|
28
|
-
npx claudient
|
|
31
|
+
npx claudient add skills [category] [--lang <lang>]
|
|
32
|
+
npx claudient add agents
|
|
33
|
+
npx claudient add rules
|
|
34
|
+
npx claudient add hooks
|
|
35
|
+
npx claudient add all [--lang <lang>]
|
|
36
|
+
npx claudient remove skills [category]
|
|
37
|
+
npx claudient remove agents
|
|
38
|
+
npx claudient remove rules
|
|
39
|
+
npx claudient update
|
|
40
|
+
npx claudient list [skills|agents|rules|hooks]
|
|
41
|
+
npx claudient help
|
|
42
|
+
|
|
43
|
+
Skill categories:
|
|
44
|
+
${SKILL_CATEGORIES.join('\n ')}
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
Languages (--lang):
|
|
47
|
+
en (default), fr, de, nl, es
|
|
32
48
|
|
|
33
49
|
Examples:
|
|
34
|
-
npx claudient add
|
|
35
|
-
npx claudient add
|
|
36
|
-
npx claudient add
|
|
50
|
+
npx claudient add skills
|
|
51
|
+
npx claudient add skills backend
|
|
52
|
+
npx claudient add skills backend --lang fr
|
|
53
|
+
npx claudient add agents
|
|
54
|
+
npx claudient add rules
|
|
55
|
+
npx claudient add hooks
|
|
56
|
+
npx claudient add all --lang de
|
|
57
|
+
npx claudient remove skills backend
|
|
58
|
+
npx claudient update
|
|
59
|
+
npx claudient list agents
|
|
37
60
|
`)
|
|
38
61
|
}
|
|
39
62
|
|
|
@@ -42,111 +65,400 @@ function checkClaudeInstalled() {
|
|
|
42
65
|
console.error(`
|
|
43
66
|
Error: ~/.claude directory not found.
|
|
44
67
|
|
|
45
|
-
Claude Code must be installed first
|
|
68
|
+
Claude Code must be installed first:
|
|
46
69
|
https://claude.ai/code
|
|
47
|
-
|
|
48
|
-
Or install the CLI:
|
|
49
|
-
npm install -g @anthropic-ai/claude-code
|
|
50
70
|
`)
|
|
51
71
|
process.exit(1)
|
|
52
72
|
}
|
|
53
73
|
}
|
|
54
74
|
|
|
55
|
-
function
|
|
75
|
+
function parseArgs(args) {
|
|
76
|
+
const flags = {}
|
|
77
|
+
const positional = []
|
|
78
|
+
for (let i = 0; i < args.length; i++) {
|
|
79
|
+
if (args[i] === '--lang' && args[i + 1]) {
|
|
80
|
+
flags.lang = args[++i]
|
|
81
|
+
} else if (!args[i].startsWith('--')) {
|
|
82
|
+
positional.push(args[i])
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { flags, positional }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function copyDir(src, dest, label = '') {
|
|
56
89
|
if (!fs.existsSync(src)) return 0
|
|
57
90
|
fs.mkdirSync(dest, { recursive: true })
|
|
58
|
-
|
|
59
91
|
let count = 0
|
|
60
|
-
const
|
|
61
|
-
for (const entry of entries) {
|
|
92
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
62
93
|
const srcPath = path.join(src, entry.name)
|
|
63
94
|
const destPath = path.join(dest, entry.name)
|
|
64
95
|
if (entry.isDirectory()) {
|
|
65
|
-
count += copyDir(srcPath, destPath,
|
|
96
|
+
count += copyDir(srcPath, destPath, label + entry.name + '/')
|
|
66
97
|
} else if (entry.name.endsWith('.md')) {
|
|
67
98
|
fs.copyFileSync(srcPath, destPath)
|
|
68
|
-
console.log(` + ${
|
|
99
|
+
console.log(` + ${label}${entry.name}`)
|
|
69
100
|
count++
|
|
70
101
|
}
|
|
71
102
|
}
|
|
72
103
|
return count
|
|
73
104
|
}
|
|
74
105
|
|
|
75
|
-
function
|
|
106
|
+
function removeDir(dir, label) {
|
|
107
|
+
if (!fs.existsSync(dir)) {
|
|
108
|
+
console.log(` ~ ${label} (not installed)`)
|
|
109
|
+
return 0
|
|
110
|
+
}
|
|
111
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
112
|
+
console.log(` - ${label} (removed)`)
|
|
113
|
+
return 1
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function addSkills(category, lang) {
|
|
76
117
|
checkClaudeInstalled()
|
|
118
|
+
fs.mkdirSync(SKILLS_DEST, { recursive: true })
|
|
119
|
+
|
|
120
|
+
const installCategory = (cat) => {
|
|
121
|
+
const src = path.join(REPO_ROOT, 'skills', cat)
|
|
122
|
+
if (!fs.existsSync(src)) return 0
|
|
77
123
|
|
|
78
|
-
|
|
79
|
-
let total = 0
|
|
80
|
-
console.log(`Installing all Claudient skills to ${SKILLS_DEST}...\n`)
|
|
81
|
-
for (const cat of VALID_CATEGORIES) {
|
|
82
|
-
const src = path.join(REPO_ROOT, 'skills', cat)
|
|
124
|
+
if (!lang || lang === 'en') {
|
|
83
125
|
const dest = path.join(SKILLS_DEST, cat)
|
|
84
126
|
const count = copyDir(src, dest, cat + '/')
|
|
85
127
|
if (count > 0) console.log(` ✓ ${cat} (${count} files)`)
|
|
128
|
+
return count
|
|
86
129
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
130
|
+
|
|
131
|
+
// Language-aware: copy only matching lang files, renaming to drop the lang subdir
|
|
132
|
+
let count = 0
|
|
133
|
+
count += copyLangFiles(src, path.join(SKILLS_DEST, cat), lang, cat + '/')
|
|
134
|
+
if (count > 0) console.log(` ✓ ${cat} [${lang}] (${count} files)`)
|
|
135
|
+
return count
|
|
90
136
|
}
|
|
91
137
|
|
|
92
|
-
if (!
|
|
93
|
-
console.
|
|
94
|
-
|
|
95
|
-
|
|
138
|
+
if (!category || category === 'all') {
|
|
139
|
+
console.log(`Installing all skills${lang && lang !== 'en' ? ` [${lang}]` : ''}...\n`)
|
|
140
|
+
for (const cat of SKILL_CATEGORIES) installCategory(cat)
|
|
141
|
+
} else {
|
|
142
|
+
if (!SKILL_CATEGORIES.includes(category)) {
|
|
143
|
+
console.error(`Unknown category: "${category}". Valid: ${SKILL_CATEGORIES.join(', ')}`)
|
|
144
|
+
process.exit(1)
|
|
145
|
+
}
|
|
146
|
+
console.log(`Installing ${category} skills${lang && lang !== 'en' ? ` [${lang}]` : ''}...\n`)
|
|
147
|
+
installCategory(category)
|
|
96
148
|
}
|
|
97
149
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
150
|
+
console.log(`\nDone. Skills installed to: ${SKILLS_DEST}`)
|
|
151
|
+
console.log('Restart Claude Code to activate.')
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Copies lang-specific files: for each file.md, checks if lang/file.md exists
|
|
155
|
+
// and copies it (as file.md) alongside the English version.
|
|
156
|
+
function copyLangFiles(src, dest, lang, prefix) {
|
|
157
|
+
if (!fs.existsSync(src)) return 0
|
|
158
|
+
fs.mkdirSync(dest, { recursive: true })
|
|
159
|
+
let count = 0
|
|
160
|
+
|
|
161
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
162
|
+
const srcPath = path.join(src, entry.name)
|
|
163
|
+
if (entry.isDirectory()) {
|
|
164
|
+
if (entry.name === lang) continue // skip the lang subdir itself
|
|
165
|
+
if (SUPPORTED_LANGS.includes(entry.name)) continue // skip other lang subdirs
|
|
166
|
+
count += copyLangFiles(srcPath, path.join(dest, entry.name), lang, prefix + entry.name + '/')
|
|
167
|
+
} else if (entry.name.endsWith('.md')) {
|
|
168
|
+
// Check for translated version
|
|
169
|
+
const translated = path.join(src, lang, entry.name)
|
|
170
|
+
const fileSrc = fs.existsSync(translated) ? translated : srcPath
|
|
171
|
+
fs.copyFileSync(fileSrc, path.join(dest, entry.name))
|
|
172
|
+
const tag = fs.existsSync(translated) ? `[${lang}]` : '[en]'
|
|
173
|
+
console.log(` + ${prefix}${entry.name} ${tag}`)
|
|
174
|
+
count++
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return count
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function addAgents() {
|
|
181
|
+
checkClaudeInstalled()
|
|
182
|
+
fs.mkdirSync(AGENTS_DEST, { recursive: true })
|
|
183
|
+
console.log(`Installing agents to ${AGENTS_DEST}...\n`)
|
|
184
|
+
const src = path.join(REPO_ROOT, 'agents')
|
|
185
|
+
let total = 0
|
|
186
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
187
|
+
if (!entry.isDirectory()) continue
|
|
188
|
+
if (SUPPORTED_LANGS.includes(entry.name)) continue
|
|
189
|
+
const count = copyDir(path.join(src, entry.name), path.join(AGENTS_DEST, entry.name), entry.name + '/')
|
|
190
|
+
if (count > 0) console.log(` ✓ ${entry.name} (${count} agents)`)
|
|
191
|
+
total += count
|
|
192
|
+
}
|
|
193
|
+
console.log(`\nInstalled ${total} agent file(s) to: ${AGENTS_DEST}`)
|
|
194
|
+
console.log('Restart Claude Code to activate.')
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function addRules() {
|
|
198
|
+
checkClaudeInstalled()
|
|
199
|
+
const rulesDir = path.join(REPO_ROOT, 'rules')
|
|
200
|
+
console.log('\nAvailable rules:\n')
|
|
201
|
+
|
|
202
|
+
// Print all rules with their content paths
|
|
203
|
+
let i = 1
|
|
204
|
+
const rulePaths = []
|
|
205
|
+
for (const cat of fs.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
206
|
+
if (!cat.isDirectory() || SUPPORTED_LANGS.includes(cat.name)) continue
|
|
207
|
+
const catDir = path.join(rulesDir, cat.name)
|
|
208
|
+
for (const f of fs.readdirSync(catDir, { withFileTypes: true })) {
|
|
209
|
+
if (!f.isFile() || !f.name.endsWith('.md')) continue
|
|
210
|
+
if (SUPPORTED_LANGS.some(l => f.name.startsWith(l + '/'))) continue
|
|
211
|
+
const filepath = path.join(catDir, f.name)
|
|
212
|
+
const relPath = `rules/${cat.name}/${f.name}`
|
|
213
|
+
rulePaths.push({ label: relPath, filepath })
|
|
214
|
+
console.log(` ${i++}. ${relPath}`)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
console.log(`
|
|
219
|
+
Rules are added to your project's CLAUDE.md file.
|
|
220
|
+
Copy the rule content you need into your project's CLAUDE.md.
|
|
221
|
+
|
|
222
|
+
Rule files are located in:
|
|
223
|
+
${rulesDir}
|
|
224
|
+
|
|
225
|
+
Quick copy all rules into a new CLAUDE.md:`)
|
|
226
|
+
console.log(` npx claudient add rules --write`)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function addRulesWrite() {
|
|
230
|
+
checkClaudeInstalled()
|
|
231
|
+
const rulesDir = path.join(REPO_ROOT, 'rules')
|
|
232
|
+
const claudeMd = path.join(process.cwd(), 'CLAUDE.md')
|
|
233
|
+
const exists = fs.existsSync(claudeMd)
|
|
234
|
+
|
|
235
|
+
let content = exists ? fs.readFileSync(claudeMd, 'utf-8') : '# Project Rules\n\n'
|
|
236
|
+
content += '\n\n---\n<!-- Claudient Rules -->\n\n'
|
|
237
|
+
|
|
238
|
+
for (const cat of fs.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
239
|
+
if (!cat.isDirectory() || SUPPORTED_LANGS.includes(cat.name)) continue
|
|
240
|
+
const catDir = path.join(rulesDir, cat.name)
|
|
241
|
+
for (const f of fs.readdirSync(catDir).filter(n => n.endsWith('.md'))) {
|
|
242
|
+
const ruleContent = fs.readFileSync(path.join(catDir, f), 'utf-8')
|
|
243
|
+
content += ruleContent + '\n\n'
|
|
244
|
+
console.log(` + rules/${cat.name}/${f}`)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
fs.writeFileSync(claudeMd, content)
|
|
249
|
+
console.log(`\n${exists ? 'Updated' : 'Created'} CLAUDE.md with all rules.`)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function addHooks() {
|
|
253
|
+
checkClaudeInstalled()
|
|
254
|
+
fs.mkdirSync(HOOKS_DEST, { recursive: true })
|
|
255
|
+
const hooksDir = path.join(REPO_ROOT, 'hooks')
|
|
256
|
+
console.log(`Installing hooks to ${HOOKS_DEST}...\n`)
|
|
257
|
+
let total = 0
|
|
258
|
+
|
|
259
|
+
for (const cat of fs.readdirSync(hooksDir, { withFileTypes: true })) {
|
|
260
|
+
if (!cat.isDirectory()) continue
|
|
261
|
+
const catDir = path.join(hooksDir, cat.name)
|
|
262
|
+
const destCatDir = path.join(HOOKS_DEST, cat.name)
|
|
263
|
+
fs.mkdirSync(destCatDir, { recursive: true })
|
|
264
|
+
|
|
265
|
+
for (const f of fs.readdirSync(catDir, { withFileTypes: true })) {
|
|
266
|
+
if (!f.isFile() || !f.name.endsWith('.sh')) continue
|
|
267
|
+
fs.copyFileSync(path.join(catDir, f.name), path.join(destCatDir, f.name))
|
|
268
|
+
fs.chmodSync(path.join(destCatDir, f.name), '755')
|
|
269
|
+
console.log(` + ${cat.name}/${f.name}`)
|
|
270
|
+
total++
|
|
271
|
+
}
|
|
102
272
|
}
|
|
103
273
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
274
|
+
console.log(`
|
|
275
|
+
Installed ${total} hook script(s) to: ${HOOKS_DEST}
|
|
276
|
+
|
|
277
|
+
Next step — add hooks to your settings.json:
|
|
278
|
+
~/.claude/settings.json (global)
|
|
279
|
+
.claude/settings.json (project)
|
|
280
|
+
|
|
281
|
+
See hook documentation at:
|
|
282
|
+
${path.join(REPO_ROOT, 'hooks')}
|
|
283
|
+
|
|
284
|
+
Or browse online: https://github.com/Claudient/Claudient/tree/main/hooks
|
|
285
|
+
`)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function removeCommand(type, category) {
|
|
289
|
+
checkClaudeInstalled()
|
|
290
|
+
switch (type) {
|
|
291
|
+
case 'skills':
|
|
292
|
+
if (category) {
|
|
293
|
+
console.log(`Removing ${category} skills...\n`)
|
|
294
|
+
removeDir(path.join(SKILLS_DEST, category), category)
|
|
295
|
+
} else {
|
|
296
|
+
console.log('Removing all skills...\n')
|
|
297
|
+
for (const cat of SKILL_CATEGORIES) {
|
|
298
|
+
removeDir(path.join(SKILLS_DEST, cat), cat)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
break
|
|
302
|
+
case 'agents':
|
|
303
|
+
console.log('Removing agents...\n')
|
|
304
|
+
removeDir(AGENTS_DEST, 'agents')
|
|
305
|
+
break
|
|
306
|
+
case 'rules':
|
|
307
|
+
console.log('Removing rules...\n')
|
|
308
|
+
removeDir(RULES_DEST, 'rules')
|
|
309
|
+
break
|
|
310
|
+
case 'hooks':
|
|
311
|
+
console.log('Removing hooks...\n')
|
|
312
|
+
removeDir(HOOKS_DEST, 'hooks')
|
|
313
|
+
break
|
|
314
|
+
default:
|
|
315
|
+
console.error(`Unknown type: "${type}". Use: skills, agents, rules, hooks`)
|
|
316
|
+
process.exit(1)
|
|
317
|
+
}
|
|
318
|
+
console.log('\nDone. Restart Claude Code to apply.')
|
|
109
319
|
}
|
|
110
320
|
|
|
111
|
-
function
|
|
112
|
-
console.log('
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
console.log(` ${f}`)
|
|
321
|
+
function updateCommand() {
|
|
322
|
+
console.log('Checking for updates...\n')
|
|
323
|
+
try {
|
|
324
|
+
const current = require(path.join(REPO_ROOT, 'package.json')).version
|
|
325
|
+
const latest = execSync('npm view claudient version', { encoding: 'utf-8' }).trim()
|
|
326
|
+
if (current === latest) {
|
|
327
|
+
console.log(`Already up to date (v${current}).`)
|
|
328
|
+
return
|
|
120
329
|
}
|
|
330
|
+
console.log(`Update available: v${current} → v${latest}`)
|
|
331
|
+
console.log('Run: npm install -g claudient or npx claudient@latest add all')
|
|
332
|
+
} catch {
|
|
333
|
+
console.log('Could not check for updates. Visit: https://www.npmjs.com/package/claudient')
|
|
121
334
|
}
|
|
122
335
|
}
|
|
123
336
|
|
|
124
|
-
function
|
|
337
|
+
function listCommand(type) {
|
|
338
|
+
const listSkills = () => {
|
|
339
|
+
console.log('Skills:\n')
|
|
340
|
+
for (const cat of SKILL_CATEGORIES) {
|
|
341
|
+
const catDir = path.join(REPO_ROOT, 'skills', cat)
|
|
342
|
+
if (!fs.existsSync(catDir)) continue
|
|
343
|
+
const files = getFiles(catDir).filter(f => !SUPPORTED_LANGS.some(l => f.includes(`/${l}/`)))
|
|
344
|
+
console.log(` ${cat}/ (${files.length})`)
|
|
345
|
+
for (const f of files) console.log(` ${f}`)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const listAgents = () => {
|
|
350
|
+
console.log('Agents:\n')
|
|
351
|
+
const agentsDir = path.join(REPO_ROOT, 'agents')
|
|
352
|
+
for (const cat of fs.readdirSync(agentsDir, { withFileTypes: true })) {
|
|
353
|
+
if (!cat.isDirectory() || SUPPORTED_LANGS.includes(cat.name)) continue
|
|
354
|
+
const files = getFiles(path.join(agentsDir, cat.name)).filter(f => !SUPPORTED_LANGS.some(l => f.includes(`/${l}/`)))
|
|
355
|
+
console.log(` ${cat.name}/ (${files.length})`)
|
|
356
|
+
for (const f of files) console.log(` ${f}`)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const listHooks = () => {
|
|
361
|
+
console.log('Hooks:\n')
|
|
362
|
+
const hooksDir = path.join(REPO_ROOT, 'hooks')
|
|
363
|
+
for (const cat of fs.readdirSync(hooksDir, { withFileTypes: true })) {
|
|
364
|
+
if (!cat.isDirectory()) continue
|
|
365
|
+
const files = fs.readdirSync(path.join(hooksDir, cat.name)).filter(f => f.endsWith('.sh'))
|
|
366
|
+
console.log(` ${cat.name}/ (${files.length})`)
|
|
367
|
+
for (const f of files) console.log(` ${f}`)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const listRules = () => {
|
|
372
|
+
console.log('Rules:\n')
|
|
373
|
+
const rulesDir = path.join(REPO_ROOT, 'rules')
|
|
374
|
+
for (const cat of fs.readdirSync(rulesDir, { withFileTypes: true })) {
|
|
375
|
+
if (!cat.isDirectory() || SUPPORTED_LANGS.includes(cat.name)) continue
|
|
376
|
+
const files = fs.readdirSync(path.join(rulesDir, cat.name)).filter(f => f.endsWith('.md'))
|
|
377
|
+
console.log(` ${cat.name}/ (${files.length})`)
|
|
378
|
+
for (const f of files) console.log(` ${f}`)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
switch (type) {
|
|
383
|
+
case 'agents': listAgents(); break
|
|
384
|
+
case 'hooks': listHooks(); break
|
|
385
|
+
case 'rules': listRules(); break
|
|
386
|
+
case 'skills':
|
|
387
|
+
default:
|
|
388
|
+
if (!type) {
|
|
389
|
+
listSkills(); console.log(); listAgents(); console.log(); listRules(); console.log(); listHooks()
|
|
390
|
+
} else {
|
|
391
|
+
listSkills()
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function getFiles(dir, prefix = '') {
|
|
125
397
|
const results = []
|
|
126
398
|
if (!fs.existsSync(dir)) return results
|
|
127
399
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
128
400
|
const p = path.join(dir, entry.name)
|
|
129
|
-
if (entry.isDirectory())
|
|
130
|
-
|
|
131
|
-
} else if (entry.name.endsWith('.md')) {
|
|
132
|
-
results.push(prefix + entry.name)
|
|
133
|
-
}
|
|
401
|
+
if (entry.isDirectory()) results.push(...getFiles(p, prefix + entry.name + '/'))
|
|
402
|
+
else if (entry.name.endsWith('.md')) results.push(prefix + entry.name)
|
|
134
403
|
}
|
|
135
404
|
return results
|
|
136
405
|
}
|
|
137
406
|
|
|
138
|
-
|
|
407
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
408
|
+
|
|
409
|
+
const [, , command, ...rawArgs] = process.argv
|
|
410
|
+
const { flags, positional } = parseArgs(rawArgs)
|
|
411
|
+
const lang = flags.lang && SUPPORTED_LANGS.includes(flags.lang) ? flags.lang : null
|
|
412
|
+
|
|
413
|
+
if (lang && !SUPPORTED_LANGS.includes(lang)) {
|
|
414
|
+
console.error(`Unknown language: "${flags.lang}". Supported: ${SUPPORTED_LANGS.join(', ')}`)
|
|
415
|
+
process.exit(1)
|
|
416
|
+
}
|
|
139
417
|
|
|
140
418
|
switch (command) {
|
|
141
|
-
case 'add':
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
419
|
+
case 'add': {
|
|
420
|
+
const type = positional[0]
|
|
421
|
+
const arg2 = positional[1]
|
|
422
|
+
switch (type) {
|
|
423
|
+
case 'skills': addSkills(arg2 || null, lang); break
|
|
424
|
+
case 'agents': addAgents(); break
|
|
425
|
+
case 'rules':
|
|
426
|
+
if (flags.write) addRulesWrite()
|
|
427
|
+
else addRules()
|
|
428
|
+
break
|
|
429
|
+
case 'hooks': addHooks(); break
|
|
430
|
+
case 'all':
|
|
431
|
+
addSkills(null, lang)
|
|
432
|
+
addAgents()
|
|
433
|
+
addHooks()
|
|
434
|
+
break
|
|
435
|
+
case undefined:
|
|
436
|
+
console.error('Usage: claudient add <skills|agents|rules|hooks|all>')
|
|
437
|
+
process.exit(1)
|
|
438
|
+
break
|
|
439
|
+
default:
|
|
440
|
+
// Backward compat: claudient add backend (old syntax)
|
|
441
|
+
if (SKILL_CATEGORIES.includes(type)) {
|
|
442
|
+
addSkills(type, lang)
|
|
443
|
+
} else {
|
|
444
|
+
console.error(`Unknown type: "${type}". Use: skills, agents, rules, hooks, all`)
|
|
445
|
+
process.exit(1)
|
|
446
|
+
}
|
|
145
447
|
}
|
|
146
|
-
|
|
448
|
+
break
|
|
449
|
+
}
|
|
450
|
+
case 'remove': {
|
|
451
|
+
const type = positional[0]
|
|
452
|
+
const arg2 = positional[1]
|
|
453
|
+
if (!type) { console.error('Usage: claudient remove <skills|agents|rules|hooks>'); process.exit(1) }
|
|
454
|
+
removeCommand(type, arg2)
|
|
455
|
+
break
|
|
456
|
+
}
|
|
457
|
+
case 'update':
|
|
458
|
+
updateCommand()
|
|
147
459
|
break
|
|
148
460
|
case 'list':
|
|
149
|
-
|
|
461
|
+
listCommand(positional[0])
|
|
150
462
|
break
|
|
151
463
|
case 'help':
|
|
152
464
|
case '--help':
|
|
@@ -155,7 +467,7 @@ switch (command) {
|
|
|
155
467
|
usage()
|
|
156
468
|
break
|
|
157
469
|
default:
|
|
158
|
-
console.error(`Unknown command: ${command}`)
|
|
470
|
+
console.error(`Unknown command: "${command}"`)
|
|
159
471
|
usage()
|
|
160
472
|
process.exit(1)
|
|
161
473
|
}
|