@take-out/cli 0.1.45 → 0.1.46
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/dist/cjs/commands/skills.cjs +37 -133
- package/dist/cjs/commands/skills.js +32 -103
- package/dist/cjs/commands/skills.js.map +2 -2
- package/dist/esm/commands/skills.js +33 -108
- package/dist/esm/commands/skills.js.map +1 -1
- package/dist/esm/commands/skills.mjs +37 -133
- package/dist/esm/commands/skills.mjs.map +1 -1
- package/package.json +4 -4
- package/src/commands/skills.ts +45 -222
- package/types/commands/skills.d.ts.map +1 -1
package/src/commands/skills.ts
CHANGED
|
@@ -8,211 +8,19 @@ import {
|
|
|
8
8
|
mkdirSync,
|
|
9
9
|
readdirSync,
|
|
10
10
|
readFileSync,
|
|
11
|
+
readlinkSync,
|
|
11
12
|
rmSync,
|
|
13
|
+
rmdirSync,
|
|
12
14
|
symlinkSync,
|
|
13
15
|
unlinkSync,
|
|
14
16
|
writeFileSync,
|
|
15
17
|
} from 'node:fs'
|
|
16
18
|
import { createRequire } from 'node:module'
|
|
17
|
-
import { dirname, join, relative } from 'node:path'
|
|
18
|
-
import { fileURLToPath } from 'node:url'
|
|
19
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
19
20
|
|
|
20
21
|
import { defineCommand } from 'citty'
|
|
21
22
|
import pc from 'picocolors'
|
|
22
23
|
|
|
23
|
-
import {
|
|
24
|
-
type ScriptMetadata,
|
|
25
|
-
discoverScripts,
|
|
26
|
-
getAllScriptMetadata,
|
|
27
|
-
getLocalScriptsDir,
|
|
28
|
-
} from '../utils/script-utils'
|
|
29
|
-
|
|
30
|
-
// --- shared helpers ---
|
|
31
|
-
|
|
32
|
-
const BUILTIN_COMMANDS: Array<{ name: string; description: string }> = [
|
|
33
|
-
{ name: 'onboard', description: 'setup wizard for new projects' },
|
|
34
|
-
{ name: 'docs', description: 'view documentation' },
|
|
35
|
-
{ name: 'env:setup', description: 'setup environment variables' },
|
|
36
|
-
{ name: 'run', description: 'run scripts in parallel' },
|
|
37
|
-
{ name: 'script', description: 'manage and run scripts' },
|
|
38
|
-
{ name: 'sync', description: 'sync fork with upstream takeout' },
|
|
39
|
-
{ name: 'changed', description: 'show changes since last sync' },
|
|
40
|
-
{ name: 'skills', description: 'manage claude code skills' },
|
|
41
|
-
{ name: 'completion', description: 'shell completion setup' },
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
function findScriptsPackageRoot(): string | null {
|
|
45
|
-
try {
|
|
46
|
-
const resolved = import.meta.resolve('@take-out/scripts/package.json')
|
|
47
|
-
const packageJsonPath = fileURLToPath(new URL(resolved))
|
|
48
|
-
return join(packageJsonPath, '..', 'src')
|
|
49
|
-
} catch {
|
|
50
|
-
return null
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// --- summary skill generation ---
|
|
55
|
-
|
|
56
|
-
function buildSummaryDescription(
|
|
57
|
-
localScripts: Map<string, string>,
|
|
58
|
-
builtInScripts: Map<string, string>
|
|
59
|
-
): string {
|
|
60
|
-
const categories = new Set<string>()
|
|
61
|
-
const keywords = new Set<string>()
|
|
62
|
-
|
|
63
|
-
for (const [name] of [...localScripts, ...builtInScripts]) {
|
|
64
|
-
keywords.add(name)
|
|
65
|
-
if (name.includes('/')) {
|
|
66
|
-
categories.add(name.split('/')[0]!)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
for (const cmd of BUILTIN_COMMANDS) {
|
|
71
|
-
keywords.add(cmd.name)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const categoryList = [...categories].sort().join(', ')
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
`CLI scripts and commands reference for the tko (takeout) CLI. ` +
|
|
78
|
-
`Use when the user asks to run scripts, manage the project, or needs to know what commands are available. ` +
|
|
79
|
-
`tko, takeout, CLI, scripts, commands, bun tko, project tasks, automation, ` +
|
|
80
|
-
`${categoryList}, ${[...keywords].sort().join(', ')}`
|
|
81
|
-
).slice(0, 2048)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function buildSummaryContent(
|
|
85
|
-
localScripts: Map<string, string>,
|
|
86
|
-
builtInScripts: Map<string, string>,
|
|
87
|
-
metadata: Map<string, ScriptMetadata>
|
|
88
|
-
): string {
|
|
89
|
-
const description = buildSummaryDescription(localScripts, builtInScripts)
|
|
90
|
-
|
|
91
|
-
const lines: string[] = []
|
|
92
|
-
lines.push('---')
|
|
93
|
-
lines.push('name: tko-scripts')
|
|
94
|
-
lines.push(`description: ${description}`)
|
|
95
|
-
lines.push('---')
|
|
96
|
-
lines.push('')
|
|
97
|
-
lines.push('# tko CLI - scripts & commands')
|
|
98
|
-
lines.push('')
|
|
99
|
-
lines.push('run with `bun tko <command>` or `bun tko <script-name>`.')
|
|
100
|
-
lines.push('')
|
|
101
|
-
|
|
102
|
-
// built-in commands
|
|
103
|
-
lines.push('## built-in commands')
|
|
104
|
-
lines.push('')
|
|
105
|
-
for (const cmd of BUILTIN_COMMANDS) {
|
|
106
|
-
lines.push(` ${cmd.name} - ${cmd.description}`)
|
|
107
|
-
}
|
|
108
|
-
lines.push('')
|
|
109
|
-
|
|
110
|
-
// helper to group and format scripts
|
|
111
|
-
const formatSection = (title: string, scripts: Map<string, string>) => {
|
|
112
|
-
if (scripts.size === 0) return
|
|
113
|
-
|
|
114
|
-
const categories = new Map<string, Array<string>>()
|
|
115
|
-
const rootScripts: string[] = []
|
|
116
|
-
|
|
117
|
-
for (const [name] of scripts) {
|
|
118
|
-
if (name.includes('/')) {
|
|
119
|
-
const category = name.split('/')[0]!
|
|
120
|
-
if (!categories.has(category)) {
|
|
121
|
-
categories.set(category, [])
|
|
122
|
-
}
|
|
123
|
-
categories.get(category)!.push(name)
|
|
124
|
-
} else {
|
|
125
|
-
rootScripts.push(name)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
lines.push(`## ${title}`)
|
|
130
|
-
lines.push('')
|
|
131
|
-
|
|
132
|
-
for (const name of rootScripts) {
|
|
133
|
-
const meta = metadata.get(name)
|
|
134
|
-
const desc = meta?.description ? ` - ${meta.description}` : ''
|
|
135
|
-
const args = meta?.args?.length ? ` [${meta.args.join(', ')}]` : ''
|
|
136
|
-
lines.push(` ${name}${desc}${args}`)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
for (const [category, categoryScripts] of categories) {
|
|
140
|
-
lines.push('')
|
|
141
|
-
lines.push(` ${category}/`)
|
|
142
|
-
for (const name of categoryScripts) {
|
|
143
|
-
const shortName = name.substring(category.length + 1)
|
|
144
|
-
const meta = metadata.get(name)
|
|
145
|
-
const desc = meta?.description ? ` - ${meta.description}` : ''
|
|
146
|
-
const args = meta?.args?.length ? ` [${meta.args.join(', ')}]` : ''
|
|
147
|
-
lines.push(` ${shortName}${desc}${args}`)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
lines.push('')
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
formatSection('local scripts', localScripts)
|
|
155
|
-
formatSection('built-in scripts', builtInScripts)
|
|
156
|
-
|
|
157
|
-
// usage
|
|
158
|
-
lines.push('## usage')
|
|
159
|
-
lines.push('')
|
|
160
|
-
lines.push('```bash')
|
|
161
|
-
lines.push('bun tko <command> # run a built-in command')
|
|
162
|
-
lines.push('bun tko <script-name> # execute direct script')
|
|
163
|
-
lines.push(
|
|
164
|
-
'bun tko <group> <script> # execute nested script (e.g. bun tko aws health)'
|
|
165
|
-
)
|
|
166
|
-
lines.push('bun tko run s1 s2 s3 # run multiple scripts in parallel')
|
|
167
|
-
lines.push('bun tko script new <path> # create a new script')
|
|
168
|
-
lines.push('```')
|
|
169
|
-
lines.push('')
|
|
170
|
-
|
|
171
|
-
return lines.join('\n')
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async function generateSummary(cwd: string): Promise<boolean> {
|
|
175
|
-
const skillsDir = join(cwd, '.claude', 'skills')
|
|
176
|
-
const skillName = 'tko-scripts'
|
|
177
|
-
const skillDir = join(skillsDir, skillName)
|
|
178
|
-
const skillFile = join(skillDir, 'SKILL.md')
|
|
179
|
-
|
|
180
|
-
// discover all scripts
|
|
181
|
-
const localScripts = discoverScripts(getLocalScriptsDir())
|
|
182
|
-
const builtInDir = findScriptsPackageRoot()
|
|
183
|
-
const builtInScripts = builtInDir ? discoverScripts(builtInDir) : new Map()
|
|
184
|
-
|
|
185
|
-
const allScripts = new Map([...localScripts, ...builtInScripts])
|
|
186
|
-
const metadata = await getAllScriptMetadata(allScripts)
|
|
187
|
-
|
|
188
|
-
const totalScripts = localScripts.size + builtInScripts.size
|
|
189
|
-
console.info(
|
|
190
|
-
pc.dim(
|
|
191
|
-
`found ${totalScripts} scripts (${localScripts.size} local, ${builtInScripts.size} built-in) + ${BUILTIN_COMMANDS.length} commands`
|
|
192
|
-
)
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
const content = buildSummaryContent(localScripts, builtInScripts, metadata)
|
|
196
|
-
|
|
197
|
-
// check if unchanged
|
|
198
|
-
try {
|
|
199
|
-
const existing = readFileSync(skillFile, 'utf-8')
|
|
200
|
-
if (existing === content) {
|
|
201
|
-
console.info(` ${pc.dim('tko-scripts')} ${pc.dim('unchanged')}`)
|
|
202
|
-
return false
|
|
203
|
-
}
|
|
204
|
-
} catch {
|
|
205
|
-
// doesn't exist yet
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (!existsSync(skillDir)) {
|
|
209
|
-
mkdirSync(skillDir, { recursive: true })
|
|
210
|
-
}
|
|
211
|
-
writeFileSync(skillFile, content)
|
|
212
|
-
console.info(` ${pc.green('✓')} tko-scripts`)
|
|
213
|
-
return true
|
|
214
|
-
}
|
|
215
|
-
|
|
216
24
|
// --- doc skills generation ---
|
|
217
25
|
|
|
218
26
|
const require = createRequire(import.meta.url)
|
|
@@ -312,17 +120,18 @@ function collectAllDocs(
|
|
|
312
120
|
async function generateDocSkills(
|
|
313
121
|
cwd: string,
|
|
314
122
|
clean: boolean
|
|
315
|
-
): Promise<{ symlinked: number; generated: number; unchanged: number }> {
|
|
123
|
+
): Promise<{ symlinked: number; generated: number; unchanged: number; removed: number }> {
|
|
316
124
|
const skillsDir = join(cwd, '.claude', 'skills')
|
|
317
125
|
const docs = collectAllDocs(cwd)
|
|
126
|
+
const localDocsDir = join(cwd, 'docs')
|
|
127
|
+
const expectedSkillNames = new Set<string>()
|
|
318
128
|
|
|
319
129
|
if (docs.length === 0) {
|
|
320
130
|
console.info(pc.yellow('no documentation files found'))
|
|
321
|
-
|
|
131
|
+
} else {
|
|
132
|
+
console.info(pc.dim(`found ${docs.length} documentation files`))
|
|
322
133
|
}
|
|
323
134
|
|
|
324
|
-
console.info(pc.dim(`found ${docs.length} documentation files`))
|
|
325
|
-
|
|
326
135
|
if (clean && existsSync(skillsDir)) {
|
|
327
136
|
const existing = readdirSync(skillsDir)
|
|
328
137
|
for (const dir of existing) {
|
|
@@ -339,6 +148,7 @@ async function generateDocSkills(
|
|
|
339
148
|
let symlinked = 0
|
|
340
149
|
let generated = 0
|
|
341
150
|
let unchanged = 0
|
|
151
|
+
let removed = 0
|
|
342
152
|
const isDev = !!process.env.IS_TAMAGUI_DEV
|
|
343
153
|
|
|
344
154
|
for (const doc of docs) {
|
|
@@ -352,6 +162,7 @@ async function generateDocSkills(
|
|
|
352
162
|
if (!nameMatch) continue
|
|
353
163
|
|
|
354
164
|
const skillName = nameMatch[1]!.trim()
|
|
165
|
+
expectedSkillNames.add(skillName)
|
|
355
166
|
const skillDir = join(skillsDir, skillName)
|
|
356
167
|
const skillFile = join(skillDir, 'SKILL.md')
|
|
357
168
|
|
|
@@ -390,6 +201,7 @@ async function generateDocSkills(
|
|
|
390
201
|
} else {
|
|
391
202
|
const baseName = toSkillName(doc.name)
|
|
392
203
|
const skillName = `${SKILL_PREFIX}${baseName}`
|
|
204
|
+
expectedSkillNames.add(skillName)
|
|
393
205
|
const skillDir = join(skillsDir, skillName)
|
|
394
206
|
const skillFile = join(skillDir, 'SKILL.md')
|
|
395
207
|
|
|
@@ -438,33 +250,45 @@ ${content}
|
|
|
438
250
|
}
|
|
439
251
|
}
|
|
440
252
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
253
|
+
for (const dir of readdirSync(skillsDir)) {
|
|
254
|
+
if (expectedSkillNames.has(dir)) continue
|
|
255
|
+
|
|
256
|
+
const skillDir = join(skillsDir, dir)
|
|
257
|
+
const skillFile = join(skillDir, 'SKILL.md')
|
|
258
|
+
let shouldUnlink = false
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
const stat = lstatSync(skillFile)
|
|
262
|
+
if (stat.isSymbolicLink()) {
|
|
263
|
+
const linkTarget = readlinkSync(skillFile)
|
|
264
|
+
const resolvedTarget = resolve(skillDir, linkTarget)
|
|
265
|
+
shouldUnlink =
|
|
266
|
+
resolvedTarget.startsWith(`${localDocsDir}/`) ||
|
|
267
|
+
(!!DOCS_DIR && resolvedTarget.startsWith(`${DOCS_DIR}/`))
|
|
268
|
+
}
|
|
269
|
+
} catch {
|
|
270
|
+
// ignore unrelated skill directories
|
|
271
|
+
}
|
|
445
272
|
|
|
446
|
-
|
|
447
|
-
meta: {
|
|
448
|
-
name: 'scripts',
|
|
449
|
-
description: 'Generate a skill summarizing all tko scripts and commands',
|
|
450
|
-
},
|
|
451
|
-
async run() {
|
|
452
|
-
const cwd = process.cwd()
|
|
273
|
+
if (!shouldUnlink) continue
|
|
453
274
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
275
|
+
unlinkSync(skillFile)
|
|
276
|
+
if (readdirSync(skillDir).length === 0) {
|
|
277
|
+
rmdirSync(skillDir)
|
|
278
|
+
}
|
|
279
|
+
removed++
|
|
280
|
+
console.info(` ${pc.red('✕')} ${dir} ${pc.dim('(removed stale symlink)')}`)
|
|
281
|
+
}
|
|
457
282
|
|
|
458
|
-
|
|
283
|
+
return { symlinked, generated, unchanged, removed }
|
|
284
|
+
}
|
|
459
285
|
|
|
460
|
-
|
|
461
|
-
},
|
|
462
|
-
})
|
|
286
|
+
// --- commands ---
|
|
463
287
|
|
|
464
288
|
const generateCommand = defineCommand({
|
|
465
289
|
meta: {
|
|
466
290
|
name: 'generate',
|
|
467
|
-
description: 'Generate
|
|
291
|
+
description: 'Generate Claude Code skills from documentation',
|
|
468
292
|
},
|
|
469
293
|
args: {
|
|
470
294
|
clean: {
|
|
@@ -489,6 +313,7 @@ const generateCommand = defineCommand({
|
|
|
489
313
|
let symlinked = 0
|
|
490
314
|
let generated = 0
|
|
491
315
|
let unchanged = 0
|
|
316
|
+
let removed = 0
|
|
492
317
|
|
|
493
318
|
// 1. doc skills (unless skipped)
|
|
494
319
|
if (!args['skip-internal-docs']) {
|
|
@@ -496,12 +321,10 @@ const generateCommand = defineCommand({
|
|
|
496
321
|
symlinked = docStats.symlinked
|
|
497
322
|
generated = docStats.generated
|
|
498
323
|
unchanged = docStats.unchanged
|
|
324
|
+
removed = docStats.removed
|
|
499
325
|
console.info()
|
|
500
326
|
}
|
|
501
327
|
|
|
502
|
-
// 2. scripts summary skill
|
|
503
|
-
await generateSummary(cwd)
|
|
504
|
-
|
|
505
328
|
// summary
|
|
506
329
|
console.info()
|
|
507
330
|
console.info(pc.bold('summary:'))
|
|
@@ -511,6 +334,7 @@ const generateCommand = defineCommand({
|
|
|
511
334
|
` ${pc.yellow(`${generated} generated`)} ${pc.dim('(add frontmatter to enable symlink)')}`
|
|
512
335
|
)
|
|
513
336
|
if (unchanged > 0) console.info(` ${pc.dim(`${unchanged} unchanged`)}`)
|
|
337
|
+
if (removed > 0) console.info(` ${pc.red(`${removed} removed`)}`)
|
|
514
338
|
console.info(pc.dim(` skills in ${skillsDir}`))
|
|
515
339
|
console.info()
|
|
516
340
|
},
|
|
@@ -523,6 +347,5 @@ export const skillsCommand = defineCommand({
|
|
|
523
347
|
},
|
|
524
348
|
subCommands: {
|
|
525
349
|
generate: generateCommand,
|
|
526
|
-
scripts: scriptsCommand,
|
|
527
350
|
},
|
|
528
351
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA;;GAEG;AAoVH,eAAO,MAAM,aAAa,qDAQxB,CAAA"}
|