@schalkneethling/toolkit 0.1.0 → 0.1.1
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/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -64,7 +64,7 @@ function deepMerge(target, source) {
|
|
|
64
64
|
return source;
|
|
65
65
|
}
|
|
66
66
|
function hashHookSource(name) {
|
|
67
|
-
return shortHash(readFileSync(join(HOOKS_SRC, name, "hook.
|
|
67
|
+
return shortHash(readFileSync(join(HOOKS_SRC, name, "hook.mjs")));
|
|
68
68
|
}
|
|
69
69
|
function hashSkillSource(name) {
|
|
70
70
|
const dir = join(SKILLS_SRC, name);
|
|
@@ -116,7 +116,7 @@ function addHook(name) {
|
|
|
116
116
|
console.error(`Hook not found: ${name}`);
|
|
117
117
|
process.exit(1);
|
|
118
118
|
}
|
|
119
|
-
const hookSrc = join(srcDir, "hook.
|
|
119
|
+
const hookSrc = join(srcDir, "hook.mjs");
|
|
120
120
|
const fragmentPath = join(srcDir, "settings-fragment.json");
|
|
121
121
|
const hooksDir = join(CLAUDE_DIR, "hooks");
|
|
122
122
|
mkdirSync(hooksDir, { recursive: true });
|
|
@@ -190,7 +190,7 @@ async function update(force) {
|
|
|
190
190
|
}
|
|
191
191
|
if (sourceChanged) {
|
|
192
192
|
const oldSrc = existsSync(installedPath) ? readFileSync(installedPath, "utf8") : "";
|
|
193
|
-
const newSrc = readFileSync(join(srcDir, "hook.
|
|
193
|
+
const newSrc = readFileSync(join(srcDir, "hook.mjs"), "utf8");
|
|
194
194
|
console.log(`\n~ hook: ${name} (${entry.hash} → ${sourceHash})`);
|
|
195
195
|
console.log(diffLines(oldSrc, newSrc));
|
|
196
196
|
if (!(force || await confirm(`Update hook "${name}"?`))) continue;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * toolkit — personal CLI for managing Claude Code hooks and skills.\n *\n * Commands:\n * toolkit add hook <name>\n * toolkit add skill <name> [--link <target>...]\n * toolkit update [--force]\n * toolkit list hook\n * toolkit list skill\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n cpSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n statSync,\n symlinkSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createInterface } from \"node:readline/promises\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { parseArgs } from \"node:util\";\n\nconst TOOLKIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), \"..\");\nconst HOOKS_SRC = join(TOOLKIT_ROOT, \"hooks\");\nconst SKILLS_SRC = join(TOOLKIT_ROOT, \"skills\");\n\nconst PROJECT_ROOT = process.cwd();\nconst CLAUDE_DIR = join(PROJECT_ROOT, \".claude\");\nconst TOOLKIT_DIR = join(PROJECT_ROOT, \".claude-toolkit\");\nconst MANIFEST_PATH = join(CLAUDE_DIR, \"toolkit-manifest.json\");\n\ntype HookEntry = { hash: string; installedAt: string };\ntype SkillEntry = { hash: string; installedAt: string; linkedTo: string[] };\ntype Manifest = {\n hooks: Record<string, HookEntry>;\n skills: Record<string, SkillEntry>;\n};\n\n// ---------- helpers ----------\n\nfunction today(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nfunction shortHash(content: string | Buffer): string {\n return createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 7);\n}\n\nfunction readManifest(): Manifest {\n if (!existsSync(MANIFEST_PATH)) {\n return { hooks: {}, skills: {} };\n }\n\n try {\n const parsed = JSON.parse(readFileSync(MANIFEST_PATH, \"utf8\")) as Partial<Manifest>;\n return { hooks: parsed.hooks ?? {}, skills: parsed.skills ?? {} };\n } catch {\n return { hooks: {}, skills: {} };\n }\n}\n\nfunction writeManifest(m: Manifest): void {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n writeFileSync(MANIFEST_PATH, JSON.stringify(m, null, 2) + \"\\n\");\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction deepMerge<T>(target: T, source: T): T {\n if (Array.isArray(target) && Array.isArray(source)) {\n return [...target, ...source] as T;\n }\n if (isPlainObject(target) && isPlainObject(source)) {\n const out: Record<string, unknown> = { ...target };\n for (const [k, v] of Object.entries(source)) {\n out[k] = k in out ? deepMerge(out[k], v) : v;\n }\n return out as T;\n }\n return source;\n}\n\nfunction hashHookSource(name: string): string {\n const p = join(HOOKS_SRC, name, \"hook.ts\");\n return shortHash(readFileSync(p));\n}\n\nfunction hashSkillSource(name: string): string {\n const dir = join(SKILLS_SRC, name);\n const files = collectFiles(dir).sort();\n const h = createHash(\"sha256\");\n for (const f of files) {\n h.update(relative(dir, f));\n h.update(\"\\0\");\n h.update(readFileSync(f));\n h.update(\"\\0\");\n }\n return h.digest(\"hex\").slice(0, 7);\n}\n\nfunction collectFiles(dir: string): string[] {\n const out: string[] = [];\n if (!existsSync(dir)) {\n return out;\n }\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === \".gitkeep\") {\n continue;\n }\n\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n out.push(...collectFiles(full));\n } else if (entry.isFile()) {\n out.push(full);\n }\n }\n return out;\n}\n\nasync function confirm(question: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();\n rl.close();\n return answer === \"y\" || answer === \"yes\";\n}\n\nfunction diffLines(oldStr: string, newStr: string): string {\n const a = oldStr.split(\"\\n\");\n const b = newStr.split(\"\\n\");\n const out: string[] = [];\n const max = Math.max(a.length, b.length);\n for (let i = 0; i < max; i++) {\n if (a[i] === b[i]) {\n continue;\n }\n\n if (a[i] !== undefined) {\n out.push(`- ${a[i]}`);\n }\n\n if (b[i] !== undefined) {\n out.push(`+ ${b[i]}`);\n }\n }\n return out.join(\"\\n\");\n}\n\n// ---------- commands ----------\n\nfunction addHook(name: string): void {\n const srcDir = join(HOOKS_SRC, name);\n if (!existsSync(srcDir)) {\n console.error(`Hook not found: ${name}`);\n process.exit(1);\n }\n\n const hookSrc = join(srcDir, \"hook.ts\");\n const fragmentPath = join(srcDir, \"settings-fragment.json\");\n\n const hooksDir = join(CLAUDE_DIR, \"hooks\");\n mkdirSync(hooksDir, { recursive: true });\n const destHook = join(hooksDir, `${name}.ts`);\n writeFileSync(destHook, readFileSync(hookSrc));\n\n if (existsSync(fragmentPath)) {\n const fragment = JSON.parse(readFileSync(fragmentPath, \"utf8\"));\n const settingsPath = join(CLAUDE_DIR, \"settings.json\");\n const current = existsSync(settingsPath) ? JSON.parse(readFileSync(settingsPath, \"utf8\")) : {};\n const merged = deepMerge(current, fragment);\n writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + \"\\n\");\n }\n\n const manifest = readManifest();\n manifest.hooks[name] = { hash: hashHookSource(name), installedAt: today() };\n writeManifest(manifest);\n\n console.log(`Installed hook: ${name} → ${relative(PROJECT_ROOT, destHook)}`);\n}\n\nfunction addSkill(name: string, links: string[]): void {\n const srcDir = join(SKILLS_SRC, name);\n if (!existsSync(srcDir) || !statSync(srcDir).isDirectory()) {\n console.error(`Skill not found: ${name}`);\n process.exit(1);\n }\n\n const destDir = join(TOOLKIT_DIR, \"skills\", name);\n mkdirSync(dirname(destDir), { recursive: true });\n cpSync(srcDir, destDir, { recursive: true });\n\n const resolvedLinks = links.length > 0 ? links : [join(\".claude\", \"skills\")];\n for (const link of resolvedLinks) {\n const linkDir = resolve(PROJECT_ROOT, link);\n mkdirSync(linkDir, { recursive: true });\n\n const linkPath = join(linkDir, name);\n if (existsSync(linkPath) || lstatExists(linkPath)) {\n unlinkSync(linkPath);\n }\n\n const relTarget = relative(linkDir, destDir);\n symlinkSync(relTarget, linkPath, \"dir\");\n }\n\n const manifest = readManifest();\n manifest.skills[name] = {\n hash: hashSkillSource(name),\n installedAt: today(),\n linkedTo: resolvedLinks,\n };\n writeManifest(manifest);\n\n console.log(`Installed skill: ${name} → ${relative(PROJECT_ROOT, destDir)}`);\n for (const l of resolvedLinks) {\n console.log(` linked: ${join(l, name)}`);\n }\n}\n\nfunction lstatExists(p: string): boolean {\n try {\n lstatSync(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function update(force: boolean): Promise<void> {\n const manifest = readManifest();\n let changed = false;\n\n for (const [name, entry] of Object.entries(manifest.hooks)) {\n const srcDir = join(HOOKS_SRC, name);\n if (!existsSync(srcDir)) {\n continue;\n }\n\n const sourceHash = hashHookSource(name);\n const installedPath = join(CLAUDE_DIR, \"hooks\", `${name}.ts`);\n const installedHash = existsSync(installedPath) ? shortHash(readFileSync(installedPath)) : null;\n\n const sourceChanged = sourceHash !== entry.hash;\n const locallyModified = installedHash !== null && installedHash !== entry.hash;\n\n if (!sourceChanged && !locallyModified) {\n continue;\n }\n\n changed = true;\n\n if (locallyModified && !force) {\n console.warn(\n `! hook \"${name}\" was modified locally (installed=${installedHash}, manifest=${entry.hash}). Use --force to overwrite.`,\n );\n continue;\n }\n\n if (sourceChanged) {\n const oldSrc = existsSync(installedPath) ? readFileSync(installedPath, \"utf8\") : \"\";\n const newSrc = readFileSync(join(srcDir, \"hook.ts\"), \"utf8\");\n console.log(`\\n~ hook: ${name} (${entry.hash} → ${sourceHash})`);\n console.log(diffLines(oldSrc, newSrc));\n const ok = force || (await confirm(`Update hook \"${name}\"?`));\n\n if (!ok) {\n continue;\n }\n\n writeFileSync(installedPath, newSrc);\n manifest.hooks[name] = { hash: sourceHash, installedAt: today() };\n }\n }\n\n for (const [name, entry] of Object.entries(manifest.skills)) {\n const srcDir = join(SKILLS_SRC, name);\n if (!existsSync(srcDir)) {\n continue;\n }\n\n const sourceHash = hashSkillSource(name);\n if (sourceHash === entry.hash) {\n continue;\n }\n\n changed = true;\n console.log(`\\n~ skill: ${name} (${entry.hash} → ${sourceHash})`);\n const ok = force || (await confirm(`Update skill \"${name}\"?`));\n if (!ok) {\n continue;\n }\n\n const destDir = join(TOOLKIT_DIR, \"skills\", name);\n cpSync(srcDir, destDir, { recursive: true, force: true });\n manifest.skills[name] = {\n hash: sourceHash,\n installedAt: today(),\n linkedTo: entry.linkedTo,\n };\n }\n\n if (changed) {\n writeManifest(manifest);\n }\n}\n\nfunction list(kind: \"hook\" | \"skill\"): void {\n const dir = kind === \"hook\" ? HOOKS_SRC : SKILLS_SRC;\n if (!existsSync(dir)) {\n console.log(`(no ${kind}s available)`);\n return;\n }\n const entries = readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory() || (kind === \"skill\" && e.isSymbolicLink()))\n .map((e) => e.name);\n\n if (entries.length === 0) {\n console.log(`(no ${kind}s available)`);\n return;\n }\n\n for (const name of entries) {\n const hash = kind === \"hook\" ? hashHookSource(name) : hashSkillSource(name);\n console.log(`${name} ${hash}`);\n }\n}\n\n// ---------- argv ----------\n\nfunction usage(): never {\n console.error(\n `Usage:\n toolkit add hook <name>\n toolkit add skill <name> [--link <target>]...\n toolkit update [--force]\n toolkit list hook\n toolkit list skill`,\n );\n process.exit(1);\n}\n\nasync function main(): Promise<void> {\n const { values, positionals } = parseArgs({\n options: {\n force: {\n default: false,\n type: \"boolean\",\n },\n links: {\n multiple: true,\n type: \"string\",\n },\n },\n allowPositionals: true,\n });\n\n const { force, links } = values;\n const [command, resource, name] = positionals;\n\n if (command === \"add\" && resource === \"hook\") {\n if (!name) {\n usage();\n }\n\n addHook(name);\n return;\n }\n\n if (command === \"add\" && resource === \"skill\") {\n if (!name) {\n usage();\n }\n\n addSkill(name, links ? links : []);\n return;\n }\n\n if (command === \"update\") {\n await update(force);\n return;\n }\n\n if (command === \"list\" && (resource === \"hook\" || resource === \"skill\")) {\n list(resource);\n return;\n }\n\n usage();\n}\n\nmain().catch((err) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * toolkit — personal CLI for managing Claude Code hooks and skills.\n *\n * Commands:\n * toolkit add hook <name>\n * toolkit add skill <name> [--link <target>...]\n * toolkit update [--force]\n * toolkit list hook\n * toolkit list skill\n */\n\nimport { createHash } from \"node:crypto\";\nimport {\n cpSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n statSync,\n symlinkSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createInterface } from \"node:readline/promises\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { parseArgs } from \"node:util\";\n\nconst TOOLKIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), \"..\");\nconst HOOKS_SRC = join(TOOLKIT_ROOT, \"hooks\");\nconst SKILLS_SRC = join(TOOLKIT_ROOT, \"skills\");\n\nconst PROJECT_ROOT = process.cwd();\nconst CLAUDE_DIR = join(PROJECT_ROOT, \".claude\");\nconst TOOLKIT_DIR = join(PROJECT_ROOT, \".claude-toolkit\");\nconst MANIFEST_PATH = join(CLAUDE_DIR, \"toolkit-manifest.json\");\n\ntype HookEntry = { hash: string; installedAt: string };\ntype SkillEntry = { hash: string; installedAt: string; linkedTo: string[] };\ntype Manifest = {\n hooks: Record<string, HookEntry>;\n skills: Record<string, SkillEntry>;\n};\n\n// ---------- helpers ----------\n\nfunction today(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nfunction shortHash(content: string | Buffer): string {\n return createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 7);\n}\n\nfunction readManifest(): Manifest {\n if (!existsSync(MANIFEST_PATH)) {\n return { hooks: {}, skills: {} };\n }\n\n try {\n const parsed = JSON.parse(\n readFileSync(MANIFEST_PATH, \"utf8\"),\n ) as Partial<Manifest>;\n return { hooks: parsed.hooks ?? {}, skills: parsed.skills ?? {} };\n } catch {\n return { hooks: {}, skills: {} };\n }\n}\n\nfunction writeManifest(m: Manifest): void {\n mkdirSync(CLAUDE_DIR, { recursive: true });\n writeFileSync(MANIFEST_PATH, JSON.stringify(m, null, 2) + \"\\n\");\n}\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n\nfunction deepMerge<T>(target: T, source: T): T {\n if (Array.isArray(target) && Array.isArray(source)) {\n return [...target, ...source] as T;\n }\n if (isPlainObject(target) && isPlainObject(source)) {\n const out: Record<string, unknown> = { ...target };\n for (const [k, v] of Object.entries(source)) {\n out[k] = k in out ? deepMerge(out[k], v) : v;\n }\n return out as T;\n }\n return source;\n}\n\nfunction hashHookSource(name: string): string {\n const p = join(HOOKS_SRC, name, \"hook.mjs\");\n return shortHash(readFileSync(p));\n}\n\nfunction hashSkillSource(name: string): string {\n const dir = join(SKILLS_SRC, name);\n const files = collectFiles(dir).sort();\n const h = createHash(\"sha256\");\n for (const f of files) {\n h.update(relative(dir, f));\n h.update(\"\\0\");\n h.update(readFileSync(f));\n h.update(\"\\0\");\n }\n return h.digest(\"hex\").slice(0, 7);\n}\n\nfunction collectFiles(dir: string): string[] {\n const out: string[] = [];\n if (!existsSync(dir)) {\n return out;\n }\n\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === \".gitkeep\") {\n continue;\n }\n\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n out.push(...collectFiles(full));\n } else if (entry.isFile()) {\n out.push(full);\n }\n }\n return out;\n}\n\nasync function confirm(question: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();\n rl.close();\n return answer === \"y\" || answer === \"yes\";\n}\n\nfunction diffLines(oldStr: string, newStr: string): string {\n const a = oldStr.split(\"\\n\");\n const b = newStr.split(\"\\n\");\n const out: string[] = [];\n const max = Math.max(a.length, b.length);\n for (let i = 0; i < max; i++) {\n if (a[i] === b[i]) {\n continue;\n }\n\n if (a[i] !== undefined) {\n out.push(`- ${a[i]}`);\n }\n\n if (b[i] !== undefined) {\n out.push(`+ ${b[i]}`);\n }\n }\n return out.join(\"\\n\");\n}\n\n// ---------- commands ----------\n\nfunction addHook(name: string): void {\n const srcDir = join(HOOKS_SRC, name);\n if (!existsSync(srcDir)) {\n console.error(`Hook not found: ${name}`);\n process.exit(1);\n }\n\n const hookSrc = join(srcDir, \"hook.mjs\");\n const fragmentPath = join(srcDir, \"settings-fragment.json\");\n\n const hooksDir = join(CLAUDE_DIR, \"hooks\");\n mkdirSync(hooksDir, { recursive: true });\n const destHook = join(hooksDir, `${name}.ts`);\n writeFileSync(destHook, readFileSync(hookSrc));\n\n if (existsSync(fragmentPath)) {\n const fragment = JSON.parse(readFileSync(fragmentPath, \"utf8\"));\n const settingsPath = join(CLAUDE_DIR, \"settings.json\");\n const current = existsSync(settingsPath)\n ? JSON.parse(readFileSync(settingsPath, \"utf8\"))\n : {};\n const merged = deepMerge(current, fragment);\n writeFileSync(settingsPath, JSON.stringify(merged, null, 2) + \"\\n\");\n }\n\n const manifest = readManifest();\n manifest.hooks[name] = { hash: hashHookSource(name), installedAt: today() };\n writeManifest(manifest);\n\n console.log(`Installed hook: ${name} → ${relative(PROJECT_ROOT, destHook)}`);\n}\n\nfunction addSkill(name: string, links: string[]): void {\n const srcDir = join(SKILLS_SRC, name);\n if (!existsSync(srcDir) || !statSync(srcDir).isDirectory()) {\n console.error(`Skill not found: ${name}`);\n process.exit(1);\n }\n\n const destDir = join(TOOLKIT_DIR, \"skills\", name);\n mkdirSync(dirname(destDir), { recursive: true });\n cpSync(srcDir, destDir, { recursive: true });\n\n const resolvedLinks = links.length > 0 ? links : [join(\".claude\", \"skills\")];\n for (const link of resolvedLinks) {\n const linkDir = resolve(PROJECT_ROOT, link);\n mkdirSync(linkDir, { recursive: true });\n\n const linkPath = join(linkDir, name);\n if (existsSync(linkPath) || lstatExists(linkPath)) {\n unlinkSync(linkPath);\n }\n\n const relTarget = relative(linkDir, destDir);\n symlinkSync(relTarget, linkPath, \"dir\");\n }\n\n const manifest = readManifest();\n manifest.skills[name] = {\n hash: hashSkillSource(name),\n installedAt: today(),\n linkedTo: resolvedLinks,\n };\n writeManifest(manifest);\n\n console.log(`Installed skill: ${name} → ${relative(PROJECT_ROOT, destDir)}`);\n for (const l of resolvedLinks) {\n console.log(` linked: ${join(l, name)}`);\n }\n}\n\nfunction lstatExists(p: string): boolean {\n try {\n lstatSync(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function update(force: boolean): Promise<void> {\n const manifest = readManifest();\n let changed = false;\n\n for (const [name, entry] of Object.entries(manifest.hooks)) {\n const srcDir = join(HOOKS_SRC, name);\n if (!existsSync(srcDir)) {\n continue;\n }\n\n const sourceHash = hashHookSource(name);\n const installedPath = join(CLAUDE_DIR, \"hooks\", `${name}.ts`);\n const installedHash = existsSync(installedPath)\n ? shortHash(readFileSync(installedPath))\n : null;\n\n const sourceChanged = sourceHash !== entry.hash;\n const locallyModified =\n installedHash !== null && installedHash !== entry.hash;\n\n if (!sourceChanged && !locallyModified) {\n continue;\n }\n\n changed = true;\n\n if (locallyModified && !force) {\n console.warn(\n `! hook \"${name}\" was modified locally (installed=${installedHash}, manifest=${entry.hash}). Use --force to overwrite.`,\n );\n continue;\n }\n\n if (sourceChanged) {\n const oldSrc = existsSync(installedPath)\n ? readFileSync(installedPath, \"utf8\")\n : \"\";\n const newSrc = readFileSync(join(srcDir, \"hook.mjs\"), \"utf8\");\n console.log(`\\n~ hook: ${name} (${entry.hash} → ${sourceHash})`);\n console.log(diffLines(oldSrc, newSrc));\n const ok = force || (await confirm(`Update hook \"${name}\"?`));\n\n if (!ok) {\n continue;\n }\n\n writeFileSync(installedPath, newSrc);\n manifest.hooks[name] = { hash: sourceHash, installedAt: today() };\n }\n }\n\n for (const [name, entry] of Object.entries(manifest.skills)) {\n const srcDir = join(SKILLS_SRC, name);\n if (!existsSync(srcDir)) {\n continue;\n }\n\n const sourceHash = hashSkillSource(name);\n if (sourceHash === entry.hash) {\n continue;\n }\n\n changed = true;\n console.log(`\\n~ skill: ${name} (${entry.hash} → ${sourceHash})`);\n const ok = force || (await confirm(`Update skill \"${name}\"?`));\n if (!ok) {\n continue;\n }\n\n const destDir = join(TOOLKIT_DIR, \"skills\", name);\n cpSync(srcDir, destDir, { recursive: true, force: true });\n manifest.skills[name] = {\n hash: sourceHash,\n installedAt: today(),\n linkedTo: entry.linkedTo,\n };\n }\n\n if (changed) {\n writeManifest(manifest);\n }\n}\n\nfunction list(kind: \"hook\" | \"skill\"): void {\n const dir = kind === \"hook\" ? HOOKS_SRC : SKILLS_SRC;\n if (!existsSync(dir)) {\n console.log(`(no ${kind}s available)`);\n return;\n }\n const entries = readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isDirectory() || (kind === \"skill\" && e.isSymbolicLink()))\n .map((e) => e.name);\n\n if (entries.length === 0) {\n console.log(`(no ${kind}s available)`);\n return;\n }\n\n for (const name of entries) {\n const hash = kind === \"hook\" ? hashHookSource(name) : hashSkillSource(name);\n console.log(`${name} ${hash}`);\n }\n}\n\n// ---------- argv ----------\n\nfunction usage(): never {\n console.error(\n `Usage:\n toolkit add hook <name>\n toolkit add skill <name> [--link <target>]...\n toolkit update [--force]\n toolkit list hook\n toolkit list skill`,\n );\n process.exit(1);\n}\n\nasync function main(): Promise<void> {\n const { values, positionals } = parseArgs({\n options: {\n force: {\n default: false,\n type: \"boolean\",\n },\n links: {\n multiple: true,\n type: \"string\",\n },\n },\n allowPositionals: true,\n });\n\n const { force, links } = values;\n const [command, resource, name] = positionals;\n\n if (command === \"add\" && resource === \"hook\") {\n if (!name) {\n usage();\n }\n\n addHook(name);\n return;\n }\n\n if (command === \"add\" && resource === \"skill\") {\n if (!name) {\n usage();\n }\n\n addSkill(name, links ? links : []);\n return;\n }\n\n if (command === \"update\") {\n await update(force);\n return;\n }\n\n if (command === \"list\" && (resource === \"hook\" || resource === \"skill\")) {\n list(resource);\n return;\n }\n\n usage();\n}\n\nmain().catch((err) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
|