@tankpkg/cli 0.9.0 → 0.10.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/bin/tank.js +92 -180
- package/dist/bin/tank.js.map +1 -1
- package/dist/{debug-logger-BJzuguP3.js → debug-logger-DnEQXtQC.js} +3 -3
- package/dist/debug-logger-DnEQXtQC.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/package.json +13 -12
- package/package.json +13 -12
- package/dist/debug-logger-BJzuguP3.js.map +0 -1
package/dist/bin/tank.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tank.js","names":["MAX_PACKAGE_SIZE","readLockfile","scoreColor","padRight","parseLockKey","readLockfile","parseLockKey","getExtractDir","crypto","getExtractDir","parseLockKey","isDomainAllowed","isPathAllowed","parseLockKey","getExtractDir","crypto","crypto","formatSize","scoreColor","parseLockKey","isNewerVersion","crypto","readLockfile"],"sources":["../../../shared/dist/index.js","../../src/lib/manifest.ts","../../src/lib/lockfile.ts","../../src/commands/audit.ts","../../src/lib/agents.ts","../../src/lib/links.ts","../../src/lib/linker.ts","../../src/commands/doctor.ts","../../src/commands/info.ts","../../src/commands/init.ts","../../src/lib/dependency-resolver.ts","../../src/lib/frontmatter.ts","../../src/lib/install-pipeline.ts","../../src/lib/permission-checker.ts","../../src/lib/permission-prompt.ts","../../src/commands/install.ts","../../src/commands/link.ts","../../src/commands/login.ts","../../src/commands/logout.ts","../../src/commands/migrate.ts","../../src/commands/permissions.ts","../../src/lib/packer.ts","../../src/commands/publish.ts","../../src/commands/remove.ts","../../src/commands/scan.ts","../../src/commands/search.ts","../../src/commands/unlink.ts","../../src/commands/update.ts","../../src/commands/upgrade.ts","../../src/commands/verify.ts","../../src/commands/whoami.ts","../../src/lib/upgrade-check.ts","../../src/bin/tank.ts"],"sourcesContent":["import semver from \"semver\";\nimport { z } from \"zod\";\n//#region src/brand.ts\n/**\n* Default Tank brand configuration\n* Used when no custom brand is configured\n*/\nconst DEFAULT_BRAND = {\n\tname: \"Tank\",\n\ttagline: \"Security-first package manager for AI agent skills\",\n\turl: \"https://tankpkg.dev\",\n\tlogo: {\n\t\tdefault: \"/logo.png\",\n\t\ttight: \"/logo-tight.png\"\n\t},\n\tfavicon: \"/favicon.ico\",\n\togImage: \"/og-default.png\",\n\tcolors: {\n\t\tprimary: \"10b981\",\n\t\tsecondary: \"3b82f6\",\n\t\taccent: \"f59e0b\",\n\t\tbackground: \"0f172a\"\n\t},\n\tdarkColors: {\n\t\tprimary: \"10b981\",\n\t\tsecondary: \"3b82f6\",\n\t\taccent: \"f59e0b\",\n\t\tbackground: \"0f172a\"\n\t},\n\tsocial: {\n\t\ttwitter: \"@tankpkg\",\n\t\tgithub: \"tankpkg/tank\"\n\t}\n};\n/**\n* Hex color validation regex\n*/\nconst HEX_COLOR_REGEX = /^[0-9a-fA-F]{6}$/;\n/**\n* Validates a hex color string (without # prefix)\n*/\nfunction isValidHexColor(color) {\n\treturn HEX_COLOR_REGEX.test(color);\n}\n/**\n* Converts hex color to oklch format for CSS\n* Uses a simplified conversion that maintains hue\n*/\nfunction hexToOklch(hex) {\n\treturn `#${hex}`;\n}\n//#endregion\n//#region src/constants/permissions.ts\nconst PERMISSION_CATEGORIES = [\n\t\"network\",\n\t\"filesystem\",\n\t\"subprocess\"\n];\nconst DEFAULT_PERMISSIONS = {\n\tnetwork: void 0,\n\tfilesystem: void 0,\n\tsubprocess: false\n};\n//#endregion\n//#region src/constants/registry.ts\nconst REGISTRY_URL = \"https://tankpkg.dev\";\nconst REGISTRY_API_VERSION = \"v1\";\nconst MAX_PACKAGE_SIZE = 50 * 1024 * 1024;\nconst MAX_FILE_COUNT = 1e3;\nconst MAX_NAME_LENGTH = 214;\nconst MAX_DESCRIPTION_LENGTH = 500;\nconst LOCKFILE_VERSION = 2;\nconst MANIFEST_FILENAME = \"tank.json\";\nconst LEGACY_MANIFEST_FILENAME = \"skills.json\";\nconst LOCKFILE_FILENAME = \"tank.lock\";\nconst LEGACY_LOCKFILE_FILENAME = \"skills.lock\";\n//#endregion\n//#region src/lib/resolver.ts\n/**\n* Resolves a semver range against a list of available versions.\n* Returns the highest version that satisfies the range, or null if none match.\n*\n* Pre-release versions are excluded from range matching unless the range\n* explicitly includes a pre-release tag (e.g., \">=1.0.0-beta.1\").\n* Exact version matches always work, including for pre-release versions.\n*\n* @param range - A semver range string (e.g., \"^2.1.0\", \"~1.0.0\", \">=2.0.0 <3.0.0\", \"*\")\n* @param versions - An array of semver version strings to match against\n* @returns The highest matching version string, or null if no match\n*/\nfunction resolve(range, versions) {\n\ttry {\n\t\tif (!range || !semver.validRange(range)) return null;\n\t\tconst validVersions = versions.filter((v) => semver.valid(v) !== null);\n\t\tif (validVersions.length === 0) return null;\n\t\treturn semver.maxSatisfying(validVersions, range) ?? null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n/**\n* Sorts an array of semver version strings in descending order (newest first).\n* Invalid version strings are filtered out.\n* Does not mutate the original array.\n*\n* @param versions - An array of semver version strings\n* @returns A new array sorted descending by semver precedence\n*/\nfunction sortVersions(versions) {\n\treturn [...versions.filter((v) => semver.valid(v) !== null)].sort((a, b) => semver.rcompare(a, b));\n}\n//#endregion\n//#region src/lib/url.ts\n/**\n* Encode a skill/package name for use in URLs.\n*\n* Like `encodeURIComponent` but keeps `@` and `/` unencoded so scoped\n* package names produce clean, human-readable paths:\n*\n* encodeSkillName('@tank/bulletproof') → '@tank/bulletproof'\n* encodeSkillName('my skill') → 'my%20skill'\n*/\nfunction encodeSkillName(name) {\n\treturn encodeURIComponent(name).replace(/%40/g, \"@\").replace(/%2F/gi, \"/\");\n}\n//#endregion\n//#region src/schemas/permissions.ts\nconst networkPermissionsSchema = z.object({ outbound: z.array(z.string()).optional() }).strict();\nconst filesystemPermissionsSchema = z.object({\n\tread: z.array(z.string()).optional(),\n\twrite: z.array(z.string()).optional()\n}).strict();\nconst permissionsSchema = z.object({\n\tnetwork: networkPermissionsSchema.optional(),\n\tfilesystem: filesystemPermissionsSchema.optional(),\n\tsubprocess: z.boolean().optional()\n}).strict();\nconst userRoleSchema = z.enum([\"user\", \"admin\"]);\nconst userStatusSchema = z.enum([\n\t\"active\",\n\t\"suspended\",\n\t\"banned\"\n]);\nconst skillStatusSchema = z.enum([\n\t\"active\",\n\t\"deprecated\",\n\t\"quarantined\",\n\t\"removed\"\n]);\nconst adminActionSchema = z.enum([\n\t\"user.ban\",\n\t\"user.suspend\",\n\t\"user.unban\",\n\t\"user.promote\",\n\t\"user.demote\",\n\t\"skill.quarantine\",\n\t\"skill.remove\",\n\t\"skill.deprecate\",\n\t\"skill.restore\",\n\t\"skill.feature\",\n\t\"skill.unfeature\",\n\t\"org.suspend\",\n\t\"org.member.remove\",\n\t\"org.delete\"\n]);\nfunction isAdmin(role) {\n\treturn role === \"admin\";\n}\nconst skillsJsonSchema = z.object({\n\tname: z.string().min(1, \"Name must not be empty\").max(214, \"Name must be 214 characters or fewer\").regex(/^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/, \"Name must be scoped (@org/name), lowercase alphanumeric and hyphens\"),\n\tversion: z.string().regex(/^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/, \"Version must be valid semver\"),\n\tdescription: z.string().max(500, \"Description must be 500 characters or fewer\").optional(),\n\tskills: z.record(z.string(), z.string()).optional(),\n\tpermissions: permissionsSchema.optional(),\n\trepository: z.string().url(\"Repository must be a valid URL\").optional(),\n\tvisibility: z.enum([\"public\", \"private\"]).optional(),\n\taudit: z.object({ min_score: z.number().min(0).max(10) }).strict().optional()\n}).strict();\n//#endregion\n//#region src/schemas/skills-lock.ts\nconst lockedSkillV1Schema = z.object({\n\tresolved: z.string().url(),\n\tintegrity: z.string().regex(/^sha512-/, \"Integrity must start with sha512-\"),\n\tpermissions: permissionsSchema,\n\taudit_score: z.number().min(0).max(10).nullable()\n});\nconst skillsLockV1Schema = z.object({\n\tlockfileVersion: z.literal(1),\n\tskills: z.record(z.string(), lockedSkillV1Schema)\n});\nconst lockedSkillSchema = z.object({\n\tresolved: z.string().url(),\n\tintegrity: z.string().regex(/^sha512-/, \"Integrity must start with sha512-\"),\n\tpermissions: permissionsSchema,\n\taudit_score: z.number().min(0).max(10).nullable(),\n\tdependencies: z.record(z.string(), z.string()).optional()\n});\nconst skillsLockSchema = z.object({\n\tlockfileVersion: z.union([z.literal(1), z.literal(2)]),\n\tskills: z.record(z.string(), lockedSkillSchema)\n});\n//#endregion\nexport { DEFAULT_BRAND, DEFAULT_PERMISSIONS, LEGACY_LOCKFILE_FILENAME, LEGACY_MANIFEST_FILENAME, LOCKFILE_FILENAME, LOCKFILE_VERSION, MANIFEST_FILENAME, MAX_DESCRIPTION_LENGTH, MAX_FILE_COUNT, MAX_NAME_LENGTH, MAX_PACKAGE_SIZE, PERMISSION_CATEGORIES, REGISTRY_API_VERSION, REGISTRY_URL, adminActionSchema, encodeSkillName, filesystemPermissionsSchema, hexToOklch, isAdmin, isValidHexColor, networkPermissionsSchema, permissionsSchema, resolve, skillStatusSchema, skillsJsonSchema, skillsLockSchema, skillsLockV1Schema, sortVersions, userRoleSchema, userStatusSchema };\n\n//# sourceMappingURL=index.js.map","import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n MANIFEST_FILENAME\n} from '@internal/shared';\nimport { logger } from './logger.js';\n\nconst warnedManifest = new Set<string>();\nconst warnedLockfile = new Set<string>();\n\nexport interface ResolvedFile {\n path: string;\n isLegacy: boolean;\n exists: boolean;\n}\n\n/**\n * Resolve the manifest file path with fallback priority:\n * 1. tank.json (preferred)\n * 2. skills.json (deprecated fallback)\n *\n * If both exist, prefers tank.json and warns about duplicate.\n * Returns the path even if neither exists (for write operations).\n */\nexport function resolveManifestPath(directory?: string): ResolvedFile {\n const dir = directory ?? process.cwd();\n const newPath = path.join(dir, MANIFEST_FILENAME);\n const legacyPath = path.join(dir, LEGACY_MANIFEST_FILENAME);\n\n const newExists = fs.existsSync(newPath);\n const legacyExists = fs.existsSync(legacyPath);\n\n if (newExists && legacyExists && !warnedManifest.has(dir)) {\n warnedManifest.add(dir);\n logger.warn(`Both ${MANIFEST_FILENAME} and ${LEGACY_MANIFEST_FILENAME} exist. Using ${MANIFEST_FILENAME}.`);\n }\n\n if (newExists) {\n return { path: newPath, isLegacy: false, exists: true };\n }\n\n if (legacyExists) {\n if (!warnedManifest.has(dir)) {\n warnedManifest.add(dir);\n logger.warn(`${LEGACY_MANIFEST_FILENAME} is deprecated — run \\`tank migrate\\` to switch to ${MANIFEST_FILENAME}`);\n }\n return { path: legacyPath, isLegacy: true, exists: true };\n }\n\n // Neither exists — return the new filename for creation\n return { path: newPath, isLegacy: false, exists: false };\n}\n\n/**\n * Resolve the lockfile path with fallback priority:\n * 1. tank.lock (preferred)\n * 2. skills.lock (deprecated fallback)\n */\nexport function resolveLockfilePath(directory?: string): ResolvedFile {\n const dir = directory ?? process.cwd();\n const newPath = path.join(dir, LOCKFILE_FILENAME);\n const legacyPath = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n\n const newExists = fs.existsSync(newPath);\n const legacyExists = fs.existsSync(legacyPath);\n\n if (newExists && legacyExists && !warnedLockfile.has(dir)) {\n warnedLockfile.add(dir);\n logger.warn(`Both ${LOCKFILE_FILENAME} and ${LEGACY_LOCKFILE_FILENAME} exist. Using ${LOCKFILE_FILENAME}.`);\n }\n\n if (newExists) {\n return { path: newPath, isLegacy: false, exists: true };\n }\n\n if (legacyExists) {\n if (!warnedLockfile.has(dir)) {\n warnedLockfile.add(dir);\n logger.warn(`${LEGACY_LOCKFILE_FILENAME} is deprecated — run \\`tank migrate\\` to switch to ${LOCKFILE_FILENAME}`);\n }\n return { path: legacyPath, isLegacy: true, exists: true };\n }\n\n return { path: newPath, isLegacy: false, exists: false };\n}\n\n/**\n * Get the path where new manifest files should be written.\n * Always returns the new filename (tank.json).\n * If a legacy file exists but no new file, returns the legacy path\n * to avoid creating a second file during write operations.\n */\nexport function getManifestWritePath(directory?: string): string {\n const resolved = resolveManifestPath(directory);\n return resolved.path;\n}\n\n/**\n * Get the path where new lockfiles should be written.\n * Always returns the new filename (tank.lock).\n * If a legacy file exists but no new file, returns the legacy path\n * to avoid creating a second file during write operations.\n */\nexport function getLockfileWritePath(directory?: string): string {\n const resolved = resolveLockfilePath(directory);\n return resolved.path;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { Permissions, SkillsLock } from '@internal/shared';\nimport { resolveLockfilePath } from './manifest.js';\n\n/**\n * Read and parse the lockfile from the given directory.\n * Returns null if the file doesn't exist or is corrupt.\n */\nexport function readLockfile(directory?: string): SkillsLock | null {\n const resolved = resolveLockfilePath(directory);\n\n if (!resolved.exists) {\n return null;\n }\n\n try {\n const raw = fs.readFileSync(resolved.path, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n return null;\n }\n}\n\n/**\n * Write a lockfile deterministically: sorted keys, consistent formatting, trailing newline.\n */\nexport function writeLockfile(lock: SkillsLock, directory?: string): void {\n const resolved = resolveLockfilePath(directory);\n const lockPath = resolved.path;\n\n // Sort skill keys alphabetically for determinism\n const sortedSkills: Record<string, SkillsLock['skills'][string]> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n\n const output: SkillsLock = {\n lockfileVersion: lock.lockfileVersion,\n skills: sortedSkills\n };\n\n fs.writeFileSync(lockPath, `${JSON.stringify(output, null, 2)}\\n`);\n}\n\n/**\n * Compute the union of all skill permissions from a lockfile.\n * Merges network outbound, filesystem read/write (deduped), and subprocess (OR).\n */\nexport function computeResolvedPermissions(lock: SkillsLock): Permissions {\n const entries = Object.values(lock.skills);\n if (entries.length === 0) {\n return {};\n }\n\n const outbound = new Set<string>();\n const fsRead = new Set<string>();\n const fsWrite = new Set<string>();\n let subprocess = false;\n\n for (const entry of entries) {\n const perms = entry.permissions;\n\n if (perms.network?.outbound) {\n for (const domain of perms.network.outbound) {\n outbound.add(domain);\n }\n }\n\n if (perms.filesystem?.read) {\n for (const pattern of perms.filesystem.read) {\n fsRead.add(pattern);\n }\n }\n\n if (perms.filesystem?.write) {\n for (const pattern of perms.filesystem.write) {\n fsWrite.add(pattern);\n }\n }\n\n if (perms.subprocess === true) {\n subprocess = true;\n }\n }\n\n const result: Permissions = {};\n\n if (outbound.size > 0) {\n result.network = { outbound: [...outbound].sort() };\n }\n\n if (fsRead.size > 0 || fsWrite.size > 0) {\n result.filesystem = {};\n if (fsRead.size > 0) {\n result.filesystem.read = [...fsRead].sort();\n }\n if (fsWrite.size > 0) {\n result.filesystem.write = [...fsWrite].sort();\n }\n }\n\n if (subprocess) {\n result.subprocess = true;\n }\n\n return result;\n}\n\n/**\n * Check if resolved permissions fit within the project's permission budget.\n *\n * Returns:\n * - 'pass' if all resolved permissions are within budget\n * - 'fail' if any resolved permission exceeds budget\n * - 'no_budget' if no project permissions are defined\n */\nexport function computeBudgetCheck(\n resolvedPermissions: Permissions,\n projectPermissions: Permissions | undefined\n): 'pass' | 'fail' | 'no_budget' {\n if (projectPermissions === undefined) {\n return 'no_budget';\n }\n\n // Check subprocess\n if (resolvedPermissions.subprocess === true && projectPermissions.subprocess === false) {\n return 'fail';\n }\n\n // Check network outbound\n if (resolvedPermissions.network?.outbound) {\n const budgetOutbound = new Set(projectPermissions.network?.outbound ?? []);\n for (const domain of resolvedPermissions.network.outbound) {\n if (!budgetOutbound.has(domain)) {\n return 'fail';\n }\n }\n }\n\n // Check filesystem read\n if (resolvedPermissions.filesystem?.read) {\n const budgetRead = new Set(projectPermissions.filesystem?.read ?? []);\n for (const pattern of resolvedPermissions.filesystem.read) {\n if (!budgetRead.has(pattern)) {\n return 'fail';\n }\n }\n }\n\n // Check filesystem write\n if (resolvedPermissions.filesystem?.write) {\n const budgetWrite = new Set(projectPermissions.filesystem?.write ?? []);\n for (const pattern of resolvedPermissions.filesystem.write) {\n if (!budgetWrite.has(pattern)) {\n return 'fail';\n }\n }\n }\n\n return 'pass';\n}\n","import chalk from 'chalk';\nimport { getConfig } from '../lib/config.js';\nimport { readLockfile } from '../lib/lockfile.js';\nimport { USER_AGENT } from '../version.js';\n\nexport interface AuditOptions {\n name?: string;\n configDir?: string;\n}\n\ninterface VersionDetails {\n name: string;\n version: string;\n permissions?: {\n network?: { outbound?: string[] };\n filesystem?: { read?: string[]; write?: string[] };\n subprocess?: boolean;\n };\n auditScore: number | null;\n auditStatus: string;\n downloadUrl: string;\n publishedAt: string;\n downloads: number;\n}\n\ninterface AuditResult {\n name: string;\n version: string;\n score: number | null;\n status: string;\n permissions?: VersionDetails['permissions'];\n error?: boolean;\n}\n\nfunction scoreColor(score: number): (text: string) => string {\n if (score >= 7) return chalk.green;\n if (score >= 4) return chalk.yellow;\n return chalk.red;\n}\n\nfunction formatScore(result: AuditResult): string {\n if (result.error) return chalk.dim('error');\n if (result.score == null || result.status !== 'completed') {\n return chalk.dim('pending');\n }\n return scoreColor(result.score)(result.score.toFixed(1));\n}\n\nfunction formatStatus(result: AuditResult): string {\n if (result.error) return chalk.dim('error');\n if (result.score == null || result.status !== 'completed') {\n return chalk.dim('Analysis pending');\n }\n if (result.score >= 4) return chalk.green('pass');\n return chalk.red('issues');\n}\n\nfunction padRight(text: string, width: number): string {\n if (text.length >= width) return text;\n return text + ' '.repeat(width - text.length);\n}\n\n/**\n * Parse a lockfile key like \"@org/skill@1.0.0\" into { name, version }.\n * Scoped packages start with @, so find the LAST @ to split.\n */\nfunction parseLockKey(key: string): { name: string; version: string } {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return {\n name: key.slice(0, lastAt),\n version: key.slice(lastAt + 1)\n };\n}\n\nasync function fetchVersionDetails(registryUrl: string, name: string, version: string): Promise<VersionDetails> {\n const encodedName = encodeURIComponent(name);\n const url = `${registryUrl}/api/v1/skills/${encodedName}/${version}`;\n\n let res: Response;\n try {\n res = await fetch(url, {\n headers: { 'User-Agent': USER_AGENT }\n });\n } catch (err) {\n throw new Error(`Network error fetching audit data: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n throw new Error(`API error for ${name}@${version}: ${res.status} ${res.statusText}`);\n }\n\n return (await res.json()) as VersionDetails;\n}\n\nfunction displayDetailedAudit(result: AuditResult): void {\n console.log('');\n console.log(chalk.bold(result.name));\n console.log('');\n console.log(`${chalk.dim('Version:'.padEnd(14))}${result.version}`);\n console.log(`${chalk.dim('Audit Score:'.padEnd(14))}${formatScore(result)}`);\n console.log(`${chalk.dim('Status:'.padEnd(14))}${result.status}`);\n\n const perms = result.permissions;\n if (perms) {\n console.log('');\n console.log(chalk.bold('Permissions:'));\n\n const networkDomains = perms.network?.outbound;\n if (networkDomains && networkDomains.length > 0) {\n console.log(` ${chalk.dim('Network:'.padEnd(14))}${networkDomains.join(', ')}`);\n }\n\n const fsRead = perms.filesystem?.read;\n const fsWrite = perms.filesystem?.write;\n if (fsRead || fsWrite) {\n const parts: string[] = [];\n if (fsRead && fsRead.length > 0) {\n parts.push(`${fsRead.join(', ')} (read)`);\n }\n if (fsWrite && fsWrite.length > 0) {\n parts.push(`${fsWrite.join(', ')} (write)`);\n }\n console.log(` ${chalk.dim('Filesystem:'.padEnd(14))}${parts.join(', ')}`);\n }\n\n console.log(` ${chalk.dim('Subprocess:'.padEnd(14))}${perms.subprocess ? 'yes' : 'no'}`);\n }\n}\n\nfunction displayTable(results: AuditResult[]): void {\n // Header\n console.log(`${padRight('NAME', 30) + padRight('VERSION', 12) + padRight('SCORE', 10)}STATUS`);\n\n for (const result of results) {\n const name = chalk.bold(padRight(result.name, 30));\n const version = padRight(result.version, 12);\n const score = padRight(formatScore(result), 10);\n const status = formatStatus(result);\n\n console.log(`${name}${version}${score}${status}`);\n }\n\n // Summary\n const total = results.length;\n const pass = results.filter((r) => !r.error && r.score != null && r.status === 'completed' && r.score >= 4).length;\n const issues = total - pass;\n\n console.log('');\n console.log(\n `${total} skill${total === 1 ? '' : 's'} audited. ` +\n `${pass} pass, ${issues} ${issues === 1 ? 'has' : 'have'} issues.`\n );\n}\n\nexport async function auditCommand(options: AuditOptions): Promise<void> {\n const { name, configDir } = options;\n const config = getConfig(configDir);\n\n // Read lockfile\n const lock = readLockfile();\n\n if (!lock) {\n console.log('No lockfile found. Run: tank install');\n return;\n }\n\n const entries = Object.entries(lock.skills);\n\n if (entries.length === 0) {\n console.log('No skills installed.');\n return;\n }\n\n // Single skill audit\n if (name) {\n // Find the skill in lockfile\n const matchingEntry = entries.find(([key]) => {\n const parsed = parseLockKey(key);\n return parsed.name === name;\n });\n\n if (!matchingEntry) {\n console.log(`Skill not installed: ${name}`);\n return;\n }\n\n const [key] = matchingEntry;\n const parsed = parseLockKey(key);\n\n const details = await fetchVersionDetails(config.registry, parsed.name, parsed.version);\n\n const result: AuditResult = {\n name: parsed.name,\n version: parsed.version,\n score: details.auditScore,\n status: details.auditStatus,\n permissions: details.permissions\n };\n\n displayDetailedAudit(result);\n return;\n }\n\n // Audit all skills\n const results: AuditResult[] = [];\n\n for (const [key] of entries) {\n const parsed = parseLockKey(key);\n\n try {\n const details = await fetchVersionDetails(config.registry, parsed.name, parsed.version);\n\n results.push({\n name: parsed.name,\n version: parsed.version,\n score: details.auditScore,\n status: details.auditStatus\n });\n } catch (err) {\n // For network errors, re-throw immediately\n if (err instanceof Error && err.message.startsWith('Network error')) {\n throw err;\n }\n\n // For API errors (404, etc.), show the skill with error status\n results.push({\n name: parsed.name,\n version: parsed.version,\n score: null,\n status: 'error',\n error: true\n });\n }\n }\n\n displayTable(results);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface AgentInfo {\n id: string;\n name: string;\n skillsDir: string;\n}\n\ninterface AgentDefinition {\n id: string;\n name: string;\n /** Returns possible config directories in priority order. First is the default. */\n configDirs: (homedir: string) => string[];\n}\n\nconst resolveHomedir = (homedir?: string): string => homedir ?? os.homedir();\n\nconst isWindows = process.platform === 'win32';\n\nexport const SUPPORTED_AGENTS: AgentDefinition[] = [\n {\n id: 'claude',\n name: 'Claude Code',\n configDirs: (homedir) => [path.join(homedir, '.claude')]\n },\n {\n id: 'opencode',\n name: 'OpenCode',\n configDirs: (homedir) => {\n const dirs = [path.join(homedir, '.config', 'opencode')];\n if (isWindows) {\n const appData = process.env.APPDATA;\n if (appData) dirs.push(path.join(appData, 'opencode'));\n }\n return dirs;\n }\n },\n {\n id: 'cursor',\n name: 'Cursor',\n configDirs: (homedir) => {\n const dirs = [path.join(homedir, '.cursor')];\n if (isWindows) {\n const appData = process.env.APPDATA;\n if (appData) dirs.push(path.join(appData, 'Cursor'));\n }\n return dirs;\n }\n },\n {\n id: 'codex',\n name: 'Codex',\n configDirs: (homedir) => [path.join(homedir, '.codex')]\n },\n {\n id: 'openclaw',\n name: 'OpenClaw',\n configDirs: (homedir) => [path.join(homedir, '.openclaw')]\n },\n {\n id: 'universal',\n name: 'Universal',\n configDirs: (homedir) => [path.join(homedir, '.agents')]\n }\n];\n\n/**\n * Returns the first existing config directory for an agent,\n * or the first (default) directory if none exist.\n */\nfunction resolveConfigDir(agent: AgentDefinition, homedir: string): string {\n const dirs = agent.configDirs(homedir);\n return dirs.find((d) => fs.existsSync(d)) ?? dirs[0];\n}\n\nfunction isAgentInstalled(agent: AgentDefinition, homedir: string): boolean {\n return agent.configDirs(homedir).some((d) => fs.existsSync(d));\n}\n\nexport function getSupportedAgents(homedir?: string): AgentInfo[] {\n const resolved = resolveHomedir(homedir);\n return SUPPORTED_AGENTS.map((agent) => ({\n id: agent.id,\n name: agent.name,\n skillsDir: path.join(resolveConfigDir(agent, resolved), 'skills')\n }));\n}\n\nexport function detectInstalledAgents(homedir?: string): AgentInfo[] {\n const resolved = resolveHomedir(homedir);\n return SUPPORTED_AGENTS.filter((agent) => isAgentInstalled(agent, resolved)).map((agent) => ({\n id: agent.id,\n name: agent.name,\n skillsDir: path.join(resolveConfigDir(agent, resolved), 'skills')\n }));\n}\n\nexport function getAgentSkillDir(agentId: string, homedir?: string): string | null {\n const resolved = resolveHomedir(homedir);\n const agent = SUPPORTED_AGENTS.find((entry) => entry.id === agentId);\n if (!agent) {\n return null;\n }\n return path.join(resolveConfigDir(agent, resolved), 'skills');\n}\n\nexport function getSymlinkName(skillName: string): string {\n const match = skillName.match(/^@([^/]+)\\/(.+)$/);\n if (!match) {\n return skillName;\n }\n const [, scope, name] = match;\n if (scope.length === 0 || name.length === 0) {\n return skillName;\n }\n return `${scope}--${name}`;\n}\n\nexport function getGlobalSkillsDir(homedir?: string): string {\n return path.join(resolveHomedir(homedir), '.tank', 'skills');\n}\n\nexport function getGlobalAgentSkillsDir(homedir?: string): string {\n return path.join(resolveHomedir(homedir), '.tank', 'agent-skills');\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface LinkEntry {\n source: 'local' | 'global' | 'dev';\n sourceDir: string;\n installedAt: string;\n agentLinks: Record<string, string>;\n}\n\nexport interface LinksManifest {\n version: 1;\n links: Record<string, LinkEntry>;\n}\n\nexport function createEmptyManifest(): LinksManifest {\n return { version: 1, links: {} };\n}\n\nexport function readLinks(linksDir: string): LinksManifest {\n if (!fs.existsSync(linksDir)) {\n return createEmptyManifest();\n }\n\n const linksPath = path.join(linksDir, 'links.json');\n if (!fs.existsSync(linksPath)) {\n return createEmptyManifest();\n }\n\n try {\n const raw = fs.readFileSync(linksPath, 'utf-8');\n return JSON.parse(raw) as LinksManifest;\n } catch {\n return createEmptyManifest();\n }\n}\n\nexport function writeLinks(linksDir: string, manifest: LinksManifest): void {\n if (!fs.existsSync(linksDir)) {\n fs.mkdirSync(linksDir, { recursive: true });\n }\n\n const sortedLinks: Record<string, LinkEntry> = {};\n for (const skillName of Object.keys(manifest.links).sort()) {\n const entry = manifest.links[skillName];\n const sortedAgentLinks: Record<string, string> = {};\n for (const agentId of Object.keys(entry.agentLinks).sort()) {\n sortedAgentLinks[agentId] = entry.agentLinks[agentId];\n }\n\n sortedLinks[skillName] = {\n source: entry.source,\n sourceDir: entry.sourceDir,\n installedAt: entry.installedAt,\n agentLinks: sortedAgentLinks\n };\n }\n\n const output: LinksManifest = {\n version: manifest.version,\n links: sortedLinks\n };\n\n fs.writeFileSync(path.join(linksDir, 'links.json'), `${JSON.stringify(output, null, 2)}\\n`);\n}\n\nexport function readLocalLinks(projectDir?: string): LinksManifest {\n const dir = projectDir ?? process.cwd();\n return readLinks(path.join(dir, '.tank'));\n}\n\nexport function readGlobalLinks(homedir?: string): LinksManifest {\n const home = homedir ?? os.homedir();\n return readLinks(path.join(home, '.tank'));\n}\n\nexport function addLink(manifest: LinksManifest, skillName: string, entry: LinkEntry): LinksManifest {\n return {\n version: manifest.version,\n links: {\n ...manifest.links,\n [skillName]: entry\n }\n };\n}\n\nexport function removeLink(manifest: LinksManifest, skillName: string): LinksManifest {\n const links = { ...manifest.links };\n if (skillName in links) {\n delete links[skillName];\n }\n\n return {\n version: manifest.version,\n links\n };\n}\n\nexport function getLinksForSkill(manifest: LinksManifest, skillName: string): LinkEntry | undefined {\n return manifest.links[skillName];\n}\n\nexport function addAgentLink(\n manifest: LinksManifest,\n skillName: string,\n agentId: string,\n symlinkPath: string\n): LinksManifest {\n const existing = manifest.links[skillName];\n const baseEntry: LinkEntry = existing ?? {\n source: 'local',\n sourceDir: process.cwd(),\n installedAt: new Date().toISOString(),\n agentLinks: {}\n };\n\n const agentLinks = {\n ...baseEntry.agentLinks,\n [agentId]: symlinkPath\n };\n\n return addLink(manifest, skillName, { ...baseEntry, agentLinks });\n}\n\nexport function removeAgentLink(manifest: LinksManifest, skillName: string, agentId: string): LinksManifest {\n const existing = manifest.links[skillName];\n if (!existing) {\n return { version: manifest.version, links: { ...manifest.links } };\n }\n\n const agentLinks = { ...existing.agentLinks };\n if (agentId in agentLinks) {\n delete agentLinks[agentId];\n }\n\n if (Object.keys(agentLinks).length === 0) {\n return removeLink(manifest, skillName);\n }\n\n return addLink(manifest, skillName, { ...existing, agentLinks });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { type AgentInfo, detectInstalledAgents, getSymlinkName } from './agents.js';\nimport { type LinkEntry, type LinksManifest, readLinks, writeLinks } from './links.js';\n\nexport interface LinkResult {\n linked: string[];\n skipped: string[];\n failed: Array<{ agentId: string; error: string }>;\n}\n\nexport interface UnlinkResult {\n unlinked: string[];\n notFound: string[];\n}\n\nexport interface AgentLinkStatus {\n agentId: string;\n agentName: string;\n linked: boolean;\n symlinkPath: string;\n targetValid: boolean;\n}\n\ninterface LinkOptions {\n skillName: string;\n sourceDir: string;\n linksDir: string;\n source: 'local' | 'global' | 'dev';\n homedir?: string;\n}\n\ninterface UnlinkOptions {\n skillName: string;\n linksDir: string;\n homedir?: string;\n}\n\ninterface StatusOptions {\n skillName: string;\n linksDir: string;\n homedir?: string;\n}\n\ninterface AllStatusOptions {\n linksDir: string;\n homedir?: string;\n}\n\ninterface SymlinkCheck {\n exists: boolean;\n isSymlink: boolean;\n targetPath: string | null;\n targetValid: boolean;\n}\n\nconst resolveSymlinkTarget = (symlinkPath: string, target: string): string => {\n if (path.isAbsolute(target)) {\n return target;\n }\n return path.resolve(path.dirname(symlinkPath), target);\n};\n\nconst checkSymlink = (symlinkPath: string): SymlinkCheck => {\n try {\n const stats = fs.lstatSync(symlinkPath);\n if (!stats.isSymbolicLink()) {\n return { exists: true, isSymlink: false, targetPath: null, targetValid: false };\n }\n\n const rawTarget = fs.readlinkSync(symlinkPath);\n const targetPath = resolveSymlinkTarget(symlinkPath, rawTarget);\n const targetValid = fs.existsSync(targetPath);\n return { exists: true, isSymlink: true, targetPath, targetValid };\n } catch {\n return { exists: false, isSymlink: false, targetPath: null, targetValid: false };\n }\n};\n\nconst createEntry = (manifest: LinksManifest, skillName: string, entry: LinkEntry): LinksManifest => ({\n version: manifest.version,\n links: {\n ...manifest.links,\n [skillName]: entry\n }\n});\n\nexport function linkSkillToAgents(options: LinkOptions): LinkResult {\n const result: LinkResult = { linked: [], skipped: [], failed: [] };\n const agents = detectInstalledAgents(options.homedir);\n const symlinkName = getSymlinkName(options.skillName);\n const resolvedSource = path.resolve(options.sourceDir);\n const agentLinks: Record<string, string> = {};\n\n for (const agent of agents) {\n const symlinkPath = path.join(agent.skillsDir, symlinkName);\n\n try {\n fs.mkdirSync(agent.skillsDir, { recursive: true });\n const check = checkSymlink(symlinkPath);\n\n if (check.exists && !check.isSymlink) {\n result.failed.push({\n agentId: agent.id,\n error: `Path exists and is not a symlink: ${symlinkPath}`\n });\n continue;\n }\n\n if (check.exists && check.isSymlink && check.targetPath) {\n const targetMatches = path.resolve(check.targetPath) === resolvedSource;\n if (targetMatches && check.targetValid) {\n result.skipped.push(agent.id);\n agentLinks[agent.id] = symlinkPath;\n continue;\n }\n\n fs.unlinkSync(symlinkPath);\n }\n\n fs.symlinkSync(options.sourceDir, symlinkPath, 'dir');\n result.linked.push(agent.id);\n agentLinks[agent.id] = symlinkPath;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n result.failed.push({ agentId: agent.id, error: message });\n }\n }\n\n const manifest = readLinks(options.linksDir);\n const entry: LinkEntry = {\n source: options.source,\n sourceDir: options.sourceDir,\n installedAt: new Date().toISOString(),\n agentLinks\n };\n const updated = createEntry(manifest, options.skillName, entry);\n writeLinks(options.linksDir, updated);\n\n return result;\n}\n\nexport function unlinkSkillFromAgents(options: UnlinkOptions): UnlinkResult {\n const manifest = readLinks(options.linksDir);\n const entry = manifest.links[options.skillName];\n if (!entry) {\n return { unlinked: [], notFound: [] };\n }\n\n const result: UnlinkResult = { unlinked: [], notFound: [] };\n for (const [agentId, symlinkPath] of Object.entries(entry.agentLinks)) {\n try {\n const stats = fs.lstatSync(symlinkPath);\n if (!stats.isSymbolicLink()) {\n result.notFound.push(agentId);\n continue;\n }\n\n fs.unlinkSync(symlinkPath);\n result.unlinked.push(agentId);\n } catch {\n result.notFound.push(agentId);\n }\n }\n\n const updated: LinksManifest = {\n version: manifest.version,\n links: { ...manifest.links }\n };\n if (options.skillName in updated.links) {\n delete updated.links[options.skillName];\n }\n writeLinks(options.linksDir, updated);\n\n return result;\n}\n\nconst getStatusForAgent = (agent: AgentInfo, skillName: string): AgentLinkStatus => {\n const symlinkName = getSymlinkName(skillName);\n const symlinkPath = path.join(agent.skillsDir, symlinkName);\n const check = checkSymlink(symlinkPath);\n\n return {\n agentId: agent.id,\n agentName: agent.name,\n linked: check.exists && check.isSymlink,\n symlinkPath,\n targetValid: check.exists && check.isSymlink && check.targetValid\n };\n};\n\nexport function getSkillLinkStatus(options: StatusOptions): AgentLinkStatus[] {\n const agents = detectInstalledAgents(options.homedir);\n readLinks(options.linksDir);\n return agents.map((agent) => getStatusForAgent(agent, options.skillName));\n}\n\nexport function getAllLinkStatuses(options: AllStatusOptions): Record<string, AgentLinkStatus[]> {\n const agents = detectInstalledAgents(options.homedir);\n const manifest = readLinks(options.linksDir);\n const result: Record<string, AgentLinkStatus[]> = {};\n\n for (const skillName of Object.keys(manifest.links)) {\n result[skillName] = agents.map((agent) => getStatusForAgent(agent, skillName));\n }\n\n return result;\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport { detectInstalledAgents, getGlobalSkillsDir, getSupportedAgents } from '../lib/agents.js';\nimport { type AgentLinkStatus, getSkillLinkStatus } from '../lib/linker.js';\nimport { readGlobalLinks } from '../lib/links.js';\nimport { resolveLockfilePath, resolveManifestPath } from '../lib/manifest.js';\n\nexport interface DoctorOptions {\n directory?: string;\n homedir?: string;\n}\n\ninterface SkillStatusSummary {\n statusText: string;\n issues: string[];\n}\n\nconst parseLockKey = (key: string): string => {\n const lastAt = key.lastIndexOf('@');\n if (lastAt > 0) {\n return key.slice(0, lastAt);\n }\n return key;\n};\n\nconst getExtractDir = (baseDir: string, skillName: string): string => {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(baseDir, scope, name);\n }\n return path.join(baseDir, skillName);\n};\n\nconst formatAgents = (agents: AgentLinkStatus[], label: string): string => {\n if (agents.length === 0) {\n return `${label}`;\n }\n return `${label} (${agents.map((agent) => agent.agentName).join(', ')})`;\n};\n\nconst summarizeStatus = (\n skillName: string,\n statuses: AgentLinkStatus[],\n extractExists: boolean,\n scope: 'local' | 'global' | 'dev'\n): SkillStatusSummary => {\n if (statuses.length === 0) {\n return {\n statusText: chalk.yellow('⚠️ no agents detected'),\n issues: []\n };\n }\n\n const brokenAgents = statuses.filter((status) => status.linked && !status.targetValid);\n const linkedAgents = statuses.filter((status) => status.linked && status.targetValid);\n\n if (brokenAgents.length > 0) {\n const base = chalk.yellow('⚠️ broken link');\n const text = formatAgents(brokenAgents, base);\n const command =\n scope === 'dev'\n ? `Run \\`tank link\\` in the skill directory to fix ${skillName}`\n : `Run \\`tank install ${skillName}\\` to fix broken link`;\n return {\n statusText: text,\n issues: [command]\n };\n }\n\n if (linkedAgents.length > 0) {\n if (!extractExists && scope !== 'dev') {\n return {\n statusText: chalk.yellow('⚠️ missing extract'),\n issues: [`Run \\`tank install ${skillName}\\` to install missing extract`]\n };\n }\n const text = formatAgents(linkedAgents, chalk.green('✅ linked'));\n return { statusText: text, issues: [] };\n }\n\n if (!extractExists && scope !== 'dev') {\n return {\n statusText: chalk.yellow('⚠️ missing extract'),\n issues: [`Run \\`tank install ${skillName}\\` to install missing extract`]\n };\n }\n\n return {\n statusText: chalk.red('❌ not linked'),\n issues: []\n };\n};\n\nconst printSectionHeader = (title: string): void => {\n console.log(`\\n${chalk.bold(title)}:`);\n};\n\nexport async function doctorCommand(options?: DoctorOptions): Promise<void> {\n try {\n const directory = options?.directory ?? process.cwd();\n const homedir = options?.homedir ?? os.homedir();\n\n const supportedAgents = getSupportedAgents(homedir);\n const installedAgents = detectInstalledAgents(homedir);\n const installedIds = new Set(installedAgents.map((agent) => agent.id));\n\n const resolvedManifest = resolveManifestPath(directory);\n const localSkills = resolvedManifest.exists\n ? Object.keys(JSON.parse(fs.readFileSync(resolvedManifest.path, 'utf-8')).skills ?? {})\n : [];\n localSkills.sort();\n\n const resolvedGlobalLock = resolveLockfilePath(path.join(homedir, '.tank'));\n const globalSkills = resolvedGlobalLock.exists\n ? Object.keys(JSON.parse(fs.readFileSync(resolvedGlobalLock.path, 'utf-8')).skills ?? {}).map(parseLockKey)\n : [];\n const uniqueGlobal = Array.from(new Set(globalSkills)).sort();\n\n const globalLinks = readGlobalLinks(homedir);\n const devLinks = Object.entries(globalLinks.links)\n .filter(([, entry]) => entry.source === 'dev')\n .map(([skillName]) => skillName)\n .sort();\n\n const suggestions = new Set<string>();\n\n console.log(chalk.bold('Tank Doctor Report'));\n console.log(chalk.bold('=================='));\n\n printSectionHeader('Detected Agents');\n for (const agent of supportedAgents) {\n const installed = installedIds.has(agent.id);\n const icon = installed ? chalk.green('✅') : chalk.red('❌');\n const details = installed ? agent.skillsDir : chalk.gray('(not found)');\n console.log(` ${icon} ${agent.name} ${details}`);\n }\n\n if (installedAgents.length === 0) {\n suggestions.add('No agents detected. Install an AI agent to enable skill linking.');\n }\n\n const localLinksDir = path.join(directory, '.tank');\n printSectionHeader(`Local Skills (${localSkills.length}): [project: ${directory}]`);\n if (localSkills.length === 0) {\n console.log(' none');\n }\n for (const skillName of localSkills) {\n const extractDir = getExtractDir(path.join(directory, '.tank', 'skills'), skillName);\n const extractExists = fs.existsSync(extractDir);\n const statuses = getSkillLinkStatus({ skillName, linksDir: localLinksDir, homedir });\n const summary = summarizeStatus(skillName, statuses, extractExists, 'local');\n for (const issue of summary.issues) suggestions.add(issue);\n console.log(` ${skillName} ${summary.statusText}`);\n }\n\n const globalLinksDir = path.join(homedir, '.tank');\n const globalSkillsDir = getGlobalSkillsDir(homedir);\n printSectionHeader(`Global Skills (${uniqueGlobal.length}): [${globalSkillsDir}]`);\n if (uniqueGlobal.length === 0) {\n console.log(' none');\n }\n for (const skillName of uniqueGlobal) {\n const extractDir = getExtractDir(globalSkillsDir, skillName);\n const extractExists = fs.existsSync(extractDir);\n const statuses = getSkillLinkStatus({ skillName, linksDir: globalLinksDir, homedir });\n const summary = summarizeStatus(skillName, statuses, extractExists, 'global');\n for (const issue of summary.issues) suggestions.add(issue);\n console.log(` ${skillName} ${summary.statusText}`);\n }\n\n printSectionHeader(`Dev Links (${devLinks.length}): [tank link]`);\n if (devLinks.length === 0) {\n console.log(' none');\n }\n for (const skillName of devLinks) {\n const statuses = getSkillLinkStatus({ skillName, linksDir: globalLinksDir, homedir });\n const summary = summarizeStatus(skillName, statuses, true, 'dev');\n for (const issue of summary.issues) suggestions.add(issue);\n console.log(` ${skillName} ${summary.statusText}`);\n }\n\n if (localSkills.length === 0 && uniqueGlobal.length === 0 && devLinks.length === 0) {\n suggestions.add('Run `tank install @tank/typescript` to add your first skill');\n }\n\n printSectionHeader('Suggestions');\n if (suggestions.size === 0) {\n console.log(' none');\n } else {\n for (const suggestion of suggestions) {\n console.log(` • ${suggestion}`);\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.log(chalk.red(`Doctor report failed: ${message}`));\n console.log('Suggestions:');\n console.log(' • Run `tank install @tank/typescript` to add your first skill');\n }\n}\n","import chalk from 'chalk';\nimport { getConfig } from '../lib/config.js';\nimport { USER_AGENT } from '../version.js';\n\nexport interface InfoOptions {\n name: string;\n configDir?: string;\n}\n\ninterface SkillMetadata {\n name: string;\n description?: string;\n visibility?: 'public' | 'private';\n latestVersion: string;\n publisher: { displayName: string };\n createdAt: string;\n updatedAt?: string;\n}\n\ninterface VersionDetails {\n name: string;\n version: string;\n permissions?: {\n network?: { outbound?: string[] };\n filesystem?: { read?: string[]; write?: string[] };\n subprocess?: boolean;\n };\n auditScore?: number;\n auditStatus?: string;\n downloadUrl: string;\n publishedAt: string;\n}\n\nfunction formatDate(iso: string): string {\n try {\n return iso.split('T')[0];\n } catch {\n return iso;\n }\n}\n\nfunction labelValue(label: string, value: string): string {\n return `${chalk.dim(label.padEnd(14))}${value}`;\n}\n\nexport async function infoCommand(options: InfoOptions): Promise<void> {\n const { name, configDir } = options;\n const config = getConfig(configDir);\n\n const encodedName = encodeURIComponent(name);\n const metaUrl = `${config.registry}/api/v1/skills/${encodedName}`;\n const headers: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n }\n\n // 1. Fetch skill metadata\n let metaRes: Response;\n try {\n metaRes = await fetch(metaUrl, {\n headers\n });\n } catch (err) {\n throw new Error(`Network error fetching skill info: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (metaRes.status === 404) {\n console.log(`Skill not found: ${name}`);\n return;\n }\n\n if (!metaRes.ok) {\n const body = (await metaRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? `Failed to fetch skill info: ${metaRes.statusText}`);\n }\n\n const meta = (await metaRes.json()) as SkillMetadata;\n\n // 2. Fetch version details for permissions\n const versionUrl = `${config.registry}/api/v1/skills/${encodedName}/${meta.latestVersion}`;\n\n let versionRes: Response;\n try {\n versionRes = await fetch(versionUrl, {\n headers\n });\n } catch (err) {\n throw new Error(`Network error fetching version details: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n let versionData: VersionDetails | undefined;\n if (versionRes.ok) {\n versionData = (await versionRes.json()) as VersionDetails;\n }\n\n // 3. Display info\n console.log('');\n console.log(chalk.bold(meta.name));\n console.log('');\n\n if (meta.description) {\n console.log(labelValue('Description:', meta.description));\n }\n\n console.log(labelValue('Version:', meta.latestVersion));\n if (meta.visibility) {\n console.log(labelValue('Visibility:', meta.visibility));\n }\n console.log(labelValue('Publisher:', meta.publisher?.displayName ?? 'unknown'));\n\n if (versionData?.auditScore != null) {\n console.log(labelValue('Audit Score:', `${versionData.auditScore}/10`));\n }\n\n console.log(labelValue('Created:', formatDate(meta.createdAt)));\n\n // 4. Display permissions\n const perms = versionData?.permissions;\n if (perms) {\n console.log('');\n console.log(chalk.bold('Permissions:'));\n\n const networkDomains = perms.network?.outbound;\n if (networkDomains && networkDomains.length > 0) {\n console.log(` ${chalk.dim('Network:'.padEnd(14))}${networkDomains.join(', ')}`);\n }\n\n const fsRead = perms.filesystem?.read;\n const fsWrite = perms.filesystem?.write;\n if (fsRead || fsWrite) {\n const parts: string[] = [];\n if (fsRead && fsRead.length > 0) {\n parts.push(`${fsRead.join(', ')} (read)`);\n }\n if (fsWrite && fsWrite.length > 0) {\n parts.push(`${fsWrite.join(', ')} (write)`);\n }\n console.log(` ${chalk.dim('Filesystem:'.padEnd(14))}${parts.join(', ')}`);\n }\n\n const subprocess = perms.subprocess;\n console.log(` ${chalk.dim('Subprocess:'.padEnd(14))}${subprocess ? 'yes' : 'no'}`);\n }\n\n // 5. Install hint\n console.log('');\n console.log(`Install: ${chalk.cyan(`tank install ${meta.name}`)}`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { confirm, input } from '@inquirer/prompts';\nimport { MANIFEST_FILENAME, skillsJsonSchema } from '@internal/shared';\nimport { getConfig } from '../lib/config.js';\nimport { logger } from '../lib/logger.js';\nimport { resolveManifestPath } from '../lib/manifest.js';\n\nconst NAME_PATTERN = /^(@[a-z0-9-]+\\/)?[a-z0-9][a-z0-9-]*$/;\nconst SEMVER_PATTERN = /^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/;\nconst MAX_NAME_LENGTH = 214;\n\nexport function validateName(value: string): true | string {\n if (!value) return 'Name must not be empty';\n if (value.length > MAX_NAME_LENGTH) return `Name must be ${MAX_NAME_LENGTH} characters or fewer`;\n if (!NAME_PATTERN.test(value)) return 'Name must be lowercase, alphanumeric + hyphens, optionally scoped (@org/name)';\n return true;\n}\n\nexport function validateVersion(value: string): true | string {\n if (!SEMVER_PATTERN.test(value)) return 'Version must be valid semver (e.g. 1.0.0)';\n return true;\n}\n\nexport interface InitOptions {\n yes?: boolean;\n name?: string;\n version?: string;\n description?: string;\n private?: boolean;\n force?: boolean;\n}\n\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const cwd = process.cwd();\n const resolved = resolveManifestPath(cwd);\n const filePath = resolved.exists ? resolved.path : path.join(cwd, MANIFEST_FILENAME);\n\n if (options.yes) {\n const dirName = path.basename(cwd);\n const name = options.name ?? dirName;\n const version = options.version ?? '0.1.0';\n const description = options.description ?? '';\n const privateChoice = options.private ?? false;\n\n const nameResult = validateName(name);\n if (nameResult !== true) {\n logger.error(nameResult);\n return;\n }\n\n const versionResult = validateVersion(version);\n if (versionResult !== true) {\n logger.error(versionResult);\n return;\n }\n\n if (resolved.exists) {\n if (!options.force) {\n logger.error(`${path.basename(resolved.path)} already exists. Use --force to overwrite.`);\n return;\n }\n }\n\n const manifest: Record<string, unknown> = {\n name,\n version,\n ...(description ? { description } : {}),\n visibility: privateChoice ? 'private' : 'public',\n skills: {},\n permissions: {\n network: { outbound: [] },\n filesystem: { read: [], write: [] },\n subprocess: false\n }\n };\n\n // Validate against schema before writing\n const result = skillsJsonSchema.safeParse(manifest);\n if (!result.success) {\n logger.error(`Generated ${MANIFEST_FILENAME} is invalid:`);\n for (const issue of result.error.issues) {\n logger.error(` ${issue.path.join('.')}: ${issue.message}`);\n }\n return;\n }\n\n // Write file\n fs.writeFileSync(filePath, JSON.stringify(manifest, null, 2) + '\\n');\n logger.success(`Created ${MANIFEST_FILENAME}`);\n return;\n }\n\n // Check if manifest already exists\n if (resolved.exists) {\n logger.warn(`${path.basename(resolved.path)} already exists in this directory.`);\n const overwrite = await confirm({\n message: `Overwrite existing ${path.basename(resolved.path)}?`,\n default: false\n });\n if (!overwrite) {\n logger.info('Aborted.');\n return;\n }\n }\n\n // Get default author from config\n const config = getConfig();\n const defaultAuthor = config.user?.name ?? '';\n\n // Prompt for values\n const dirName = path.basename(cwd);\n\n const name = await input({\n message: 'Skill name:',\n default: dirName,\n validate: validateName\n });\n\n const version = await input({\n message: 'Version:',\n default: '0.1.0',\n validate: validateVersion\n });\n\n const description = await input({\n message: 'Description:',\n default: ''\n });\n\n const privateChoice = await confirm({\n message: 'Make this skill private?',\n default: name.startsWith('@')\n });\n\n const author = await input({\n message: 'Author:',\n default: defaultAuthor\n });\n\n void author;\n\n const manifest: Record<string, unknown> = {\n name,\n version,\n ...(description ? { description } : {}),\n visibility: privateChoice ? 'private' : 'public',\n skills: {},\n permissions: {\n network: { outbound: [] },\n filesystem: { read: [], write: [] },\n subprocess: false\n }\n };\n\n // Validate against schema before writing\n const result = skillsJsonSchema.safeParse(manifest);\n if (!result.success) {\n logger.error(`Generated ${MANIFEST_FILENAME} is invalid:`);\n for (const issue of result.error.issues) {\n logger.error(` ${issue.path.join('.')}: ${issue.message}`);\n }\n return;\n }\n\n // Write file\n fs.writeFileSync(filePath, JSON.stringify(manifest, null, 2) + '\\n');\n logger.success(`Created ${MANIFEST_FILENAME}`);\n}\n","import { resolve } from '@internal/shared';\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport type SkillName = string;\nexport type SemverRange = string;\nexport type SemverVersion = string;\nexport type SkillKey = string;\n\nexport interface RequirementSource {\n kind: 'root' | 'skill';\n from?: string;\n}\n\nexport interface Requirement {\n name: SkillName;\n range: SemverRange;\n source: RequirementSource;\n}\n\nexport interface RegistryVersionInfo {\n version: string;\n integrity: string;\n auditScore: number | null;\n auditStatus: string;\n publishedAt: string;\n}\n\nexport interface RegistrySkillMeta {\n name: string;\n version: string;\n description?: string;\n integrity: string;\n permissions: Record<string, unknown>;\n auditScore: number | null;\n downloadUrl: string;\n dependencies: Record<string, string>;\n}\n\nexport interface RegistryFetcher {\n fetchVersions(name: string): Promise<RegistryVersionInfo[]>;\n fetchMetadata(name: string, version: string): Promise<RegistrySkillMeta>;\n}\n\nexport interface ResolvedNode {\n name: SkillName;\n version: SemverVersion;\n meta: RegistrySkillMeta;\n dependencies: Record<SkillName, SemverVersion>;\n}\n\nexport interface ResolvedGraph {\n nodes: Map<SkillName, ResolvedNode>;\n installOrder: string[];\n}\n\nexport interface ConflictError {\n skillName: SkillName;\n requirements: Requirement[];\n availableVersions: string[];\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nexport function buildSkillKey(name: SkillName, version: SemverVersion): string {\n return `${name}@${version}`;\n}\n\nfunction formatConflictMessage(conflict: ConflictError): string {\n const lines = [`Version conflict for ${conflict.skillName}:`];\n for (const req of conflict.requirements) {\n const origin = req.source.kind === 'root' ? 'root' : (req.source.from ?? 'unknown');\n lines.push(` - ${req.range} (required by ${origin})`);\n }\n lines.push(`Available versions: ${conflict.availableVersions.join(', ')}`);\n lines.push('No single version satisfies all constraints.');\n return lines.join('\\n');\n}\n\n// ── Resolution Algorithm ────────────────────────────────────────────────────\n\nexport async function resolveDependencyTree(\n rootDependencies: Record<SkillName, SemverRange>,\n fetcher: RegistryFetcher\n): Promise<ResolvedGraph> {\n const constraintsByName = new Map<SkillName, Requirement[]>();\n const selectedByName = new Map<SkillName, SemverVersion>();\n const metadataCache = new Map<SkillKey, RegistrySkillMeta>();\n const versionsCache = new Map<SkillName, RegistryVersionInfo[]>();\n\n // Maps SkillKey → dep names it contributed, so stale requirements\n // can be removed when a selected version changes during fixpoint iteration.\n const contributedDeps = new Map<SkillKey, SkillName[]>();\n\n const queue = new Set<SkillName>();\n const inProgress = new Set<SkillName>();\n\n const sortedRootNames = Object.keys(rootDependencies).sort();\n for (const name of sortedRootNames) {\n const range = rootDependencies[name];\n constraintsByName.set(name, [{ name, range, source: { kind: 'root' } }]);\n queue.add(name);\n }\n\n const MAX_ITERATIONS = 10_000;\n let iterations = 0;\n while (queue.size > 0) {\n if (++iterations > MAX_ITERATIONS) {\n throw new Error(\n `Dependency resolution exceeded ${MAX_ITERATIONS} iterations. This likely indicates a degenerate dependency graph.`\n );\n }\n const sorted = [...queue].sort();\n const name = sorted[0];\n queue.delete(name);\n\n // Cycle guard: if this name is mid-expansion up the stack, defer —\n // its constraints will be resolved when expansion completes.\n if (inProgress.has(name)) {\n continue;\n }\n\n const constraints = constraintsByName.get(name);\n if (!constraints || constraints.length === 0) {\n continue;\n }\n\n let versionInfos = versionsCache.get(name);\n if (!versionInfos) {\n versionInfos = await fetcher.fetchVersions(name);\n versionsCache.set(name, versionInfos);\n }\n\n const availableVersions = versionInfos.map((v) => v.version).sort();\n\n const selectedVersion = findSatisfyingVersion(availableVersions, constraints);\n\n if (selectedVersion === null) {\n const conflict: ConflictError = {\n skillName: name,\n requirements: constraints,\n availableVersions\n };\n throw new Error(formatConflictMessage(conflict));\n }\n\n const previousVersion = selectedByName.get(name);\n if (previousVersion === selectedVersion) {\n continue;\n }\n\n inProgress.add(name);\n\n if (previousVersion !== undefined) {\n const prevKey = buildSkillKey(name, previousVersion);\n const prevDeps = contributedDeps.get(prevKey) ?? [];\n for (const depName of prevDeps) {\n const depConstraints = constraintsByName.get(depName);\n if (depConstraints) {\n const filtered = depConstraints.filter((r) => r.source.from !== prevKey);\n if (filtered.length > 0) {\n constraintsByName.set(depName, filtered);\n } else {\n constraintsByName.delete(depName);\n }\n queue.add(depName);\n }\n }\n contributedDeps.delete(prevKey);\n }\n\n selectedByName.set(name, selectedVersion);\n\n const skillKey = buildSkillKey(name, selectedVersion);\n let meta = metadataCache.get(skillKey);\n if (!meta) {\n meta = await fetcher.fetchMetadata(name, selectedVersion);\n metadataCache.set(skillKey, meta);\n }\n\n const depNames: SkillName[] = [];\n const sortedDepEntries = Object.entries(meta.dependencies).sort(([a], [b]) => a.localeCompare(b));\n\n for (const [depName, depRange] of sortedDepEntries) {\n depNames.push(depName);\n\n const requirement: Requirement = {\n name: depName,\n range: depRange,\n source: { kind: 'skill', from: skillKey }\n };\n\n const existing = constraintsByName.get(depName) ?? [];\n existing.push(requirement);\n constraintsByName.set(depName, existing);\n\n queue.add(depName);\n }\n\n contributedDeps.set(skillKey, depNames);\n inProgress.delete(name);\n }\n\n for (const [name] of selectedByName) {\n const constraints = constraintsByName.get(name);\n if (!constraints || constraints.length === 0) {\n selectedByName.delete(name);\n }\n }\n\n return buildGraph(selectedByName, metadataCache);\n}\n\n// ── Version Selection ───────────────────────────────────────────────────────\n\nfunction findSatisfyingVersion(availableVersions: string[], constraints: Requirement[]): string | null {\n const ranges = [...new Set(constraints.map((c) => c.range))];\n\n const satisfyingSets = ranges.map((range) => {\n const matching = new Set<string>();\n for (const v of availableVersions) {\n if (resolve(range, [v]) !== null) {\n matching.add(v);\n }\n }\n return matching;\n });\n\n if (satisfyingSets.length === 0) {\n return null;\n }\n\n let intersection = satisfyingSets[0];\n for (let i = 1; i < satisfyingSets.length; i++) {\n intersection = new Set([...intersection].filter((v) => satisfyingSets[i].has(v)));\n }\n\n if (intersection.size === 0) {\n return null;\n }\n\n const candidates = [...intersection];\n return resolve('*', candidates);\n}\n\n// ── Graph Construction ──────────────────────────────────────────────────────\n\nfunction buildGraph(\n selectedByName: Map<SkillName, SemverVersion>,\n metadataCache: Map<SkillKey, RegistrySkillMeta>\n): ResolvedGraph {\n const nodes = new Map<SkillName, ResolvedNode>();\n const installOrder: string[] = [];\n\n const sortedEntries = [...selectedByName.entries()].sort(([a], [b]) => a.localeCompare(b));\n\n for (const [name, version] of sortedEntries) {\n const skillKey = buildSkillKey(name, version);\n const meta = metadataCache.get(skillKey);\n\n if (!meta) {\n throw new Error(`Internal error: missing metadata for ${skillKey}`);\n }\n\n const resolvedDeps: Record<SkillName, SemverVersion> = {};\n const sortedDepNames = Object.keys(meta.dependencies).sort();\n for (const depName of sortedDepNames) {\n const depVersion = selectedByName.get(depName);\n if (depVersion !== undefined) {\n resolvedDeps[depName] = depVersion;\n }\n }\n\n nodes.set(name, {\n name,\n version,\n meta,\n dependencies: resolvedDeps\n });\n\n installOrder.push(skillKey);\n }\n\n return { nodes, installOrder };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { getSymlinkName } from './agents.js';\n\nexport interface PrepareOptions {\n skillName: string;\n extractDir: string;\n agentSkillsBaseDir: string;\n description?: string;\n}\n\nexport function hasFrontmatter(content: string): boolean {\n return /^---\\s*\\n/.test(content);\n}\n\nexport function stripScope(skillName: string): string {\n const match = skillName.match(/^@[^/]+\\/(.+)$/);\n if (!match) {\n return skillName;\n }\n return match[1] ?? skillName;\n}\n\nexport function extractDescriptionFromMarkdown(content: string): string {\n const lines = content.split(/\\r?\\n/);\n const firstLine = lines.find((line) => line.trim().length > 0);\n if (firstLine && /^#\\s+/.test(firstLine)) {\n return firstLine.replace(/^#\\s+/, '').trim();\n }\n\n let seenHeading = false;\n let paragraphLines: string[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (/^#{1,6}\\s+/.test(trimmed)) {\n seenHeading = true;\n paragraphLines = [];\n continue;\n }\n if (!seenHeading) {\n continue;\n }\n if (trimmed.length === 0) {\n if (paragraphLines.length > 0) {\n break;\n }\n continue;\n }\n paragraphLines.push(trimmed);\n }\n\n if (paragraphLines.length > 0) {\n const paragraph = paragraphLines.join(' ').trim();\n const match = paragraph.match(/^(.+?[.!?])(\\s|$)/);\n return (match ? match[1] : paragraph).trim();\n }\n\n return 'An AI agent skill';\n}\n\nexport function generateFrontmatter(name: string, description: string): string {\n const indented = description\n .split(/\\r?\\n/)\n .map((line) => ` ${line}`)\n .join('\\n');\n return `---\\nname: ${name}\\ndescription: |\\n${indented}\\n---\\n\\n`;\n}\n\nexport function prepareAgentSkillDir(options: PrepareOptions): string {\n const { skillName, extractDir, agentSkillsBaseDir, description } = options;\n const symlinkName = getSymlinkName(skillName);\n const targetDir = path.resolve(agentSkillsBaseDir, symlinkName);\n fs.mkdirSync(targetDir, { recursive: true });\n\n const sourceSkillPath = path.join(extractDir, 'SKILL.md');\n const targetSkillPath = path.join(targetDir, 'SKILL.md');\n const baseName = stripScope(skillName);\n\n if (!fs.existsSync(sourceSkillPath)) {\n const fallbackDescription = description ?? 'An AI agent skill';\n const minimal = generateFrontmatter(baseName, fallbackDescription);\n fs.writeFileSync(targetSkillPath, minimal, 'utf-8');\n } else {\n const content = fs.readFileSync(sourceSkillPath, 'utf-8');\n if (hasFrontmatter(content)) {\n fs.writeFileSync(targetSkillPath, content, 'utf-8');\n } else {\n const resolvedDescription = description ?? extractDescriptionFromMarkdown(content);\n const frontmatter = generateFrontmatter(baseName, resolvedDescription);\n fs.writeFileSync(targetSkillPath, `${frontmatter}${content}`, 'utf-8');\n }\n }\n\n const entries = fs.readdirSync(extractDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name === 'SKILL.md') {\n continue;\n }\n const sourcePath = path.join(extractDir, entry.name);\n const targetPath = path.join(targetDir, entry.name);\n fs.cpSync(sourcePath, targetPath, { recursive: true });\n }\n\n return targetDir;\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { LEGACY_MANIFEST_FILENAME, MANIFEST_FILENAME, type Permissions, type SkillsLock } from '@internal/shared';\nimport type ora from 'ora';\nimport { extract } from 'tar';\nimport { buildSkillKey, type ResolvedNode } from './dependency-resolver.js';\nimport { logger } from './logger.js';\n\nexport async function downloadAllParallel(\n nodes: ResolvedNode[],\n spinner: ReturnType<typeof ora>\n): Promise<Map<string, { buffer: Buffer; integrity: string }>> {\n const results = new Map<string, { buffer: Buffer; integrity: string }>();\n const CONCURRENCY_LIMIT = 8;\n\n for (let i = 0; i < nodes.length; i += CONCURRENCY_LIMIT) {\n const batch = nodes.slice(i, i + CONCURRENCY_LIMIT);\n const promises = batch.map(async (node) => {\n spinner.text = `Downloading ${node.name}@${node.version}...`;\n let res: Response;\n try {\n res = await fetch(node.meta.downloadUrl);\n } catch (err) {\n throw new Error(\n `Network error downloading tarball for ${node.name}@${node.version}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (!res.ok) {\n throw new Error(`Failed to download ${node.name}@${node.version}: ${res.status} ${res.statusText}`);\n }\n\n const buffer = Buffer.from(await res.arrayBuffer());\n const hash = crypto.createHash('sha512').update(buffer).digest('base64');\n const computedIntegrity = `sha512-${hash}`;\n if (computedIntegrity !== node.meta.integrity) {\n throw new Error(\n `Integrity mismatch for ${node.name}@${node.version}. Expected: ${node.meta.integrity}, Got: ${computedIntegrity}`\n );\n }\n\n return { name: node.name, buffer, integrity: computedIntegrity };\n });\n\n const batchResults = await Promise.all(promises);\n for (const result of batchResults) {\n results.set(result.name, { buffer: result.buffer, integrity: result.integrity });\n }\n }\n\n return results;\n}\n\nexport function verifyExtractedDependencies(extractDir: string, node: ResolvedNode): void {\n let extractedManifestPath = path.join(extractDir, MANIFEST_FILENAME);\n if (!fs.existsSync(extractedManifestPath)) {\n extractedManifestPath = path.join(extractDir, LEGACY_MANIFEST_FILENAME);\n }\n if (!fs.existsSync(extractedManifestPath)) {\n return;\n }\n\n try {\n const raw = fs.readFileSync(extractedManifestPath, 'utf-8');\n const manifest = JSON.parse(raw) as Record<string, unknown>;\n const extractedDeps = (manifest.skills ?? {}) as Record<string, string>;\n const apiDeps = node.meta.dependencies;\n const extractedSorted = Object.fromEntries(Object.entries(extractedDeps).sort(([a], [b]) => a.localeCompare(b)));\n const apiSorted = Object.fromEntries(Object.entries(apiDeps).sort(([a], [b]) => a.localeCompare(b)));\n if (JSON.stringify(extractedSorted) !== JSON.stringify(apiSorted)) {\n logger.warn(`Dependency mismatch for ${node.name}@${node.version}: manifest deps differ from registry`);\n }\n } catch {\n // Non-fatal.\n }\n}\n\nexport function readExtractedDependencies(extractDir: string): Record<string, string> {\n let extractedManifestPath = path.join(extractDir, MANIFEST_FILENAME);\n if (!fs.existsSync(extractedManifestPath)) {\n extractedManifestPath = path.join(extractDir, LEGACY_MANIFEST_FILENAME);\n }\n if (!fs.existsSync(extractedManifestPath)) {\n return {};\n }\n\n try {\n const raw = fs.readFileSync(extractedManifestPath, 'utf-8');\n const manifest = JSON.parse(raw) as Record<string, unknown>;\n const extractedDeps = manifest.skills;\n if (!extractedDeps || typeof extractedDeps !== 'object') {\n return {};\n }\n\n const deps: Record<string, string> = {};\n for (const [depName, depRange] of Object.entries(extractedDeps as Record<string, unknown>)) {\n if (typeof depRange === 'string') {\n deps[depName] = depRange;\n }\n }\n return deps;\n } catch {\n return {};\n }\n}\n\nexport function writeLockfileWithResolvedGraph(\n lock: SkillsLock,\n nodes: ResolvedNode[],\n downloaded: Map<string, { buffer: Buffer; integrity: string }>\n): SkillsLock {\n for (const node of nodes) {\n const key = buildSkillKey(node.name, node.version);\n\n if (!downloaded.has(node.name) && lock.skills[key]) {\n continue;\n }\n\n const integrity = downloaded.get(node.name)?.integrity ?? lock.skills[key]?.integrity ?? node.meta.integrity;\n\n lock.skills[key] = {\n resolved: node.meta.downloadUrl,\n integrity,\n permissions: node.meta.permissions as Permissions,\n audit_score: node.meta.auditScore ?? null,\n dependencies: node.dependencies\n };\n }\n\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n return lock;\n}\n\n/**\n * Extract a tarball safely with security checks.\n * Rejects: absolute paths, path traversal (..), symlinks/hardlinks.\n */\nexport async function extractSafely(tarball: Buffer, destDir: string): Promise<void> {\n const tmpTarball = path.join(destDir, '.tmp-tarball.tgz');\n fs.writeFileSync(tmpTarball, tarball);\n\n try {\n await extract({\n file: tmpTarball,\n cwd: destDir,\n filter: (entryPath: string) => {\n if (path.isAbsolute(entryPath)) {\n throw new Error(`Absolute path in tarball: ${entryPath}`);\n }\n if (entryPath.split('/').includes('..') || entryPath.split(path.sep).includes('..')) {\n throw new Error(`Path traversal in tarball: ${entryPath}`);\n }\n return true;\n },\n onReadEntry: (entry) => {\n if (entry.type === 'SymbolicLink' || entry.type === 'Link') {\n throw new Error(`Symlink/hardlink in tarball: ${entry.path}`);\n }\n }\n });\n } finally {\n if (fs.existsSync(tmpTarball)) {\n fs.unlinkSync(tmpTarball);\n }\n }\n}\n\nexport function getExtractDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n\nexport function getGlobalExtractDir(homedir: string, skillName: string): string {\n const globalDir = path.join(homedir, '.tank', 'skills');\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(globalDir, scope, name);\n }\n return path.join(globalDir, skillName);\n}\n\nexport function parseLockKey(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return key.slice(0, lastAt);\n}\n\nexport function parseVersionFromLockKey(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0 || lastAt === key.length - 1) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return key.slice(lastAt + 1);\n}\n\nexport function getResolvedNodesInOrder(nodes: Map<string, ResolvedNode>, installOrder: string[]): ResolvedNode[] {\n const orderedNodes: ResolvedNode[] = [];\n for (const key of installOrder) {\n const skillName = parseLockKey(key);\n const node = nodes.get(skillName);\n if (!node) {\n throw new Error(`Internal error: missing resolved node for ${key}`);\n }\n orderedNodes.push(node);\n }\n return orderedNodes;\n}\n","import type { Permissions } from '@internal/shared';\n\n/**\n * Check if a skill's permissions fit within the project's permission budget.\n * Throws if any permission exceeds the budget.\n */\nexport function checkPermissionBudget(\n budget: Permissions,\n skillPerms: Permissions | undefined,\n skillName: string\n): void {\n if (!skillPerms) return;\n\n // Check subprocess\n if (skillPerms.subprocess === true && budget.subprocess !== true) {\n throw new Error(`Permission denied: ${skillName} requires subprocess access, but project budget does not allow it`);\n }\n\n // Check network outbound\n if (skillPerms.network?.outbound && skillPerms.network.outbound.length > 0) {\n const budgetDomains = budget.network?.outbound ?? [];\n for (const domain of skillPerms.network.outbound) {\n if (!isDomainAllowed(domain, budgetDomains)) {\n throw new Error(\n `Permission denied: ${skillName} requests network access to \"${domain}\", which is not in the project's permission budget`\n );\n }\n }\n }\n\n // Check filesystem read\n if (skillPerms.filesystem?.read && skillPerms.filesystem.read.length > 0) {\n const budgetPaths = budget.filesystem?.read ?? [];\n for (const p of skillPerms.filesystem.read) {\n if (!isPathAllowed(p, budgetPaths)) {\n throw new Error(\n `Permission denied: ${skillName} requests filesystem read access to \"${p}\", which is not in the project's permission budget`\n );\n }\n }\n }\n\n // Check filesystem write\n if (skillPerms.filesystem?.write && skillPerms.filesystem.write.length > 0) {\n const budgetPaths = budget.filesystem?.write ?? [];\n for (const p of skillPerms.filesystem.write) {\n if (!isPathAllowed(p, budgetPaths)) {\n throw new Error(\n `Permission denied: ${skillName} requests filesystem write access to \"${p}\", which is not in the project's permission budget`\n );\n }\n }\n }\n}\n\n/**\n * Check if a domain is allowed by the budget's domain list.\n * Supports wildcard matching: *.example.com matches sub.example.com\n */\nexport function isDomainAllowed(domain: string, allowedDomains: string[]): boolean {\n for (const allowed of allowedDomains) {\n if (allowed === domain) return true;\n // Wildcard matching: *.example.com\n if (allowed.startsWith('*.')) {\n const suffix = allowed.slice(1); // .example.com\n if (domain.endsWith(suffix) || domain === allowed.slice(2)) {\n return true;\n }\n // Also match if the skill requests the same wildcard pattern\n if (domain === allowed) return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a path is allowed by the budget's path list.\n * Simple subset check: skill path must match one of the budget paths.\n */\nexport function isPathAllowed(requestedPath: string, allowedPaths: string[]): boolean {\n for (const allowed of allowedPaths) {\n if (allowed === requestedPath) return true;\n // If budget allows ./src/** and skill requests ./src/foo, it's allowed\n if (allowed.endsWith('/**')) {\n const prefix = allowed.slice(0, -3); // ./src\n if (requestedPath.startsWith(prefix)) return true;\n }\n }\n return false;\n}\n\nexport interface PermissionViolation {\n skillName: string;\n type: 'network.outbound' | 'filesystem.read' | 'filesystem.write' | 'subprocess';\n requested: string;\n}\n\n/**\n * Collect all permission violations without throwing.\n * Mirrors checkPermissionBudget() logic but returns violations as an array.\n */\nexport function collectPermissionViolations(\n budget: Permissions,\n skillPerms: Permissions | undefined,\n skillName: string\n): PermissionViolation[] {\n if (!skillPerms) return [];\n\n const violations: PermissionViolation[] = [];\n\n // Check subprocess\n if (skillPerms.subprocess === true && budget.subprocess !== true) {\n violations.push({ skillName, type: 'subprocess', requested: 'true' });\n }\n\n // Check network outbound\n if (skillPerms.network?.outbound && skillPerms.network.outbound.length > 0) {\n const budgetDomains = budget.network?.outbound ?? [];\n for (const domain of skillPerms.network.outbound) {\n if (!isDomainAllowed(domain, budgetDomains)) {\n violations.push({ skillName, type: 'network.outbound', requested: domain });\n }\n }\n }\n\n // Check filesystem read\n if (skillPerms.filesystem?.read && skillPerms.filesystem.read.length > 0) {\n const budgetPaths = budget.filesystem?.read ?? [];\n for (const p of skillPerms.filesystem.read) {\n if (!isPathAllowed(p, budgetPaths)) {\n violations.push({ skillName, type: 'filesystem.read', requested: p });\n }\n }\n }\n\n // Check filesystem write\n if (skillPerms.filesystem?.write && skillPerms.filesystem.write.length > 0) {\n const budgetPaths = budget.filesystem?.write ?? [];\n for (const p of skillPerms.filesystem.write) {\n if (!isPathAllowed(p, budgetPaths)) {\n violations.push({ skillName, type: 'filesystem.write', requested: p });\n }\n }\n }\n\n return violations;\n}\n","import { confirm } from '@inquirer/prompts';\nimport type { Permissions } from '@internal/shared';\nimport { logger } from './logger.js';\nimport type { PermissionViolation } from './permission-checker.js';\n\nexport async function promptForPermissionExpansion(\n violations: PermissionViolation[],\n options: { yes?: boolean; isInteractive?: boolean }\n): Promise<'accept' | 'decline'> {\n if (options.yes === true) return 'accept';\n if (options.isInteractive === false) return 'decline';\n\n logger.warn('The following permissions exceed your project budget:');\n for (const v of violations) {\n logger.warn(` ${v.skillName}: ${v.type} → ${v.requested}`);\n }\n\n const accepted = await confirm({\n message: 'Would you like to add these permissions to tank.json?',\n default: true\n });\n\n return accepted ? 'accept' : 'decline';\n}\n\nexport function mergePermissionsIntoBudget(currentBudget: Permissions, violations: PermissionViolation[]): Permissions {\n const result: Permissions = {\n ...currentBudget,\n network: currentBudget.network\n ? { ...currentBudget.network, outbound: [...(currentBudget.network.outbound ?? [])] }\n : undefined,\n filesystem: currentBudget.filesystem\n ? {\n ...currentBudget.filesystem,\n read: currentBudget.filesystem.read ? [...currentBudget.filesystem.read] : undefined,\n write: currentBudget.filesystem.write ? [...currentBudget.filesystem.write] : undefined\n }\n : undefined\n };\n\n for (const v of violations) {\n switch (v.type) {\n case 'filesystem.read': {\n if (!result.filesystem) result.filesystem = {};\n if (!result.filesystem.read) result.filesystem.read = [];\n if (!result.filesystem.read.includes(v.requested)) {\n result.filesystem.read.push(v.requested);\n }\n break;\n }\n case 'filesystem.write': {\n if (!result.filesystem) result.filesystem = {};\n if (!result.filesystem.write) result.filesystem.write = [];\n if (!result.filesystem.write.includes(v.requested)) {\n result.filesystem.write.push(v.requested);\n }\n break;\n }\n case 'network.outbound': {\n if (!result.network) result.network = {};\n if (!result.network.outbound) result.network.outbound = [];\n if (!result.network.outbound.includes(v.requested)) {\n result.network.outbound.push(v.requested);\n }\n break;\n }\n case 'subprocess': {\n result.subprocess = true;\n break;\n }\n }\n }\n\n return result;\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n LOCKFILE_FILENAME,\n LOCKFILE_VERSION,\n MANIFEST_FILENAME,\n type Permissions,\n resolve,\n type SkillsLock\n} from '@internal/shared';\nimport ora from 'ora';\nimport { detectInstalledAgents, getGlobalAgentSkillsDir, getGlobalSkillsDir } from '../lib/agents.js';\nimport { getConfig } from '../lib/config.js';\nimport {\n buildSkillKey,\n type RegistryFetcher,\n type RegistrySkillMeta,\n type RegistryVersionInfo,\n type ResolvedNode,\n resolveDependencyTree\n} from '../lib/dependency-resolver.js';\nimport { prepareAgentSkillDir } from '../lib/frontmatter.js';\nimport {\n downloadAllParallel,\n extractSafely,\n getExtractDir,\n getGlobalExtractDir,\n getResolvedNodesInOrder,\n parseLockKey,\n parseVersionFromLockKey,\n readExtractedDependencies,\n verifyExtractedDependencies,\n writeLockfileWithResolvedGraph\n} from '../lib/install-pipeline.js';\nimport { linkSkillToAgents } from '../lib/linker.js';\nimport { logger } from '../lib/logger.js';\nimport { resolveLockfilePath, resolveManifestPath } from '../lib/manifest.js';\nimport { collectPermissionViolations } from '../lib/permission-checker.js';\nimport { mergePermissionsIntoBudget, promptForPermissionExpansion } from '../lib/permission-prompt.js';\nimport { USER_AGENT } from '../version.js';\n\nexport interface InstallOptions {\n name: string;\n versionRange?: string;\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n isTransitive?: boolean;\n yes?: boolean;\n}\n\nexport interface LockfileInstallOptions {\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n}\n\nexport interface InstallAllOptions {\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n yes?: boolean;\n}\n\ninterface VersionMetadata {\n name: string;\n version: string;\n description?: string;\n integrity: string;\n permissions: Permissions;\n auditScore: number;\n auditStatus: string;\n downloadUrl: string;\n publishedAt: string;\n}\n\ninterface ExecuteInstallPipelineOptions {\n directory: string;\n configDir?: string;\n global: boolean;\n homedir?: string;\n resolvedHome: string;\n lock: SkillsLock;\n lockPath: string;\n resolvedNodes: ResolvedNode[];\n nodesToInstall: ResolvedNode[];\n rootSkillNames: string[];\n projectPermissions?: Permissions;\n auditMinScore?: number;\n spinner: ReturnType<typeof ora>;\n yes?: boolean;\n skillsJsonPath?: string;\n skillsJson?: Record<string, unknown>;\n}\n\nfunction createRegistryFetcher(registry: string, headers: Record<string, string>): RegistryFetcher {\n const versionsCache = new Map<string, RegistryVersionInfo[]>();\n const metadataCache = new Map<string, RegistrySkillMeta>();\n\n return {\n async fetchVersions(name: string): Promise<RegistryVersionInfo[]> {\n const cached = versionsCache.get(name);\n if (cached) {\n return cached;\n }\n\n const encoded = encodeURIComponent(name);\n let res: Response;\n try {\n res = await fetch(`${registry}/api/v1/skills/${encoded}/versions`, { headers });\n } catch (err) {\n throw new Error(`Network error fetching versions: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n if (res.status === 403) throw new Error('Token lacks required scope: skills:read');\n if (res.status === 404) throw new Error(`Skill not found or no access: ${name}`);\n const body = (await res.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? res.statusText);\n }\n const data = (await res.json()) as { name: string; versions: RegistryVersionInfo[] };\n versionsCache.set(name, data.versions);\n return data.versions;\n },\n async fetchMetadata(name: string, version: string): Promise<RegistrySkillMeta> {\n const cacheKey = buildSkillKey(name, version);\n const cached = metadataCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const encoded = encodeURIComponent(name);\n let res: Response;\n try {\n res = await fetch(`${registry}/api/v1/skills/${encoded}/${version}`, { headers });\n } catch (err) {\n throw new Error(`Network error fetching metadata: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n if (res.status === 403) throw new Error('Token lacks required scope: skills:read');\n if (res.status === 404) throw new Error(`Skill not found or no access: ${name}@${version}`);\n const body = (await res.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? res.statusText);\n }\n\n const data = (await res.json()) as RegistrySkillMeta;\n const normalized: RegistrySkillMeta = {\n ...data,\n dependencies: data.dependencies ?? {}\n };\n metadataCache.set(cacheKey, normalized);\n return normalized;\n }\n };\n}\n\nfunction readSkillsJson(skillsJsonPath: string): Record<string, unknown> {\n try {\n const raw = fs.readFileSync(skillsJsonPath, 'utf-8');\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(skillsJsonPath)}`);\n }\n}\n\nfunction readOrCreateSkillsJson(skillsJsonPath: string): Record<string, unknown> {\n if (!fs.existsSync(skillsJsonPath)) {\n const skillsJson: Record<string, unknown> = { skills: {} };\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n logger.info(`Created ${MANIFEST_FILENAME}`);\n return skillsJson;\n }\n\n return readSkillsJson(skillsJsonPath);\n}\n\nfunction readLockOrFresh(lockPath: string): SkillsLock {\n if (!fs.existsSync(lockPath)) {\n return { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n return { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n}\n\nfunction buildLockedVersionByName(lock: SkillsLock): Map<string, string> {\n const lockedVersionByName = new Map<string, string>();\n for (const key of Object.keys(lock.skills)) {\n lockedVersionByName.set(parseLockKey(key), parseVersionFromLockKey(key));\n }\n return lockedVersionByName;\n}\n\nfunction createExtractDirResolver(\n directory: string,\n global: boolean,\n resolvedHome: string\n): (skillName: string) => string {\n return (skillName: string): string =>\n global ? getGlobalExtractDir(resolvedHome, skillName) : getExtractDir(directory, skillName);\n}\n\nasync function validateResolvedNodes(\n resolvedNodes: ResolvedNode[],\n projectPermissions: Permissions | undefined,\n auditMinScore: number | undefined,\n options?: { yes?: boolean; skillsJsonPath?: string; skillsJson?: Record<string, unknown> }\n): Promise<void> {\n if (!projectPermissions) {\n logger.warn(`No permission budget defined in ${MANIFEST_FILENAME}. Install proceeding without permission checks.`);\n }\n\n if (projectPermissions) {\n const allViolations = resolvedNodes.flatMap((node) =>\n collectPermissionViolations(projectPermissions, node.meta.permissions as Permissions, node.name)\n );\n\n if (allViolations.length > 0) {\n const isInteractive = !process.env.CI && process.stdout.isTTY === true;\n const decision = await promptForPermissionExpansion(allViolations, {\n yes: options?.yes,\n isInteractive\n });\n\n if (decision === 'accept' && options?.skillsJsonPath && options?.skillsJson) {\n const merged = mergePermissionsIntoBudget(projectPermissions, allViolations);\n options.skillsJson.permissions = merged;\n fs.writeFileSync(options.skillsJsonPath, `${JSON.stringify(options.skillsJson, null, 2)}\\n`);\n } else if (decision === 'decline') {\n const first = allViolations[0];\n throw new Error(\n `Permission denied: ${first.skillName} requests ${first.type} access to \"${first.requested}\", which is not in the project's permission budget`\n );\n }\n }\n }\n\n for (const node of resolvedNodes) {\n if (auditMinScore !== undefined) {\n if (node.meta.auditScore === null || node.meta.auditScore === undefined) {\n logger.warn(`Audit score not yet available for ${node.name}. Install proceeding without audit score check.`);\n } else if (node.meta.auditScore < auditMinScore) {\n throw new Error(\n `Audit score ${node.meta.auditScore} for ${node.name} is below minimum threshold ${auditMinScore} defined in ${MANIFEST_FILENAME}`\n );\n }\n }\n }\n}\n\nasync function runLegacyFallback(options: {\n rootSkillNames: string[];\n resolvedNodeByName: Map<string, ResolvedNode>;\n extractDirForSkill: (skillName: string) => string;\n directory: string;\n configDir?: string;\n global: boolean;\n homedir?: string;\n}): Promise<void> {\n const { rootSkillNames, resolvedNodeByName, extractDirForSkill, directory, configDir, global, homedir } = options;\n\n for (const skillName of rootSkillNames) {\n const node = resolvedNodeByName.get(skillName);\n if (!node || Object.keys(node.meta.dependencies).length > 0) {\n continue;\n }\n\n const extractedDeps = readExtractedDependencies(extractDirForSkill(skillName));\n for (const [depName, depRange] of Object.entries(extractedDeps)) {\n if (depName === skillName) {\n continue;\n }\n\n await installCommand({\n name: depName,\n versionRange: depRange,\n directory,\n configDir,\n global,\n homedir,\n isTransitive: true\n });\n }\n }\n}\n\nfunction linkInstalledRoots(options: {\n rootSkillNames: string[];\n resolvedNodeByName: Map<string, ResolvedNode>;\n extractDirForSkill: (skillName: string) => string;\n directory: string;\n global: boolean;\n resolvedHome: string;\n homedir?: string;\n}): void {\n const { rootSkillNames, resolvedNodeByName, extractDirForSkill, directory, global, resolvedHome, homedir } = options;\n\n const agentSkillsBaseDir = global\n ? getGlobalAgentSkillsDir(resolvedHome)\n : path.join(directory, '.tank', 'agent-skills');\n const linksDir = global ? path.join(resolvedHome, '.tank') : path.join(directory, '.tank');\n\n for (const skillName of rootSkillNames) {\n try {\n const node = resolvedNodeByName.get(skillName);\n if (!node) {\n continue;\n }\n\n const agentSkillDir = prepareAgentSkillDir({\n skillName,\n extractDir: extractDirForSkill(skillName),\n agentSkillsBaseDir,\n description: node.meta.description\n });\n const linkResult = linkSkillToAgents({\n skillName,\n sourceDir: agentSkillDir,\n linksDir,\n source: global ? 'global' : 'local',\n homedir\n });\n\n if (linkResult.linked.length > 0) {\n logger.info(`Linked to ${linkResult.linked.length} agent(s)`);\n }\n if (linkResult.failed.length > 0) {\n for (const failedLink of linkResult.failed) {\n logger.warn(`Failed to link to ${failedLink.agentId}: ${failedLink.error}`);\n }\n }\n } catch {\n if (rootSkillNames.length === 1) {\n logger.warn('Agent linking skipped (non-fatal)');\n } else {\n logger.warn(`Agent linking skipped for ${skillName} (non-fatal)`);\n }\n }\n }\n\n const detectedAgents = detectInstalledAgents(homedir);\n if (detectedAgents.length === 0) {\n logger.warn('No agents detected for linking');\n }\n}\n\nasync function executeInstallPipeline(options: ExecuteInstallPipelineOptions): Promise<SkillsLock> {\n const {\n directory,\n configDir,\n global,\n homedir,\n resolvedHome,\n lock,\n lockPath,\n resolvedNodes,\n nodesToInstall,\n rootSkillNames,\n projectPermissions,\n auditMinScore,\n spinner,\n yes,\n skillsJsonPath,\n skillsJson\n } = options;\n\n if (!global) {\n await validateResolvedNodes(resolvedNodes, projectPermissions, auditMinScore, {\n yes,\n skillsJsonPath,\n skillsJson\n });\n }\n\n const extractDirForSkill = createExtractDirResolver(directory, global, resolvedHome);\n const resolvedNodeByName = new Map(resolvedNodes.map((node) => [node.name, node]));\n const downloaded = await downloadAllParallel(nodesToInstall, spinner);\n\n for (const node of nodesToInstall) {\n const payload = downloaded.get(node.name);\n if (!payload) {\n throw new Error(`Missing downloaded tarball for ${node.name}@${node.version}`);\n }\n\n spinner.text = `Extracting ${node.name}@${node.version}...`;\n const extractDir = extractDirForSkill(node.name);\n fs.mkdirSync(extractDir, { recursive: true });\n await extractSafely(payload.buffer, extractDir);\n verifyExtractedDependencies(extractDir, node);\n }\n\n lock.lockfileVersion = LOCKFILE_VERSION;\n const updatedLock = writeLockfileWithResolvedGraph(lock, resolvedNodes, downloaded);\n fs.mkdirSync(path.dirname(lockPath), { recursive: true });\n fs.writeFileSync(lockPath, `${JSON.stringify(updatedLock, null, 2)}\\n`);\n\n await runLegacyFallback({\n rootSkillNames,\n resolvedNodeByName,\n extractDirForSkill,\n directory,\n configDir,\n global,\n homedir\n });\n\n linkInstalledRoots({\n rootSkillNames,\n resolvedNodeByName,\n extractDirForSkill,\n directory,\n global,\n resolvedHome,\n homedir\n });\n\n return updatedLock;\n}\n\nexport async function installCommand(options: InstallOptions): Promise<void> {\n const {\n name,\n versionRange = '*',\n directory = process.cwd(),\n configDir,\n global = false,\n homedir,\n isTransitive = false,\n yes\n } = options;\n\n const config = getConfig(configDir);\n const resolvedHome = homedir ?? os.homedir();\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const resolvedManifest = resolveManifestPath(directory);\n const skillsJsonPath = resolvedManifest.exists ? resolvedManifest.path : path.join(directory, MANIFEST_FILENAME);\n const skillsJson = global ? { skills: {} } : readOrCreateSkillsJson(skillsJsonPath);\n const resolvedLock = global ? resolveLockfilePath(path.join(resolvedHome, '.tank')) : resolveLockfilePath(directory);\n const lockPath = resolvedLock.exists\n ? resolvedLock.path\n : global\n ? path.join(resolvedHome, '.tank', LOCKFILE_FILENAME)\n : path.join(directory, LOCKFILE_FILENAME);\n const lock = readLockOrFresh(lockPath);\n const spinner = ora('Resolving dependency graph...').start();\n\n try {\n const fetcher = createRegistryFetcher(config.registry, requestHeaders);\n const requestedVersions = await fetcher.fetchVersions(name);\n const requestedAvailableVersions = requestedVersions.map((versionInfo) => versionInfo.version);\n const requestedResolvedVersion = resolve(versionRange, requestedAvailableVersions);\n if (!requestedResolvedVersion) {\n throw new Error(\n `No version of ${name} satisfies range \"${versionRange}\". Available: ${requestedAvailableVersions.join(', ')}`\n );\n }\n\n const requestedLockKey = buildSkillKey(name, requestedResolvedVersion);\n if (lock.skills[requestedLockKey]) {\n logger.info(`${name}@${requestedResolvedVersion} is already installed`);\n spinner.succeed(`${name}@${requestedResolvedVersion} is already installed`);\n return;\n }\n\n const rootDependencies: Record<string, string> = {};\n if (!global && !isTransitive) {\n const existingSkills = (skillsJson.skills ?? {}) as Record<string, string>;\n const lockedVersionByName = buildLockedVersionByName(lock);\n\n for (const [skillName, range] of Object.entries(existingSkills)) {\n if (typeof range !== 'string') {\n continue;\n }\n\n rootDependencies[skillName] = lockedVersionByName.get(skillName) ?? range;\n }\n }\n rootDependencies[name] = versionRange;\n\n const resolvedGraph = await resolveDependencyTree(rootDependencies, fetcher);\n const resolvedNodes = getResolvedNodesInOrder(resolvedGraph.nodes, resolvedGraph.installOrder);\n const rootNode = resolvedGraph.nodes.get(name);\n if (!rootNode) {\n throw new Error(`Failed to resolve requested skill: ${name}`);\n }\n\n const nodesToInstall = resolvedNodes.filter((node) => {\n const lockKey = buildSkillKey(node.name, node.version);\n return !lock.skills[lockKey];\n });\n\n const projectPermissions = global ? undefined : (skillsJson.permissions as Permissions | undefined);\n const auditMinScore = global ? undefined : (skillsJson.audit as { min_score?: number } | undefined)?.min_score;\n\n await executeInstallPipeline({\n directory,\n configDir,\n global,\n homedir,\n resolvedHome,\n lock,\n lockPath,\n resolvedNodes,\n nodesToInstall,\n rootSkillNames: [name],\n projectPermissions,\n auditMinScore,\n spinner,\n yes,\n skillsJsonPath,\n skillsJson\n });\n\n if (!global && !isTransitive) {\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n skills[name] = versionRange === '*' ? `^${rootNode.version}` : versionRange;\n skillsJson.skills = skills;\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n }\n\n spinner.succeed(`Installed ${name}@${rootNode.version}`);\n } catch (err) {\n spinner.fail('Install failed');\n throw err;\n }\n}\n\nexport async function installFromLockfile(options: LockfileInstallOptions): Promise<void> {\n const { directory = process.cwd(), configDir, global = false, homedir } = options;\n const resolvedHome = homedir ?? os.homedir();\n const config = getConfig(configDir);\n\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const resolvedLock = global ? resolveLockfilePath(path.join(resolvedHome, '.tank')) : resolveLockfilePath(directory);\n const lockPath = resolvedLock.path;\n if (!resolvedLock.exists) {\n throw new Error(`No ${LOCKFILE_FILENAME} found in ${directory}`);\n }\n\n let lock: SkillsLock;\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(lockPath)}`);\n }\n\n const entries = Object.entries(lock.skills);\n if (entries.length === 0) {\n logger.info('No skills in lockfile');\n return;\n }\n\n const spinner = ora('Installing from lockfile...').start();\n const skillsDir = global ? getGlobalSkillsDir(resolvedHome) : path.join(directory, '.tank', 'skills');\n\n try {\n for (const [key, entry] of entries) {\n const skillName = parseLockKey(key);\n const version = parseVersionFromLockKey(key);\n spinner.text = `Installing ${key}...`;\n\n const encodedName = encodeURIComponent(skillName);\n const metaUrl = `${config.registry}/api/v1/skills/${encodedName}/${version}`;\n\n let metaRes: Response;\n try {\n metaRes = await fetch(metaUrl, {\n headers: requestHeaders\n });\n } catch (err) {\n throw new Error(`Network error fetching ${key}: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!metaRes.ok) {\n if (metaRes.status === 404) {\n throw new Error(`Skill or version not found: ${key}`);\n }\n const body = (await metaRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(`Failed to fetch ${key}: ${body?.error ?? metaRes.statusText}`);\n }\n\n const metadata = (await metaRes.json()) as VersionMetadata;\n const downloadUrl = metadata.downloadUrl;\n const downloadRes = await fetch(downloadUrl);\n if (!downloadRes.ok) {\n throw new Error(`Failed to download ${key}: ${downloadRes.status} ${downloadRes.statusText}`);\n }\n\n const tarballBuffer = Buffer.from(await downloadRes.arrayBuffer());\n const computedIntegrity = buildIntegrity(tarballBuffer);\n if (computedIntegrity !== entry.integrity) {\n throw new Error(`Integrity mismatch for ${key}. Expected: ${entry.integrity}, Got: ${computedIntegrity}`);\n }\n\n const extractDir = global ? getGlobalExtractDir(resolvedHome, skillName) : getExtractDir(directory, skillName);\n\n if (fs.existsSync(extractDir)) {\n fs.rmSync(extractDir, { recursive: true, force: true });\n }\n fs.mkdirSync(extractDir, { recursive: true });\n\n await extractSafely(tarballBuffer, extractDir);\n\n if (global) {\n try {\n const agentSkillsBaseDir = getGlobalAgentSkillsDir(resolvedHome);\n const agentSkillDir = prepareAgentSkillDir({\n skillName,\n extractDir,\n agentSkillsBaseDir\n });\n const linkResult = linkSkillToAgents({\n skillName,\n sourceDir: agentSkillDir,\n linksDir: path.join(resolvedHome, '.tank'),\n source: 'global',\n homedir\n });\n const detectedAgents = detectInstalledAgents(homedir);\n if (detectedAgents.length === 0) {\n logger.warn('No agents detected for linking');\n }\n if (linkResult.linked.length > 0) {\n logger.info(`Linked to ${linkResult.linked.length} agent(s)`);\n }\n if (linkResult.failed.length > 0) {\n for (const failedLink of linkResult.failed) {\n logger.warn(`Failed to link to ${failedLink.agentId}: ${failedLink.error}`);\n }\n }\n } catch {\n logger.warn('Agent linking skipped (non-fatal)');\n }\n }\n }\n\n spinner.succeed(`Installed ${entries.length} skill${entries.length === 1 ? '' : 's'} from lockfile`);\n } catch (err) {\n spinner.fail('Install from lockfile failed');\n if (fs.existsSync(skillsDir)) {\n fs.rmSync(skillsDir, { recursive: true, force: true });\n }\n throw err;\n }\n}\n\nexport async function installAll(options: InstallAllOptions): Promise<void> {\n const { directory = process.cwd(), configDir, global = false, homedir, yes } = options;\n const resolvedHome = homedir ?? os.homedir();\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const resolvedLock = global ? resolveLockfilePath(path.join(resolvedHome, '.tank')) : resolveLockfilePath(directory);\n const lockPath = resolvedLock.exists\n ? resolvedLock.path\n : global\n ? path.join(resolvedHome, '.tank', LOCKFILE_FILENAME)\n : path.join(directory, LOCKFILE_FILENAME);\n const resolvedManifest = resolveManifestPath(directory);\n const skillsJsonPath = resolvedManifest.path;\n\n if (resolvedLock.exists) {\n return installFromLockfile({ directory, configDir, global, homedir });\n }\n\n if (global) {\n logger.info(`No ${LOCKFILE_FILENAME} found — nothing to install`);\n return;\n }\n\n if (!resolvedManifest.exists) {\n logger.info(`No ${MANIFEST_FILENAME} found — nothing to install`);\n return;\n }\n\n const skillsJson = readSkillsJson(skillsJsonPath);\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n const skillEntries = Object.entries(skills);\n\n if (skillEntries.length === 0) {\n logger.info(`No skills defined in ${MANIFEST_FILENAME}`);\n return;\n }\n\n const spinner = ora('Resolving dependency graph...').start();\n\n try {\n const rootDependencies: Record<string, string> = {};\n for (const [skillName, range] of skillEntries) {\n if (typeof range === 'string') {\n rootDependencies[skillName] = range;\n }\n }\n\n const fetcher = createRegistryFetcher(config.registry, requestHeaders);\n const resolvedGraph = await resolveDependencyTree(rootDependencies, fetcher);\n const resolvedNodes = getResolvedNodesInOrder(resolvedGraph.nodes, resolvedGraph.installOrder);\n const lock: SkillsLock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n const projectPermissions = skillsJson.permissions as Permissions | undefined;\n const auditMinScore = (skillsJson.audit as { min_score?: number } | undefined)?.min_score;\n\n await executeInstallPipeline({\n directory,\n configDir,\n global,\n homedir,\n resolvedHome,\n lock,\n lockPath,\n resolvedNodes,\n nodesToInstall: resolvedNodes,\n rootSkillNames: skillEntries.map(([skillName]) => skillName),\n projectPermissions,\n auditMinScore,\n spinner,\n yes,\n skillsJsonPath,\n skillsJson\n });\n\n spinner.succeed(`Installed ${skillEntries.length} root skill${skillEntries.length === 1 ? '' : 's'}`);\n } catch (err) {\n spinner.fail('Install failed');\n throw err;\n }\n}\n\nfunction buildIntegrity(buffer: Buffer): string {\n const hash = crypto.createHash('sha512').update(buffer).digest('base64');\n return `sha512-${hash}`;\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { MANIFEST_FILENAME } from '@internal/shared';\nimport { detectInstalledAgents, getGlobalAgentSkillsDir } from '../lib/agents.js';\nimport { hasFrontmatter, prepareAgentSkillDir } from '../lib/frontmatter.js';\nimport { linkSkillToAgents } from '../lib/linker.js';\nimport { readGlobalLinks } from '../lib/links.js';\nimport { logger } from '../lib/logger.js';\nimport { resolveManifestPath } from '../lib/manifest.js';\n\nexport interface LinkOptions {\n directory?: string;\n homedir?: string;\n}\n\nexport async function linkCommand(options: LinkOptions = {}): Promise<void> {\n const workDir = options.directory ?? process.cwd();\n const homedir = options.homedir ?? os.homedir();\n const resolvedManifest = resolveManifestPath(workDir);\n\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found. Run this command from a skill directory.`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n const skillName = skillsJson.name;\n if (typeof skillName !== 'string' || skillName.trim().length === 0) {\n throw new Error(`Missing 'name' in ${path.basename(resolvedManifest.path)}`);\n }\n\n const description = typeof skillsJson.description === 'string' ? skillsJson.description : undefined;\n\n const agents = detectInstalledAgents(options.homedir);\n if (agents.length === 0) {\n logger.info('No AI agents detected. Skills linked to agents will be available once agents are installed.');\n return;\n }\n\n const skillMdPath = path.join(workDir, 'SKILL.md');\n let sourceDir = workDir;\n\n if (fs.existsSync(skillMdPath)) {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n if (!hasFrontmatter(content)) {\n sourceDir = prepareAgentSkillDir({\n skillName,\n extractDir: workDir,\n agentSkillsBaseDir: getGlobalAgentSkillsDir(homedir),\n description\n });\n }\n } else {\n sourceDir = prepareAgentSkillDir({\n skillName,\n extractDir: workDir,\n agentSkillsBaseDir: getGlobalAgentSkillsDir(homedir),\n description\n });\n }\n\n void readGlobalLinks(homedir);\n\n const result = linkSkillToAgents({\n skillName,\n sourceDir,\n linksDir: path.join(homedir, '.tank'),\n source: 'dev',\n homedir: options.homedir\n });\n\n const agentNames = new Map(agents.map((agent) => [agent.id, agent.name]));\n\n for (const agentId of result.linked) {\n logger.success(agentNames.get(agentId) ?? agentId);\n }\n\n for (const agentId of result.skipped) {\n const name = agentNames.get(agentId) ?? agentId;\n logger.warn(`- ${name} (already linked)`);\n }\n\n for (const failure of result.failed) {\n const name = agentNames.get(failure.agentId) ?? failure.agentId;\n logger.error(`${name}: ${failure.error}`);\n }\n\n logger.success(`Linked ${skillName} to ${result.linked.length} agent(s)`);\n}\n","import open from 'open';\nimport { getConfig, setConfig } from '../lib/config.js';\nimport { authFlowLog } from '../lib/debug-logger.js';\nimport { logger } from '../lib/logger.js';\n\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\nexport interface LoginOptions {\n configDir?: string;\n timeout?: number;\n pollInterval?: number;\n}\n\n/**\n * Start the CLI login flow:\n * 1. Generate random state\n * 2. POST /api/v1/cli-auth/start → get authUrl + sessionCode\n * 3. Open browser to authUrl\n * 4. Poll POST /api/v1/cli-auth/exchange until authorized or timeout\n * 5. Write token + user to config\n */\nexport async function loginCommand(options: LoginOptions = {}): Promise<void> {\n const { configDir, timeout = DEFAULT_TIMEOUT_MS, pollInterval = DEFAULT_POLL_INTERVAL_MS } = options;\n const config = getConfig(configDir);\n const baseUrl = config.registry;\n\n // Step 1: Generate random state for CSRF protection\n const state = crypto.randomUUID();\n authFlowLog.info({ state: `${state.slice(0, 8)}...` }, 'Login flow started');\n\n // Step 2: Start auth session\n logger.info('Starting login...');\n\n const startRes = await fetch(`${baseUrl}/api/v1/cli-auth/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ state })\n });\n\n if (!startRes.ok) {\n const body = (await startRes.json().catch(() => null)) as { error?: string } | null;\n authFlowLog.error({ status: startRes.status, error: body?.error }, 'Start request failed');\n throw new Error(`Failed to start auth session: ${body?.error ?? startRes.statusText}`);\n }\n\n authFlowLog.info({ ok: startRes.ok, status: startRes.status }, 'Start response received');\n\n const { authUrl, sessionCode } = (await startRes.json()) as {\n authUrl: string;\n sessionCode: string;\n };\n authFlowLog.info({ authUrl, sessionCode: `${sessionCode.slice(0, 8)}...` }, 'Session created, opening browser');\n\n // Step 3: Open browser\n try {\n await open(authUrl);\n logger.info('Opened browser for authentication.');\n } catch {\n // Browser failed to open — print URL for manual copy\n logger.warn('Could not open browser automatically.');\n logger.info(`Open this URL in your browser:\\n ${authUrl}`);\n }\n\n logger.info('Waiting for authorization...');\n\n // Step 4: Poll exchange endpoint\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n try {\n const exchangeRes = await fetch(`${baseUrl}/api/v1/cli-auth/exchange`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionCode, state })\n });\n authFlowLog.debug({ status: exchangeRes.status, ok: exchangeRes.ok }, 'Exchange poll response');\n\n if (exchangeRes.ok) {\n const { token, user } = (await exchangeRes.json()) as {\n token: string;\n user: { name: string | null; email: string | null };\n };\n\n authFlowLog.info({ userName: user.name, userEmail: user.email }, 'Login successful, saving config');\n\n // Step 5: Write to config\n setConfig({ token, user: user as { name: string; email: string } }, configDir);\n\n const displayName = user.name ?? user.email ?? 'unknown';\n logger.success(`Logged in as ${displayName}`);\n return;\n }\n\n // 400 means session not yet authorized — keep polling\n // Any other error is unexpected\n if (exchangeRes.status !== 400) {\n const body = (await exchangeRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(`Exchange failed: ${body?.error ?? exchangeRes.statusText}`);\n }\n } catch (err) {\n authFlowLog.warn({ error: err instanceof Error ? err.message : String(err) }, 'Exchange poll error');\n // If it's our own thrown error, re-throw\n if (err instanceof Error && err.message.startsWith('Exchange failed:')) {\n throw err;\n }\n // Network errors during polling are transient — keep trying\n }\n\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n authFlowLog.error({}, 'Login timed out');\n throw new Error('Login timed out. Please try again.');\n}\n","import { getConfig, setConfig } from '../lib/config.js';\nimport { logger } from '../lib/logger.js';\n\nexport interface LogoutOptions {\n configDir?: string;\n}\n\n/**\n * Logout command: Remove token and user from config.\n * If not logged in, prints \"Not logged in\" and returns.\n * If logged in, removes token and user, prints success message.\n */\nexport async function logoutCommand(options: LogoutOptions = {}): Promise<void> {\n const { configDir } = options;\n const config = getConfig(configDir);\n\n // Check if logged in\n if (!config.token) {\n logger.warn('Not logged in. Run: tank login');\n return;\n }\n\n // Remove token and user from config\n setConfig({ token: undefined, user: undefined }, configDir);\n\n logger.success('Logged out');\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n MANIFEST_FILENAME\n} from '@internal/shared';\nimport { logger } from '../lib/logger.js';\n\nexport interface MigrateOptions {\n directory?: string;\n}\n\nexport async function migrateCommand(options: MigrateOptions = {}): Promise<void> {\n const dir = options.directory ?? process.cwd();\n let migrated = false;\n\n // Migrate skills.json → tank.json\n const legacyManifest = path.join(dir, LEGACY_MANIFEST_FILENAME);\n const newManifest = path.join(dir, MANIFEST_FILENAME);\n\n if (fs.existsSync(newManifest)) {\n logger.info(`${MANIFEST_FILENAME} already exists — skipping manifest migration`);\n } else if (fs.existsSync(legacyManifest)) {\n fs.copyFileSync(legacyManifest, newManifest);\n logger.success(`${LEGACY_MANIFEST_FILENAME} → ${MANIFEST_FILENAME}`);\n migrated = true;\n } else {\n logger.info(`No ${LEGACY_MANIFEST_FILENAME} found — nothing to migrate`);\n }\n\n // Migrate skills.lock → tank.lock\n const legacyLock = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n const newLock = path.join(dir, LOCKFILE_FILENAME);\n\n if (fs.existsSync(newLock)) {\n logger.info(`${LOCKFILE_FILENAME} already exists — skipping lockfile migration`);\n } else if (fs.existsSync(legacyLock)) {\n fs.copyFileSync(legacyLock, newLock);\n logger.success(`${LEGACY_LOCKFILE_FILENAME} → ${LOCKFILE_FILENAME}`);\n migrated = true;\n } else {\n logger.info(`No ${LEGACY_LOCKFILE_FILENAME} found — nothing to migrate`);\n }\n\n if (migrated) {\n logger.info('Old files were kept. Remove them when ready:');\n if (fs.existsSync(legacyManifest) && fs.existsSync(newManifest)) {\n logger.info(` rm ${LEGACY_MANIFEST_FILENAME}`);\n }\n if (fs.existsSync(legacyLock) && fs.existsSync(newLock)) {\n logger.info(` rm ${LEGACY_LOCKFILE_FILENAME}`);\n }\n logger.info('If your .gitignore or CI configs reference the old filenames, update them too.');\n } else {\n logger.info('Already migrated — nothing to do');\n }\n}\n","import fs from 'node:fs';\nimport type { Permissions, SkillsJson, SkillsLock } from '@internal/shared';\nimport chalk from 'chalk';\nimport { resolveLockfilePath, resolveManifestPath } from '../lib/manifest.js';\n\nexport interface PermissionsOptions {\n directory?: string;\n}\n\n/**\n * Parse a lockfile key like \"@org/skill@1.0.0\" into the skill name \"@org/skill\".\n */\nfunction parseSkillName(key: string): string {\n const lastAt = key.lastIndexOf('@');\n // For scoped packages, lastIndexOf('@') finds the version separator\n // e.g. \"@org/skill@1.0.0\" → lastAt = 10, name = \"@org/skill\"\n if (lastAt > 0) {\n return key.slice(0, lastAt);\n }\n return key;\n}\n\ninterface PermissionEntry {\n value: string;\n skills: string[];\n}\n\ninterface ResolvedPermissions {\n networkOutbound: PermissionEntry[];\n filesystemRead: PermissionEntry[];\n filesystemWrite: PermissionEntry[];\n subprocess: string[]; // list of skill names that request subprocess\n}\n\nfunction collectPermissions(lockfile: SkillsLock): ResolvedPermissions {\n const networkMap = new Map<string, string[]>();\n const fsReadMap = new Map<string, string[]>();\n const fsWriteMap = new Map<string, string[]>();\n const subprocessSkills: string[] = [];\n\n for (const [key, entry] of Object.entries(lockfile.skills)) {\n const skillName = parseSkillName(key);\n const perms = entry.permissions;\n\n if (perms.network?.outbound) {\n for (const domain of perms.network.outbound) {\n const existing = networkMap.get(domain) ?? [];\n existing.push(skillName);\n networkMap.set(domain, existing);\n }\n }\n\n if (perms.filesystem?.read) {\n for (const p of perms.filesystem.read) {\n const existing = fsReadMap.get(p) ?? [];\n existing.push(skillName);\n fsReadMap.set(p, existing);\n }\n }\n\n if (perms.filesystem?.write) {\n for (const p of perms.filesystem.write) {\n const existing = fsWriteMap.get(p) ?? [];\n existing.push(skillName);\n fsWriteMap.set(p, existing);\n }\n }\n\n if (perms.subprocess === true) {\n subprocessSkills.push(skillName);\n }\n }\n\n const toEntries = (map: Map<string, string[]>): PermissionEntry[] =>\n Array.from(map.entries()).map(([value, skills]) => ({ value, skills }));\n\n return {\n networkOutbound: toEntries(networkMap),\n filesystemRead: toEntries(fsReadMap),\n filesystemWrite: toEntries(fsWriteMap),\n subprocess: subprocessSkills\n };\n}\n\n/**\n * Check if a domain is allowed by the budget's domain list.\n * Supports wildcard matching: *.example.com matches sub.example.com\n */\nfunction isDomainAllowed(domain: string, allowedDomains: string[]): boolean {\n for (const allowed of allowedDomains) {\n if (allowed === domain) return true;\n if (allowed.startsWith('*.')) {\n const suffix = allowed.slice(1);\n if (domain.endsWith(suffix) || domain === allowed.slice(2)) {\n return true;\n }\n if (domain === allowed) return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a path is allowed by the budget's path list.\n */\nfunction isPathAllowed(requestedPath: string, allowedPaths: string[]): boolean {\n for (const allowed of allowedPaths) {\n if (allowed === requestedPath) return true;\n if (allowed.endsWith('/**')) {\n const prefix = allowed.slice(0, -3);\n if (requestedPath.startsWith(prefix)) return true;\n }\n }\n return false;\n}\n\ninterface BudgetViolation {\n category: string;\n value: string;\n skills: string[];\n}\n\nfunction checkBudget(resolved: ResolvedPermissions, budget: Permissions): BudgetViolation[] {\n const violations: BudgetViolation[] = [];\n\n const budgetDomains = budget.network?.outbound ?? [];\n for (const entry of resolved.networkOutbound) {\n if (!isDomainAllowed(entry.value, budgetDomains)) {\n violations.push({\n category: 'network outbound',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n const budgetReadPaths = budget.filesystem?.read ?? [];\n for (const entry of resolved.filesystemRead) {\n if (!isPathAllowed(entry.value, budgetReadPaths)) {\n violations.push({\n category: 'filesystem read',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n const budgetWritePaths = budget.filesystem?.write ?? [];\n for (const entry of resolved.filesystemWrite) {\n if (!isPathAllowed(entry.value, budgetWritePaths)) {\n violations.push({\n category: 'filesystem write',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n if (resolved.subprocess.length > 0 && budget.subprocess !== true) {\n violations.push({\n category: 'subprocess',\n value: 'subprocess access',\n skills: resolved.subprocess\n });\n }\n\n return violations;\n}\n\nfunction formatAttribution(skills: string[]): string {\n return chalk.gray(`← ${skills.join(', ')}`);\n}\n\nfunction printPermissionSection(title: string, entries: PermissionEntry[]): void {\n console.log(`\\n${chalk.bold(title)}:`);\n if (entries.length === 0) {\n console.log(' none');\n } else {\n for (const entry of entries) {\n console.log(` ${entry.value} ${formatAttribution(entry.skills)}`);\n }\n }\n}\n\nexport async function permissionsCommand(options?: PermissionsOptions): Promise<void> {\n const dir = options?.directory ?? process.cwd();\n const resolvedLock = resolveLockfilePath(dir);\n\n // 1. Read lockfile\n if (!resolvedLock.exists) {\n console.log('No skills installed.');\n return;\n }\n\n const lockfileContent = fs.readFileSync(resolvedLock.path, 'utf-8');\n const lockfile: SkillsLock = JSON.parse(lockfileContent);\n\n if (!lockfile.skills || Object.keys(lockfile.skills).length === 0) {\n console.log('No skills installed.');\n return;\n }\n\n // 2. Collect permissions\n const resolved = collectPermissions(lockfile);\n\n // 3. Display\n console.log('\\nResolved permissions for this project:\\n');\n\n printPermissionSection('Network (outbound)', resolved.networkOutbound);\n printPermissionSection('Filesystem (read)', resolved.filesystemRead);\n printPermissionSection('Filesystem (write)', resolved.filesystemWrite);\n\n // Subprocess section\n console.log(`\\n${chalk.bold('Subprocess')}:`);\n if (resolved.subprocess.length === 0) {\n console.log(' none');\n } else {\n console.log(` allowed ${formatAttribution(resolved.subprocess)}`);\n }\n\n // 4. Budget check\n const resolvedManifest = resolveManifestPath(dir);\n let budget: Permissions | undefined;\n\n if (resolvedManifest.exists) {\n const skillsJsonContent = fs.readFileSync(resolvedManifest.path, 'utf-8');\n const skillsJson: SkillsJson = JSON.parse(skillsJsonContent);\n budget = skillsJson.permissions;\n }\n\n console.log('');\n\n if (!budget) {\n console.log(`Budget status: ${chalk.yellow('⚠ No budget defined')}`);\n return;\n }\n\n const violations = checkBudget(resolved, budget);\n\n if (violations.length === 0) {\n console.log(`Budget status: ${chalk.green('✓ PASS')} (all within budget)`);\n } else {\n console.log(`Budget status: ${chalk.red('✗ FAIL')}`);\n for (const v of violations) {\n console.log(chalk.red(` - ${v.category}: \"${v.value}\" not in budget (requested by ${v.skills.join(', ')})`));\n }\n }\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Readable } from 'node:stream';\nimport { LEGACY_MANIFEST_FILENAME, MANIFEST_FILENAME, skillsJsonSchema } from '@internal/shared';\nimport ignore from 'ignore';\nimport { create } from 'tar';\n\n// Limits\nconst MAX_PACKAGE_SIZE = 50 * 1024 * 1024; // 50MB\nconst MAX_FILE_COUNT = 1000;\n\n// Default ignore patterns (used when no .tankignore or .gitignore exists)\nconst DEFAULT_IGNORES = ['node_modules', '.git', '.env*', '*.log', '.tank', '.DS_Store'];\n\n// Always ignored regardless of ignore file contents\nconst ALWAYS_IGNORED = ['node_modules', '.git'];\n\n// Ignore file names (not packed into tarball)\nconst IGNORE_FILES = ['.tankignore', '.gitignore'];\n\nexport interface PackResult {\n tarball: Buffer;\n integrity: string; // \"sha512-{base64}\"\n fileCount: number;\n totalSize: number;\n readme: string;\n files: string[];\n}\n\n/**\n * Pack a skill directory into a .tgz tarball with integrity hashing.\n *\n * Validates:\n * - skills.json exists and is valid\n * - SKILL.md exists\n * - No symlinks or hardlinks\n * - No path traversal (.. components)\n * - No absolute paths\n * - File count <= 1000\n * - Tarball size <= 50MB\n */\nexport async function pack(directory: string): Promise<PackResult> {\n const absDir = path.resolve(directory);\n\n // 1. Verify directory exists\n if (!fs.existsSync(absDir)) {\n throw new Error(`Directory does not exist: ${absDir}`);\n }\n\n const stat = fs.statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`);\n }\n\n // 2. Verify manifest (tank.json or skills.json) exists and is valid\n let manifestPath = path.join(absDir, MANIFEST_FILENAME);\n let manifestFilename = MANIFEST_FILENAME;\n if (!fs.existsSync(manifestPath)) {\n manifestPath = path.join(absDir, LEGACY_MANIFEST_FILENAME);\n manifestFilename = LEGACY_MANIFEST_FILENAME;\n }\n if (!fs.existsSync(manifestPath)) {\n throw new Error(`Missing required file: ${MANIFEST_FILENAME}`);\n }\n\n let skillsJsonContent: string;\n try {\n skillsJsonContent = fs.readFileSync(manifestPath, 'utf-8');\n } catch {\n throw new Error(`Failed to read ${manifestFilename}`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(skillsJsonContent);\n } catch {\n throw new Error(`Invalid ${manifestFilename}: not valid JSON`);\n }\n\n const validation = skillsJsonSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid ${manifestFilename}:\\n${issues}`);\n }\n\n // 3. Verify SKILL.md exists and read its content\n const skillMdPath = path.join(absDir, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) {\n throw new Error('Missing required file: SKILL.md');\n }\n\n let readmeContent: string;\n try {\n readmeContent = fs.readFileSync(skillMdPath, 'utf-8');\n } catch {\n throw new Error('Failed to read SKILL.md');\n }\n\n // 4. Build ignore filter\n const ig = buildIgnoreFilter(absDir);\n\n // 5. Collect files with validation\n const files = collectFiles(absDir, absDir, ig);\n\n // 6. Enforce file count limit\n if (files.length > MAX_FILE_COUNT) {\n throw new Error(`Too many files: ${files.length} exceeds maximum of ${MAX_FILE_COUNT}`);\n }\n\n // 7. Calculate total size of source files\n let totalSize = 0;\n for (const file of files) {\n const filePath = path.join(absDir, file);\n const fileStat = fs.statSync(filePath);\n totalSize += fileStat.size;\n }\n\n // 8. Create tarball\n const tarball = await createTarball(absDir, files);\n\n // 9. Enforce tarball size limit\n if (tarball.length > MAX_PACKAGE_SIZE) {\n throw new Error(`Tarball too large: ${tarball.length} bytes exceeds maximum of ${MAX_PACKAGE_SIZE} bytes (50MB)`);\n }\n\n // 10. Compute integrity hash\n const hash = crypto.createHash('sha512').update(tarball).digest('base64');\n const integrity = `sha512-${hash}`;\n\n return {\n tarball,\n integrity,\n fileCount: files.length,\n totalSize,\n readme: readmeContent,\n files\n };\n}\n\n/**\n * Pack a directory into a .tgz tarball for security scanning.\n *\n * Unlike pack(), this function does NOT require skills.json or SKILL.md.\n * It applies the same security checks (no symlinks, no path traversal, etc.)\n * and returns the same PackResult interface.\n *\n * Validates:\n * - Directory exists\n * - No symlinks or hardlinks\n * - No path traversal (.. components)\n * - No absolute paths\n * - File count <= 1000\n * - Tarball size <= 50MB\n *\n * Does NOT validate:\n * - skills.json existence or validity\n * - SKILL.md existence (but reads it if present)\n */\nexport async function packForScan(directory: string): Promise<PackResult> {\n const absDir = path.resolve(directory);\n\n // 1. Verify directory exists\n if (!fs.existsSync(absDir)) {\n throw new Error(`Directory does not exist: ${absDir}`);\n }\n\n const stat = fs.statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`);\n }\n\n // 2. Try to read SKILL.md if it exists (optional for scan)\n let readmeContent = '';\n const skillMdPath = path.join(absDir, 'SKILL.md');\n if (fs.existsSync(skillMdPath)) {\n try {\n readmeContent = fs.readFileSync(skillMdPath, 'utf-8');\n } catch {\n // If we can't read it, just use empty string\n readmeContent = '';\n }\n }\n\n // 3. Build ignore filter\n const ig = buildIgnoreFilter(absDir);\n\n // 4. Collect files with validation\n const files = collectFiles(absDir, absDir, ig);\n\n // 5. Enforce file count limit\n if (files.length > MAX_FILE_COUNT) {\n throw new Error(`Too many files: ${files.length} exceeds maximum of ${MAX_FILE_COUNT}`);\n }\n\n // 6. Calculate total size of source files\n let totalSize = 0;\n for (const file of files) {\n const filePath = path.join(absDir, file);\n const fileStat = fs.statSync(filePath);\n totalSize += fileStat.size;\n }\n\n // 7. Create tarball\n const tarball = await createTarball(absDir, files);\n\n // 8. Enforce tarball size limit\n if (tarball.length > MAX_PACKAGE_SIZE) {\n throw new Error(`Tarball too large: ${tarball.length} bytes exceeds maximum of ${MAX_PACKAGE_SIZE} bytes (50MB)`);\n }\n\n // 9. Compute integrity hash\n const hash = crypto.createHash('sha512').update(tarball).digest('base64');\n const integrity = `sha512-${hash}`;\n\n return {\n tarball,\n integrity,\n fileCount: files.length,\n totalSize,\n readme: readmeContent,\n files\n };\n}\n\n/**\n * Build an ignore filter from .tankignore, .gitignore, or defaults.\n */\nfunction buildIgnoreFilter(dir: string): ReturnType<typeof ignore> {\n const ig = ignore();\n\n // Always add the forced ignores\n ig.add(ALWAYS_IGNORED);\n\n // Check for .tankignore first, then .gitignore, then defaults\n const tankIgnorePath = path.join(dir, '.tankignore');\n const gitIgnorePath = path.join(dir, '.gitignore');\n\n if (fs.existsSync(tankIgnorePath)) {\n const content = fs.readFileSync(tankIgnorePath, 'utf-8');\n ig.add(content);\n // Also ignore the ignore files themselves\n ig.add(IGNORE_FILES);\n } else if (fs.existsSync(gitIgnorePath)) {\n const content = fs.readFileSync(gitIgnorePath, 'utf-8');\n ig.add(content);\n ig.add(IGNORE_FILES);\n } else {\n ig.add(DEFAULT_IGNORES);\n }\n\n return ig;\n}\n\n/**\n * Recursively collect files from a directory, applying ignore rules and security checks.\n */\nfunction collectFiles(baseDir: string, currentDir: string, ig: ReturnType<typeof ignore>): string[] {\n const files: string[] = [];\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n const relativePath = path.relative(baseDir, fullPath);\n\n // Security: check for path traversal\n if (relativePath.split(path.sep).includes('..')) {\n throw new Error(`Path traversal detected: \"${relativePath}\" contains \"..\" component`);\n }\n\n // Security: check for absolute paths\n if (path.isAbsolute(relativePath)) {\n throw new Error(`Absolute path detected: \"${relativePath}\"`);\n }\n\n // Security: check for symlinks using lstat (not stat which follows symlinks)\n const lstatResult = fs.lstatSync(fullPath);\n if (lstatResult.isSymbolicLink()) {\n throw new Error(`Symlink detected: \"${relativePath}\" — symlinks are not allowed in skill packages`);\n }\n\n // Check if this path should be ignored\n // For directories, append '/' so ignore patterns like 'dir/' work correctly\n const pathForIgnore = lstatResult.isDirectory() ? `${relativePath}/` : relativePath;\n\n if (ig.ignores(pathForIgnore)) {\n continue;\n }\n\n if (lstatResult.isDirectory()) {\n // Recurse into subdirectory\n const subFiles = collectFiles(baseDir, fullPath, ig);\n files.push(...subFiles);\n } else if (lstatResult.isFile()) {\n files.push(relativePath);\n }\n // Skip other types (block devices, character devices, FIFOs, sockets)\n }\n\n return files;\n}\n\n/**\n * Create a gzipped tarball from the given files in the directory.\n */\nasync function createTarball(cwd: string, files: string[]): Promise<Buffer> {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n\n // tar.create without `file` returns a readable stream\n const stream = create(\n {\n gzip: true,\n cwd,\n portable: true // Omit system-specific metadata\n },\n files\n ) as unknown as Readable;\n\n stream.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n stream.on('end', () => {\n resolve(Buffer.concat(chunks));\n });\n\n stream.on('error', (err: Error) => {\n reject(err);\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { MANIFEST_FILENAME } from '@internal/shared';\nimport ora from 'ora';\nimport { getConfig } from '../lib/config.js';\nimport { logger } from '../lib/logger.js';\nimport { resolveManifestPath } from '../lib/manifest.js';\nimport { pack } from '../lib/packer.js';\nimport { USER_AGENT } from '../version.js';\n\nexport interface PublishOptions {\n directory?: string;\n configDir?: string;\n dryRun?: boolean;\n private?: boolean;\n visibility?: string;\n}\n\n/**\n * Format bytes into a human-readable size string.\n */\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Publish a skill package to the Tank registry.\n *\n * Flow:\n * 1. Check auth (token exists)\n * 2. Read skills.json from directory\n * 3. Pack directory into tarball\n * 4. If --dry-run: print summary and exit\n * 5. POST /api/v1/skills with manifest → get uploadUrl, skillId, versionId\n * 6. PUT tarball to uploadUrl\n * 7. POST /api/v1/skills/confirm with integrity data\n * 8. Print success\n */\nexport async function publishCommand(options: PublishOptions = {}): Promise<void> {\n const { directory = process.cwd(), configDir, dryRun = false, private: privateFlag, visibility } = options;\n\n // 1. Check auth\n const config = getConfig(configDir);\n if (!config.token) {\n throw new Error('Not logged in. Run: tank login');\n }\n\n // 2. Read manifest (tank.json or skills.json)\n const resolvedManifest = resolveManifestPath(directory);\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found in ${directory}. Run: tank init`);\n }\n\n let manifest: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n manifest = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n if (visibility && visibility !== 'public' && visibility !== 'private') {\n throw new Error(\"Invalid visibility. Use 'public' or 'private'\");\n }\n\n const effectiveVisibility = visibility ?? (privateFlag ? 'private' : undefined);\n if (effectiveVisibility) {\n manifest.visibility = effectiveVisibility;\n }\n\n const name = manifest.name as string;\n const version = manifest.version as string;\n\n // 3. Pack\n const spinner = ora('Packing...').start();\n let packResult: Awaited<ReturnType<typeof pack>>;\n try {\n packResult = await pack(directory);\n } catch (err) {\n spinner.fail('Packing failed');\n throw err;\n }\n\n const { tarball, integrity, fileCount, totalSize, readme, files } = packResult;\n\n // 4. Dry run — print summary, verify auth with server, and exit\n if (dryRun) {\n spinner.stop();\n logger.info(`name: ${name}`);\n logger.info(`version: ${version}`);\n logger.info(`visibility: ${String(manifest.visibility ?? 'default')}`);\n logger.info(`size: ${formatSize(totalSize)} (${fileCount} files)`);\n logger.info(`tarball: ${formatSize(tarball.length)} (compressed)`);\n\n // Verify token with server to catch stale auth before real publish\n try {\n const verifyRes = await fetch(`${config.registry}/api/v1/auth/whoami`, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'User-Agent': USER_AGENT\n }\n });\n\n if (verifyRes.status === 401) {\n logger.warn('Token is invalid or expired. Run: tank login');\n } else if (!verifyRes.ok) {\n logger.warn('Could not verify token with server. Publish may fail.');\n } else {\n logger.success('Auth verified with server.');\n }\n } catch {\n logger.warn('Could not reach server to verify token. Publish may fail.');\n }\n\n logger.success('Dry run complete — no files were uploaded.');\n return;\n }\n\n // 5. Step 1: POST /api/v1/skills\n spinner.text = 'Publishing...';\n const headers = {\n Authorization: `Bearer ${config.token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': USER_AGENT\n };\n\n const step1Res = await fetch(`${config.registry}/api/v1/skills`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ manifest, readme, files })\n });\n\n if (!step1Res.ok) {\n spinner.fail('Publish failed');\n const body = (await step1Res.json().catch(() => null)) as { error?: string } | null;\n const errorMsg = body?.error ?? step1Res.statusText;\n\n if (step1Res.status === 401) {\n throw new Error('Authentication failed. Your token may be expired or invalid. Run: tank login');\n }\n if (step1Res.status === 403) {\n throw new Error(`Publish failed: ${errorMsg}`);\n }\n if (step1Res.status === 404) {\n throw new Error(`Publish failed: ${errorMsg}`);\n }\n if (step1Res.status === 409) {\n throw new Error(`Version already exists. Bump the version in ${MANIFEST_FILENAME}`);\n }\n throw new Error(errorMsg);\n }\n\n const { uploadUrl, versionId } = (await step1Res.json()) as {\n uploadUrl: string;\n skillId: string;\n versionId: string;\n };\n\n // 6. Step 2: Upload tarball to signed URL\n spinner.text = 'Uploading...';\n const uploadRes = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/octet-stream' },\n body: new Uint8Array(tarball)\n });\n\n if (!uploadRes.ok) {\n spinner.fail('Upload failed');\n throw new Error(`Failed to upload tarball: ${uploadRes.status} ${uploadRes.statusText}`);\n }\n\n // 7. Step 3: Confirm publish\n spinner.text = 'Confirming...';\n const confirmRes = await fetch(`${config.registry}/api/v1/skills/confirm`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n versionId,\n integrity,\n fileCount,\n tarballSize: totalSize,\n readme\n })\n });\n\n if (!confirmRes.ok) {\n spinner.fail('Publish confirmation failed');\n const body = (await confirmRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(`Failed to confirm publish: ${body?.error ?? confirmRes.statusText}`);\n }\n\n spinner.succeed(`Published ${name}@${version} (${formatSize(totalSize)}, ${fileCount} files)`);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { LOCKFILE_VERSION, MANIFEST_FILENAME, type SkillsLock } from '@internal/shared';\nimport { getGlobalAgentSkillsDir, getGlobalSkillsDir, getSymlinkName } from '../lib/agents.js';\nimport { unlinkSkillFromAgents } from '../lib/linker.js';\nimport { logger } from '../lib/logger.js';\nimport { resolveLockfilePath, resolveManifestPath } from '../lib/manifest.js';\n\nexport interface RemoveOptions {\n name: string;\n directory?: string;\n global?: boolean;\n homedir?: string;\n}\n\nexport async function removeCommand(options: RemoveOptions): Promise<void> {\n const { name, directory = process.cwd(), global, homedir } = options;\n\n if (global) {\n const resolvedHome = homedir ?? os.homedir();\n\n // 1. Unlink from agents (failures are warnings)\n try {\n const unlinkResult = unlinkSkillFromAgents({\n skillName: name,\n linksDir: path.join(resolvedHome, '.tank'),\n homedir\n });\n if (unlinkResult.unlinked.length > 0) {\n logger.info(`Unlinked from ${unlinkResult.unlinked.length} agent(s)`);\n }\n } catch {\n logger.warn('Agent unlinking skipped (non-fatal)');\n }\n\n // 2. Remove agent-skills wrapper dir\n const symlinkName = getSymlinkName(name);\n const agentSkillDir = path.join(getGlobalAgentSkillsDir(resolvedHome), symlinkName);\n if (fs.existsSync(agentSkillDir)) {\n fs.rmSync(agentSkillDir, { recursive: true, force: true });\n }\n\n // 3. Remove global skills dir\n const skillDir = getGlobalSkillDir(resolvedHome, name);\n if (fs.existsSync(skillDir)) {\n fs.rmSync(skillDir, { recursive: true, force: true });\n }\n\n // 4. Update global lockfile\n const resolvedLock = resolveLockfilePath(path.join(resolvedHome, '.tank'));\n const lockPath = resolvedLock.path;\n if (resolvedLock.exists) {\n let lock: SkillsLock;\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n\n for (const key of Object.keys(lock.skills)) {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) continue;\n const keyName = key.slice(0, lastAt);\n if (keyName === name) {\n delete lock.skills[key];\n }\n }\n\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n\n fs.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\\n`);\n }\n\n logger.success(`Removed ${name} (global)`);\n return;\n }\n\n // 1. Read manifest (tank.json or skills.json)\n const resolvedManifest = resolveManifestPath(directory);\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found in ${directory}. Run: tank init`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n // 2. Check if skill exists in skills map\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n if (!(name in skills)) {\n throw new Error(`Skill \"${name}\" is not installed (not found in ${path.basename(resolvedManifest.path)})`);\n }\n\n // 3. Remove skill from manifest\n delete skills[name];\n skillsJson.skills = skills;\n\n // 4. Write updated manifest\n fs.writeFileSync(resolvedManifest.path, JSON.stringify(skillsJson, null, 2) + '\\n');\n\n // 5. Read lockfile if present\n const resolvedLocalLock = resolveLockfilePath(directory);\n const lockPath = resolvedLocalLock.path;\n if (resolvedLocalLock.exists) {\n let lock: SkillsLock;\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n\n // 6. Remove ALL lockfile entries for this skill name\n // Key format: name@version — use lastIndexOf('@') to split scoped names\n for (const key of Object.keys(lock.skills)) {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) continue;\n const keyName = key.slice(0, lastAt);\n if (keyName === name) {\n delete lock.skills[key];\n }\n }\n\n // 7. Write updated skills.lock (sorted keys, trailing newline)\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n\n fs.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\\n`);\n }\n\n // 7.5. Unlink from agents (failures are warnings)\n try {\n const unlinkResult = unlinkSkillFromAgents({\n skillName: name,\n linksDir: path.join(directory, '.tank'),\n homedir\n });\n if (unlinkResult.unlinked.length > 0) {\n logger.info(`Unlinked from ${unlinkResult.unlinked.length} agent(s)`);\n }\n } catch {\n logger.warn('Agent unlinking skipped (non-fatal)');\n }\n\n // 7.6. Remove agent-skills wrapper dir\n const symlinkName = getSymlinkName(name);\n const agentSkillDir = path.join(directory, '.tank', 'agent-skills', symlinkName);\n if (fs.existsSync(agentSkillDir)) {\n fs.rmSync(agentSkillDir, { recursive: true, force: true });\n }\n\n // 8. Delete .tank/skills/{name}/ directory\n const skillDir = getSkillDir(directory, name);\n if (fs.existsSync(skillDir)) {\n fs.rmSync(skillDir, { recursive: true, force: true });\n }\n\n // 9. Print success\n logger.success(`Removed ${name}`);\n}\n\nfunction getSkillDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n\nfunction getGlobalSkillDir(homedir: string, skillName: string): string {\n const globalDir = getGlobalSkillsDir(homedir);\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(globalDir, scope, name);\n }\n return path.join(globalDir, skillName);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { getConfig } from '../lib/config.js';\nimport { resolveManifestPath } from '../lib/manifest.js';\nimport { pack, packForScan } from '../lib/packer.js';\nimport { USER_AGENT } from '../version.js';\n\nexport interface ScanOptions {\n directory?: string;\n configDir?: string;\n}\n\ninterface ScanFinding {\n stage: string;\n severity: 'critical' | 'high' | 'medium' | 'low';\n type: string;\n description: string;\n location: string | null;\n confidence: number | null;\n tool: string | null;\n evidence: string | null;\n}\n\ninterface ScanResponse {\n scan_id: string | null;\n verdict: 'pass' | 'pass_with_notes' | 'flagged' | 'fail';\n audit_score: number;\n findings: ScanFinding[];\n stage_results: Array<{\n stage: string;\n status: string;\n findings: ScanFinding[];\n duration_ms: number;\n }>;\n duration_ms: number;\n}\n\nfunction verdictColor(verdict: string): (text: string) => string {\n switch (verdict) {\n case 'pass':\n return chalk.green;\n case 'pass_with_notes':\n return chalk.yellow;\n case 'flagged':\n return chalk.hex('#FF8C00');\n case 'fail':\n return chalk.red;\n default:\n return chalk.white;\n }\n}\n\nfunction severityColor(severity: string): (text: string) => string {\n switch (severity) {\n case 'critical':\n return chalk.red;\n case 'high':\n return chalk.hex('#FF8C00');\n case 'medium':\n return chalk.yellow;\n case 'low':\n return chalk.green;\n default:\n return chalk.white;\n }\n}\n\nfunction scoreColor(score: number): (text: string) => string {\n if (score >= 7) return chalk.green;\n if (score >= 4) return chalk.yellow;\n return chalk.red;\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport async function scanCommand(options: ScanOptions = {}): Promise<void> {\n const { directory = process.cwd(), configDir } = options;\n const absDir = path.resolve(directory);\n\n const config = getConfig(configDir);\n if (!config.token) {\n throw new Error('Not logged in. Run: tank login');\n }\n\n const spinner = ora('Packing skill...').start();\n\n // Try to read manifest (tank.json or skills.json) first\n const resolvedManifest = resolveManifestPath(absDir);\n let manifest: Record<string, unknown>;\n let packResult: Awaited<ReturnType<typeof pack>>;\n\n if (resolvedManifest.exists) {\n // Manifest exists: use pack() and read it\n try {\n packResult = await pack(absDir);\n } catch (err) {\n spinner.fail('Packing failed');\n throw err;\n }\n\n try {\n manifest = JSON.parse(fs.readFileSync(resolvedManifest.path, 'utf-8')) as Record<string, unknown>;\n } catch (err) {\n spinner.fail(`Failed to read ${path.basename(resolvedManifest.path)}`);\n throw err;\n }\n } else {\n // No manifest: use packForScan() and synthesize manifest\n try {\n packResult = await packForScan(absDir);\n } catch (err) {\n spinner.fail('Packing failed');\n throw err;\n }\n\n const dirName = path.basename(absDir);\n manifest = { name: dirName, version: '0.0.0', description: 'Local scan' };\n }\n\n const name = (manifest.name as string) ?? 'unknown';\n const version = (manifest.version as string) ?? '0.0.0';\n\n spinner.text = `Scanning ${name}@${version}...`;\n\n const formData = new FormData();\n const blob = new Blob([new Uint8Array(packResult.tarball)], { type: 'application/gzip' });\n formData.append('tarball', blob, `${name}-${version}.tgz`);\n formData.append('manifest', JSON.stringify(manifest));\n\n let scanRes: Response;\n try {\n scanRes = await fetch(`${config.registry}/api/v1/scan`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'User-Agent': USER_AGENT\n },\n body: formData\n });\n } catch (err) {\n spinner.fail('Scan failed');\n throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!scanRes.ok) {\n spinner.fail('Scan failed');\n const body = (await scanRes.json().catch(() => null)) as { error?: string } | null;\n\n if (scanRes.status === 401) {\n throw new Error('Authentication failed. Your token may be expired or invalid. Run: tank login');\n }\n throw new Error(body?.error ?? scanRes.statusText);\n }\n\n const result = (await scanRes.json()) as ScanResponse;\n\n spinner.stop();\n\n const verdictLabel = verdictColor(result.verdict)(result.verdict.toUpperCase());\n const auditScore = result.audit_score ?? 0;\n const scoreLabel = scoreColor(auditScore)(auditScore.toFixed(1));\n\n console.log('');\n console.log(chalk.bold(`Security Scan: ${name}@${version}`));\n console.log('');\n console.log(`${chalk.dim('Verdict:'.padEnd(14))}${verdictLabel}`);\n console.log(`${chalk.dim('Score:'.padEnd(14))}${scoreLabel}/10`);\n console.log(`${chalk.dim('Duration:'.padEnd(14))}${(result.duration_ms / 1000).toFixed(1)}s`);\n console.log(`${chalk.dim('Files:'.padEnd(14))}${packResult.fileCount} (${formatSize(packResult.totalSize)})`);\n\n if (result.findings.length > 0) {\n console.log('');\n console.log(chalk.bold(`Findings (${result.findings.length})`));\n\n const bySeverity: Record<string, ScanFinding[]> = {\n critical: [],\n high: [],\n medium: [],\n low: []\n };\n for (const f of result.findings) {\n bySeverity[f.severity].push(f);\n }\n\n for (const severity of ['critical', 'high', 'medium', 'low'] as const) {\n const findings = bySeverity[severity];\n if (findings.length === 0) continue;\n\n console.log('');\n const label = severityColor(severity)(`${severity.toUpperCase()} (${findings.length})`);\n console.log(` ${label}`);\n\n for (const f of findings) {\n console.log(` - ${chalk.bold(f.type)}: ${f.description}`);\n if (f.location) {\n console.log(` ${chalk.dim('Location:')} ${f.location}`);\n }\n }\n }\n } else {\n console.log('');\n console.log(chalk.green('No findings. Your skill looks secure!'));\n }\n\n if (result.stage_results?.length > 0) {\n console.log('');\n console.log(chalk.bold('Scan Stages'));\n for (const stage of result.stage_results) {\n const icon = stage.status === 'passed' ? chalk.green('✓') : chalk.red('✗');\n console.log(` ${icon} ${stage.stage} (${stage.duration_ms}ms)`);\n }\n }\n\n if (result.scan_id) {\n console.log('');\n console.log(chalk.dim(`Full report: ${config.registry}/scans/${result.scan_id}`));\n }\n\n console.log('');\n}\n","import chalk from 'chalk';\nimport { getConfig } from '../lib/config.js';\nimport { USER_AGENT } from '../version.js';\n\nexport interface SearchOptions {\n query: string;\n configDir?: string;\n}\n\ninterface SearchResult {\n name: string;\n description: string;\n latestVersion: string;\n auditScore: number;\n publisher: string;\n downloads: number;\n}\n\ninterface SearchResponse {\n results: SearchResult[];\n page: number;\n limit: number;\n total: number;\n}\n\nconst MAX_DESC_LENGTH = 60;\n\nfunction scoreColor(score: number): (text: string) => string {\n if (score >= 7) return chalk.green;\n if (score >= 4) return chalk.yellow;\n return chalk.red;\n}\n\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return `${text.slice(0, maxLen - 3)}...`;\n}\n\nfunction padRight(text: string, width: number): string {\n if (text.length >= width) return text;\n return text + ' '.repeat(width - text.length);\n}\n\nexport async function searchCommand(options: SearchOptions): Promise<void> {\n const { query, configDir } = options;\n const config = getConfig(configDir);\n\n const url = `${config.registry}/api/v1/search?q=${encodeURIComponent(query)}&limit=20`;\n\n let res: Response;\n try {\n const headers: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n }\n res = await fetch(url, {\n headers\n });\n } catch (err) {\n throw new Error(`Network error searching: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n const body = (await res.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? `Search failed: ${res.statusText}`);\n }\n\n const data = (await res.json()) as SearchResponse;\n\n if (data.results.length === 0) {\n console.log(`No skills found for \"${query}\"`);\n return;\n }\n\n // Print table header\n console.log(`${padRight('NAME', 30) + padRight('VERSION', 10) + padRight('SCORE', 8)}DESCRIPTION`);\n\n // Print each result\n for (const result of data.results) {\n const name = chalk.bold(padRight(result.name, 30));\n const version = padRight(result.latestVersion, 10);\n const scoreStr = Number.isInteger(result.auditScore) ? result.auditScore.toFixed(1) : String(result.auditScore);\n const score = scoreColor(result.auditScore)(padRight(scoreStr, 8));\n const desc = truncate(result.description ?? '', MAX_DESC_LENGTH);\n\n console.log(`${name}${version}${score}${desc}`);\n }\n\n console.log('');\n console.log(`${data.results.length} skill${data.results.length === 1 ? '' : 's'} found`);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { MANIFEST_FILENAME } from '@internal/shared';\nimport { getGlobalAgentSkillsDir, getSymlinkName } from '../lib/agents.js';\nimport { unlinkSkillFromAgents } from '../lib/linker.js';\nimport { logger } from '../lib/logger.js';\nimport { resolveManifestPath } from '../lib/manifest.js';\n\nexport interface UnlinkOptions {\n directory?: string;\n homedir?: string;\n}\n\nexport async function unlinkCommand(options: UnlinkOptions = {}): Promise<void> {\n const workDir = options.directory ?? process.cwd();\n const resolvedManifest = resolveManifestPath(workDir);\n\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found. Run this command from a skill directory.`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n const skillName = skillsJson.name;\n if (typeof skillName !== 'string' || skillName.trim().length === 0) {\n throw new Error(`Missing 'name' in ${path.basename(resolvedManifest.path)}`);\n }\n\n const homedir = options.homedir ?? os.homedir();\n const result = unlinkSkillFromAgents({\n skillName,\n linksDir: path.join(homedir, '.tank'),\n homedir: options.homedir\n });\n\n const symlinkName = getSymlinkName(skillName);\n const wrapperDir = path.join(getGlobalAgentSkillsDir(options.homedir), symlinkName);\n if (fs.existsSync(wrapperDir)) {\n fs.rmSync(wrapperDir, { recursive: true, force: true });\n }\n\n if (result.unlinked.length === 0 && result.notFound.length === 0) {\n logger.info(`No links found for ${skillName}`);\n return;\n }\n\n logger.success(`Unlinked ${skillName} from ${result.unlinked.length} agent(s)`);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { LOCKFILE_FILENAME, MANIFEST_FILENAME, resolve, type SkillsLock } from '@internal/shared';\nimport { getConfig } from '../lib/config.js';\nimport { logger } from '../lib/logger.js';\nimport { resolveLockfilePath, resolveManifestPath } from '../lib/manifest.js';\nimport { USER_AGENT } from '../version.js';\nimport { installCommand } from './install.js';\n\nexport interface UpdateOptions {\n name?: string; // undefined = update all\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n}\n\ninterface VersionInfo {\n version: string;\n integrity: string;\n auditScore: number;\n auditStatus: string;\n publishedAt: string;\n}\n\nconst VERSION_CHECK_CONCURRENCY = 8;\n\nexport async function updateCommand(options: UpdateOptions): Promise<void> {\n const { name, directory = process.cwd(), configDir, global = false, homedir } = options;\n\n if (global) {\n if (name) {\n await updateSingleGlobal(name, configDir, homedir);\n } else {\n await updateAllGlobal(configDir, homedir);\n }\n return;\n }\n\n // 1. Read manifest (tank.json or skills.json)\n const resolvedManifest = resolveManifestPath(directory);\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found in ${directory}. Run: tank init`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n\n if (name) {\n // Update single skill\n await updateSingle(name, skills, directory, configDir, global, homedir);\n } else {\n // Update all skills\n await updateAll(skills, directory, configDir, global, homedir);\n }\n}\n\nfunction parseLockKey(key: string): { name: string; version: string } | null {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) return null;\n return { name: key.slice(0, lastAt), version: key.slice(lastAt + 1) };\n}\n\nfunction readLockfile(lockPath: string): SkillsLock | null {\n if (!fs.existsSync(lockPath)) {\n return null;\n }\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n return null;\n }\n}\n\nfunction readLockfileStrict(lockPath: string): SkillsLock {\n if (!fs.existsSync(lockPath)) {\n throw new Error(`Global ${LOCKFILE_FILENAME} not found at ${lockPath}`);\n }\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n throw new Error(`Failed to read or parse global ${LOCKFILE_FILENAME}`);\n }\n}\n\nfunction getGlobalLockPath(homedir?: string): string {\n const resolvedHome = homedir ?? os.homedir();\n const resolved = resolveLockfilePath(path.join(resolvedHome, '.tank'));\n return resolved.path;\n}\n\nasync function fetchAvailableVersions(\n name: string,\n registry: string,\n headers: Record<string, string>\n): Promise<string[]> {\n const encodedName = encodeURIComponent(name);\n const versionsUrl = `${registry}/api/v1/skills/${encodedName}/versions`;\n\n let versionsRes: Response;\n try {\n versionsRes = await fetch(versionsUrl, { headers });\n } catch (err) {\n throw new Error(`Network error fetching versions for ${name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!versionsRes.ok) {\n if (versionsRes.status === 404) {\n throw new Error(`Skill not found in registry: ${name}`);\n }\n const body = (await versionsRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? versionsRes.statusText);\n }\n\n const versionsData = (await versionsRes.json()) as { name: string; versions: VersionInfo[] };\n return versionsData.versions.map((v) => v.version);\n}\n\n/**\n * Deduplicate lockfile entries by skill name.\n * When multiple versions of the same skill exist in the lockfile (e.g. from\n * transitive dependencies), keeps only the highest version per skill name.\n */\nfunction deduplicateByName(entries: string[]): Map<string, string> {\n const versionsByName = new Map<string, string[]>();\n for (const key of entries) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n const versions = versionsByName.get(parsed.name) ?? [];\n versions.push(parsed.version);\n versionsByName.set(parsed.name, versions);\n }\n\n const latestByName = new Map<string, string>();\n for (const [name, versions] of versionsByName) {\n const latest = resolve('*', versions);\n if (latest) latestByName.set(name, latest);\n }\n\n return latestByName;\n}\n\nasync function fetchVersionsBatch(\n skillNames: string[],\n registry: string,\n headers: Record<string, string>\n): Promise<Map<string, string[]>> {\n const results = new Map<string, string[]>();\n\n for (let i = 0; i < skillNames.length; i += VERSION_CHECK_CONCURRENCY) {\n const batch = skillNames.slice(i, i + VERSION_CHECK_CONCURRENCY);\n const settled = await Promise.allSettled(\n batch.map(async (name) => {\n const versions = await fetchAvailableVersions(name, registry, headers);\n return { name, versions };\n })\n );\n\n for (const result of settled) {\n if (result.status === 'fulfilled') {\n results.set(result.value.name, result.value.versions);\n } else {\n throw result.reason;\n }\n }\n }\n\n return results;\n}\n\nasync function updateSingle(\n name: string,\n skills: Record<string, string>,\n directory: string,\n configDir?: string,\n global = false,\n homedir?: string\n): Promise<void> {\n const versionRange = skills[name];\n if (!versionRange) {\n throw new Error(`Skill \"${name}\" is not installed (not found in ${MANIFEST_FILENAME})`);\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const availableVersions = await fetchAvailableVersions(name, config.registry, requestHeaders);\n\n const resolved = resolve(versionRange, availableVersions);\n if (!resolved) {\n throw new Error(\n `No version of ${name} satisfies range \"${versionRange}\". Available: ${availableVersions.join(', ')}`\n );\n }\n\n const lockPath = global ? getGlobalLockPath(homedir) : resolveLockfilePath(directory).path;\n let currentVersion: string | null = null;\n\n const lock = readLockfile(lockPath);\n if (lock) {\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n if (parsed.name === name) {\n currentVersion = parsed.version;\n break;\n }\n }\n }\n\n if (resolved === currentVersion) {\n logger.info(`Already at latest: ${name}@${resolved}`);\n return;\n }\n\n await installCommand({\n name,\n versionRange,\n directory,\n configDir,\n global,\n homedir\n });\n\n logger.success(`Updated ${name} to ${resolved}`);\n}\n\nasync function updateAll(\n skills: Record<string, string>,\n directory: string,\n configDir?: string,\n global = false,\n homedir?: string\n): Promise<void> {\n const skillEntries = Object.entries(skills);\n\n if (skillEntries.length === 0) {\n logger.info(`No skills defined in ${MANIFEST_FILENAME}`);\n return;\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const lockPath = global ? getGlobalLockPath(homedir) : resolveLockfilePath(directory).path;\n const lock = readLockfile(lockPath);\n\n const currentVersionByName = new Map<string, string>();\n if (lock) {\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n const existing = currentVersionByName.get(parsed.name);\n if (!existing) {\n currentVersionByName.set(parsed.name, parsed.version);\n } else {\n const higher = resolve('*', [existing, parsed.version]);\n if (higher) currentVersionByName.set(parsed.name, higher);\n }\n }\n }\n\n const skillNames = skillEntries.map(([name]) => name);\n const allVersions = await fetchVersionsBatch(skillNames, config.registry, requestHeaders);\n\n const toUpdate: Array<{ name: string; versionRange: string }> = [];\n\n for (const [name, versionRange] of skillEntries) {\n const availableVersions = allVersions.get(name);\n if (!availableVersions) continue;\n\n const resolved = resolve(versionRange, availableVersions);\n if (!resolved) continue;\n\n const currentVersion = currentVersionByName.get(name) ?? null;\n if (resolved === currentVersion) continue;\n\n toUpdate.push({ name, versionRange });\n }\n\n if (toUpdate.length === 0) {\n logger.info('All skills up to date');\n return;\n }\n\n for (const { name, versionRange } of toUpdate) {\n await installCommand({\n name,\n versionRange,\n directory,\n configDir,\n global,\n homedir\n });\n }\n\n logger.success(`Updated ${toUpdate.length} skill${toUpdate.length === 1 ? '' : 's'}`);\n}\n\nasync function updateSingleGlobal(name: string, configDir?: string, homedir?: string): Promise<void> {\n const lockPath = getGlobalLockPath(homedir);\n const lock = readLockfileStrict(lockPath);\n\n let currentVersion: string | null = null;\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n if (parsed.name === name) {\n currentVersion = parsed.version;\n break;\n }\n }\n\n if (!currentVersion) {\n throw new Error(`Skill \"${name}\" is not installed globally (not found in ${LOCKFILE_FILENAME})`);\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const availableVersions = await fetchAvailableVersions(name, config.registry, requestHeaders);\n const versionRange = `>=${currentVersion}`;\n const resolved = resolve(versionRange, availableVersions);\n\n if (!resolved) {\n throw new Error(\n `No version of ${name} satisfies range \"${versionRange}\". Available: ${availableVersions.join(', ')}`\n );\n }\n\n if (resolved === currentVersion) {\n logger.info(`Already at latest: ${name}@${resolved}`);\n return;\n }\n\n await installCommand({\n name,\n versionRange,\n global: true,\n homedir,\n configDir\n });\n\n logger.success(`Updated ${name} to ${resolved}`);\n}\n\nasync function updateAllGlobal(configDir?: string, homedir?: string): Promise<void> {\n const lockPath = getGlobalLockPath(homedir);\n const lock = readLockfileStrict(lockPath);\n const entries = Object.keys(lock.skills);\n\n if (entries.length === 0) {\n logger.info(`No skills defined in global ${LOCKFILE_FILENAME}`);\n return;\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const latestByName = deduplicateByName(entries);\n const skillNames = Array.from(latestByName.keys());\n\n const allVersions = await fetchVersionsBatch(skillNames, config.registry, requestHeaders);\n\n const toUpdate: string[] = [];\n\n for (const [name, currentVersion] of latestByName) {\n const availableVersions = allVersions.get(name);\n if (!availableVersions) continue;\n\n const resolved = resolve('*', availableVersions);\n if (!resolved) continue;\n\n if (resolved === currentVersion) continue;\n\n toUpdate.push(name);\n }\n\n if (toUpdate.length === 0) {\n logger.info('All skills up to date');\n return;\n }\n\n for (const name of toUpdate) {\n await installCommand({\n name,\n versionRange: '*',\n global: true,\n homedir,\n configDir\n });\n }\n\n logger.success(`Updated ${toUpdate.length} skill${toUpdate.length === 1 ? '' : 's'}`);\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { resolve } from '@internal/shared';\nimport chalk from 'chalk';\nimport { USER_AGENT, VERSION } from '../version.js';\n\nexport interface UpgradeOptions {\n version?: string;\n dryRun?: boolean;\n force?: boolean;\n}\n\nfunction isNewerVersion(candidateVersion: string, currentVersion: string): boolean {\n if (candidateVersion === currentVersion) {\n return false;\n }\n\n return resolve('*', [candidateVersion, currentVersion]) === candidateVersion;\n}\n\nfunction resolveCurrentBinary(): string {\n try {\n return fs.realpathSync(process.argv[1]);\n } catch {\n return process.execPath;\n }\n}\n\nexport async function upgradeCommand(opts?: UpgradeOptions): Promise<void> {\n const currentBinaryPath = resolveCurrentBinary();\n if (\n process.platform !== 'win32' &&\n (currentBinaryPath.includes('/Cellar/') || currentBinaryPath.includes('/homebrew/'))\n ) {\n console.log(chalk.yellow('Tank was installed via Homebrew. Run `brew upgrade tank` instead.'));\n return;\n }\n\n let targetVersion: string;\n\n if (opts?.version) {\n targetVersion = opts.version;\n } else {\n const res = await fetch('https://api.github.com/repos/tankpkg/tank/releases/latest', {\n headers: { 'User-Agent': USER_AGENT }\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch latest release: ${res.status} ${res.statusText}`);\n }\n const data = (await res.json()) as { tag_name: string };\n targetVersion = data.tag_name.replace(/^v/, '');\n }\n\n if (!isNewerVersion(targetVersion, VERSION) && !opts?.force) {\n console.log(chalk.green(`✓ Already on latest version: ${VERSION}`));\n return;\n }\n\n const platform = process.platform === 'win32' ? 'windows' : process.platform === 'darwin' ? 'darwin' : 'linux';\n const arch = process.arch === 'arm64' ? 'arm64' : 'x64';\n const binaryName = `tank-${platform}-${arch}${process.platform === 'win32' ? '.exe' : ''}`;\n\n if (opts?.dryRun) {\n console.log(`Would upgrade tank ${VERSION} → ${targetVersion}`);\n return;\n }\n\n console.log(chalk.cyan(`Upgrading tank ${VERSION} → ${targetVersion}...`));\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tank-upgrade-'));\n\n try {\n const binaryUrl = `https://github.com/tankpkg/tank/releases/download/v${targetVersion}/${binaryName}`;\n const tmpBin = path.join(tmpDir, binaryName);\n\n const binRes = await fetch(binaryUrl, {\n headers: { 'User-Agent': USER_AGENT }\n });\n if (!binRes.ok) {\n throw new Error(`Failed to download binary: ${binRes.status} ${binRes.statusText}`);\n }\n if (!binRes.body) {\n throw new Error('Empty response body when downloading binary');\n }\n\n const binBuffer = Buffer.from(await binRes.arrayBuffer());\n fs.writeFileSync(tmpBin, binBuffer);\n\n const sumsUrl = `https://github.com/tankpkg/tank/releases/download/v${targetVersion}/SHA256SUMS`;\n const sumsRes = await fetch(sumsUrl, {\n headers: { 'User-Agent': USER_AGENT }\n });\n if (!sumsRes.ok) {\n throw new Error(`Failed to download SHA256SUMS: ${sumsRes.status} ${sumsRes.statusText}`);\n }\n const sumsText = await sumsRes.text();\n\n let expectedHash: string | undefined;\n for (const line of sumsText.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n const parts = trimmed.split(/\\s+/);\n if (parts.length >= 2 && parts[1] === binaryName) {\n expectedHash = parts[0];\n break;\n }\n }\n if (!expectedHash) {\n throw new Error(`No checksum found for ${binaryName} in SHA256SUMS`);\n }\n\n const fileBuffer = fs.readFileSync(tmpBin);\n const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex');\n\n if (actualHash !== expectedHash) {\n console.log(chalk.red('Checksum mismatch. Aborting for security.'));\n return;\n }\n\n if (process.platform !== 'win32') {\n fs.chmodSync(tmpBin, 0o755);\n }\n\n fs.copyFileSync(tmpBin, currentBinaryPath);\n if (process.platform !== 'win32') {\n fs.chmodSync(currentBinaryPath, 0o755);\n }\n\n console.log(chalk.green(`✓ Upgraded tank ${VERSION} → ${targetVersion}`));\n console.log(chalk.gray(`Release notes: https://github.com/tankpkg/tank/releases/tag/v${targetVersion}`));\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { LOCKFILE_FILENAME } from '@internal/shared';\nimport { readLockfile } from '../lib/lockfile.js';\nimport { logger } from '../lib/logger.js';\n\nexport interface VerifyOptions {\n directory?: string;\n}\n\nexport interface VerifyResult {\n valid: boolean;\n mismatches: Array<{\n key: string;\n expected: string;\n actual: string | null; // null if file missing\n }>;\n}\n\n/**\n * Verify that installed skills match the lockfile.\n *\n * For each entry in skills.lock:\n * 1. Parse the skill name from the lock key\n * 2. Check that the skill directory exists in .tank/skills/\n * 3. Check that the directory is not empty\n *\n * Throws on failure (exit code 1). Prints success message on pass.\n */\nexport async function verifyCommand(options?: VerifyOptions): Promise<void> {\n const directory = options?.directory ?? process.cwd();\n\n const lock = readLockfile(directory);\n if (!lock) {\n throw new Error(`No ${LOCKFILE_FILENAME} found in ${directory}. Run: tank install`);\n }\n\n const entries = Object.entries(lock.skills);\n if (entries.length === 0) {\n logger.success('No skills to verify (lockfile is empty)');\n return;\n }\n\n const issues: string[] = [];\n\n for (const [key] of entries) {\n const skillName = parseLockKey(key);\n const skillDir = getExtractDir(directory, skillName);\n\n if (!fs.existsSync(skillDir)) {\n issues.push(`${key}: directory missing at ${skillDir}`);\n continue;\n }\n\n // Check directory is not empty\n const contents = fs.readdirSync(skillDir);\n if (contents.length === 0) {\n issues.push(`${key}: directory exists but is empty`);\n }\n }\n\n if (issues.length > 0) {\n for (const issue of issues) {\n logger.error(issue);\n }\n throw new Error(`Verification failed: ${issues.length} issue${issues.length === 1 ? '' : 's'} found`);\n }\n\n const count = entries.length;\n logger.success(`All ${count} skill${count === 1 ? '' : 's'} verified`);\n}\n\nfunction parseLockKey(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return key.slice(0, lastAt);\n}\n\nfunction getExtractDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n","import { getConfig } from '../lib/config.js';\nimport { logger } from '../lib/logger.js';\nimport { USER_AGENT } from '../version.js';\n\nexport interface WhoamiOptions {\n configDir?: string;\n}\n\nexport async function whoamiCommand(options: WhoamiOptions = {}): Promise<void> {\n const { configDir } = options;\n const config = getConfig(configDir);\n\n if (!config.token) {\n logger.warn('Not logged in. Run: tank login');\n return;\n }\n\n try {\n const res = await fetch(`${config.registry}/api/v1/auth/whoami`, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'User-Agent': USER_AGENT\n }\n });\n\n if (res.status === 401) {\n logger.error('Token is invalid or expired. Run: tank login');\n return;\n }\n\n if (!res.ok) {\n if (config.user) {\n printUserInfo(config.user);\n logger.warn('Could not verify token with server. Run: tank login');\n } else {\n logger.error('Could not verify token. Server returned an error. Run: tank login');\n }\n process.exitCode = 1;\n return;\n }\n\n if (config.user) {\n printUserInfo(config.user);\n } else {\n logger.info('Logged in (token verified).');\n }\n } catch {\n if (config.user) {\n logger.info(`Logged in as: ${config.user.name ?? 'unknown'} (offline)`);\n logger.info(`Email: ${config.user.email ?? 'unknown'}`);\n logger.warn('Could not reach server to verify token. Run: tank login');\n } else {\n logger.error('Could not verify token. Check your network connection.');\n }\n process.exitCode = 1;\n }\n}\n\nfunction printUserInfo(user: { name: string; email: string }): void {\n logger.info(`Logged in as: ${user.name ?? 'unknown'}`);\n logger.info(`Email: ${user.email ?? 'unknown'}`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { resolve } from '@internal/shared';\nimport chalk from 'chalk';\nimport { VERSION } from '../version.js';\nimport { getConfigDir } from './config.js';\n\ninterface UpgradeCache {\n lastCheck: number;\n latestVersion: string;\n}\n\nfunction isNewerVersion(candidateVersion: string, currentVersion: string): boolean {\n if (candidateVersion === currentVersion) {\n return false;\n }\n\n return resolve('*', [candidateVersion, currentVersion]) === candidateVersion;\n}\n\nexport async function checkForUpgrade(configDir?: string): Promise<void> {\n try {\n if (process.env.TANK_NO_UPDATE_CHECK || process.env.CI) {\n return;\n }\n\n const cacheDir = getConfigDir(configDir);\n const cachePath = path.join(cacheDir, 'upgrade_check.json');\n\n let cache: UpgradeCache | null = null;\n try {\n const raw = fs.readFileSync(cachePath, 'utf-8');\n cache = JSON.parse(raw) as UpgradeCache;\n } catch {\n // File doesn't exist or invalid JSON — treat as stale\n }\n\n const isFresh = cache !== null && Date.now() - cache.lastCheck < 24 * 60 * 60 * 1000;\n\n if (isFresh && cache !== null) {\n if (isNewerVersion(cache.latestVersion, VERSION)) {\n console.error(\n `\\n ${chalk.cyan('ℹ')} New version available: ${chalk.gray(VERSION)} → ${chalk.green(cache.latestVersion)}`\n );\n console.error(` Run ${chalk.cyan('`tank upgrade`')} to update.\\n`);\n }\n return;\n }\n\n const res = await fetch('https://api.github.com/repos/tankpkg/tank/releases/latest', {\n headers: { 'User-Agent': `tank-cli/${VERSION}` },\n signal: AbortSignal.timeout(3000)\n });\n if (!res.ok) {\n return;\n }\n const data = (await res.json()) as { tag_name: string };\n const latestVersion = data.tag_name.replace(/^v/, '');\n\n if (!fs.existsSync(cacheDir)) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n const newCache: UpgradeCache = { lastCheck: Date.now(), latestVersion };\n fs.writeFileSync(cachePath, `${JSON.stringify(newCache, null, 2)}\\n`);\n\n if (isNewerVersion(latestVersion, VERSION)) {\n console.error(\n `\\n ${chalk.cyan('ℹ')} New version available: ${chalk.gray(VERSION)} → ${chalk.green(latestVersion)}`\n );\n console.error(` Run ${chalk.cyan('`tank upgrade`')} to update.\\n`);\n }\n } catch {\n // Silently swallow ALL errors — background check must never cause CLI to fail\n }\n}\n","#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { auditCommand } from '../commands/audit.js';\nimport { doctorCommand } from '../commands/doctor.js';\nimport { infoCommand } from '../commands/info.js';\nimport { initCommand } from '../commands/init.js';\nimport { installAll, installCommand } from '../commands/install.js';\nimport { linkCommand } from '../commands/link.js';\nimport { loginCommand } from '../commands/login.js';\nimport { logoutCommand } from '../commands/logout.js';\nimport { migrateCommand } from '../commands/migrate.js';\nimport { permissionsCommand } from '../commands/permissions.js';\nimport { publishCommand } from '../commands/publish.js';\nimport { removeCommand } from '../commands/remove.js';\nimport { scanCommand } from '../commands/scan.js';\nimport { searchCommand } from '../commands/search.js';\nimport { unlinkCommand } from '../commands/unlink.js';\nimport { updateCommand } from '../commands/update.js';\nimport { upgradeCommand } from '../commands/upgrade.js';\nimport { verifyCommand } from '../commands/verify.js';\nimport { whoamiCommand } from '../commands/whoami.js';\nimport { flushLogs } from '../lib/debug-logger.js';\nimport { checkForUpgrade } from '../lib/upgrade-check.js';\nimport { VERSION } from '../version.js';\n\nconst program = new Command();\n\nprogram.name('tank').description('Security-first package manager for AI agent skills').version(VERSION);\n\nprogram\n .command('init')\n .description('Create a new tank.json in the current directory')\n .option('-y, --yes', 'Skip prompts, use defaults')\n .option('--name <name>', 'Skill name')\n .option('--skill-version <version>', 'Skill version (default: 0.1.0)')\n .option('--description <desc>', 'Skill description')\n .option('--private', 'Make skill private')\n .option('--force', 'Overwrite existing tank.json')\n .action(\n async (opts: {\n yes?: boolean;\n name?: string;\n skillVersion?: string;\n description?: string;\n private?: boolean;\n force?: boolean;\n }) => {\n try {\n await initCommand({\n yes: opts.yes,\n name: opts.name,\n version: opts.skillVersion,\n description: opts.description,\n private: opts.private,\n force: opts.force\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Init failed: ${msg}`);\n process.exit(1);\n }\n }\n );\n\nprogram\n .command('login')\n .description('Authenticate with the Tank registry via browser')\n .action(async () => {\n try {\n await loginCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Login failed: ${msg}`);\n await flushLogs();\n process.exit(1);\n }\n await flushLogs();\n });\n\nprogram\n .command('whoami')\n .description('Show the currently logged-in user')\n .action(async () => {\n try {\n await whoamiCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('logout')\n .description('Remove authentication token from config')\n .action(async () => {\n try {\n await logoutCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Logout failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('publish')\n .alias('pub')\n .description('Pack and publish a skill to the Tank registry')\n .option('--dry-run', 'Validate and pack without uploading')\n .option('--private', 'Publish skill as private')\n .option('--visibility <mode>', 'Skill visibility (public|private)')\n .action(async (opts: { dryRun?: boolean; private?: boolean; visibility?: string }) => {\n try {\n await publishCommand({\n dryRun: opts.dryRun,\n private: opts.private,\n visibility: opts.visibility\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Publish failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('install')\n .alias('i')\n .description('Install a skill from the Tank registry, or all skills from lockfile')\n .argument('[name]', 'Skill name (e.g., @org/skill-name). Omit to install from lockfile.')\n .argument('[version-range]', 'Semver range (default: *)', '*')\n .option('-g, --global', 'Install skill globally (available to all projects)')\n .option('-y, --yes', 'Auto-accept permission budget expansion')\n .action(async (name: string | undefined, versionRange: string, opts: { global?: boolean; yes?: boolean }) => {\n try {\n if (name) {\n await installCommand({ name, versionRange, global: opts.global, yes: opts.yes });\n } else {\n await installAll({ global: opts.global, yes: opts.yes });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Install failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('remove')\n .aliases(['rm', 'r'])\n .description('Remove an installed skill')\n .argument('<name>', 'Skill name (e.g., @org/skill-name)')\n .option('-g, --global', 'Remove a globally installed skill')\n .action(async (name: string, opts: { global?: boolean }) => {\n try {\n await removeCommand({ name, global: opts.global });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Remove failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('update')\n .alias('up')\n .description('Update skills to latest versions within their ranges')\n .argument('[name]', 'Skill name to update (omit to update all)')\n .option('-g, --global', 'Update globally installed skills')\n .action(async (name: string | undefined, opts: { global?: boolean }) => {\n try {\n await updateCommand({ name, global: opts.global });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Update failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('verify')\n .description('Verify installed skills match the lockfile')\n .action(async () => {\n try {\n await verifyCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Verify failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('permissions')\n .alias('perms')\n .description('Display resolved permission summary for installed skills')\n .action(async () => {\n try {\n await permissionsCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('search')\n .alias('s')\n .description('Search for skills in the Tank registry')\n .argument('<query>', 'Search query')\n .action(async (query: string) => {\n try {\n await searchCommand({ query });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Search failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('info')\n .alias('show')\n .description('Show detailed information about a skill')\n .argument('<name>', 'Skill name (e.g., @org/skill-name)')\n .action(async (name: string) => {\n try {\n await infoCommand({ name });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Info failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('audit')\n .description('Display security audit results for installed skills')\n .argument('[name]', 'Skill name to audit (omit to audit all)')\n .action(async (name: string | undefined) => {\n try {\n await auditCommand({ name });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Audit failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('scan')\n .description('Scan a local skill for security issues without publishing')\n .option('-d, --directory <path>', 'Directory to scan (default: current directory)')\n .action(async (opts: { directory?: string }) => {\n try {\n await scanCommand({ directory: opts.directory });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Scan failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('link')\n .alias('ln')\n .description('Link current skill directory to AI agent directories (for development)')\n .action(async () => {\n try {\n await linkCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Link failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('unlink')\n .description('Remove skill symlinks from AI agent directories')\n .action(async () => {\n try {\n await unlinkCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Unlink failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('doctor')\n .description('Diagnose agent integration health')\n .action(async () => {\n try {\n await doctorCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Doctor failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('migrate')\n .description('Migrate skills.json → tank.json and skills.lock → tank.lock')\n .action(async () => {\n try {\n await migrateCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Migration failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('upgrade')\n .description('Update tank to the latest version')\n .argument('[version]', 'Target version (default: latest)')\n .option('--dry-run', 'Check for updates without installing')\n .option('--force', 'Reinstall even if already on the target version')\n .action(async (version: string | undefined, opts: { dryRun?: boolean; force?: boolean }) => {\n try {\n await upgradeCommand({ version, dryRun: opts.dryRun, force: opts.force });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Upgrade failed: ${msg}`);\n await flushLogs();\n process.exit(1);\n }\n await flushLogs();\n });\n\ncheckForUpgrade().catch(() => {});\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tank.js","names":["MAX_PACKAGE_SIZE","readLockfile","scoreColor","padRight","parseLockKey","readLockfile","parseLockKey","getExtractDir","crypto","getExtractDir","parseLockKey","isDomainAllowed","isPathAllowed","parseLockKey","getExtractDir","crypto","crypto","formatSize","scoreColor","parseLockKey","isNewerVersion","crypto","readLockfile"],"sources":["../../../internals-schemas/dist/index.js","../../src/lib/manifest.ts","../../src/lib/lockfile.ts","../../src/commands/audit.ts","../../src/lib/agents.ts","../../src/lib/links.ts","../../src/lib/linker.ts","../../src/commands/doctor.ts","../../src/commands/info.ts","../../src/commands/init.ts","../../../internals-helpers/dist/index.js","../../src/lib/dependency-resolver.ts","../../src/lib/frontmatter.ts","../../src/lib/install-pipeline.ts","../../src/lib/permission-checker.ts","../../src/commands/install.ts","../../src/commands/link.ts","../../src/commands/login.ts","../../src/commands/logout.ts","../../src/commands/migrate.ts","../../src/commands/permissions.ts","../../src/lib/packer.ts","../../src/commands/publish.ts","../../src/commands/remove.ts","../../src/commands/scan.ts","../../src/commands/search.ts","../../src/commands/unlink.ts","../../src/commands/update.ts","../../src/commands/upgrade.ts","../../src/commands/verify.ts","../../src/commands/whoami.ts","../../src/lib/upgrade-check.ts","../../src/bin/tank.ts"],"sourcesContent":["import { z } from \"zod\";\n//#region src/constants/permissions.ts\nconst PERMISSION_CATEGORIES = [\n\t\"network\",\n\t\"filesystem\",\n\t\"subprocess\"\n];\nconst DEFAULT_PERMISSIONS = {\n\tnetwork: void 0,\n\tfilesystem: void 0,\n\tsubprocess: false\n};\n//#endregion\n//#region src/constants/registry.ts\nconst REGISTRY_URL = process.env.TANK_REGISTRY_URL || \"https://www.tankpkg.dev\";\nconst REGISTRY_API_VERSION = \"v1\";\nconst MAX_PACKAGE_SIZE = 50 * 1024 * 1024;\nconst MAX_FILE_COUNT = 1e3;\nconst MAX_NAME_LENGTH = 214;\nconst MAX_DESCRIPTION_LENGTH = 500;\nconst LOCKFILE_VERSION = 2;\nconst MANIFEST_FILENAME = \"tank.json\";\nconst LEGACY_MANIFEST_FILENAME = \"skills.json\";\nconst LOCKFILE_FILENAME = \"tank.lock\";\nconst LEGACY_LOCKFILE_FILENAME = \"skills.lock\";\n//#endregion\n//#region src/schemas/permissions.ts\nconst networkPermissionsSchema = z.object({ outbound: z.array(z.string()).optional() }).strict();\nconst filesystemPermissionsSchema = z.object({\n\tread: z.array(z.string()).optional(),\n\twrite: z.array(z.string()).optional()\n}).strict();\nconst permissionsSchema = z.object({\n\tnetwork: networkPermissionsSchema.optional(),\n\tfilesystem: filesystemPermissionsSchema.optional(),\n\tsubprocess: z.boolean().optional()\n}).strict();\nconst userRoleSchema = z.enum([\"user\", \"admin\"]);\nconst userStatusSchema = z.enum([\n\t\"active\",\n\t\"suspended\",\n\t\"banned\"\n]);\nconst skillStatusSchema = z.enum([\n\t\"active\",\n\t\"deprecated\",\n\t\"quarantined\",\n\t\"removed\"\n]);\nconst adminActionSchema = z.enum([\n\t\"user.ban\",\n\t\"user.suspend\",\n\t\"user.unban\",\n\t\"user.promote\",\n\t\"user.demote\",\n\t\"skill.quarantine\",\n\t\"skill.remove\",\n\t\"skill.deprecate\",\n\t\"skill.restore\",\n\t\"skill.feature\",\n\t\"skill.unfeature\",\n\t\"org.suspend\",\n\t\"org.member.remove\",\n\t\"org.delete\"\n]);\nfunction isAdmin(role) {\n\treturn role === \"admin\";\n}\nconst skillsJsonSchema = z.object({\n\tname: z.string().min(1, \"Name must not be empty\").max(214, `Name must be 214 characters or fewer`).regex(/^@[a-z0-9-]+\\/[a-z0-9][a-z0-9-]*$/, \"Name must be scoped (@org/name), lowercase alphanumeric and hyphens\"),\n\tversion: z.string().regex(/^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/, \"Version must be valid semver\"),\n\tdescription: z.string().max(500, `Description must be 500 characters or fewer`).optional(),\n\tskills: z.record(z.string(), z.string()).optional(),\n\tpermissions: permissionsSchema.optional(),\n\trepository: z.string().url(\"Repository must be a valid URL\").optional(),\n\tvisibility: z.enum([\"public\", \"private\"]).optional(),\n\taudit: z.object({ min_score: z.number().min(0).max(10) }).strict().optional()\n}).strict();\n//#endregion\n//#region src/schemas/skills-lock.ts\nconst lockedSkillV1Schema = z.object({\n\tresolved: z.string().url(),\n\tintegrity: z.string().regex(/^sha512-/, \"Integrity must start with sha512-\"),\n\tpermissions: permissionsSchema,\n\taudit_score: z.number().min(0).max(10).nullable()\n});\nconst skillsLockV1Schema = z.object({\n\tlockfileVersion: z.literal(1),\n\tskills: z.record(z.string(), lockedSkillV1Schema)\n});\nconst lockedSkillSchema = z.object({\n\tresolved: z.string().url(),\n\tintegrity: z.string().regex(/^sha512-/, \"Integrity must start with sha512-\"),\n\tpermissions: permissionsSchema,\n\taudit_score: z.number().min(0).max(10).nullable(),\n\tdependencies: z.record(z.string(), z.string()).optional()\n});\nconst skillsLockSchema = z.object({\n\tlockfileVersion: z.union([z.literal(1), z.literal(2)]),\n\tskills: z.record(z.string(), lockedSkillSchema)\n});\n//#endregion\nexport { DEFAULT_PERMISSIONS, LEGACY_LOCKFILE_FILENAME, LEGACY_MANIFEST_FILENAME, LOCKFILE_FILENAME, LOCKFILE_VERSION, MANIFEST_FILENAME, MAX_DESCRIPTION_LENGTH, MAX_FILE_COUNT, MAX_NAME_LENGTH, MAX_PACKAGE_SIZE, PERMISSION_CATEGORIES, REGISTRY_API_VERSION, REGISTRY_URL, adminActionSchema, filesystemPermissionsSchema, isAdmin, lockedSkillSchema, lockedSkillV1Schema, networkPermissionsSchema, permissionsSchema, skillStatusSchema, skillsJsonSchema, skillsLockSchema, skillsLockV1Schema, userRoleSchema, userStatusSchema };\n\n//# sourceMappingURL=index.js.map","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n MANIFEST_FILENAME\n} from '@internals/schemas';\n\nimport { logger } from './logger.js';\n\nconst warnedManifest = new Set<string>();\nconst warnedLockfile = new Set<string>();\n\nexport interface ResolvedFile {\n path: string;\n isLegacy: boolean;\n exists: boolean;\n}\n\n/**\n * Resolve the manifest file path with fallback priority:\n * 1. tank.json (preferred)\n * 2. skills.json (deprecated fallback)\n *\n * If both exist, prefers tank.json and warns about duplicate.\n * Returns the path even if neither exists (for write operations).\n */\nexport function resolveManifestPath(directory?: string): ResolvedFile {\n const dir = directory ?? process.cwd();\n const newPath = path.join(dir, MANIFEST_FILENAME);\n const legacyPath = path.join(dir, LEGACY_MANIFEST_FILENAME);\n\n const newExists = fs.existsSync(newPath);\n const legacyExists = fs.existsSync(legacyPath);\n\n if (newExists && legacyExists && !warnedManifest.has(dir)) {\n warnedManifest.add(dir);\n logger.warn(`Both ${MANIFEST_FILENAME} and ${LEGACY_MANIFEST_FILENAME} exist. Using ${MANIFEST_FILENAME}.`);\n }\n\n if (newExists) {\n return { path: newPath, isLegacy: false, exists: true };\n }\n\n if (legacyExists) {\n if (!warnedManifest.has(dir)) {\n warnedManifest.add(dir);\n logger.warn(`${LEGACY_MANIFEST_FILENAME} is deprecated — run \\`tank migrate\\` to switch to ${MANIFEST_FILENAME}`);\n }\n return { path: legacyPath, isLegacy: true, exists: true };\n }\n\n // Neither exists — return the new filename for creation\n return { path: newPath, isLegacy: false, exists: false };\n}\n\n/**\n * Resolve the lockfile path with fallback priority:\n * 1. tank.lock (preferred)\n * 2. skills.lock (deprecated fallback)\n */\nexport function resolveLockfilePath(directory?: string): ResolvedFile {\n const dir = directory ?? process.cwd();\n const newPath = path.join(dir, LOCKFILE_FILENAME);\n const legacyPath = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n\n const newExists = fs.existsSync(newPath);\n const legacyExists = fs.existsSync(legacyPath);\n\n if (newExists && legacyExists && !warnedLockfile.has(dir)) {\n warnedLockfile.add(dir);\n logger.warn(`Both ${LOCKFILE_FILENAME} and ${LEGACY_LOCKFILE_FILENAME} exist. Using ${LOCKFILE_FILENAME}.`);\n }\n\n if (newExists) {\n return { path: newPath, isLegacy: false, exists: true };\n }\n\n if (legacyExists) {\n if (!warnedLockfile.has(dir)) {\n warnedLockfile.add(dir);\n logger.warn(`${LEGACY_LOCKFILE_FILENAME} is deprecated — run \\`tank migrate\\` to switch to ${LOCKFILE_FILENAME}`);\n }\n return { path: legacyPath, isLegacy: true, exists: true };\n }\n\n return { path: newPath, isLegacy: false, exists: false };\n}\n\n/**\n * Get the path where new manifest files should be written.\n * Always returns the new filename (tank.json).\n * If a legacy file exists but no new file, returns the legacy path\n * to avoid creating a second file during write operations.\n */\nexport function getManifestWritePath(directory?: string): string {\n const resolved = resolveManifestPath(directory);\n return resolved.path;\n}\n\n/**\n * Get the path where new lockfiles should be written.\n * Always returns the new filename (tank.lock).\n * If a legacy file exists but no new file, returns the legacy path\n * to avoid creating a second file during write operations.\n */\nexport function getLockfileWritePath(directory?: string): string {\n const resolved = resolveLockfilePath(directory);\n return resolved.path;\n}\n","import fs from 'node:fs';\n\nimport type { Permissions, SkillsLock } from '@internals/schemas';\n\nimport { resolveLockfilePath } from './manifest.js';\n\n/**\n * Read and parse the lockfile from the given directory.\n * Returns null if the file doesn't exist or is corrupt.\n */\nexport function readLockfile(directory?: string): SkillsLock | null {\n const resolved = resolveLockfilePath(directory);\n\n if (!resolved.exists) {\n return null;\n }\n\n try {\n const raw = fs.readFileSync(resolved.path, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n return null;\n }\n}\n\n/**\n * Write a lockfile deterministically: sorted keys, consistent formatting, trailing newline.\n */\nexport function writeLockfile(lock: SkillsLock, directory?: string): void {\n const resolved = resolveLockfilePath(directory);\n const lockPath = resolved.path;\n\n // Sort skill keys alphabetically for determinism\n const sortedSkills: Record<string, SkillsLock['skills'][string]> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n\n const output: SkillsLock = {\n lockfileVersion: lock.lockfileVersion,\n skills: sortedSkills\n };\n\n fs.writeFileSync(lockPath, `${JSON.stringify(output, null, 2)}\\n`);\n}\n\n/**\n * Compute the union of all skill permissions from a lockfile.\n * Merges network outbound, filesystem read/write (deduped), and subprocess (OR).\n */\nexport function computeResolvedPermissions(lock: SkillsLock): Permissions {\n const entries = Object.values(lock.skills);\n if (entries.length === 0) {\n return {};\n }\n\n const outbound = new Set<string>();\n const fsRead = new Set<string>();\n const fsWrite = new Set<string>();\n let subprocess = false;\n\n for (const entry of entries) {\n const perms = entry.permissions;\n\n if (perms.network?.outbound) {\n for (const domain of perms.network.outbound) {\n outbound.add(domain);\n }\n }\n\n if (perms.filesystem?.read) {\n for (const pattern of perms.filesystem.read) {\n fsRead.add(pattern);\n }\n }\n\n if (perms.filesystem?.write) {\n for (const pattern of perms.filesystem.write) {\n fsWrite.add(pattern);\n }\n }\n\n if (perms.subprocess === true) {\n subprocess = true;\n }\n }\n\n const result: Permissions = {};\n\n if (outbound.size > 0) {\n result.network = { outbound: [...outbound].sort() };\n }\n\n if (fsRead.size > 0 || fsWrite.size > 0) {\n result.filesystem = {};\n if (fsRead.size > 0) {\n result.filesystem.read = [...fsRead].sort();\n }\n if (fsWrite.size > 0) {\n result.filesystem.write = [...fsWrite].sort();\n }\n }\n\n if (subprocess) {\n result.subprocess = true;\n }\n\n return result;\n}\n\n/**\n * Check if resolved permissions fit within the project's permission budget.\n *\n * Returns:\n * - 'pass' if all resolved permissions are within budget\n * - 'fail' if any resolved permission exceeds budget\n * - 'no_budget' if no project permissions are defined\n */\nexport function computeBudgetCheck(\n resolvedPermissions: Permissions,\n projectPermissions: Permissions | undefined\n): 'pass' | 'fail' | 'no_budget' {\n if (projectPermissions === undefined) {\n return 'no_budget';\n }\n\n // Check subprocess\n if (resolvedPermissions.subprocess === true && projectPermissions.subprocess === false) {\n return 'fail';\n }\n\n // Check network outbound\n if (resolvedPermissions.network?.outbound) {\n const budgetOutbound = new Set(projectPermissions.network?.outbound ?? []);\n for (const domain of resolvedPermissions.network.outbound) {\n if (!budgetOutbound.has(domain)) {\n return 'fail';\n }\n }\n }\n\n // Check filesystem read\n if (resolvedPermissions.filesystem?.read) {\n const budgetRead = new Set(projectPermissions.filesystem?.read ?? []);\n for (const pattern of resolvedPermissions.filesystem.read) {\n if (!budgetRead.has(pattern)) {\n return 'fail';\n }\n }\n }\n\n // Check filesystem write\n if (resolvedPermissions.filesystem?.write) {\n const budgetWrite = new Set(projectPermissions.filesystem?.write ?? []);\n for (const pattern of resolvedPermissions.filesystem.write) {\n if (!budgetWrite.has(pattern)) {\n return 'fail';\n }\n }\n }\n\n return 'pass';\n}\n","import chalk from 'chalk';\n\nimport { getConfig } from '~/lib/config.js';\nimport { readLockfile } from '~/lib/lockfile.js';\nimport { USER_AGENT } from '~/version.js';\n\nexport interface AuditOptions {\n name?: string;\n configDir?: string;\n}\n\ninterface VersionDetails {\n name: string;\n version: string;\n permissions?: {\n network?: { outbound?: string[] };\n filesystem?: { read?: string[]; write?: string[] };\n subprocess?: boolean;\n };\n auditScore: number | null;\n auditStatus: string;\n downloadUrl: string;\n publishedAt: string;\n downloads: number;\n}\n\ninterface AuditResult {\n name: string;\n version: string;\n score: number | null;\n status: string;\n permissions?: VersionDetails['permissions'];\n error?: boolean;\n}\n\nfunction scoreColor(score: number): (text: string) => string {\n if (score >= 7) return chalk.green;\n if (score >= 4) return chalk.yellow;\n return chalk.red;\n}\n\nfunction formatScore(result: AuditResult): string {\n if (result.error) return chalk.dim('error');\n if (result.score == null || result.status !== 'completed') {\n return chalk.dim('pending');\n }\n return scoreColor(result.score)(result.score.toFixed(1));\n}\n\nfunction formatStatus(result: AuditResult): string {\n if (result.error) return chalk.dim('error');\n if (result.score == null || result.status !== 'completed') {\n return chalk.dim('Analysis pending');\n }\n if (result.score >= 4) return chalk.green('pass');\n return chalk.red('issues');\n}\n\nfunction padRight(text: string, width: number): string {\n if (text.length >= width) return text;\n return text + ' '.repeat(width - text.length);\n}\n\n/**\n * Parse a lockfile key like \"@org/skill@1.0.0\" into { name, version }.\n * Scoped packages start with @, so find the LAST @ to split.\n */\nfunction parseLockKey(key: string): { name: string; version: string } {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return {\n name: key.slice(0, lastAt),\n version: key.slice(lastAt + 1)\n };\n}\n\nasync function fetchVersionDetails(registryUrl: string, name: string, version: string): Promise<VersionDetails> {\n const encodedName = encodeURIComponent(name);\n const url = `${registryUrl}/api/v1/skills/${encodedName}/${version}`;\n\n let res: Response;\n try {\n res = await fetch(url, {\n headers: { 'User-Agent': USER_AGENT }\n });\n } catch (err) {\n throw new Error(`Network error fetching audit data: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n throw new Error(`API error for ${name}@${version}: ${res.status} ${res.statusText}`);\n }\n\n return (await res.json()) as VersionDetails;\n}\n\nfunction displayDetailedAudit(result: AuditResult): void {\n console.log('');\n console.log(chalk.bold(result.name));\n console.log('');\n console.log(`${chalk.dim('Version:'.padEnd(14))}${result.version}`);\n console.log(`${chalk.dim('Audit Score:'.padEnd(14))}${formatScore(result)}`);\n console.log(`${chalk.dim('Status:'.padEnd(14))}${result.status}`);\n\n const perms = result.permissions;\n if (perms) {\n console.log('');\n console.log(chalk.bold('Permissions:'));\n\n const networkDomains = perms.network?.outbound;\n if (networkDomains && networkDomains.length > 0) {\n console.log(` ${chalk.dim('Network:'.padEnd(14))}${networkDomains.join(', ')}`);\n }\n\n const fsRead = perms.filesystem?.read;\n const fsWrite = perms.filesystem?.write;\n if (fsRead || fsWrite) {\n const parts: string[] = [];\n if (fsRead && fsRead.length > 0) {\n parts.push(`${fsRead.join(', ')} (read)`);\n }\n if (fsWrite && fsWrite.length > 0) {\n parts.push(`${fsWrite.join(', ')} (write)`);\n }\n console.log(` ${chalk.dim('Filesystem:'.padEnd(14))}${parts.join(', ')}`);\n }\n\n console.log(` ${chalk.dim('Subprocess:'.padEnd(14))}${perms.subprocess ? 'yes' : 'no'}`);\n }\n}\n\nfunction displayTable(results: AuditResult[]): void {\n // Header\n console.log(`${padRight('NAME', 30) + padRight('VERSION', 12) + padRight('SCORE', 10)}STATUS`);\n\n for (const result of results) {\n const name = chalk.bold(padRight(result.name, 30));\n const version = padRight(result.version, 12);\n const score = padRight(formatScore(result), 10);\n const status = formatStatus(result);\n\n console.log(`${name}${version}${score}${status}`);\n }\n\n // Summary\n const total = results.length;\n const pass = results.filter((r) => !r.error && r.score != null && r.status === 'completed' && r.score >= 4).length;\n const issues = total - pass;\n\n console.log('');\n console.log(\n `${total} skill${total === 1 ? '' : 's'} audited. ` +\n `${pass} pass, ${issues} ${issues === 1 ? 'has' : 'have'} issues.`\n );\n}\n\nexport async function auditCommand(options: AuditOptions): Promise<void> {\n const { name, configDir } = options;\n const config = getConfig(configDir);\n\n // Read lockfile\n const lock = readLockfile();\n\n if (!lock) {\n console.log('No lockfile found. Run: tank install');\n return;\n }\n\n const entries = Object.entries(lock.skills);\n\n if (entries.length === 0) {\n console.log('No skills installed.');\n return;\n }\n\n // Single skill audit\n if (name) {\n // Find the skill in lockfile\n const matchingEntry = entries.find(([key]) => {\n const parsed = parseLockKey(key);\n return parsed.name === name;\n });\n\n if (!matchingEntry) {\n console.log(`Skill not installed: ${name}`);\n return;\n }\n\n const [key] = matchingEntry;\n const parsed = parseLockKey(key);\n\n const details = await fetchVersionDetails(config.registry, parsed.name, parsed.version);\n\n const result: AuditResult = {\n name: parsed.name,\n version: parsed.version,\n score: details.auditScore,\n status: details.auditStatus,\n permissions: details.permissions\n };\n\n displayDetailedAudit(result);\n return;\n }\n\n // Audit all skills\n const results: AuditResult[] = [];\n\n for (const [key] of entries) {\n const parsed = parseLockKey(key);\n\n try {\n const details = await fetchVersionDetails(config.registry, parsed.name, parsed.version);\n\n results.push({\n name: parsed.name,\n version: parsed.version,\n score: details.auditScore,\n status: details.auditStatus\n });\n } catch (err) {\n // For network errors, re-throw immediately\n if (err instanceof Error && err.message.startsWith('Network error')) {\n throw err;\n }\n\n // For API errors (404, etc.), show the skill with error status\n results.push({\n name: parsed.name,\n version: parsed.version,\n score: null,\n status: 'error',\n error: true\n });\n }\n }\n\n displayTable(results);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface AgentInfo {\n id: string;\n name: string;\n skillsDir: string;\n}\n\ninterface AgentDefinition {\n id: string;\n name: string;\n /** Returns possible config directories in priority order. First is the default. */\n configDirs: (homedir: string) => string[];\n}\n\nconst resolveHomedir = (homedir?: string): string => homedir ?? os.homedir();\n\nconst isWindows = process.platform === 'win32';\n\nexport const SUPPORTED_AGENTS: AgentDefinition[] = [\n {\n id: 'claude',\n name: 'Claude Code',\n configDirs: (homedir) => [path.join(homedir, '.claude')]\n },\n {\n id: 'opencode',\n name: 'OpenCode',\n configDirs: (homedir) => {\n const dirs = [path.join(homedir, '.config', 'opencode')];\n if (isWindows) {\n const appData = process.env.APPDATA;\n if (appData) dirs.push(path.join(appData, 'opencode'));\n }\n return dirs;\n }\n },\n {\n id: 'cursor',\n name: 'Cursor',\n configDirs: (homedir) => {\n const dirs = [path.join(homedir, '.cursor')];\n if (isWindows) {\n const appData = process.env.APPDATA;\n if (appData) dirs.push(path.join(appData, 'Cursor'));\n }\n return dirs;\n }\n },\n {\n id: 'codex',\n name: 'Codex',\n configDirs: (homedir) => [path.join(homedir, '.codex')]\n },\n {\n id: 'openclaw',\n name: 'OpenClaw',\n configDirs: (homedir) => [path.join(homedir, '.openclaw')]\n },\n {\n id: 'universal',\n name: 'Universal',\n configDirs: (homedir) => [path.join(homedir, '.agents')]\n }\n];\n\n/**\n * Returns the first existing config directory for an agent,\n * or the first (default) directory if none exist.\n */\nfunction resolveConfigDir(agent: AgentDefinition, homedir: string): string {\n const dirs = agent.configDirs(homedir);\n return dirs.find((d) => fs.existsSync(d)) ?? dirs[0];\n}\n\nfunction isAgentInstalled(agent: AgentDefinition, homedir: string): boolean {\n return agent.configDirs(homedir).some((d) => fs.existsSync(d));\n}\n\nexport function getSupportedAgents(homedir?: string): AgentInfo[] {\n const resolved = resolveHomedir(homedir);\n return SUPPORTED_AGENTS.map((agent) => ({\n id: agent.id,\n name: agent.name,\n skillsDir: path.join(resolveConfigDir(agent, resolved), 'skills')\n }));\n}\n\nexport function detectInstalledAgents(homedir?: string): AgentInfo[] {\n const resolved = resolveHomedir(homedir);\n return SUPPORTED_AGENTS.filter((agent) => isAgentInstalled(agent, resolved)).map((agent) => ({\n id: agent.id,\n name: agent.name,\n skillsDir: path.join(resolveConfigDir(agent, resolved), 'skills')\n }));\n}\n\nexport function getAgentSkillDir(agentId: string, homedir?: string): string | null {\n const resolved = resolveHomedir(homedir);\n const agent = SUPPORTED_AGENTS.find((entry) => entry.id === agentId);\n if (!agent) {\n return null;\n }\n return path.join(resolveConfigDir(agent, resolved), 'skills');\n}\n\nexport function getSymlinkName(skillName: string): string {\n const match = skillName.match(/^@([^/]+)\\/(.+)$/);\n if (!match) {\n return skillName;\n }\n const [, scope, name] = match;\n if (scope.length === 0 || name.length === 0) {\n return skillName;\n }\n return `${scope}--${name}`;\n}\n\nexport function getGlobalSkillsDir(homedir?: string): string {\n return path.join(resolveHomedir(homedir), '.tank', 'skills');\n}\n\nexport function getGlobalAgentSkillsDir(homedir?: string): string {\n return path.join(resolveHomedir(homedir), '.tank', 'agent-skills');\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport interface LinkEntry {\n source: 'local' | 'global' | 'dev';\n sourceDir: string;\n installedAt: string;\n agentLinks: Record<string, string>;\n}\n\nexport interface LinksManifest {\n version: 1;\n links: Record<string, LinkEntry>;\n}\n\nexport function createEmptyManifest(): LinksManifest {\n return { version: 1, links: {} };\n}\n\nexport function readLinks(linksDir: string): LinksManifest {\n if (!fs.existsSync(linksDir)) {\n return createEmptyManifest();\n }\n\n const linksPath = path.join(linksDir, 'links.json');\n if (!fs.existsSync(linksPath)) {\n return createEmptyManifest();\n }\n\n try {\n const raw = fs.readFileSync(linksPath, 'utf-8');\n return JSON.parse(raw) as LinksManifest;\n } catch {\n return createEmptyManifest();\n }\n}\n\nexport function writeLinks(linksDir: string, manifest: LinksManifest): void {\n if (!fs.existsSync(linksDir)) {\n fs.mkdirSync(linksDir, { recursive: true });\n }\n\n const sortedLinks: Record<string, LinkEntry> = {};\n for (const skillName of Object.keys(manifest.links).sort()) {\n const entry = manifest.links[skillName];\n const sortedAgentLinks: Record<string, string> = {};\n for (const agentId of Object.keys(entry.agentLinks).sort()) {\n sortedAgentLinks[agentId] = entry.agentLinks[agentId];\n }\n\n sortedLinks[skillName] = {\n source: entry.source,\n sourceDir: entry.sourceDir,\n installedAt: entry.installedAt,\n agentLinks: sortedAgentLinks\n };\n }\n\n const output: LinksManifest = {\n version: manifest.version,\n links: sortedLinks\n };\n\n fs.writeFileSync(path.join(linksDir, 'links.json'), `${JSON.stringify(output, null, 2)}\\n`);\n}\n\nexport function readLocalLinks(projectDir?: string): LinksManifest {\n const dir = projectDir ?? process.cwd();\n return readLinks(path.join(dir, '.tank'));\n}\n\nexport function readGlobalLinks(homedir?: string): LinksManifest {\n const home = homedir ?? os.homedir();\n return readLinks(path.join(home, '.tank'));\n}\n\nexport function addLink(manifest: LinksManifest, skillName: string, entry: LinkEntry): LinksManifest {\n return {\n version: manifest.version,\n links: {\n ...manifest.links,\n [skillName]: entry\n }\n };\n}\n\nexport function removeLink(manifest: LinksManifest, skillName: string): LinksManifest {\n const links = { ...manifest.links };\n if (skillName in links) {\n delete links[skillName];\n }\n\n return {\n version: manifest.version,\n links\n };\n}\n\nexport function getLinksForSkill(manifest: LinksManifest, skillName: string): LinkEntry | undefined {\n return manifest.links[skillName];\n}\n\nexport function addAgentLink(\n manifest: LinksManifest,\n skillName: string,\n agentId: string,\n symlinkPath: string\n): LinksManifest {\n const existing = manifest.links[skillName];\n const baseEntry: LinkEntry = existing ?? {\n source: 'local',\n sourceDir: process.cwd(),\n installedAt: new Date().toISOString(),\n agentLinks: {}\n };\n\n const agentLinks = {\n ...baseEntry.agentLinks,\n [agentId]: symlinkPath\n };\n\n return addLink(manifest, skillName, { ...baseEntry, agentLinks });\n}\n\nexport function removeAgentLink(manifest: LinksManifest, skillName: string, agentId: string): LinksManifest {\n const existing = manifest.links[skillName];\n if (!existing) {\n return { version: manifest.version, links: { ...manifest.links } };\n }\n\n const agentLinks = { ...existing.agentLinks };\n if (agentId in agentLinks) {\n delete agentLinks[agentId];\n }\n\n if (Object.keys(agentLinks).length === 0) {\n return removeLink(manifest, skillName);\n }\n\n return addLink(manifest, skillName, { ...existing, agentLinks });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { type AgentInfo, detectInstalledAgents, getSymlinkName } from './agents.js';\nimport { type LinkEntry, type LinksManifest, readLinks, writeLinks } from './links.js';\n\nexport interface LinkResult {\n linked: string[];\n skipped: string[];\n failed: Array<{ agentId: string; error: string }>;\n}\n\nexport interface UnlinkResult {\n unlinked: string[];\n notFound: string[];\n}\n\nexport interface AgentLinkStatus {\n agentId: string;\n agentName: string;\n linked: boolean;\n symlinkPath: string;\n targetValid: boolean;\n}\n\ninterface LinkOptions {\n skillName: string;\n sourceDir: string;\n linksDir: string;\n source: 'local' | 'global' | 'dev';\n homedir?: string;\n}\n\ninterface UnlinkOptions {\n skillName: string;\n linksDir: string;\n homedir?: string;\n}\n\ninterface StatusOptions {\n skillName: string;\n linksDir: string;\n homedir?: string;\n}\n\ninterface AllStatusOptions {\n linksDir: string;\n homedir?: string;\n}\n\ninterface SymlinkCheck {\n exists: boolean;\n isSymlink: boolean;\n targetPath: string | null;\n targetValid: boolean;\n}\n\nconst resolveSymlinkTarget = (symlinkPath: string, target: string): string => {\n if (path.isAbsolute(target)) {\n return target;\n }\n return path.resolve(path.dirname(symlinkPath), target);\n};\n\nconst checkSymlink = (symlinkPath: string): SymlinkCheck => {\n try {\n const stats = fs.lstatSync(symlinkPath);\n if (!stats.isSymbolicLink()) {\n return { exists: true, isSymlink: false, targetPath: null, targetValid: false };\n }\n\n const rawTarget = fs.readlinkSync(symlinkPath);\n const targetPath = resolveSymlinkTarget(symlinkPath, rawTarget);\n const targetValid = fs.existsSync(targetPath);\n return { exists: true, isSymlink: true, targetPath, targetValid };\n } catch {\n return { exists: false, isSymlink: false, targetPath: null, targetValid: false };\n }\n};\n\nconst createEntry = (manifest: LinksManifest, skillName: string, entry: LinkEntry): LinksManifest => ({\n version: manifest.version,\n links: {\n ...manifest.links,\n [skillName]: entry\n }\n});\n\nexport function linkSkillToAgents(options: LinkOptions): LinkResult {\n const result: LinkResult = { linked: [], skipped: [], failed: [] };\n const agents = detectInstalledAgents(options.homedir);\n const symlinkName = getSymlinkName(options.skillName);\n const resolvedSource = path.resolve(options.sourceDir);\n const agentLinks: Record<string, string> = {};\n\n for (const agent of agents) {\n const symlinkPath = path.join(agent.skillsDir, symlinkName);\n\n try {\n fs.mkdirSync(agent.skillsDir, { recursive: true });\n const check = checkSymlink(symlinkPath);\n\n if (check.exists && !check.isSymlink) {\n result.failed.push({\n agentId: agent.id,\n error: `Path exists and is not a symlink: ${symlinkPath}`\n });\n continue;\n }\n\n if (check.exists && check.isSymlink && check.targetPath) {\n const targetMatches = path.resolve(check.targetPath) === resolvedSource;\n if (targetMatches && check.targetValid) {\n result.skipped.push(agent.id);\n agentLinks[agent.id] = symlinkPath;\n continue;\n }\n\n fs.unlinkSync(symlinkPath);\n }\n\n fs.symlinkSync(options.sourceDir, symlinkPath, 'dir');\n result.linked.push(agent.id);\n agentLinks[agent.id] = symlinkPath;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n result.failed.push({ agentId: agent.id, error: message });\n }\n }\n\n const manifest = readLinks(options.linksDir);\n const entry: LinkEntry = {\n source: options.source,\n sourceDir: options.sourceDir,\n installedAt: new Date().toISOString(),\n agentLinks\n };\n const updated = createEntry(manifest, options.skillName, entry);\n writeLinks(options.linksDir, updated);\n\n return result;\n}\n\nexport function unlinkSkillFromAgents(options: UnlinkOptions): UnlinkResult {\n const manifest = readLinks(options.linksDir);\n const entry = manifest.links[options.skillName];\n if (!entry) {\n return { unlinked: [], notFound: [] };\n }\n\n const result: UnlinkResult = { unlinked: [], notFound: [] };\n for (const [agentId, symlinkPath] of Object.entries(entry.agentLinks)) {\n try {\n const stats = fs.lstatSync(symlinkPath);\n if (!stats.isSymbolicLink()) {\n result.notFound.push(agentId);\n continue;\n }\n\n fs.unlinkSync(symlinkPath);\n result.unlinked.push(agentId);\n } catch {\n result.notFound.push(agentId);\n }\n }\n\n const updated: LinksManifest = {\n version: manifest.version,\n links: { ...manifest.links }\n };\n if (options.skillName in updated.links) {\n delete updated.links[options.skillName];\n }\n writeLinks(options.linksDir, updated);\n\n return result;\n}\n\nconst getStatusForAgent = (agent: AgentInfo, skillName: string): AgentLinkStatus => {\n const symlinkName = getSymlinkName(skillName);\n const symlinkPath = path.join(agent.skillsDir, symlinkName);\n const check = checkSymlink(symlinkPath);\n\n return {\n agentId: agent.id,\n agentName: agent.name,\n linked: check.exists && check.isSymlink,\n symlinkPath,\n targetValid: check.exists && check.isSymlink && check.targetValid\n };\n};\n\nexport function getSkillLinkStatus(options: StatusOptions): AgentLinkStatus[] {\n const agents = detectInstalledAgents(options.homedir);\n readLinks(options.linksDir);\n return agents.map((agent) => getStatusForAgent(agent, options.skillName));\n}\n\nexport function getAllLinkStatuses(options: AllStatusOptions): Record<string, AgentLinkStatus[]> {\n const agents = detectInstalledAgents(options.homedir);\n const manifest = readLinks(options.linksDir);\n const result: Record<string, AgentLinkStatus[]> = {};\n\n for (const skillName of Object.keys(manifest.links)) {\n result[skillName] = agents.map((agent) => getStatusForAgent(agent, skillName));\n }\n\n return result;\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport chalk from 'chalk';\n\nimport { detectInstalledAgents, getGlobalSkillsDir, getSupportedAgents } from '~/lib/agents.js';\nimport { type AgentLinkStatus, getSkillLinkStatus } from '~/lib/linker.js';\nimport { readGlobalLinks } from '~/lib/links.js';\nimport { resolveLockfilePath, resolveManifestPath } from '~/lib/manifest.js';\n\nexport interface DoctorOptions {\n directory?: string;\n homedir?: string;\n}\n\ninterface SkillStatusSummary {\n statusText: string;\n issues: string[];\n}\n\nconst parseLockKey = (key: string): string => {\n const lastAt = key.lastIndexOf('@');\n if (lastAt > 0) {\n return key.slice(0, lastAt);\n }\n return key;\n};\n\nconst getExtractDir = (baseDir: string, skillName: string): string => {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(baseDir, scope, name);\n }\n return path.join(baseDir, skillName);\n};\n\nconst formatAgents = (agents: AgentLinkStatus[], label: string): string => {\n if (agents.length === 0) {\n return `${label}`;\n }\n return `${label} (${agents.map((agent) => agent.agentName).join(', ')})`;\n};\n\nconst summarizeStatus = (\n skillName: string,\n statuses: AgentLinkStatus[],\n extractExists: boolean,\n scope: 'local' | 'global' | 'dev'\n): SkillStatusSummary => {\n if (statuses.length === 0) {\n return {\n statusText: chalk.yellow('⚠️ no agents detected'),\n issues: []\n };\n }\n\n const brokenAgents = statuses.filter((status) => status.linked && !status.targetValid);\n const linkedAgents = statuses.filter((status) => status.linked && status.targetValid);\n\n if (brokenAgents.length > 0) {\n const base = chalk.yellow('⚠️ broken link');\n const text = formatAgents(brokenAgents, base);\n const command =\n scope === 'dev'\n ? `Run \\`tank link\\` in the skill directory to fix ${skillName}`\n : `Run \\`tank install ${skillName}\\` to fix broken link`;\n return {\n statusText: text,\n issues: [command]\n };\n }\n\n if (linkedAgents.length > 0) {\n if (!extractExists && scope !== 'dev') {\n return {\n statusText: chalk.yellow('⚠️ missing extract'),\n issues: [`Run \\`tank install ${skillName}\\` to install missing extract`]\n };\n }\n const text = formatAgents(linkedAgents, chalk.green('✅ linked'));\n return { statusText: text, issues: [] };\n }\n\n if (!extractExists && scope !== 'dev') {\n return {\n statusText: chalk.yellow('⚠️ missing extract'),\n issues: [`Run \\`tank install ${skillName}\\` to install missing extract`]\n };\n }\n\n return {\n statusText: chalk.red('❌ not linked'),\n issues: []\n };\n};\n\nconst printSectionHeader = (title: string): void => {\n console.log(`\\n${chalk.bold(title)}:`);\n};\n\nexport async function doctorCommand(options?: DoctorOptions): Promise<void> {\n try {\n const directory = options?.directory ?? process.cwd();\n const homedir = options?.homedir ?? os.homedir();\n\n const supportedAgents = getSupportedAgents(homedir);\n const installedAgents = detectInstalledAgents(homedir);\n const installedIds = new Set(installedAgents.map((agent) => agent.id));\n\n const resolvedManifest = resolveManifestPath(directory);\n const localSkills = resolvedManifest.exists\n ? Object.keys(JSON.parse(fs.readFileSync(resolvedManifest.path, 'utf-8')).skills ?? {})\n : [];\n localSkills.sort();\n\n const resolvedGlobalLock = resolveLockfilePath(path.join(homedir, '.tank'));\n const globalSkills = resolvedGlobalLock.exists\n ? Object.keys(JSON.parse(fs.readFileSync(resolvedGlobalLock.path, 'utf-8')).skills ?? {}).map(parseLockKey)\n : [];\n const uniqueGlobal = Array.from(new Set(globalSkills)).sort();\n\n const globalLinks = readGlobalLinks(homedir);\n const devLinks = Object.entries(globalLinks.links)\n .filter(([, entry]) => entry.source === 'dev')\n .map(([skillName]) => skillName)\n .sort();\n\n const suggestions = new Set<string>();\n\n console.log(chalk.bold('Tank Doctor Report'));\n console.log(chalk.bold('=================='));\n\n printSectionHeader('Detected Agents');\n for (const agent of supportedAgents) {\n const installed = installedIds.has(agent.id);\n const icon = installed ? chalk.green('✅') : chalk.red('❌');\n const details = installed ? agent.skillsDir : chalk.gray('(not found)');\n console.log(` ${icon} ${agent.name} ${details}`);\n }\n\n if (installedAgents.length === 0) {\n suggestions.add('No agents detected. Install an AI agent to enable skill linking.');\n }\n\n const localLinksDir = path.join(directory, '.tank');\n printSectionHeader(`Local Skills (${localSkills.length}): [project: ${directory}]`);\n if (localSkills.length === 0) {\n console.log(' none');\n }\n for (const skillName of localSkills) {\n const extractDir = getExtractDir(path.join(directory, '.tank', 'skills'), skillName);\n const extractExists = fs.existsSync(extractDir);\n const statuses = getSkillLinkStatus({ skillName, linksDir: localLinksDir, homedir });\n const summary = summarizeStatus(skillName, statuses, extractExists, 'local');\n for (const issue of summary.issues) suggestions.add(issue);\n console.log(` ${skillName} ${summary.statusText}`);\n }\n\n const globalLinksDir = path.join(homedir, '.tank');\n const globalSkillsDir = getGlobalSkillsDir(homedir);\n printSectionHeader(`Global Skills (${uniqueGlobal.length}): [${globalSkillsDir}]`);\n if (uniqueGlobal.length === 0) {\n console.log(' none');\n }\n for (const skillName of uniqueGlobal) {\n const extractDir = getExtractDir(globalSkillsDir, skillName);\n const extractExists = fs.existsSync(extractDir);\n const statuses = getSkillLinkStatus({ skillName, linksDir: globalLinksDir, homedir });\n const summary = summarizeStatus(skillName, statuses, extractExists, 'global');\n for (const issue of summary.issues) suggestions.add(issue);\n console.log(` ${skillName} ${summary.statusText}`);\n }\n\n printSectionHeader(`Dev Links (${devLinks.length}): [tank link]`);\n if (devLinks.length === 0) {\n console.log(' none');\n }\n for (const skillName of devLinks) {\n const statuses = getSkillLinkStatus({ skillName, linksDir: globalLinksDir, homedir });\n const summary = summarizeStatus(skillName, statuses, true, 'dev');\n for (const issue of summary.issues) suggestions.add(issue);\n console.log(` ${skillName} ${summary.statusText}`);\n }\n\n if (localSkills.length === 0 && uniqueGlobal.length === 0 && devLinks.length === 0) {\n suggestions.add('Run `tank install @tank/typescript` to add your first skill');\n }\n\n printSectionHeader('Suggestions');\n if (suggestions.size === 0) {\n console.log(' none');\n } else {\n for (const suggestion of suggestions) {\n console.log(` • ${suggestion}`);\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.log(chalk.red(`Doctor report failed: ${message}`));\n console.log('Suggestions:');\n console.log(' • Run `tank install @tank/typescript` to add your first skill');\n }\n}\n","import chalk from 'chalk';\n\nimport { getConfig } from '~/lib/config.js';\nimport { USER_AGENT } from '~/version.js';\n\nexport interface InfoOptions {\n name: string;\n configDir?: string;\n}\n\ninterface SkillMetadata {\n name: string;\n description?: string;\n visibility?: 'public' | 'private';\n latestVersion: string;\n publisher: { displayName: string };\n createdAt: string;\n updatedAt?: string;\n}\n\ninterface VersionDetails {\n name: string;\n version: string;\n permissions?: {\n network?: { outbound?: string[] };\n filesystem?: { read?: string[]; write?: string[] };\n subprocess?: boolean;\n };\n auditScore?: number;\n auditStatus?: string;\n downloadUrl: string;\n publishedAt: string;\n}\n\nfunction formatDate(iso: string): string {\n try {\n return iso.split('T')[0];\n } catch {\n return iso;\n }\n}\n\nfunction labelValue(label: string, value: string): string {\n return `${chalk.dim(label.padEnd(14))}${value}`;\n}\n\nexport async function infoCommand(options: InfoOptions): Promise<void> {\n const { name, configDir } = options;\n const config = getConfig(configDir);\n\n const encodedName = encodeURIComponent(name);\n const metaUrl = `${config.registry}/api/v1/skills/${encodedName}`;\n const headers: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n }\n\n // 1. Fetch skill metadata\n let metaRes: Response;\n try {\n metaRes = await fetch(metaUrl, {\n headers\n });\n } catch (err) {\n throw new Error(`Network error fetching skill info: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (metaRes.status === 404) {\n console.log(`Skill not found: ${name}`);\n return;\n }\n\n if (!metaRes.ok) {\n const body = (await metaRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? `Failed to fetch skill info: ${metaRes.statusText}`);\n }\n\n const meta = (await metaRes.json()) as SkillMetadata;\n\n // 2. Fetch version details for permissions\n const versionUrl = `${config.registry}/api/v1/skills/${encodedName}/${meta.latestVersion}`;\n\n let versionRes: Response;\n try {\n versionRes = await fetch(versionUrl, {\n headers\n });\n } catch (err) {\n throw new Error(`Network error fetching version details: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n let versionData: VersionDetails | undefined;\n if (versionRes.ok) {\n versionData = (await versionRes.json()) as VersionDetails;\n }\n\n // 3. Display info\n console.log('');\n console.log(chalk.bold(meta.name));\n console.log('');\n\n if (meta.description) {\n console.log(labelValue('Description:', meta.description));\n }\n\n console.log(labelValue('Version:', meta.latestVersion));\n if (meta.visibility) {\n console.log(labelValue('Visibility:', meta.visibility));\n }\n console.log(labelValue('Publisher:', meta.publisher?.displayName ?? 'unknown'));\n\n if (versionData?.auditScore != null) {\n console.log(labelValue('Audit Score:', `${versionData.auditScore}/10`));\n }\n\n console.log(labelValue('Created:', formatDate(meta.createdAt)));\n\n // 4. Display permissions\n const perms = versionData?.permissions;\n if (perms) {\n console.log('');\n console.log(chalk.bold('Permissions:'));\n\n const networkDomains = perms.network?.outbound;\n if (networkDomains && networkDomains.length > 0) {\n console.log(` ${chalk.dim('Network:'.padEnd(14))}${networkDomains.join(', ')}`);\n }\n\n const fsRead = perms.filesystem?.read;\n const fsWrite = perms.filesystem?.write;\n if (fsRead || fsWrite) {\n const parts: string[] = [];\n if (fsRead && fsRead.length > 0) {\n parts.push(`${fsRead.join(', ')} (read)`);\n }\n if (fsWrite && fsWrite.length > 0) {\n parts.push(`${fsWrite.join(', ')} (write)`);\n }\n console.log(` ${chalk.dim('Filesystem:'.padEnd(14))}${parts.join(', ')}`);\n }\n\n const subprocess = perms.subprocess;\n console.log(` ${chalk.dim('Subprocess:'.padEnd(14))}${subprocess ? 'yes' : 'no'}`);\n }\n\n // 5. Install hint\n console.log('');\n console.log(`Install: ${chalk.cyan(`tank install ${meta.name}`)}`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { confirm, input } from '@inquirer/prompts';\nimport { MANIFEST_FILENAME, skillsJsonSchema } from '@internals/schemas';\n\nimport { getConfig } from '~/lib/config.js';\nimport { logger } from '~/lib/logger.js';\nimport { resolveManifestPath } from '~/lib/manifest.js';\n\nconst NAME_PATTERN = /^(@[a-z0-9-]+\\/)?[a-z0-9][a-z0-9-]*$/;\nconst SEMVER_PATTERN = /^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/;\nconst MAX_NAME_LENGTH = 214;\n\nexport function validateName(value: string): true | string {\n if (!value) return 'Name must not be empty';\n if (value.length > MAX_NAME_LENGTH) return `Name must be ${MAX_NAME_LENGTH} characters or fewer`;\n if (!NAME_PATTERN.test(value)) return 'Name must be lowercase, alphanumeric + hyphens, optionally scoped (@org/name)';\n return true;\n}\n\nexport function validateVersion(value: string): true | string {\n if (!SEMVER_PATTERN.test(value)) return 'Version must be valid semver (e.g. 1.0.0)';\n return true;\n}\n\nexport interface InitOptions {\n yes?: boolean;\n name?: string;\n version?: string;\n description?: string;\n private?: boolean;\n force?: boolean;\n}\n\nexport async function initCommand(options: InitOptions = {}): Promise<void> {\n const cwd = process.cwd();\n const resolved = resolveManifestPath(cwd);\n const filePath = resolved.exists ? resolved.path : path.join(cwd, MANIFEST_FILENAME);\n\n if (options.yes) {\n const dirName = path.basename(cwd);\n const name = options.name ?? dirName;\n const version = options.version ?? '0.1.0';\n const description = options.description ?? '';\n const privateChoice = options.private ?? false;\n\n const nameResult = validateName(name);\n if (nameResult !== true) {\n logger.error(nameResult);\n return;\n }\n\n const versionResult = validateVersion(version);\n if (versionResult !== true) {\n logger.error(versionResult);\n return;\n }\n\n if (resolved.exists) {\n if (!options.force) {\n logger.error(`${path.basename(resolved.path)} already exists. Use --force to overwrite.`);\n return;\n }\n }\n\n const manifest: Record<string, unknown> = {\n name,\n version,\n ...(description ? { description } : {}),\n visibility: privateChoice ? 'private' : 'public',\n skills: {},\n permissions: {\n network: { outbound: [] },\n filesystem: { read: [], write: [] },\n subprocess: false\n }\n };\n\n // Validate against schema before writing\n const result = skillsJsonSchema.safeParse(manifest);\n if (!result.success) {\n logger.error(`Generated ${MANIFEST_FILENAME} is invalid:`);\n for (const issue of result.error.issues) {\n logger.error(` ${issue.path.join('.')}: ${issue.message}`);\n }\n return;\n }\n\n // Write file\n fs.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}\\n`);\n logger.success(`Created ${MANIFEST_FILENAME}`);\n return;\n }\n\n // Check if manifest already exists\n if (resolved.exists) {\n logger.warn(`${path.basename(resolved.path)} already exists in this directory.`);\n const overwrite = await confirm({\n message: `Overwrite existing ${path.basename(resolved.path)}?`,\n default: false\n });\n if (!overwrite) {\n logger.info('Aborted.');\n return;\n }\n }\n\n // Get default author from config\n const config = getConfig();\n const defaultAuthor = config.user?.name ?? '';\n\n // Prompt for values\n const dirName = path.basename(cwd);\n\n const name = await input({\n message: 'Skill name:',\n default: dirName,\n validate: validateName\n });\n\n const version = await input({\n message: 'Version:',\n default: '0.1.0',\n validate: validateVersion\n });\n\n const description = await input({\n message: 'Description:',\n default: ''\n });\n\n const privateChoice = await confirm({\n message: 'Make this skill private?',\n default: name.startsWith('@')\n });\n\n const author = await input({\n message: 'Author:',\n default: defaultAuthor\n });\n\n void author;\n\n const manifest: Record<string, unknown> = {\n name,\n version,\n ...(description ? { description } : {}),\n visibility: privateChoice ? 'private' : 'public',\n skills: {},\n permissions: {\n network: { outbound: [] },\n filesystem: { read: [], write: [] },\n subprocess: false\n }\n };\n\n // Validate against schema before writing\n const result = skillsJsonSchema.safeParse(manifest);\n if (!result.success) {\n logger.error(`Generated ${MANIFEST_FILENAME} is invalid:`);\n for (const issue of result.error.issues) {\n logger.error(` ${issue.path.join('.')}: ${issue.message}`);\n }\n return;\n }\n\n // Write file\n fs.writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}\\n`);\n logger.success(`Created ${MANIFEST_FILENAME}`);\n}\n","import semver from \"semver\";\n//#region src/lib/resolver.ts\nfunction resolve(range, versions) {\n\ttry {\n\t\tif (!range || !semver.validRange(range)) return null;\n\t\tconst validVersions = versions.filter((v) => semver.valid(v) !== null);\n\t\tif (validVersions.length === 0) return null;\n\t\treturn semver.maxSatisfying(validVersions, range) ?? null;\n\t} catch {\n\t\treturn null;\n\t}\n}\nfunction sortVersions(versions) {\n\treturn [...versions.filter((v) => semver.valid(v) !== null)].sort((a, b) => semver.rcompare(a, b));\n}\n//#endregion\n//#region src/lib/url.ts\nfunction encodeSkillName(name) {\n\treturn encodeURIComponent(name).replace(/%40/g, \"@\").replace(/%2F/gi, \"/\");\n}\n//#endregion\n//#region src/permission-escalation.ts\nfunction parseParts(version) {\n\tconst parts = version.split(\".\").map((p) => {\n\t\tconst n = Number.parseInt(p, 10);\n\t\treturn Number.isNaN(n) ? 0 : n;\n\t});\n\treturn [\n\t\tparts[0] ?? 0,\n\t\tparts[1] ?? 0,\n\t\tparts[2] ?? 0\n\t];\n}\nfunction determineBump(prevVersion, newVersion) {\n\tif (!prevVersion) return \"none\";\n\tconst [pMaj, pMin] = parseParts(prevVersion);\n\tconst [nMaj, nMin] = parseParts(newVersion);\n\tif (nMaj > pMaj) return \"major\";\n\tif (nMin > pMin) return \"minor\";\n\treturn \"patch\";\n}\nfunction asStringArray(value) {\n\tif (!Array.isArray(value)) return [];\n\treturn value.filter((v) => typeof v === \"string\");\n}\nfunction findNewOutboundHosts(prev, next) {\n\tconst prevNetwork = prev.network;\n\tconst nextNetwork = next.network;\n\tif (!nextNetwork) return [];\n\tconst prevHosts = new Set(asStringArray(prevNetwork?.outbound));\n\treturn asStringArray(nextNetwork.outbound).filter((h) => !prevHosts.has(h)).map((host) => ({\n\t\tfield: \"network.outbound\",\n\t\treason: `New outbound host added: ${host}. Requires MAJOR bump`\n\t}));\n}\nfunction findNewFilesystemPaths(prev, next) {\n\tconst prevFs = prev.filesystem;\n\tconst nextFs = next.filesystem;\n\tif (!nextFs) return {\n\t\treads: [],\n\t\twrites: []\n\t};\n\tconst prevRead = new Set(asStringArray(prevFs?.read));\n\tconst addedRead = asStringArray(nextFs.read).filter((p) => !prevRead.has(p));\n\tconst prevWrite = new Set(asStringArray(prevFs?.write));\n\tconst addedWrite = asStringArray(nextFs.write).filter((p) => !prevWrite.has(p));\n\treturn {\n\t\treads: addedRead.map((p) => ({\n\t\t\tfield: \"filesystem.read\",\n\t\t\treason: `New read path added: ${p}. Requires MINOR bump`\n\t\t})),\n\t\twrites: addedWrite.map((p) => ({\n\t\t\tfield: \"filesystem.write\",\n\t\t\treason: `New write path added: ${p}. Requires MINOR bump`\n\t\t}))\n\t};\n}\nfunction checkSubprocessEscalation(prev, next) {\n\tconst prevSub = prev.subprocess;\n\tif (next.subprocess === true && prevSub !== true) return [{\n\t\tfield: \"subprocess\",\n\t\treason: \"Subprocess execution enabled (was disabled). Requires MAJOR bump\"\n\t}];\n\treturn [];\n}\nfunction checkPermissionEscalation(prevVersion, prevPermissions, newVersion, newPermissions) {\n\tconst bump = determineBump(prevVersion, newVersion);\n\tif (bump === \"major\" || bump === \"none\") return {\n\t\tallowed: true,\n\t\tviolations: []\n\t};\n\tconst highRisk = [...findNewOutboundHosts(prevPermissions, newPermissions), ...checkSubprocessEscalation(prevPermissions, newPermissions)];\n\tconst fsPaths = findNewFilesystemPaths(prevPermissions, newPermissions);\n\tconst lowRisk = [...fsPaths.reads, ...fsPaths.writes];\n\tif (bump === \"minor\") return {\n\t\tallowed: highRisk.length === 0,\n\t\tviolations: highRisk\n\t};\n\tconst violations = [...highRisk, ...lowRisk];\n\treturn {\n\t\tallowed: violations.length === 0,\n\t\tviolations\n\t};\n}\n//#endregion\nexport { checkPermissionEscalation, encodeSkillName, resolve, sortVersions };\n\n//# sourceMappingURL=index.js.map","import { resolve } from '@internals/helpers';\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport type SkillName = string;\nexport type SemverRange = string;\nexport type SemverVersion = string;\nexport type SkillKey = string;\n\nexport interface RequirementSource {\n kind: 'root' | 'skill';\n from?: string;\n}\n\nexport interface Requirement {\n name: SkillName;\n range: SemverRange;\n source: RequirementSource;\n}\n\nexport interface RegistryVersionInfo {\n version: string;\n integrity: string;\n auditScore: number | null;\n auditStatus: string;\n publishedAt: string;\n}\n\nexport interface RegistrySkillMeta {\n name: string;\n version: string;\n description?: string;\n integrity: string;\n permissions: Record<string, unknown>;\n auditScore: number | null;\n downloadUrl: string;\n dependencies: Record<string, string>;\n}\n\nexport interface RegistryFetcher {\n fetchVersions(name: string): Promise<RegistryVersionInfo[]>;\n fetchMetadata(name: string, version: string): Promise<RegistrySkillMeta>;\n}\n\nexport interface ResolvedNode {\n name: SkillName;\n version: SemverVersion;\n meta: RegistrySkillMeta;\n dependencies: Record<SkillName, SemverVersion>;\n}\n\nexport interface ResolvedGraph {\n nodes: Map<SkillName, ResolvedNode>;\n installOrder: string[];\n}\n\nexport interface ConflictError {\n skillName: SkillName;\n requirements: Requirement[];\n availableVersions: string[];\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nexport function buildSkillKey(name: SkillName, version: SemverVersion): string {\n return `${name}@${version}`;\n}\n\nfunction formatConflictMessage(conflict: ConflictError): string {\n const lines = [`Version conflict for ${conflict.skillName}:`];\n for (const req of conflict.requirements) {\n const origin = req.source.kind === 'root' ? 'root' : (req.source.from ?? 'unknown');\n lines.push(` - ${req.range} (required by ${origin})`);\n }\n lines.push(`Available versions: ${conflict.availableVersions.join(', ')}`);\n lines.push('No single version satisfies all constraints.');\n return lines.join('\\n');\n}\n\n// ── Resolution Algorithm ────────────────────────────────────────────────────\n\nexport async function resolveDependencyTree(\n rootDependencies: Record<SkillName, SemverRange>,\n fetcher: RegistryFetcher\n): Promise<ResolvedGraph> {\n const constraintsByName = new Map<SkillName, Requirement[]>();\n const selectedByName = new Map<SkillName, SemverVersion>();\n const metadataCache = new Map<SkillKey, RegistrySkillMeta>();\n const versionsCache = new Map<SkillName, RegistryVersionInfo[]>();\n\n // Maps SkillKey → dep names it contributed, so stale requirements\n // can be removed when a selected version changes during fixpoint iteration.\n const contributedDeps = new Map<SkillKey, SkillName[]>();\n\n const queue = new Set<SkillName>();\n const inProgress = new Set<SkillName>();\n\n const sortedRootNames = Object.keys(rootDependencies).sort();\n for (const name of sortedRootNames) {\n const range = rootDependencies[name];\n constraintsByName.set(name, [{ name, range, source: { kind: 'root' } }]);\n queue.add(name);\n }\n\n const MAX_ITERATIONS = 10_000;\n let iterations = 0;\n while (queue.size > 0) {\n if (++iterations > MAX_ITERATIONS) {\n throw new Error(\n `Dependency resolution exceeded ${MAX_ITERATIONS} iterations. This likely indicates a degenerate dependency graph.`\n );\n }\n const sorted = [...queue].sort();\n const name = sorted[0];\n queue.delete(name);\n\n // Cycle guard: if this name is mid-expansion up the stack, defer —\n // its constraints will be resolved when expansion completes.\n if (inProgress.has(name)) {\n continue;\n }\n\n const constraints = constraintsByName.get(name);\n if (!constraints || constraints.length === 0) {\n continue;\n }\n\n let versionInfos = versionsCache.get(name);\n if (!versionInfos) {\n versionInfos = await fetcher.fetchVersions(name);\n versionsCache.set(name, versionInfos);\n }\n\n const availableVersions = versionInfos.map((v) => v.version).sort();\n\n const selectedVersion = findSatisfyingVersion(availableVersions, constraints);\n\n if (selectedVersion === null) {\n const conflict: ConflictError = {\n skillName: name,\n requirements: constraints,\n availableVersions\n };\n throw new Error(formatConflictMessage(conflict));\n }\n\n const previousVersion = selectedByName.get(name);\n if (previousVersion === selectedVersion) {\n continue;\n }\n\n inProgress.add(name);\n\n if (previousVersion !== undefined) {\n const prevKey = buildSkillKey(name, previousVersion);\n const prevDeps = contributedDeps.get(prevKey) ?? [];\n for (const depName of prevDeps) {\n const depConstraints = constraintsByName.get(depName);\n if (depConstraints) {\n const filtered = depConstraints.filter((r) => r.source.from !== prevKey);\n if (filtered.length > 0) {\n constraintsByName.set(depName, filtered);\n } else {\n constraintsByName.delete(depName);\n }\n queue.add(depName);\n }\n }\n contributedDeps.delete(prevKey);\n }\n\n selectedByName.set(name, selectedVersion);\n\n const skillKey = buildSkillKey(name, selectedVersion);\n let meta = metadataCache.get(skillKey);\n if (!meta) {\n meta = await fetcher.fetchMetadata(name, selectedVersion);\n metadataCache.set(skillKey, meta);\n }\n\n const depNames: SkillName[] = [];\n const sortedDepEntries = Object.entries(meta.dependencies).sort(([a], [b]) => a.localeCompare(b));\n\n for (const [depName, depRange] of sortedDepEntries) {\n depNames.push(depName);\n\n const requirement: Requirement = {\n name: depName,\n range: depRange,\n source: { kind: 'skill', from: skillKey }\n };\n\n const existing = constraintsByName.get(depName) ?? [];\n existing.push(requirement);\n constraintsByName.set(depName, existing);\n\n queue.add(depName);\n }\n\n contributedDeps.set(skillKey, depNames);\n inProgress.delete(name);\n }\n\n for (const [name] of selectedByName) {\n const constraints = constraintsByName.get(name);\n if (!constraints || constraints.length === 0) {\n selectedByName.delete(name);\n }\n }\n\n return buildGraph(selectedByName, metadataCache);\n}\n\n// ── Version Selection ───────────────────────────────────────────────────────\n\nfunction findSatisfyingVersion(availableVersions: string[], constraints: Requirement[]): string | null {\n const ranges = [...new Set(constraints.map((c) => c.range))];\n\n const satisfyingSets = ranges.map((range) => {\n const matching = new Set<string>();\n for (const v of availableVersions) {\n if (resolve(range, [v]) !== null) {\n matching.add(v);\n }\n }\n return matching;\n });\n\n if (satisfyingSets.length === 0) {\n return null;\n }\n\n let intersection = satisfyingSets[0];\n for (let i = 1; i < satisfyingSets.length; i++) {\n intersection = new Set([...intersection].filter((v) => satisfyingSets[i].has(v)));\n }\n\n if (intersection.size === 0) {\n return null;\n }\n\n const candidates = [...intersection];\n return resolve('*', candidates);\n}\n\n// ── Graph Construction ──────────────────────────────────────────────────────\n\nfunction buildGraph(\n selectedByName: Map<SkillName, SemverVersion>,\n metadataCache: Map<SkillKey, RegistrySkillMeta>\n): ResolvedGraph {\n const nodes = new Map<SkillName, ResolvedNode>();\n const installOrder: string[] = [];\n\n const sortedEntries = [...selectedByName.entries()].sort(([a], [b]) => a.localeCompare(b));\n\n for (const [name, version] of sortedEntries) {\n const skillKey = buildSkillKey(name, version);\n const meta = metadataCache.get(skillKey);\n\n if (!meta) {\n throw new Error(`Internal error: missing metadata for ${skillKey}`);\n }\n\n const resolvedDeps: Record<SkillName, SemverVersion> = {};\n const sortedDepNames = Object.keys(meta.dependencies).sort();\n for (const depName of sortedDepNames) {\n const depVersion = selectedByName.get(depName);\n if (depVersion !== undefined) {\n resolvedDeps[depName] = depVersion;\n }\n }\n\n nodes.set(name, {\n name,\n version,\n meta,\n dependencies: resolvedDeps\n });\n\n installOrder.push(skillKey);\n }\n\n return { nodes, installOrder };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { getSymlinkName } from './agents.js';\n\nexport interface PrepareOptions {\n skillName: string;\n extractDir: string;\n agentSkillsBaseDir: string;\n description?: string;\n}\n\nexport function hasFrontmatter(content: string): boolean {\n return /^---\\s*\\n/.test(content);\n}\n\nexport function stripScope(skillName: string): string {\n const match = skillName.match(/^@[^/]+\\/(.+)$/);\n if (!match) {\n return skillName;\n }\n return match[1] ?? skillName;\n}\n\nexport function extractDescriptionFromMarkdown(content: string): string {\n const lines = content.split(/\\r?\\n/);\n const firstLine = lines.find((line) => line.trim().length > 0);\n if (firstLine && /^#\\s+/.test(firstLine)) {\n return firstLine.replace(/^#\\s+/, '').trim();\n }\n\n let seenHeading = false;\n let paragraphLines: string[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (/^#{1,6}\\s+/.test(trimmed)) {\n seenHeading = true;\n paragraphLines = [];\n continue;\n }\n if (!seenHeading) {\n continue;\n }\n if (trimmed.length === 0) {\n if (paragraphLines.length > 0) {\n break;\n }\n continue;\n }\n paragraphLines.push(trimmed);\n }\n\n if (paragraphLines.length > 0) {\n const paragraph = paragraphLines.join(' ').trim();\n const match = paragraph.match(/^(.+?[.!?])(\\s|$)/);\n return (match ? match[1] : paragraph).trim();\n }\n\n return 'An AI agent skill';\n}\n\nexport function generateFrontmatter(name: string, description: string): string {\n const indented = description\n .split(/\\r?\\n/)\n .map((line) => ` ${line}`)\n .join('\\n');\n return `---\\nname: ${name}\\ndescription: |\\n${indented}\\n---\\n\\n`;\n}\n\nexport function prepareAgentSkillDir(options: PrepareOptions): string {\n const { skillName, extractDir, agentSkillsBaseDir, description } = options;\n const symlinkName = getSymlinkName(skillName);\n const targetDir = path.resolve(agentSkillsBaseDir, symlinkName);\n fs.mkdirSync(targetDir, { recursive: true });\n\n const sourceSkillPath = path.join(extractDir, 'SKILL.md');\n const targetSkillPath = path.join(targetDir, 'SKILL.md');\n const baseName = stripScope(skillName);\n\n if (!fs.existsSync(sourceSkillPath)) {\n const fallbackDescription = description ?? 'An AI agent skill';\n const minimal = generateFrontmatter(baseName, fallbackDescription);\n fs.writeFileSync(targetSkillPath, minimal, 'utf-8');\n } else {\n const content = fs.readFileSync(sourceSkillPath, 'utf-8');\n if (hasFrontmatter(content)) {\n fs.writeFileSync(targetSkillPath, content, 'utf-8');\n } else {\n const resolvedDescription = description ?? extractDescriptionFromMarkdown(content);\n const frontmatter = generateFrontmatter(baseName, resolvedDescription);\n fs.writeFileSync(targetSkillPath, `${frontmatter}${content}`, 'utf-8');\n }\n }\n\n const entries = fs.readdirSync(extractDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name === 'SKILL.md') {\n continue;\n }\n const sourcePath = path.join(extractDir, entry.name);\n const targetPath = path.join(targetDir, entry.name);\n fs.cpSync(sourcePath, targetPath, { recursive: true });\n }\n\n return targetDir;\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { LEGACY_MANIFEST_FILENAME, MANIFEST_FILENAME, type Permissions, type SkillsLock } from '@internals/schemas';\nimport type ora from 'ora';\nimport { extract } from 'tar';\n\nimport { buildSkillKey, type ResolvedNode } from './dependency-resolver.js';\nimport { logger } from './logger.js';\n\nexport async function downloadAllParallel(\n nodes: ResolvedNode[],\n spinner: ReturnType<typeof ora>\n): Promise<Map<string, { buffer: Buffer; integrity: string }>> {\n const results = new Map<string, { buffer: Buffer; integrity: string }>();\n const CONCURRENCY_LIMIT = 8;\n\n for (let i = 0; i < nodes.length; i += CONCURRENCY_LIMIT) {\n const batch = nodes.slice(i, i + CONCURRENCY_LIMIT);\n const promises = batch.map(async (node) => {\n spinner.text = `Downloading ${node.name}@${node.version}...`;\n let res: Response;\n try {\n res = await fetch(node.meta.downloadUrl);\n } catch (err) {\n throw new Error(\n `Network error downloading tarball for ${node.name}@${node.version}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (!res.ok) {\n throw new Error(`Failed to download ${node.name}@${node.version}: ${res.status} ${res.statusText}`);\n }\n\n const buffer = Buffer.from(await res.arrayBuffer());\n const hash = crypto.createHash('sha512').update(buffer).digest('base64');\n const computedIntegrity = `sha512-${hash}`;\n if (computedIntegrity !== node.meta.integrity) {\n throw new Error(\n `Integrity mismatch for ${node.name}@${node.version}. Expected: ${node.meta.integrity}, Got: ${computedIntegrity}`\n );\n }\n\n return { name: node.name, buffer, integrity: computedIntegrity };\n });\n\n const batchResults = await Promise.all(promises);\n for (const result of batchResults) {\n results.set(result.name, { buffer: result.buffer, integrity: result.integrity });\n }\n }\n\n return results;\n}\n\nexport function verifyExtractedDependencies(extractDir: string, node: ResolvedNode): void {\n let extractedManifestPath = path.join(extractDir, MANIFEST_FILENAME);\n if (!fs.existsSync(extractedManifestPath)) {\n extractedManifestPath = path.join(extractDir, LEGACY_MANIFEST_FILENAME);\n }\n if (!fs.existsSync(extractedManifestPath)) {\n return;\n }\n\n try {\n const raw = fs.readFileSync(extractedManifestPath, 'utf-8');\n const manifest = JSON.parse(raw) as Record<string, unknown>;\n const extractedDeps = (manifest.skills ?? {}) as Record<string, string>;\n const apiDeps = node.meta.dependencies;\n const extractedSorted = Object.fromEntries(Object.entries(extractedDeps).sort(([a], [b]) => a.localeCompare(b)));\n const apiSorted = Object.fromEntries(Object.entries(apiDeps).sort(([a], [b]) => a.localeCompare(b)));\n if (JSON.stringify(extractedSorted) !== JSON.stringify(apiSorted)) {\n logger.warn(`Dependency mismatch for ${node.name}@${node.version}: manifest deps differ from registry`);\n }\n } catch {\n // Non-fatal.\n }\n}\n\nexport function readExtractedDependencies(extractDir: string): Record<string, string> {\n let extractedManifestPath = path.join(extractDir, MANIFEST_FILENAME);\n if (!fs.existsSync(extractedManifestPath)) {\n extractedManifestPath = path.join(extractDir, LEGACY_MANIFEST_FILENAME);\n }\n if (!fs.existsSync(extractedManifestPath)) {\n return {};\n }\n\n try {\n const raw = fs.readFileSync(extractedManifestPath, 'utf-8');\n const manifest = JSON.parse(raw) as Record<string, unknown>;\n const extractedDeps = manifest.skills;\n if (!extractedDeps || typeof extractedDeps !== 'object') {\n return {};\n }\n\n const deps: Record<string, string> = {};\n for (const [depName, depRange] of Object.entries(extractedDeps as Record<string, unknown>)) {\n if (typeof depRange === 'string') {\n deps[depName] = depRange;\n }\n }\n return deps;\n } catch {\n return {};\n }\n}\n\nexport function writeLockfileWithResolvedGraph(\n lock: SkillsLock,\n nodes: ResolvedNode[],\n downloaded: Map<string, { buffer: Buffer; integrity: string }>\n): SkillsLock {\n for (const node of nodes) {\n const key = buildSkillKey(node.name, node.version);\n\n if (!downloaded.has(node.name) && lock.skills[key]) {\n continue;\n }\n\n const integrity = downloaded.get(node.name)?.integrity ?? lock.skills[key]?.integrity ?? node.meta.integrity;\n\n lock.skills[key] = {\n resolved: node.meta.downloadUrl,\n integrity,\n permissions: node.meta.permissions as Permissions,\n audit_score: node.meta.auditScore ?? null,\n dependencies: node.dependencies\n };\n }\n\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n return lock;\n}\n\n/**\n * Extract a tarball safely with security checks.\n * Rejects: absolute paths, path traversal (..), symlinks/hardlinks.\n */\nexport async function extractSafely(tarball: Buffer, destDir: string): Promise<void> {\n const tmpTarball = path.join(destDir, '.tmp-tarball.tgz');\n fs.writeFileSync(tmpTarball, tarball);\n\n try {\n await extract({\n file: tmpTarball,\n cwd: destDir,\n filter: (entryPath: string) => {\n if (path.isAbsolute(entryPath)) {\n throw new Error(`Absolute path in tarball: ${entryPath}`);\n }\n if (entryPath.split('/').includes('..') || entryPath.split(path.sep).includes('..')) {\n throw new Error(`Path traversal in tarball: ${entryPath}`);\n }\n return true;\n },\n onReadEntry: (entry) => {\n if (entry.type === 'SymbolicLink' || entry.type === 'Link') {\n throw new Error(`Symlink/hardlink in tarball: ${entry.path}`);\n }\n }\n });\n } finally {\n if (fs.existsSync(tmpTarball)) {\n fs.unlinkSync(tmpTarball);\n }\n }\n}\n\nexport function getExtractDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n\nexport function getGlobalExtractDir(homedir: string, skillName: string): string {\n const globalDir = path.join(homedir, '.tank', 'skills');\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(globalDir, scope, name);\n }\n return path.join(globalDir, skillName);\n}\n\nexport function parseLockKey(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return key.slice(0, lastAt);\n}\n\nexport function parseVersionFromLockKey(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0 || lastAt === key.length - 1) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return key.slice(lastAt + 1);\n}\n\nexport function getResolvedNodesInOrder(nodes: Map<string, ResolvedNode>, installOrder: string[]): ResolvedNode[] {\n const orderedNodes: ResolvedNode[] = [];\n for (const key of installOrder) {\n const skillName = parseLockKey(key);\n const node = nodes.get(skillName);\n if (!node) {\n throw new Error(`Internal error: missing resolved node for ${key}`);\n }\n orderedNodes.push(node);\n }\n return orderedNodes;\n}\n","import type { Permissions } from '@internals/schemas';\n\nexport interface PermissionViolation {\n skillName: string;\n type: 'network.outbound' | 'filesystem.read' | 'filesystem.write' | 'subprocess';\n requested: string;\n}\n\n/**\n * Check if a skill's permissions fit within the project's permission budget.\n * Throws if any permission exceeds the budget.\n */\nexport function checkPermissionBudget(\n budget: Permissions,\n skillPerms: Permissions | undefined,\n skillName: string\n): void {\n if (!skillPerms) return;\n\n // Check subprocess\n if (skillPerms.subprocess === true && budget.subprocess !== true) {\n throw new Error(`Permission denied: ${skillName} requires subprocess access, but project budget does not allow it`);\n }\n\n // Check network outbound\n if (skillPerms.network?.outbound && skillPerms.network.outbound.length > 0) {\n const budgetDomains = budget.network?.outbound ?? [];\n for (const domain of skillPerms.network.outbound) {\n if (!isDomainAllowed(domain, budgetDomains)) {\n throw new Error(\n `Permission denied: ${skillName} requests network access to \"${domain}\", which is not in the project's permission budget`\n );\n }\n }\n }\n\n // Check filesystem read\n if (skillPerms.filesystem?.read && skillPerms.filesystem.read.length > 0) {\n const budgetPaths = budget.filesystem?.read ?? [];\n for (const p of skillPerms.filesystem.read) {\n if (!isPathAllowed(p, budgetPaths)) {\n throw new Error(\n `Permission denied: ${skillName} requests filesystem read access to \"${p}\", which is not in the project's permission budget`\n );\n }\n }\n }\n\n // Check filesystem write\n if (skillPerms.filesystem?.write && skillPerms.filesystem.write.length > 0) {\n const budgetPaths = budget.filesystem?.write ?? [];\n for (const p of skillPerms.filesystem.write) {\n if (!isPathAllowed(p, budgetPaths)) {\n throw new Error(\n `Permission denied: ${skillName} requests filesystem write access to \"${p}\", which is not in the project's permission budget`\n );\n }\n }\n }\n}\n\n/**\n * Check if a domain is allowed by the budget's domain list.\n * Supports wildcard matching: *.example.com matches sub.example.com\n */\nexport function isDomainAllowed(domain: string, allowedDomains: string[]): boolean {\n for (const allowed of allowedDomains) {\n if (allowed === domain) return true;\n // Wildcard matching: *.example.com\n if (allowed.startsWith('*.')) {\n const suffix = allowed.slice(1); // .example.com\n if (domain.endsWith(suffix) || domain === allowed.slice(2)) {\n return true;\n }\n // Also match if the skill requests the same wildcard pattern\n if (domain === allowed) return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a path is allowed by the budget's path list.\n * Simple subset check: skill path must match one of the budget paths.\n */\nexport function isPathAllowed(requestedPath: string, allowedPaths: string[]): boolean {\n for (const allowed of allowedPaths) {\n if (allowed === requestedPath) return true;\n // If budget allows ./src/** and skill requests ./src/foo, it's allowed\n if (allowed.endsWith('/**')) {\n const prefix = allowed.slice(0, -3); // ./src\n if (requestedPath.startsWith(prefix)) return true;\n }\n }\n return false;\n}\n\nexport function collectPermissionViolations(\n budget: Permissions,\n skillPerms: Permissions | undefined,\n skillName: string\n): PermissionViolation[] {\n const violations: PermissionViolation[] = [];\n if (!skillPerms) return violations;\n\n if (skillPerms.subprocess === true && budget.subprocess !== true) {\n violations.push({ skillName, type: 'subprocess', requested: 'true' });\n }\n\n if (skillPerms.network?.outbound) {\n const budgetDomains = budget.network?.outbound ?? [];\n for (const domain of skillPerms.network.outbound) {\n if (!isDomainAllowed(domain, budgetDomains)) {\n violations.push({ skillName, type: 'network.outbound', requested: domain });\n }\n }\n }\n\n if (skillPerms.filesystem?.read) {\n const budgetPaths = budget.filesystem?.read ?? [];\n for (const p of skillPerms.filesystem.read) {\n if (!isPathAllowed(p, budgetPaths)) {\n violations.push({ skillName, type: 'filesystem.read', requested: p });\n }\n }\n }\n\n if (skillPerms.filesystem?.write) {\n const budgetPaths = budget.filesystem?.write ?? [];\n for (const p of skillPerms.filesystem.write) {\n if (!isPathAllowed(p, budgetPaths)) {\n violations.push({ skillName, type: 'filesystem.write', requested: p });\n }\n }\n }\n\n return violations;\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { resolve } from '@internals/helpers';\nimport {\n LOCKFILE_FILENAME,\n LOCKFILE_VERSION,\n MANIFEST_FILENAME,\n type Permissions,\n type SkillsLock\n} from '@internals/schemas';\nimport ora from 'ora';\n\nimport { detectInstalledAgents, getGlobalAgentSkillsDir, getGlobalSkillsDir } from '~/lib/agents.js';\nimport { getConfig } from '~/lib/config.js';\nimport {\n buildSkillKey,\n type RegistryFetcher,\n type RegistrySkillMeta,\n type RegistryVersionInfo,\n type ResolvedNode,\n resolveDependencyTree\n} from '~/lib/dependency-resolver.js';\nimport { prepareAgentSkillDir } from '~/lib/frontmatter.js';\nimport {\n downloadAllParallel,\n extractSafely,\n getExtractDir,\n getGlobalExtractDir,\n getResolvedNodesInOrder,\n parseLockKey,\n parseVersionFromLockKey,\n readExtractedDependencies,\n verifyExtractedDependencies,\n writeLockfileWithResolvedGraph\n} from '~/lib/install-pipeline.js';\nimport { linkSkillToAgents } from '~/lib/linker.js';\nimport { logger } from '~/lib/logger.js';\nimport { resolveLockfilePath, resolveManifestPath } from '~/lib/manifest.js';\nimport { checkPermissionBudget } from '~/lib/permission-checker.js';\nimport { USER_AGENT } from '~/version.js';\n\nexport interface InstallOptions {\n name: string;\n versionRange?: string;\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n isTransitive?: boolean;\n}\n\nexport interface LockfileInstallOptions {\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n}\n\nexport interface InstallAllOptions {\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n}\n\ninterface VersionMetadata {\n name: string;\n version: string;\n description?: string;\n integrity: string;\n permissions: Permissions;\n auditScore: number;\n auditStatus: string;\n downloadUrl: string;\n publishedAt: string;\n}\n\ninterface ExecuteInstallPipelineOptions {\n directory: string;\n configDir?: string;\n global: boolean;\n homedir?: string;\n resolvedHome: string;\n lock: SkillsLock;\n lockPath: string;\n resolvedNodes: ResolvedNode[];\n nodesToInstall: ResolvedNode[];\n rootSkillNames: string[];\n projectPermissions?: Permissions;\n auditMinScore?: number;\n spinner: ReturnType<typeof ora>;\n}\n\nfunction createRegistryFetcher(registry: string, headers: Record<string, string>): RegistryFetcher {\n const versionsCache = new Map<string, RegistryVersionInfo[]>();\n const metadataCache = new Map<string, RegistrySkillMeta>();\n\n return {\n async fetchVersions(name: string): Promise<RegistryVersionInfo[]> {\n const cached = versionsCache.get(name);\n if (cached) {\n return cached;\n }\n\n const encoded = encodeURIComponent(name);\n let res: Response;\n try {\n res = await fetch(`${registry}/api/v1/skills/${encoded}/versions`, { headers });\n } catch (err) {\n throw new Error(`Network error fetching versions: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n if (res.status === 403) throw new Error('Token lacks required scope: skills:read');\n if (res.status === 404) throw new Error(`Skill not found or no access: ${name}`);\n const body = (await res.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? res.statusText);\n }\n const data = (await res.json()) as { name: string; versions: RegistryVersionInfo[] };\n versionsCache.set(name, data.versions);\n return data.versions;\n },\n async fetchMetadata(name: string, version: string): Promise<RegistrySkillMeta> {\n const cacheKey = buildSkillKey(name, version);\n const cached = metadataCache.get(cacheKey);\n if (cached) {\n return cached;\n }\n\n const encoded = encodeURIComponent(name);\n let res: Response;\n try {\n res = await fetch(`${registry}/api/v1/skills/${encoded}/${version}`, { headers });\n } catch (err) {\n throw new Error(`Network error fetching metadata: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n if (res.status === 403) throw new Error('Token lacks required scope: skills:read');\n if (res.status === 404) throw new Error(`Skill not found or no access: ${name}@${version}`);\n const body = (await res.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? res.statusText);\n }\n\n const data = (await res.json()) as RegistrySkillMeta;\n const normalized: RegistrySkillMeta = {\n ...data,\n dependencies: data.dependencies ?? {}\n };\n metadataCache.set(cacheKey, normalized);\n return normalized;\n }\n };\n}\n\nfunction readSkillsJson(skillsJsonPath: string): Record<string, unknown> {\n try {\n const raw = fs.readFileSync(skillsJsonPath, 'utf-8');\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(skillsJsonPath)}`);\n }\n}\n\nfunction readOrCreateSkillsJson(skillsJsonPath: string): Record<string, unknown> {\n if (!fs.existsSync(skillsJsonPath)) {\n const skillsJson: Record<string, unknown> = { skills: {} };\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n logger.info(`Created ${MANIFEST_FILENAME}`);\n return skillsJson;\n }\n\n return readSkillsJson(skillsJsonPath);\n}\n\nfunction readLockOrFresh(lockPath: string): SkillsLock {\n if (!fs.existsSync(lockPath)) {\n return { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n return { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n}\n\nfunction buildLockedVersionByName(lock: SkillsLock): Map<string, string> {\n const lockedVersionByName = new Map<string, string>();\n for (const key of Object.keys(lock.skills)) {\n lockedVersionByName.set(parseLockKey(key), parseVersionFromLockKey(key));\n }\n return lockedVersionByName;\n}\n\nfunction createExtractDirResolver(\n directory: string,\n global: boolean,\n resolvedHome: string\n): (skillName: string) => string {\n return (skillName: string): string =>\n global ? getGlobalExtractDir(resolvedHome, skillName) : getExtractDir(directory, skillName);\n}\n\nfunction validateResolvedNodes(\n resolvedNodes: ResolvedNode[],\n projectPermissions: Permissions | undefined,\n auditMinScore: number | undefined\n): void {\n if (!projectPermissions) {\n logger.warn(`No permission budget defined in ${MANIFEST_FILENAME}. Install proceeding without permission checks.`);\n }\n\n for (const node of resolvedNodes) {\n if (projectPermissions) {\n checkPermissionBudget(projectPermissions, node.meta.permissions as Permissions, node.name);\n }\n\n if (auditMinScore !== undefined) {\n if (node.meta.auditScore === null || node.meta.auditScore === undefined) {\n logger.warn(`Audit score not yet available for ${node.name}. Install proceeding without audit score check.`);\n } else if (node.meta.auditScore < auditMinScore) {\n throw new Error(\n `Audit score ${node.meta.auditScore} for ${node.name} is below minimum threshold ${auditMinScore} defined in ${MANIFEST_FILENAME}`\n );\n }\n }\n }\n}\n\nasync function runLegacyFallback(options: {\n rootSkillNames: string[];\n resolvedNodeByName: Map<string, ResolvedNode>;\n extractDirForSkill: (skillName: string) => string;\n directory: string;\n configDir?: string;\n global: boolean;\n homedir?: string;\n}): Promise<void> {\n const { rootSkillNames, resolvedNodeByName, extractDirForSkill, directory, configDir, global, homedir } = options;\n\n for (const skillName of rootSkillNames) {\n const node = resolvedNodeByName.get(skillName);\n if (!node || Object.keys(node.meta.dependencies).length > 0) {\n continue;\n }\n\n const extractedDeps = readExtractedDependencies(extractDirForSkill(skillName));\n for (const [depName, depRange] of Object.entries(extractedDeps)) {\n if (depName === skillName) {\n continue;\n }\n\n await installCommand({\n name: depName,\n versionRange: depRange,\n directory,\n configDir,\n global,\n homedir,\n isTransitive: true\n });\n }\n }\n}\n\nfunction linkInstalledRoots(options: {\n rootSkillNames: string[];\n resolvedNodeByName: Map<string, ResolvedNode>;\n extractDirForSkill: (skillName: string) => string;\n directory: string;\n global: boolean;\n resolvedHome: string;\n homedir?: string;\n}): void {\n const { rootSkillNames, resolvedNodeByName, extractDirForSkill, directory, global, resolvedHome, homedir } = options;\n\n const agentSkillsBaseDir = global\n ? getGlobalAgentSkillsDir(resolvedHome)\n : path.join(directory, '.tank', 'agent-skills');\n const linksDir = global ? path.join(resolvedHome, '.tank') : path.join(directory, '.tank');\n\n for (const skillName of rootSkillNames) {\n try {\n const node = resolvedNodeByName.get(skillName);\n if (!node) {\n continue;\n }\n\n const agentSkillDir = prepareAgentSkillDir({\n skillName,\n extractDir: extractDirForSkill(skillName),\n agentSkillsBaseDir,\n description: node.meta.description\n });\n const linkResult = linkSkillToAgents({\n skillName,\n sourceDir: agentSkillDir,\n linksDir,\n source: global ? 'global' : 'local',\n homedir\n });\n\n if (linkResult.linked.length > 0) {\n logger.info(`Linked to ${linkResult.linked.length} agent(s)`);\n }\n if (linkResult.failed.length > 0) {\n for (const failedLink of linkResult.failed) {\n logger.warn(`Failed to link to ${failedLink.agentId}: ${failedLink.error}`);\n }\n }\n } catch {\n if (rootSkillNames.length === 1) {\n logger.warn('Agent linking skipped (non-fatal)');\n } else {\n logger.warn(`Agent linking skipped for ${skillName} (non-fatal)`);\n }\n }\n }\n\n const detectedAgents = detectInstalledAgents(homedir);\n if (detectedAgents.length === 0) {\n logger.warn('No agents detected for linking');\n }\n}\n\nasync function executeInstallPipeline(options: ExecuteInstallPipelineOptions): Promise<SkillsLock> {\n const {\n directory,\n configDir,\n global,\n homedir,\n resolvedHome,\n lock,\n lockPath,\n resolvedNodes,\n nodesToInstall,\n rootSkillNames,\n projectPermissions,\n auditMinScore,\n spinner\n } = options;\n\n if (!global) {\n validateResolvedNodes(resolvedNodes, projectPermissions, auditMinScore);\n }\n\n const extractDirForSkill = createExtractDirResolver(directory, global, resolvedHome);\n const resolvedNodeByName = new Map(resolvedNodes.map((node) => [node.name, node]));\n const downloaded = await downloadAllParallel(nodesToInstall, spinner);\n\n for (const node of nodesToInstall) {\n const payload = downloaded.get(node.name);\n if (!payload) {\n throw new Error(`Missing downloaded tarball for ${node.name}@${node.version}`);\n }\n\n spinner.text = `Extracting ${node.name}@${node.version}...`;\n const extractDir = extractDirForSkill(node.name);\n fs.mkdirSync(extractDir, { recursive: true });\n await extractSafely(payload.buffer, extractDir);\n verifyExtractedDependencies(extractDir, node);\n }\n\n lock.lockfileVersion = LOCKFILE_VERSION;\n const updatedLock = writeLockfileWithResolvedGraph(lock, resolvedNodes, downloaded);\n fs.mkdirSync(path.dirname(lockPath), { recursive: true });\n fs.writeFileSync(lockPath, `${JSON.stringify(updatedLock, null, 2)}\\n`);\n\n await runLegacyFallback({\n rootSkillNames,\n resolvedNodeByName,\n extractDirForSkill,\n directory,\n configDir,\n global,\n homedir\n });\n\n linkInstalledRoots({\n rootSkillNames,\n resolvedNodeByName,\n extractDirForSkill,\n directory,\n global,\n resolvedHome,\n homedir\n });\n\n return updatedLock;\n}\n\nexport async function installCommand(options: InstallOptions): Promise<void> {\n const {\n name,\n versionRange = '*',\n directory = process.cwd(),\n configDir,\n global = false,\n homedir,\n isTransitive = false\n } = options;\n\n const config = getConfig(configDir);\n const resolvedHome = homedir ?? os.homedir();\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const resolvedManifest = resolveManifestPath(directory);\n const skillsJsonPath = resolvedManifest.exists ? resolvedManifest.path : path.join(directory, MANIFEST_FILENAME);\n const skillsJson = global ? { skills: {} } : readOrCreateSkillsJson(skillsJsonPath);\n const resolvedLock = global ? resolveLockfilePath(path.join(resolvedHome, '.tank')) : resolveLockfilePath(directory);\n const lockPath = resolvedLock.exists\n ? resolvedLock.path\n : global\n ? path.join(resolvedHome, '.tank', LOCKFILE_FILENAME)\n : path.join(directory, LOCKFILE_FILENAME);\n const lock = readLockOrFresh(lockPath);\n const spinner = ora('Resolving dependency graph...').start();\n\n try {\n const fetcher = createRegistryFetcher(config.registry, requestHeaders);\n const requestedVersions = await fetcher.fetchVersions(name);\n const requestedAvailableVersions = requestedVersions.map((versionInfo) => versionInfo.version);\n const requestedResolvedVersion = resolve(versionRange, requestedAvailableVersions);\n if (!requestedResolvedVersion) {\n throw new Error(\n `No version of ${name} satisfies range \"${versionRange}\". Available: ${requestedAvailableVersions.join(', ')}`\n );\n }\n\n const requestedLockKey = buildSkillKey(name, requestedResolvedVersion);\n if (lock.skills[requestedLockKey]) {\n logger.info(`${name}@${requestedResolvedVersion} is already installed`);\n spinner.succeed(`${name}@${requestedResolvedVersion} is already installed`);\n return;\n }\n\n const rootDependencies: Record<string, string> = {};\n if (!global && !isTransitive) {\n const existingSkills = (skillsJson.skills ?? {}) as Record<string, string>;\n const lockedVersionByName = buildLockedVersionByName(lock);\n\n for (const [skillName, range] of Object.entries(existingSkills)) {\n if (typeof range !== 'string') {\n continue;\n }\n\n rootDependencies[skillName] = lockedVersionByName.get(skillName) ?? range;\n }\n }\n rootDependencies[name] = versionRange;\n\n const resolvedGraph = await resolveDependencyTree(rootDependencies, fetcher);\n const resolvedNodes = getResolvedNodesInOrder(resolvedGraph.nodes, resolvedGraph.installOrder);\n const rootNode = resolvedGraph.nodes.get(name);\n if (!rootNode) {\n throw new Error(`Failed to resolve requested skill: ${name}`);\n }\n\n const nodesToInstall = resolvedNodes.filter((node) => {\n const lockKey = buildSkillKey(node.name, node.version);\n return !lock.skills[lockKey];\n });\n\n const projectPermissions = global ? undefined : (skillsJson.permissions as Permissions | undefined);\n const auditMinScore = global ? undefined : (skillsJson.audit as { min_score?: number } | undefined)?.min_score;\n\n await executeInstallPipeline({\n directory,\n configDir,\n global,\n homedir,\n resolvedHome,\n lock,\n lockPath,\n resolvedNodes,\n nodesToInstall,\n rootSkillNames: [name],\n projectPermissions,\n auditMinScore,\n spinner\n });\n\n if (!global && !isTransitive) {\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n skills[name] = versionRange === '*' ? `^${rootNode.version}` : versionRange;\n skillsJson.skills = skills;\n fs.writeFileSync(skillsJsonPath, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n }\n\n spinner.succeed(`Installed ${name}@${rootNode.version}`);\n } catch (err) {\n spinner.fail('Install failed');\n throw err;\n }\n}\n\nexport async function installFromLockfile(options: LockfileInstallOptions): Promise<void> {\n const { directory = process.cwd(), configDir, global = false, homedir } = options;\n const resolvedHome = homedir ?? os.homedir();\n const config = getConfig(configDir);\n\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const resolvedLock = global ? resolveLockfilePath(path.join(resolvedHome, '.tank')) : resolveLockfilePath(directory);\n const lockPath = resolvedLock.path;\n if (!resolvedLock.exists) {\n throw new Error(`No ${LOCKFILE_FILENAME} found in ${directory}`);\n }\n\n let lock: SkillsLock;\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(lockPath)}`);\n }\n\n const entries = Object.entries(lock.skills);\n if (entries.length === 0) {\n logger.info('No skills in lockfile');\n return;\n }\n\n const spinner = ora('Installing from lockfile...').start();\n const skillsDir = global ? getGlobalSkillsDir(resolvedHome) : path.join(directory, '.tank', 'skills');\n\n try {\n for (const [key, entry] of entries) {\n const skillName = parseLockKey(key);\n const version = parseVersionFromLockKey(key);\n spinner.text = `Installing ${key}...`;\n\n const encodedName = encodeURIComponent(skillName);\n const metaUrl = `${config.registry}/api/v1/skills/${encodedName}/${version}`;\n\n let metaRes: Response;\n try {\n metaRes = await fetch(metaUrl, {\n headers: requestHeaders\n });\n } catch (err) {\n throw new Error(`Network error fetching ${key}: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!metaRes.ok) {\n if (metaRes.status === 404) {\n throw new Error(`Skill or version not found: ${key}`);\n }\n const body = (await metaRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(`Failed to fetch ${key}: ${body?.error ?? metaRes.statusText}`);\n }\n\n const metadata = (await metaRes.json()) as VersionMetadata;\n const downloadUrl = metadata.downloadUrl;\n const downloadRes = await fetch(downloadUrl);\n if (!downloadRes.ok) {\n throw new Error(`Failed to download ${key}: ${downloadRes.status} ${downloadRes.statusText}`);\n }\n\n const tarballBuffer = Buffer.from(await downloadRes.arrayBuffer());\n const computedIntegrity = buildIntegrity(tarballBuffer);\n if (computedIntegrity !== entry.integrity) {\n throw new Error(`Integrity mismatch for ${key}. Expected: ${entry.integrity}, Got: ${computedIntegrity}`);\n }\n\n const extractDir = global ? getGlobalExtractDir(resolvedHome, skillName) : getExtractDir(directory, skillName);\n\n if (fs.existsSync(extractDir)) {\n fs.rmSync(extractDir, { recursive: true, force: true });\n }\n fs.mkdirSync(extractDir, { recursive: true });\n\n await extractSafely(tarballBuffer, extractDir);\n\n if (global) {\n try {\n const agentSkillsBaseDir = getGlobalAgentSkillsDir(resolvedHome);\n const agentSkillDir = prepareAgentSkillDir({\n skillName,\n extractDir,\n agentSkillsBaseDir\n });\n const linkResult = linkSkillToAgents({\n skillName,\n sourceDir: agentSkillDir,\n linksDir: path.join(resolvedHome, '.tank'),\n source: 'global',\n homedir\n });\n const detectedAgents = detectInstalledAgents(homedir);\n if (detectedAgents.length === 0) {\n logger.warn('No agents detected for linking');\n }\n if (linkResult.linked.length > 0) {\n logger.info(`Linked to ${linkResult.linked.length} agent(s)`);\n }\n if (linkResult.failed.length > 0) {\n for (const failedLink of linkResult.failed) {\n logger.warn(`Failed to link to ${failedLink.agentId}: ${failedLink.error}`);\n }\n }\n } catch {\n logger.warn('Agent linking skipped (non-fatal)');\n }\n }\n }\n\n spinner.succeed(`Installed ${entries.length} skill${entries.length === 1 ? '' : 's'} from lockfile`);\n } catch (err) {\n spinner.fail('Install from lockfile failed');\n if (fs.existsSync(skillsDir)) {\n fs.rmSync(skillsDir, { recursive: true, force: true });\n }\n throw err;\n }\n}\n\nexport async function installAll(options: InstallAllOptions): Promise<void> {\n const { directory = process.cwd(), configDir, global = false, homedir } = options;\n const resolvedHome = homedir ?? os.homedir();\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const resolvedLock = global ? resolveLockfilePath(path.join(resolvedHome, '.tank')) : resolveLockfilePath(directory);\n const lockPath = resolvedLock.exists\n ? resolvedLock.path\n : global\n ? path.join(resolvedHome, '.tank', LOCKFILE_FILENAME)\n : path.join(directory, LOCKFILE_FILENAME);\n const resolvedManifest = resolveManifestPath(directory);\n const skillsJsonPath = resolvedManifest.path;\n\n if (resolvedLock.exists) {\n return installFromLockfile({ directory, configDir, global, homedir });\n }\n\n if (global) {\n logger.info(`No ${LOCKFILE_FILENAME} found — nothing to install`);\n return;\n }\n\n if (!resolvedManifest.exists) {\n logger.info(`No ${MANIFEST_FILENAME} found — nothing to install`);\n return;\n }\n\n const skillsJson = readSkillsJson(skillsJsonPath);\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n const skillEntries = Object.entries(skills);\n\n if (skillEntries.length === 0) {\n logger.info(`No skills defined in ${MANIFEST_FILENAME}`);\n return;\n }\n\n const spinner = ora('Resolving dependency graph...').start();\n\n try {\n const rootDependencies: Record<string, string> = {};\n for (const [skillName, range] of skillEntries) {\n if (typeof range === 'string') {\n rootDependencies[skillName] = range;\n }\n }\n\n const fetcher = createRegistryFetcher(config.registry, requestHeaders);\n const resolvedGraph = await resolveDependencyTree(rootDependencies, fetcher);\n const resolvedNodes = getResolvedNodesInOrder(resolvedGraph.nodes, resolvedGraph.installOrder);\n const lock: SkillsLock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n const projectPermissions = skillsJson.permissions as Permissions | undefined;\n const auditMinScore = (skillsJson.audit as { min_score?: number } | undefined)?.min_score;\n\n await executeInstallPipeline({\n directory,\n configDir,\n global,\n homedir,\n resolvedHome,\n lock,\n lockPath,\n resolvedNodes,\n nodesToInstall: resolvedNodes,\n rootSkillNames: skillEntries.map(([skillName]) => skillName),\n projectPermissions,\n auditMinScore,\n spinner\n });\n\n spinner.succeed(`Installed ${skillEntries.length} root skill${skillEntries.length === 1 ? '' : 's'}`);\n } catch (err) {\n spinner.fail('Install failed');\n throw err;\n }\n}\n\nfunction buildIntegrity(buffer: Buffer): string {\n const hash = crypto.createHash('sha512').update(buffer).digest('base64');\n return `sha512-${hash}`;\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { MANIFEST_FILENAME } from '@internals/schemas';\n\nimport { detectInstalledAgents, getGlobalAgentSkillsDir } from '~/lib/agents.js';\nimport { hasFrontmatter, prepareAgentSkillDir } from '~/lib/frontmatter.js';\nimport { linkSkillToAgents } from '~/lib/linker.js';\nimport { readGlobalLinks } from '~/lib/links.js';\nimport { logger } from '~/lib/logger.js';\nimport { resolveManifestPath } from '~/lib/manifest.js';\n\nexport interface LinkOptions {\n directory?: string;\n homedir?: string;\n}\n\nexport async function linkCommand(options: LinkOptions = {}): Promise<void> {\n const workDir = options.directory ?? process.cwd();\n const homedir = options.homedir ?? os.homedir();\n const resolvedManifest = resolveManifestPath(workDir);\n\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found. Run this command from a skill directory.`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n const skillName = skillsJson.name;\n if (typeof skillName !== 'string' || skillName.trim().length === 0) {\n throw new Error(`Missing 'name' in ${path.basename(resolvedManifest.path)}`);\n }\n\n const description = typeof skillsJson.description === 'string' ? skillsJson.description : undefined;\n\n const agents = detectInstalledAgents(options.homedir);\n if (agents.length === 0) {\n logger.info('No AI agents detected. Skills linked to agents will be available once agents are installed.');\n return;\n }\n\n const skillMdPath = path.join(workDir, 'SKILL.md');\n let sourceDir = workDir;\n\n if (fs.existsSync(skillMdPath)) {\n const content = fs.readFileSync(skillMdPath, 'utf-8');\n if (!hasFrontmatter(content)) {\n sourceDir = prepareAgentSkillDir({\n skillName,\n extractDir: workDir,\n agentSkillsBaseDir: getGlobalAgentSkillsDir(homedir),\n description\n });\n }\n } else {\n sourceDir = prepareAgentSkillDir({\n skillName,\n extractDir: workDir,\n agentSkillsBaseDir: getGlobalAgentSkillsDir(homedir),\n description\n });\n }\n\n void readGlobalLinks(homedir);\n\n const result = linkSkillToAgents({\n skillName,\n sourceDir,\n linksDir: path.join(homedir, '.tank'),\n source: 'dev',\n homedir: options.homedir\n });\n\n const agentNames = new Map(agents.map((agent) => [agent.id, agent.name]));\n\n for (const agentId of result.linked) {\n logger.success(agentNames.get(agentId) ?? agentId);\n }\n\n for (const agentId of result.skipped) {\n const name = agentNames.get(agentId) ?? agentId;\n logger.warn(`- ${name} (already linked)`);\n }\n\n for (const failure of result.failed) {\n const name = agentNames.get(failure.agentId) ?? failure.agentId;\n logger.error(`${name}: ${failure.error}`);\n }\n\n logger.success(`Linked ${skillName} to ${result.linked.length} agent(s)`);\n}\n","import open from 'open';\n\nimport { getConfig, setConfig } from '~/lib/config.js';\nimport { authFlowLog } from '~/lib/debug-logger.js';\nimport { logger } from '~/lib/logger.js';\n\nconst DEFAULT_POLL_INTERVAL_MS = 2000;\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes\n\nexport interface LoginOptions {\n configDir?: string;\n timeout?: number;\n pollInterval?: number;\n}\n\n/**\n * Start the CLI login flow:\n * 1. Generate random state\n * 2. POST /api/v1/cli-auth/start → get authUrl + sessionCode\n * 3. Open browser to authUrl\n * 4. Poll POST /api/v1/cli-auth/exchange until authorized or timeout\n * 5. Write token + user to config\n */\nexport async function loginCommand(options: LoginOptions = {}): Promise<void> {\n const { configDir, timeout = DEFAULT_TIMEOUT_MS, pollInterval = DEFAULT_POLL_INTERVAL_MS } = options;\n const config = getConfig(configDir);\n const baseUrl = config.registry;\n\n // Step 1: Generate random state for CSRF protection\n const state = crypto.randomUUID();\n authFlowLog.info({ state: `${state.slice(0, 8)}...` }, 'Login flow started');\n\n // Step 2: Start auth session\n logger.info('Starting login...');\n\n let authUrl: string;\n let sessionCode: string;\n\n try {\n const startRes = await fetch(`${baseUrl}/api/v1/cli-auth/start`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ state })\n });\n\n if (!startRes.ok) {\n const body = (await startRes.json().catch(() => null)) as { error?: string } | null;\n authFlowLog.error({ status: startRes.status, error: body?.error }, 'Start request failed');\n throw new Error(`Failed to start auth session: ${body?.error ?? startRes.statusText}`);\n }\n\n authFlowLog.info({ ok: startRes.ok, status: startRes.status }, 'Start response received');\n\n const startData = (await startRes.json()) as { authUrl: string; sessionCode: string };\n authUrl = startData.authUrl;\n sessionCode = startData.sessionCode;\n authFlowLog.info({ authUrl, sessionCode: `${sessionCode.slice(0, 8)}...` }, 'Session created, opening browser');\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Failed to start auth session:')) {\n throw err;\n }\n authFlowLog.error({ error: err instanceof Error ? err.message : String(err) }, 'Start request failed');\n throw new Error(\n `Could not reach registry at ${baseUrl}. Check your internet connection or registry URL.\\n Error: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n // Step 3: Open browser\n try {\n await open(authUrl);\n logger.info('Opened browser for authentication.');\n } catch {\n // Browser failed to open — print URL for manual copy\n logger.warn('Could not open browser automatically.');\n logger.info(`Open this URL in your browser:\\n ${authUrl}`);\n }\n\n logger.info('Waiting for authorization...');\n\n // Step 4: Poll exchange endpoint\n const deadline = Date.now() + timeout;\n\n while (Date.now() < deadline) {\n try {\n const exchangeRes = await fetch(`${baseUrl}/api/v1/cli-auth/exchange`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionCode, state })\n });\n authFlowLog.debug({ status: exchangeRes.status, ok: exchangeRes.ok }, 'Exchange poll response');\n\n if (exchangeRes.ok) {\n const { token, user } = (await exchangeRes.json()) as {\n token: string;\n user: { name: string | null; email: string | null };\n };\n\n authFlowLog.info({ userName: user.name, userEmail: user.email }, 'Login successful, saving config');\n\n // Step 5: Write to config\n setConfig({ token, user: user as { name: string; email: string } }, configDir);\n\n const displayName = user.name ?? user.email ?? 'unknown';\n logger.success(`Logged in as ${displayName}`);\n return;\n }\n\n // 400 means session not yet authorized — keep polling\n // Any other error is unexpected\n if (exchangeRes.status !== 400) {\n const body = (await exchangeRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(`Exchange failed: ${body?.error ?? exchangeRes.statusText}`);\n }\n } catch (err) {\n authFlowLog.warn({ error: err instanceof Error ? err.message : String(err) }, 'Exchange poll error');\n // If it's our own thrown error, re-throw\n if (err instanceof Error && err.message.startsWith('Exchange failed:')) {\n throw err;\n }\n // Network errors during polling are transient — keep trying\n }\n\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n authFlowLog.error({}, 'Login timed out');\n throw new Error('Login timed out. Please try again.');\n}\n","import { getConfig, setConfig } from '~/lib/config.js';\nimport { logger } from '~/lib/logger.js';\n\nexport interface LogoutOptions {\n configDir?: string;\n}\n\n/**\n * Logout command: Remove token and user from config.\n * If not logged in, prints \"Not logged in\" and returns.\n * If logged in, removes token and user, prints success message.\n */\nexport async function logoutCommand(options: LogoutOptions = {}): Promise<void> {\n const { configDir } = options;\n const config = getConfig(configDir);\n\n // Check if logged in\n if (!config.token) {\n logger.warn('Not logged in. Run: tank login');\n return;\n }\n\n // Remove token and user from config\n setConfig({ token: undefined, user: undefined }, configDir);\n\n logger.success('Logged out');\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport {\n LEGACY_LOCKFILE_FILENAME,\n LEGACY_MANIFEST_FILENAME,\n LOCKFILE_FILENAME,\n MANIFEST_FILENAME\n} from '@internals/schemas';\n\nimport { logger } from '~/lib/logger.js';\n\nexport interface MigrateOptions {\n directory?: string;\n}\n\nexport async function migrateCommand(options: MigrateOptions = {}): Promise<void> {\n const dir = options.directory ?? process.cwd();\n let migrated = false;\n\n // Migrate skills.json → tank.json\n const legacyManifest = path.join(dir, LEGACY_MANIFEST_FILENAME);\n const newManifest = path.join(dir, MANIFEST_FILENAME);\n\n if (fs.existsSync(newManifest)) {\n logger.info(`${MANIFEST_FILENAME} already exists — skipping manifest migration`);\n } else if (fs.existsSync(legacyManifest)) {\n fs.copyFileSync(legacyManifest, newManifest);\n logger.success(`${LEGACY_MANIFEST_FILENAME} → ${MANIFEST_FILENAME}`);\n migrated = true;\n } else {\n logger.info(`No ${LEGACY_MANIFEST_FILENAME} found — nothing to migrate`);\n }\n\n // Migrate skills.lock → tank.lock\n const legacyLock = path.join(dir, LEGACY_LOCKFILE_FILENAME);\n const newLock = path.join(dir, LOCKFILE_FILENAME);\n\n if (fs.existsSync(newLock)) {\n logger.info(`${LOCKFILE_FILENAME} already exists — skipping lockfile migration`);\n } else if (fs.existsSync(legacyLock)) {\n fs.copyFileSync(legacyLock, newLock);\n logger.success(`${LEGACY_LOCKFILE_FILENAME} → ${LOCKFILE_FILENAME}`);\n migrated = true;\n } else {\n logger.info(`No ${LEGACY_LOCKFILE_FILENAME} found — nothing to migrate`);\n }\n\n if (migrated) {\n logger.info('Old files were kept. Remove them when ready:');\n if (fs.existsSync(legacyManifest) && fs.existsSync(newManifest)) {\n logger.info(` rm ${LEGACY_MANIFEST_FILENAME}`);\n }\n if (fs.existsSync(legacyLock) && fs.existsSync(newLock)) {\n logger.info(` rm ${LEGACY_LOCKFILE_FILENAME}`);\n }\n logger.info('If your .gitignore or CI configs reference the old filenames, update them too.');\n } else {\n logger.info('Already migrated — nothing to do');\n }\n}\n","import fs from 'node:fs';\n\nimport type { Permissions, SkillsJson, SkillsLock } from '@internals/schemas';\nimport chalk from 'chalk';\n\nimport { resolveLockfilePath, resolveManifestPath } from '~/lib/manifest.js';\n\nexport interface PermissionsOptions {\n directory?: string;\n}\n\n/**\n * Parse a lockfile key like \"@org/skill@1.0.0\" into the skill name \"@org/skill\".\n */\nfunction parseSkillName(key: string): string {\n const lastAt = key.lastIndexOf('@');\n // For scoped packages, lastIndexOf('@') finds the version separator\n // e.g. \"@org/skill@1.0.0\" → lastAt = 10, name = \"@org/skill\"\n if (lastAt > 0) {\n return key.slice(0, lastAt);\n }\n return key;\n}\n\ninterface PermissionEntry {\n value: string;\n skills: string[];\n}\n\ninterface ResolvedPermissions {\n networkOutbound: PermissionEntry[];\n filesystemRead: PermissionEntry[];\n filesystemWrite: PermissionEntry[];\n subprocess: string[]; // list of skill names that request subprocess\n}\n\nfunction collectPermissions(lockfile: SkillsLock): ResolvedPermissions {\n const networkMap = new Map<string, string[]>();\n const fsReadMap = new Map<string, string[]>();\n const fsWriteMap = new Map<string, string[]>();\n const subprocessSkills: string[] = [];\n\n for (const [key, entry] of Object.entries(lockfile.skills)) {\n const skillName = parseSkillName(key);\n const perms = entry.permissions;\n\n if (perms.network?.outbound) {\n for (const domain of perms.network.outbound) {\n const existing = networkMap.get(domain) ?? [];\n existing.push(skillName);\n networkMap.set(domain, existing);\n }\n }\n\n if (perms.filesystem?.read) {\n for (const p of perms.filesystem.read) {\n const existing = fsReadMap.get(p) ?? [];\n existing.push(skillName);\n fsReadMap.set(p, existing);\n }\n }\n\n if (perms.filesystem?.write) {\n for (const p of perms.filesystem.write) {\n const existing = fsWriteMap.get(p) ?? [];\n existing.push(skillName);\n fsWriteMap.set(p, existing);\n }\n }\n\n if (perms.subprocess === true) {\n subprocessSkills.push(skillName);\n }\n }\n\n const toEntries = (map: Map<string, string[]>): PermissionEntry[] =>\n Array.from(map.entries()).map(([value, skills]) => ({ value, skills }));\n\n return {\n networkOutbound: toEntries(networkMap),\n filesystemRead: toEntries(fsReadMap),\n filesystemWrite: toEntries(fsWriteMap),\n subprocess: subprocessSkills\n };\n}\n\n/**\n * Check if a domain is allowed by the budget's domain list.\n * Supports wildcard matching: *.example.com matches sub.example.com\n */\nfunction isDomainAllowed(domain: string, allowedDomains: string[]): boolean {\n for (const allowed of allowedDomains) {\n if (allowed === domain) return true;\n if (allowed.startsWith('*.')) {\n const suffix = allowed.slice(1);\n if (domain.endsWith(suffix) || domain === allowed.slice(2)) {\n return true;\n }\n if (domain === allowed) return true;\n }\n }\n return false;\n}\n\n/**\n * Check if a path is allowed by the budget's path list.\n */\nfunction isPathAllowed(requestedPath: string, allowedPaths: string[]): boolean {\n for (const allowed of allowedPaths) {\n if (allowed === requestedPath) return true;\n if (allowed.endsWith('/**')) {\n const prefix = allowed.slice(0, -3);\n if (requestedPath.startsWith(prefix)) return true;\n }\n }\n return false;\n}\n\ninterface BudgetViolation {\n category: string;\n value: string;\n skills: string[];\n}\n\nfunction checkBudget(resolved: ResolvedPermissions, budget: Permissions): BudgetViolation[] {\n const violations: BudgetViolation[] = [];\n\n const budgetDomains = budget.network?.outbound ?? [];\n for (const entry of resolved.networkOutbound) {\n if (!isDomainAllowed(entry.value, budgetDomains)) {\n violations.push({\n category: 'network outbound',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n const budgetReadPaths = budget.filesystem?.read ?? [];\n for (const entry of resolved.filesystemRead) {\n if (!isPathAllowed(entry.value, budgetReadPaths)) {\n violations.push({\n category: 'filesystem read',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n const budgetWritePaths = budget.filesystem?.write ?? [];\n for (const entry of resolved.filesystemWrite) {\n if (!isPathAllowed(entry.value, budgetWritePaths)) {\n violations.push({\n category: 'filesystem write',\n value: entry.value,\n skills: entry.skills\n });\n }\n }\n\n if (resolved.subprocess.length > 0 && budget.subprocess !== true) {\n violations.push({\n category: 'subprocess',\n value: 'subprocess access',\n skills: resolved.subprocess\n });\n }\n\n return violations;\n}\n\nfunction formatAttribution(skills: string[]): string {\n return chalk.gray(`← ${skills.join(', ')}`);\n}\n\nfunction printPermissionSection(title: string, entries: PermissionEntry[]): void {\n console.log(`\\n${chalk.bold(title)}:`);\n if (entries.length === 0) {\n console.log(' none');\n } else {\n for (const entry of entries) {\n console.log(` ${entry.value} ${formatAttribution(entry.skills)}`);\n }\n }\n}\n\nexport async function permissionsCommand(options?: PermissionsOptions): Promise<void> {\n const dir = options?.directory ?? process.cwd();\n const resolvedLock = resolveLockfilePath(dir);\n\n // 1. Read lockfile\n if (!resolvedLock.exists) {\n console.log('No skills installed.');\n return;\n }\n\n const lockfileContent = fs.readFileSync(resolvedLock.path, 'utf-8');\n const lockfile: SkillsLock = JSON.parse(lockfileContent);\n\n if (!lockfile.skills || Object.keys(lockfile.skills).length === 0) {\n console.log('No skills installed.');\n return;\n }\n\n // 2. Collect permissions\n const resolved = collectPermissions(lockfile);\n\n // 3. Display\n console.log('\\nResolved permissions for this project:\\n');\n\n printPermissionSection('Network (outbound)', resolved.networkOutbound);\n printPermissionSection('Filesystem (read)', resolved.filesystemRead);\n printPermissionSection('Filesystem (write)', resolved.filesystemWrite);\n\n // Subprocess section\n console.log(`\\n${chalk.bold('Subprocess')}:`);\n if (resolved.subprocess.length === 0) {\n console.log(' none');\n } else {\n console.log(` allowed ${formatAttribution(resolved.subprocess)}`);\n }\n\n // 4. Budget check\n const resolvedManifest = resolveManifestPath(dir);\n let budget: Permissions | undefined;\n\n if (resolvedManifest.exists) {\n const skillsJsonContent = fs.readFileSync(resolvedManifest.path, 'utf-8');\n const skillsJson: SkillsJson = JSON.parse(skillsJsonContent);\n budget = skillsJson.permissions;\n }\n\n console.log('');\n\n if (!budget) {\n console.log(`Budget status: ${chalk.yellow('⚠ No budget defined')}`);\n return;\n }\n\n const violations = checkBudget(resolved, budget);\n\n if (violations.length === 0) {\n console.log(`Budget status: ${chalk.green('✓ PASS')} (all within budget)`);\n } else {\n console.log(`Budget status: ${chalk.red('✗ FAIL')}`);\n for (const v of violations) {\n console.log(chalk.red(` - ${v.category}: \"${v.value}\" not in budget (requested by ${v.skills.join(', ')})`));\n }\n }\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Readable } from 'node:stream';\n\nimport { LEGACY_MANIFEST_FILENAME, MANIFEST_FILENAME, skillsJsonSchema } from '@internals/schemas';\nimport ignore from 'ignore';\nimport { create } from 'tar';\n\n// Limits\nconst MAX_PACKAGE_SIZE = 50 * 1024 * 1024; // 50MB\nconst MAX_FILE_COUNT = 1000;\n\n// Default ignore patterns (used when no .tankignore or .gitignore exists)\nconst DEFAULT_IGNORES = ['node_modules', '.git', '.env*', '*.log', '.tank', '.DS_Store'];\n\n// Always ignored regardless of ignore file contents\nconst ALWAYS_IGNORED = ['node_modules', '.git'];\n\n// Ignore file names (not packed into tarball)\nconst IGNORE_FILES = ['.tankignore', '.gitignore'];\n\nexport interface PackResult {\n tarball: Buffer;\n integrity: string; // \"sha512-{base64}\"\n fileCount: number;\n totalSize: number;\n readme: string;\n files: string[];\n}\n\n/**\n * Pack a skill directory into a .tgz tarball with integrity hashing.\n *\n * Validates:\n * - skills.json exists and is valid\n * - SKILL.md exists\n * - No symlinks or hardlinks\n * - No path traversal (.. components)\n * - No absolute paths\n * - File count <= 1000\n * - Tarball size <= 50MB\n */\nexport async function pack(directory: string): Promise<PackResult> {\n const absDir = path.resolve(directory);\n\n // 1. Verify directory exists\n if (!fs.existsSync(absDir)) {\n throw new Error(`Directory does not exist: ${absDir}`);\n }\n\n const stat = fs.statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`);\n }\n\n // 2. Verify manifest (tank.json or skills.json) exists and is valid\n let manifestPath = path.join(absDir, MANIFEST_FILENAME);\n let manifestFilename = MANIFEST_FILENAME;\n if (!fs.existsSync(manifestPath)) {\n manifestPath = path.join(absDir, LEGACY_MANIFEST_FILENAME);\n manifestFilename = LEGACY_MANIFEST_FILENAME;\n }\n if (!fs.existsSync(manifestPath)) {\n throw new Error(`Missing required file: ${MANIFEST_FILENAME}`);\n }\n\n let skillsJsonContent: string;\n try {\n skillsJsonContent = fs.readFileSync(manifestPath, 'utf-8');\n } catch {\n throw new Error(`Failed to read ${manifestFilename}`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(skillsJsonContent);\n } catch {\n throw new Error(`Invalid ${manifestFilename}: not valid JSON`);\n }\n\n const validation = skillsJsonSchema.safeParse(parsed);\n if (!validation.success) {\n const issues = validation.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n');\n throw new Error(`Invalid ${manifestFilename}:\\n${issues}`);\n }\n\n // 3. Verify SKILL.md exists and read its content\n const skillMdPath = path.join(absDir, 'SKILL.md');\n if (!fs.existsSync(skillMdPath)) {\n throw new Error('Missing required file: SKILL.md');\n }\n\n let readmeContent: string;\n try {\n readmeContent = fs.readFileSync(skillMdPath, 'utf-8');\n } catch {\n throw new Error('Failed to read SKILL.md');\n }\n\n // 4. Build ignore filter\n const ig = buildIgnoreFilter(absDir);\n\n // 5. Collect files with validation\n const files = collectFiles(absDir, absDir, ig);\n\n // 6. Enforce file count limit\n if (files.length > MAX_FILE_COUNT) {\n throw new Error(`Too many files: ${files.length} exceeds maximum of ${MAX_FILE_COUNT}`);\n }\n\n // 7. Calculate total size of source files\n let totalSize = 0;\n for (const file of files) {\n const filePath = path.join(absDir, file);\n const fileStat = fs.statSync(filePath);\n totalSize += fileStat.size;\n }\n\n // 8. Create tarball\n const tarball = await createTarball(absDir, files);\n\n // 9. Enforce tarball size limit\n if (tarball.length > MAX_PACKAGE_SIZE) {\n throw new Error(`Tarball too large: ${tarball.length} bytes exceeds maximum of ${MAX_PACKAGE_SIZE} bytes (50MB)`);\n }\n\n // 10. Compute integrity hash\n const hash = crypto.createHash('sha512').update(tarball).digest('base64');\n const integrity = `sha512-${hash}`;\n\n return {\n tarball,\n integrity,\n fileCount: files.length,\n totalSize,\n readme: readmeContent,\n files\n };\n}\n\n/**\n * Pack a directory into a .tgz tarball for security scanning.\n *\n * Unlike pack(), this function does NOT require skills.json or SKILL.md.\n * It applies the same security checks (no symlinks, no path traversal, etc.)\n * and returns the same PackResult interface.\n *\n * Validates:\n * - Directory exists\n * - No symlinks or hardlinks\n * - No path traversal (.. components)\n * - No absolute paths\n * - File count <= 1000\n * - Tarball size <= 50MB\n *\n * Does NOT validate:\n * - skills.json existence or validity\n * - SKILL.md existence (but reads it if present)\n */\nexport async function packForScan(directory: string): Promise<PackResult> {\n const absDir = path.resolve(directory);\n\n // 1. Verify directory exists\n if (!fs.existsSync(absDir)) {\n throw new Error(`Directory does not exist: ${absDir}`);\n }\n\n const stat = fs.statSync(absDir);\n if (!stat.isDirectory()) {\n throw new Error(`Not a directory: ${absDir}`);\n }\n\n // 2. Try to read SKILL.md if it exists (optional for scan)\n let readmeContent = '';\n const skillMdPath = path.join(absDir, 'SKILL.md');\n if (fs.existsSync(skillMdPath)) {\n try {\n readmeContent = fs.readFileSync(skillMdPath, 'utf-8');\n } catch {\n // If we can't read it, just use empty string\n readmeContent = '';\n }\n }\n\n // 3. Build ignore filter\n const ig = buildIgnoreFilter(absDir);\n\n // 4. Collect files with validation\n const files = collectFiles(absDir, absDir, ig);\n\n if (files.length === 0) {\n throw new Error(`No manifest found and no files to scan in ${absDir}`);\n }\n\n // 5. Enforce file count limit\n if (files.length > MAX_FILE_COUNT) {\n throw new Error(`Too many files: ${files.length} exceeds maximum of ${MAX_FILE_COUNT}`);\n }\n\n // 6. Calculate total size of source files\n let totalSize = 0;\n for (const file of files) {\n const filePath = path.join(absDir, file);\n const fileStat = fs.statSync(filePath);\n totalSize += fileStat.size;\n }\n\n // 7. Create tarball\n const tarball = await createTarball(absDir, files);\n\n // 8. Enforce tarball size limit\n if (tarball.length > MAX_PACKAGE_SIZE) {\n throw new Error(`Tarball too large: ${tarball.length} bytes exceeds maximum of ${MAX_PACKAGE_SIZE} bytes (50MB)`);\n }\n\n // 9. Compute integrity hash\n const hash = crypto.createHash('sha512').update(tarball).digest('base64');\n const integrity = `sha512-${hash}`;\n\n return {\n tarball,\n integrity,\n fileCount: files.length,\n totalSize,\n readme: readmeContent,\n files\n };\n}\n\n/**\n * Build an ignore filter from .tankignore, .gitignore, or defaults.\n */\nfunction buildIgnoreFilter(dir: string): ReturnType<typeof ignore> {\n const ig = ignore();\n\n // Always add the forced ignores\n ig.add(ALWAYS_IGNORED);\n\n // Check for .tankignore first, then .gitignore, then defaults\n const tankIgnorePath = path.join(dir, '.tankignore');\n const gitIgnorePath = path.join(dir, '.gitignore');\n\n if (fs.existsSync(tankIgnorePath)) {\n const content = fs.readFileSync(tankIgnorePath, 'utf-8');\n ig.add(content);\n // Also ignore the ignore files themselves\n ig.add(IGNORE_FILES);\n } else if (fs.existsSync(gitIgnorePath)) {\n const content = fs.readFileSync(gitIgnorePath, 'utf-8');\n ig.add(content);\n ig.add(IGNORE_FILES);\n } else {\n ig.add(DEFAULT_IGNORES);\n }\n\n return ig;\n}\n\n/**\n * Recursively collect files from a directory, applying ignore rules and security checks.\n */\nfunction collectFiles(baseDir: string, currentDir: string, ig: ReturnType<typeof ignore>): string[] {\n const files: string[] = [];\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n const relativePath = path.relative(baseDir, fullPath);\n\n // Security: check for path traversal\n if (relativePath.split(path.sep).includes('..')) {\n throw new Error(`Path traversal detected: \"${relativePath}\" contains \"..\" component`);\n }\n\n // Security: check for absolute paths\n if (path.isAbsolute(relativePath)) {\n throw new Error(`Absolute path detected: \"${relativePath}\"`);\n }\n\n // Security: check for symlinks using lstat (not stat which follows symlinks)\n const lstatResult = fs.lstatSync(fullPath);\n if (lstatResult.isSymbolicLink()) {\n throw new Error(`Symlink detected: \"${relativePath}\" — symlinks are not allowed in skill packages`);\n }\n\n // Check if this path should be ignored\n // For directories, append '/' so ignore patterns like 'dir/' work correctly\n const pathForIgnore = lstatResult.isDirectory() ? `${relativePath}/` : relativePath;\n\n if (ig.ignores(pathForIgnore)) {\n continue;\n }\n\n if (lstatResult.isDirectory()) {\n // Recurse into subdirectory\n const subFiles = collectFiles(baseDir, fullPath, ig);\n files.push(...subFiles);\n } else if (lstatResult.isFile()) {\n files.push(relativePath);\n }\n // Skip other types (block devices, character devices, FIFOs, sockets)\n }\n\n return files;\n}\n\n/**\n * Create a gzipped tarball from the given files in the directory.\n */\nasync function createTarball(cwd: string, files: string[]): Promise<Buffer> {\n return new Promise<Buffer>((resolve, reject) => {\n const chunks: Buffer[] = [];\n\n // tar.create without `file` returns a readable stream\n const stream = create(\n {\n gzip: true,\n cwd,\n portable: true // Omit system-specific metadata\n },\n files\n ) as unknown as Readable;\n\n stream.on('data', (chunk: Buffer) => {\n chunks.push(chunk);\n });\n\n stream.on('end', () => {\n resolve(Buffer.concat(chunks));\n });\n\n stream.on('error', (err: Error) => {\n reject(err);\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { MANIFEST_FILENAME } from '@internals/schemas';\nimport ora from 'ora';\n\nimport { getConfig } from '~/lib/config.js';\nimport { logger } from '~/lib/logger.js';\nimport { resolveManifestPath } from '~/lib/manifest.js';\nimport { pack } from '~/lib/packer.js';\nimport { USER_AGENT } from '~/version.js';\n\nexport interface PublishOptions {\n directory?: string;\n configDir?: string;\n dryRun?: boolean;\n private?: boolean;\n visibility?: string;\n}\n\n/**\n * Format bytes into a human-readable size string.\n */\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * Publish a skill package to the Tank registry.\n *\n * Flow:\n * 1. Check auth (token exists)\n * 2. Read skills.json from directory\n * 3. Pack directory into tarball\n * 4. If --dry-run: print summary and exit\n * 5. POST /api/v1/skills with manifest → get uploadUrl, skillId, versionId\n * 6. PUT tarball to uploadUrl\n * 7. POST /api/v1/skills/confirm with integrity data\n * 8. Print success\n */\nexport async function publishCommand(options: PublishOptions = {}): Promise<void> {\n const { directory = process.cwd(), configDir, dryRun = false, private: privateFlag, visibility } = options;\n\n // 1. Check auth\n const config = getConfig(configDir);\n if (!config.token) {\n throw new Error('Not logged in. Run: tank login');\n }\n\n // 2. Read manifest (tank.json or skills.json)\n const resolvedManifest = resolveManifestPath(directory);\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found in ${directory}. Run: tank init`);\n }\n\n let manifest: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n manifest = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n if (visibility && visibility !== 'public' && visibility !== 'private') {\n throw new Error(\"Invalid visibility. Use 'public' or 'private'\");\n }\n\n const effectiveVisibility = visibility ?? (privateFlag ? 'private' : undefined);\n if (effectiveVisibility) {\n manifest.visibility = effectiveVisibility;\n }\n\n const name = manifest.name as string;\n const version = manifest.version as string;\n\n // 3. Pack\n const spinner = ora('Packing...').start();\n let packResult: Awaited<ReturnType<typeof pack>>;\n try {\n packResult = await pack(directory);\n } catch (err) {\n spinner.fail('Packing failed');\n throw err;\n }\n\n const { tarball, integrity, fileCount, totalSize, readme, files } = packResult;\n\n // 4. Dry run — print summary, verify auth with server, and exit\n if (dryRun) {\n spinner.stop();\n logger.info(`name: ${name}`);\n logger.info(`version: ${version}`);\n logger.info(`visibility: ${String(manifest.visibility ?? 'default')}`);\n logger.info(`size: ${formatSize(totalSize)} (${fileCount} files)`);\n logger.info(`tarball: ${formatSize(tarball.length)} (compressed)`);\n\n // Verify token with server to catch stale auth before real publish\n try {\n const verifyRes = await fetch(`${config.registry}/api/v1/auth/whoami`, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'User-Agent': USER_AGENT\n }\n });\n\n if (verifyRes.status === 401) {\n logger.warn('Token is invalid or expired. Run: tank login');\n } else if (!verifyRes.ok) {\n logger.warn('Could not verify token with server. Publish may fail.');\n } else {\n logger.success('Auth verified with server.');\n }\n } catch {\n logger.warn('Could not reach server to verify token. Publish may fail.');\n }\n\n logger.success('Dry run complete — no files were uploaded.');\n return;\n }\n\n // 5. Step 1: POST /api/v1/skills\n spinner.text = 'Publishing...';\n const headers = {\n Authorization: `Bearer ${config.token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': USER_AGENT\n };\n\n const step1Res = await fetch(`${config.registry}/api/v1/skills`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ manifest, readme, files })\n });\n\n if (!step1Res.ok) {\n spinner.fail('Publish failed');\n const body = (await step1Res.json().catch(() => null)) as { error?: string } | null;\n const errorMsg = body?.error ?? step1Res.statusText;\n\n if (step1Res.status === 401) {\n throw new Error('Authentication failed. Your token may be expired or invalid. Run: tank login');\n }\n if (step1Res.status === 403) {\n throw new Error(`Publish failed: ${errorMsg}`);\n }\n if (step1Res.status === 404) {\n throw new Error(`Publish failed: ${errorMsg}`);\n }\n if (step1Res.status === 409) {\n throw new Error(`Version already exists. Bump the version in ${MANIFEST_FILENAME}`);\n }\n throw new Error(errorMsg);\n }\n\n const { uploadUrl, versionId } = (await step1Res.json()) as {\n uploadUrl: string;\n skillId: string;\n versionId: string;\n };\n\n // 6. Step 2: Upload tarball to signed URL\n spinner.text = 'Uploading...';\n const uploadRes = await fetch(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/octet-stream' },\n body: new Uint8Array(tarball)\n });\n\n if (!uploadRes.ok) {\n spinner.fail('Upload failed');\n throw new Error(`Failed to upload tarball: ${uploadRes.status} ${uploadRes.statusText}`);\n }\n\n // 7. Step 3: Confirm publish\n spinner.text = 'Confirming...';\n const confirmRes = await fetch(`${config.registry}/api/v1/skills/confirm`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n versionId,\n integrity,\n fileCount,\n tarballSize: totalSize,\n readme\n })\n });\n\n if (!confirmRes.ok) {\n spinner.fail('Publish confirmation failed');\n const body = (await confirmRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(`Failed to confirm publish: ${body?.error ?? confirmRes.statusText}`);\n }\n\n spinner.succeed(`Published ${name}@${version} (${formatSize(totalSize)}, ${fileCount} files)`);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { LOCKFILE_VERSION, MANIFEST_FILENAME, type SkillsLock } from '@internals/schemas';\n\nimport { getGlobalAgentSkillsDir, getGlobalSkillsDir, getSymlinkName } from '~/lib/agents.js';\nimport { unlinkSkillFromAgents } from '~/lib/linker.js';\nimport { logger } from '~/lib/logger.js';\nimport { resolveLockfilePath, resolveManifestPath } from '~/lib/manifest.js';\n\nexport interface RemoveOptions {\n name: string;\n directory?: string;\n global?: boolean;\n homedir?: string;\n}\n\nexport async function removeCommand(options: RemoveOptions): Promise<void> {\n const { name, directory = process.cwd(), global, homedir } = options;\n\n if (global) {\n const resolvedHome = homedir ?? os.homedir();\n\n // 1. Unlink from agents (failures are warnings)\n try {\n const unlinkResult = unlinkSkillFromAgents({\n skillName: name,\n linksDir: path.join(resolvedHome, '.tank'),\n homedir\n });\n if (unlinkResult.unlinked.length > 0) {\n logger.info(`Unlinked from ${unlinkResult.unlinked.length} agent(s)`);\n }\n } catch {\n logger.warn('Agent unlinking skipped (non-fatal)');\n }\n\n // 2. Remove agent-skills wrapper dir\n const symlinkName = getSymlinkName(name);\n const agentSkillDir = path.join(getGlobalAgentSkillsDir(resolvedHome), symlinkName);\n if (fs.existsSync(agentSkillDir)) {\n fs.rmSync(agentSkillDir, { recursive: true, force: true });\n }\n\n // 3. Remove global skills dir\n const skillDir = getGlobalSkillDir(resolvedHome, name);\n if (fs.existsSync(skillDir)) {\n fs.rmSync(skillDir, { recursive: true, force: true });\n }\n\n // 4. Update global lockfile\n const resolvedLock = resolveLockfilePath(path.join(resolvedHome, '.tank'));\n const lockPath = resolvedLock.path;\n if (resolvedLock.exists) {\n let lock: SkillsLock;\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n\n for (const key of Object.keys(lock.skills)) {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) continue;\n const keyName = key.slice(0, lastAt);\n if (keyName === name) {\n delete lock.skills[key];\n }\n }\n\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n\n fs.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\\n`);\n }\n\n logger.success(`Removed ${name} (global)`);\n return;\n }\n\n // 1. Read manifest (tank.json or skills.json)\n const resolvedManifest = resolveManifestPath(directory);\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found in ${directory}. Run: tank init`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n // 2. Check if skill exists in skills map\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n if (!(name in skills)) {\n throw new Error(`Skill \"${name}\" is not installed (not found in ${path.basename(resolvedManifest.path)})`);\n }\n\n // 3. Remove skill from manifest\n delete skills[name];\n skillsJson.skills = skills;\n\n // 4. Write updated manifest\n fs.writeFileSync(resolvedManifest.path, `${JSON.stringify(skillsJson, null, 2)}\\n`);\n\n // 5. Read lockfile if present\n const resolvedLocalLock = resolveLockfilePath(directory);\n const lockPath = resolvedLocalLock.path;\n if (resolvedLocalLock.exists) {\n let lock: SkillsLock;\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n lock = JSON.parse(raw) as SkillsLock;\n } catch {\n lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };\n }\n\n // 6. Remove ALL lockfile entries for this skill name\n // Key format: name@version — use lastIndexOf('@') to split scoped names\n for (const key of Object.keys(lock.skills)) {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) continue;\n const keyName = key.slice(0, lastAt);\n if (keyName === name) {\n delete lock.skills[key];\n }\n }\n\n // 7. Write updated skills.lock (sorted keys, trailing newline)\n const sortedSkills: Record<string, unknown> = {};\n for (const key of Object.keys(lock.skills).sort()) {\n sortedSkills[key] = lock.skills[key];\n }\n lock.skills = sortedSkills as SkillsLock['skills'];\n\n fs.writeFileSync(lockPath, `${JSON.stringify(lock, null, 2)}\\n`);\n }\n\n // 7.5. Unlink from agents (failures are warnings)\n try {\n const unlinkResult = unlinkSkillFromAgents({\n skillName: name,\n linksDir: path.join(directory, '.tank'),\n homedir\n });\n if (unlinkResult.unlinked.length > 0) {\n logger.info(`Unlinked from ${unlinkResult.unlinked.length} agent(s)`);\n }\n } catch {\n logger.warn('Agent unlinking skipped (non-fatal)');\n }\n\n // 7.6. Remove agent-skills wrapper dir\n const symlinkName = getSymlinkName(name);\n const agentSkillDir = path.join(directory, '.tank', 'agent-skills', symlinkName);\n if (fs.existsSync(agentSkillDir)) {\n fs.rmSync(agentSkillDir, { recursive: true, force: true });\n }\n\n // 8. Delete .tank/skills/{name}/ directory\n const skillDir = getSkillDir(directory, name);\n if (fs.existsSync(skillDir)) {\n fs.rmSync(skillDir, { recursive: true, force: true });\n }\n\n // 9. Print success\n logger.success(`Removed ${name}`);\n}\n\nfunction getSkillDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n\nfunction getGlobalSkillDir(homedir: string, skillName: string): string {\n const globalDir = getGlobalSkillsDir(homedir);\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(globalDir, scope, name);\n }\n return path.join(globalDir, skillName);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport chalk from 'chalk';\nimport ora from 'ora';\n\nimport { getConfig } from '~/lib/config.js';\nimport { resolveManifestPath } from '~/lib/manifest.js';\nimport { pack, packForScan } from '~/lib/packer.js';\nimport { USER_AGENT } from '~/version.js';\n\nexport interface ScanOptions {\n directory?: string;\n configDir?: string;\n}\n\ninterface ScanFinding {\n stage: string;\n severity: 'critical' | 'high' | 'medium' | 'low';\n type: string;\n description: string;\n location: string | null;\n confidence: number | null;\n tool: string | null;\n evidence: string | null;\n}\n\ninterface ScanResponse {\n scan_id: string | null;\n verdict: 'pass' | 'pass_with_notes' | 'flagged' | 'fail';\n audit_score: number;\n findings: ScanFinding[];\n stage_results: Array<{\n stage: string;\n status: string;\n findings: ScanFinding[];\n duration_ms: number;\n }>;\n duration_ms: number;\n}\n\nfunction verdictColor(verdict: string): (text: string) => string {\n switch (verdict) {\n case 'pass':\n return chalk.green;\n case 'pass_with_notes':\n return chalk.yellow;\n case 'flagged':\n return chalk.hex('#FF8C00');\n case 'fail':\n return chalk.red;\n default:\n return chalk.white;\n }\n}\n\nfunction severityColor(severity: string): (text: string) => string {\n switch (severity) {\n case 'critical':\n return chalk.red;\n case 'high':\n return chalk.hex('#FF8C00');\n case 'medium':\n return chalk.yellow;\n case 'low':\n return chalk.green;\n default:\n return chalk.white;\n }\n}\n\nfunction scoreColor(score: number): (text: string) => string {\n if (score >= 7) return chalk.green;\n if (score >= 4) return chalk.yellow;\n return chalk.red;\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport async function scanCommand(options: ScanOptions = {}): Promise<void> {\n const { directory = process.cwd(), configDir } = options;\n const absDir = path.resolve(directory);\n\n const config = getConfig(configDir);\n if (!config.token) {\n throw new Error('Not logged in. Run: tank login');\n }\n\n const spinner = ora('Packing skill...').start();\n\n // Try to read manifest (tank.json or skills.json) first\n const resolvedManifest = resolveManifestPath(absDir);\n let manifest: Record<string, unknown>;\n let packResult: Awaited<ReturnType<typeof pack>>;\n\n if (resolvedManifest.exists) {\n // Manifest exists: use pack() and read it\n try {\n packResult = await pack(absDir);\n } catch (err) {\n spinner.fail('Packing failed');\n throw err;\n }\n\n try {\n manifest = JSON.parse(fs.readFileSync(resolvedManifest.path, 'utf-8')) as Record<string, unknown>;\n } catch (err) {\n spinner.fail(`Failed to read ${path.basename(resolvedManifest.path)}`);\n throw err;\n }\n } else {\n // No manifest: use packForScan() and synthesize manifest\n try {\n packResult = await packForScan(absDir);\n } catch (err) {\n spinner.fail('Packing failed');\n throw err;\n }\n\n const dirName = path.basename(absDir);\n manifest = { name: dirName, version: '0.0.0', description: 'Local scan' };\n }\n\n const name = (manifest.name as string) ?? 'unknown';\n const version = (manifest.version as string) ?? '0.0.0';\n\n spinner.text = `Scanning ${name}@${version}...`;\n\n const formData = new FormData();\n const blob = new Blob([new Uint8Array(packResult.tarball)], { type: 'application/gzip' });\n formData.append('tarball', blob, `${name}-${version}.tgz`);\n formData.append('manifest', JSON.stringify(manifest));\n\n let scanRes: Response;\n try {\n scanRes = await fetch(`${config.registry}/api/v1/scan`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'User-Agent': USER_AGENT\n },\n body: formData\n });\n } catch (err) {\n spinner.fail('Scan failed');\n throw new Error(`Network error: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!scanRes.ok) {\n spinner.fail('Scan failed');\n const body = (await scanRes.json().catch(() => null)) as { error?: string } | null;\n\n if (scanRes.status === 401) {\n throw new Error('Authentication failed. Your token may be expired or invalid. Run: tank login');\n }\n throw new Error(body?.error ?? scanRes.statusText);\n }\n\n const result = (await scanRes.json()) as ScanResponse;\n\n spinner.stop();\n\n const verdictLabel = verdictColor(result.verdict)(result.verdict.toUpperCase());\n const auditScore = result.audit_score ?? 0;\n const scoreLabel = scoreColor(auditScore)(auditScore.toFixed(1));\n\n console.log('');\n console.log(chalk.bold(`Security Scan: ${name}@${version}`));\n console.log('');\n console.log(`${chalk.dim('Verdict:'.padEnd(14))}${verdictLabel}`);\n console.log(`${chalk.dim('Score:'.padEnd(14))}${scoreLabel}/10`);\n console.log(`${chalk.dim('Duration:'.padEnd(14))}${(result.duration_ms / 1000).toFixed(1)}s`);\n console.log(`${chalk.dim('Files:'.padEnd(14))}${packResult.fileCount} (${formatSize(packResult.totalSize)})`);\n\n if (result.findings.length > 0) {\n console.log('');\n console.log(chalk.bold(`Findings (${result.findings.length})`));\n\n const bySeverity: Record<string, ScanFinding[]> = {\n critical: [],\n high: [],\n medium: [],\n low: []\n };\n for (const f of result.findings) {\n bySeverity[f.severity].push(f);\n }\n\n for (const severity of ['critical', 'high', 'medium', 'low'] as const) {\n const findings = bySeverity[severity];\n if (findings.length === 0) continue;\n\n console.log('');\n const label = severityColor(severity)(`${severity.toUpperCase()} (${findings.length})`);\n console.log(` ${label}`);\n\n for (const f of findings) {\n console.log(` - ${chalk.bold(f.type)}: ${f.description}`);\n if (f.location) {\n console.log(` ${chalk.dim('Location:')} ${f.location}`);\n }\n }\n }\n } else {\n console.log('');\n console.log(chalk.green('No findings. Your skill looks secure!'));\n }\n\n if (result.stage_results?.length > 0) {\n console.log('');\n console.log(chalk.bold('Scan Stages'));\n for (const stage of result.stage_results) {\n const icon = stage.status === 'passed' ? chalk.green('✓') : chalk.red('✗');\n console.log(` ${icon} ${stage.stage} (${stage.duration_ms}ms)`);\n }\n }\n\n if (result.scan_id) {\n console.log('');\n console.log(chalk.dim(`Full report: ${config.registry}/scans/${result.scan_id}`));\n }\n\n console.log('');\n}\n","import chalk from 'chalk';\n\nimport { getConfig } from '~/lib/config.js';\nimport { USER_AGENT } from '~/version.js';\n\nexport interface SearchOptions {\n query: string;\n configDir?: string;\n}\n\ninterface SearchResult {\n name: string;\n description: string;\n latestVersion: string;\n auditScore: number;\n publisher: string;\n downloads: number;\n}\n\ninterface SearchResponse {\n results: SearchResult[];\n page: number;\n limit: number;\n total: number;\n}\n\nconst MAX_DESC_LENGTH = 60;\n\nfunction scoreColor(score: number): (text: string) => string {\n if (score >= 7) return chalk.green;\n if (score >= 4) return chalk.yellow;\n return chalk.red;\n}\n\nfunction truncate(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text;\n return `${text.slice(0, maxLen - 3)}...`;\n}\n\nfunction padRight(text: string, width: number): string {\n if (text.length >= width) return text;\n return text + ' '.repeat(width - text.length);\n}\n\nexport async function searchCommand(options: SearchOptions): Promise<void> {\n const { query, configDir } = options;\n const config = getConfig(configDir);\n\n const url = `${config.registry}/api/v1/search?q=${encodeURIComponent(query)}&limit=20`;\n\n let res: Response;\n try {\n const headers: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n headers.Authorization = `Bearer ${config.token}`;\n }\n res = await fetch(url, {\n headers\n });\n } catch (err) {\n throw new Error(`Network error searching: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!res.ok) {\n const body = (await res.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? `Search failed: ${res.statusText}`);\n }\n\n const data = (await res.json()) as SearchResponse;\n\n if (data.results.length === 0) {\n console.log(`No skills found for \"${query}\"`);\n return;\n }\n\n // Print table header\n console.log(`${padRight('NAME', 30) + padRight('VERSION', 10) + padRight('SCORE', 8)}DESCRIPTION`);\n\n // Print each result\n for (const result of data.results) {\n const name = chalk.bold(padRight(result.name, 30));\n const version = padRight(result.latestVersion, 10);\n const scoreStr = Number.isInteger(result.auditScore) ? result.auditScore.toFixed(1) : String(result.auditScore);\n const score = scoreColor(result.auditScore)(padRight(scoreStr, 8));\n const desc = truncate(result.description ?? '', MAX_DESC_LENGTH);\n\n console.log(`${name}${version}${score}${desc}`);\n }\n\n console.log('');\n console.log(`${data.results.length} skill${data.results.length === 1 ? '' : 's'} found`);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { MANIFEST_FILENAME } from '@internals/schemas';\n\nimport { getGlobalAgentSkillsDir, getSymlinkName } from '~/lib/agents.js';\nimport { unlinkSkillFromAgents } from '~/lib/linker.js';\nimport { logger } from '~/lib/logger.js';\nimport { resolveManifestPath } from '~/lib/manifest.js';\n\nexport interface UnlinkOptions {\n directory?: string;\n homedir?: string;\n}\n\nexport async function unlinkCommand(options: UnlinkOptions = {}): Promise<void> {\n const workDir = options.directory ?? process.cwd();\n const resolvedManifest = resolveManifestPath(workDir);\n\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found. Run this command from a skill directory.`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n const skillName = skillsJson.name;\n if (typeof skillName !== 'string' || skillName.trim().length === 0) {\n throw new Error(`Missing 'name' in ${path.basename(resolvedManifest.path)}`);\n }\n\n const homedir = options.homedir ?? os.homedir();\n const result = unlinkSkillFromAgents({\n skillName,\n linksDir: path.join(homedir, '.tank'),\n homedir: options.homedir\n });\n\n const symlinkName = getSymlinkName(skillName);\n const wrapperDir = path.join(getGlobalAgentSkillsDir(options.homedir), symlinkName);\n if (fs.existsSync(wrapperDir)) {\n fs.rmSync(wrapperDir, { recursive: true, force: true });\n }\n\n if (result.unlinked.length === 0 && result.notFound.length === 0) {\n logger.info(`No links found for ${skillName}`);\n return;\n }\n\n logger.success(`Unlinked ${skillName} from ${result.unlinked.length} agent(s)`);\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { resolve } from '@internals/helpers';\nimport { LOCKFILE_FILENAME, MANIFEST_FILENAME, type SkillsLock } from '@internals/schemas';\n\nimport { getConfig } from '~/lib/config.js';\nimport { logger } from '~/lib/logger.js';\nimport { resolveLockfilePath, resolveManifestPath } from '~/lib/manifest.js';\nimport { USER_AGENT } from '~/version.js';\n\nimport { installCommand } from './install.js';\n\nexport interface UpdateOptions {\n name?: string; // undefined = update all\n directory?: string;\n configDir?: string;\n global?: boolean;\n homedir?: string;\n}\n\ninterface VersionInfo {\n version: string;\n integrity: string;\n auditScore: number;\n auditStatus: string;\n publishedAt: string;\n}\n\nconst VERSION_CHECK_CONCURRENCY = 8;\n\nexport async function updateCommand(options: UpdateOptions): Promise<void> {\n const { name, directory = process.cwd(), configDir, global = false, homedir } = options;\n\n if (global) {\n if (name) {\n await updateSingleGlobal(name, configDir, homedir);\n } else {\n await updateAllGlobal(configDir, homedir);\n }\n return;\n }\n\n // 1. Read manifest (tank.json or skills.json)\n const resolvedManifest = resolveManifestPath(directory);\n if (!resolvedManifest.exists) {\n throw new Error(`No ${MANIFEST_FILENAME} found in ${directory}. Run: tank init`);\n }\n\n let skillsJson: Record<string, unknown>;\n try {\n const raw = fs.readFileSync(resolvedManifest.path, 'utf-8');\n skillsJson = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n throw new Error(`Failed to read or parse ${path.basename(resolvedManifest.path)}`);\n }\n\n const skills = (skillsJson.skills ?? {}) as Record<string, string>;\n\n if (name) {\n // Update single skill\n await updateSingle(name, skills, directory, configDir, global, homedir);\n } else {\n // Update all skills\n await updateAll(skills, directory, configDir, global, homedir);\n }\n}\n\nfunction parseLockKey(key: string): { name: string; version: string } | null {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) return null;\n return { name: key.slice(0, lastAt), version: key.slice(lastAt + 1) };\n}\n\nfunction readLockfile(lockPath: string): SkillsLock | null {\n if (!fs.existsSync(lockPath)) {\n return null;\n }\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n return null;\n }\n}\n\nfunction readLockfileStrict(lockPath: string): SkillsLock {\n if (!fs.existsSync(lockPath)) {\n throw new Error(`Global ${LOCKFILE_FILENAME} not found at ${lockPath}`);\n }\n try {\n const raw = fs.readFileSync(lockPath, 'utf-8');\n return JSON.parse(raw) as SkillsLock;\n } catch {\n throw new Error(`Failed to read or parse global ${LOCKFILE_FILENAME}`);\n }\n}\n\nfunction getGlobalLockPath(homedir?: string): string {\n const resolvedHome = homedir ?? os.homedir();\n const resolved = resolveLockfilePath(path.join(resolvedHome, '.tank'));\n return resolved.path;\n}\n\nasync function fetchAvailableVersions(\n name: string,\n registry: string,\n headers: Record<string, string>\n): Promise<string[]> {\n const encodedName = encodeURIComponent(name);\n const versionsUrl = `${registry}/api/v1/skills/${encodedName}/versions`;\n\n let versionsRes: Response;\n try {\n versionsRes = await fetch(versionsUrl, { headers });\n } catch (err) {\n throw new Error(`Network error fetching versions for ${name}: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (!versionsRes.ok) {\n if (versionsRes.status === 404) {\n throw new Error(`Skill not found in registry: ${name}`);\n }\n const body = (await versionsRes.json().catch(() => null)) as { error?: string } | null;\n throw new Error(body?.error ?? versionsRes.statusText);\n }\n\n const versionsData = (await versionsRes.json()) as { name: string; versions: VersionInfo[] };\n return versionsData.versions.map((v) => v.version);\n}\n\n/**\n * Deduplicate lockfile entries by skill name.\n * When multiple versions of the same skill exist in the lockfile (e.g. from\n * transitive dependencies), keeps only the highest version per skill name.\n */\nfunction deduplicateByName(entries: string[]): Map<string, string> {\n const versionsByName = new Map<string, string[]>();\n for (const key of entries) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n const versions = versionsByName.get(parsed.name) ?? [];\n versions.push(parsed.version);\n versionsByName.set(parsed.name, versions);\n }\n\n const latestByName = new Map<string, string>();\n for (const [name, versions] of versionsByName) {\n const latest = resolve('*', versions);\n if (latest) latestByName.set(name, latest);\n }\n\n return latestByName;\n}\n\nasync function fetchVersionsBatch(\n skillNames: string[],\n registry: string,\n headers: Record<string, string>\n): Promise<Map<string, string[]>> {\n const results = new Map<string, string[]>();\n\n for (let i = 0; i < skillNames.length; i += VERSION_CHECK_CONCURRENCY) {\n const batch = skillNames.slice(i, i + VERSION_CHECK_CONCURRENCY);\n const settled = await Promise.allSettled(\n batch.map(async (name) => {\n const versions = await fetchAvailableVersions(name, registry, headers);\n return { name, versions };\n })\n );\n\n for (const result of settled) {\n if (result.status === 'fulfilled') {\n results.set(result.value.name, result.value.versions);\n } else {\n throw result.reason;\n }\n }\n }\n\n return results;\n}\n\nasync function updateSingle(\n name: string,\n skills: Record<string, string>,\n directory: string,\n configDir?: string,\n global = false,\n homedir?: string\n): Promise<void> {\n const versionRange = skills[name];\n if (!versionRange) {\n throw new Error(`Skill \"${name}\" is not installed (not found in ${MANIFEST_FILENAME})`);\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const availableVersions = await fetchAvailableVersions(name, config.registry, requestHeaders);\n\n const resolved = resolve(versionRange, availableVersions);\n if (!resolved) {\n throw new Error(\n `No version of ${name} satisfies range \"${versionRange}\". Available: ${availableVersions.join(', ')}`\n );\n }\n\n const lockPath = global ? getGlobalLockPath(homedir) : resolveLockfilePath(directory).path;\n let currentVersion: string | null = null;\n\n const lock = readLockfile(lockPath);\n if (lock) {\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n if (parsed.name === name) {\n currentVersion = parsed.version;\n break;\n }\n }\n }\n\n if (resolved === currentVersion) {\n logger.info(`Already at latest: ${name}@${resolved}`);\n return;\n }\n\n await installCommand({\n name,\n versionRange,\n directory,\n configDir,\n global,\n homedir\n });\n\n logger.success(`Updated ${name} to ${resolved}`);\n}\n\nasync function updateAll(\n skills: Record<string, string>,\n directory: string,\n configDir?: string,\n global = false,\n homedir?: string\n): Promise<void> {\n const skillEntries = Object.entries(skills);\n\n if (skillEntries.length === 0) {\n logger.info(`No skills defined in ${MANIFEST_FILENAME}`);\n return;\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const lockPath = global ? getGlobalLockPath(homedir) : resolveLockfilePath(directory).path;\n const lock = readLockfile(lockPath);\n\n const currentVersionByName = new Map<string, string>();\n if (lock) {\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n const existing = currentVersionByName.get(parsed.name);\n if (!existing) {\n currentVersionByName.set(parsed.name, parsed.version);\n } else {\n const higher = resolve('*', [existing, parsed.version]);\n if (higher) currentVersionByName.set(parsed.name, higher);\n }\n }\n }\n\n const skillNames = skillEntries.map(([name]) => name);\n const allVersions = await fetchVersionsBatch(skillNames, config.registry, requestHeaders);\n\n const toUpdate: Array<{ name: string; versionRange: string }> = [];\n\n for (const [name, versionRange] of skillEntries) {\n const availableVersions = allVersions.get(name);\n if (!availableVersions) continue;\n\n const resolved = resolve(versionRange, availableVersions);\n if (!resolved) continue;\n\n const currentVersion = currentVersionByName.get(name) ?? null;\n if (resolved === currentVersion) continue;\n\n toUpdate.push({ name, versionRange });\n }\n\n if (toUpdate.length === 0) {\n logger.info('All skills up to date');\n return;\n }\n\n for (const { name, versionRange } of toUpdate) {\n await installCommand({\n name,\n versionRange,\n directory,\n configDir,\n global,\n homedir\n });\n }\n\n logger.success(`Updated ${toUpdate.length} skill${toUpdate.length === 1 ? '' : 's'}`);\n}\n\nasync function updateSingleGlobal(name: string, configDir?: string, homedir?: string): Promise<void> {\n const lockPath = getGlobalLockPath(homedir);\n const lock = readLockfileStrict(lockPath);\n\n let currentVersion: string | null = null;\n for (const key of Object.keys(lock.skills)) {\n const parsed = parseLockKey(key);\n if (!parsed) continue;\n if (parsed.name === name) {\n currentVersion = parsed.version;\n break;\n }\n }\n\n if (!currentVersion) {\n throw new Error(`Skill \"${name}\" is not installed globally (not found in ${LOCKFILE_FILENAME})`);\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const availableVersions = await fetchAvailableVersions(name, config.registry, requestHeaders);\n const versionRange = `>=${currentVersion}`;\n const resolved = resolve(versionRange, availableVersions);\n\n if (!resolved) {\n throw new Error(\n `No version of ${name} satisfies range \"${versionRange}\". Available: ${availableVersions.join(', ')}`\n );\n }\n\n if (resolved === currentVersion) {\n logger.info(`Already at latest: ${name}@${resolved}`);\n return;\n }\n\n await installCommand({\n name,\n versionRange,\n global: true,\n homedir,\n configDir\n });\n\n logger.success(`Updated ${name} to ${resolved}`);\n}\n\nasync function updateAllGlobal(configDir?: string, homedir?: string): Promise<void> {\n const lockPath = getGlobalLockPath(homedir);\n const lock = readLockfileStrict(lockPath);\n const entries = Object.keys(lock.skills);\n\n if (entries.length === 0) {\n logger.info(`No skills defined in global ${LOCKFILE_FILENAME}`);\n return;\n }\n\n const config = getConfig(configDir);\n const requestHeaders: Record<string, string> = { 'User-Agent': USER_AGENT };\n if (config.token) {\n requestHeaders.Authorization = `Bearer ${config.token}`;\n }\n\n const latestByName = deduplicateByName(entries);\n const skillNames = Array.from(latestByName.keys());\n\n const allVersions = await fetchVersionsBatch(skillNames, config.registry, requestHeaders);\n\n const toUpdate: string[] = [];\n\n for (const [name, currentVersion] of latestByName) {\n const availableVersions = allVersions.get(name);\n if (!availableVersions) continue;\n\n const resolved = resolve('*', availableVersions);\n if (!resolved) continue;\n\n if (resolved === currentVersion) continue;\n\n toUpdate.push(name);\n }\n\n if (toUpdate.length === 0) {\n logger.info('All skills up to date');\n return;\n }\n\n for (const name of toUpdate) {\n await installCommand({\n name,\n versionRange: '*',\n global: true,\n homedir,\n configDir\n });\n }\n\n logger.success(`Updated ${toUpdate.length} skill${toUpdate.length === 1 ? '' : 's'}`);\n}\n","import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { resolve } from '@internals/helpers';\nimport chalk from 'chalk';\n\nimport { USER_AGENT, VERSION } from '~/version.js';\n\nexport interface UpgradeOptions {\n version?: string;\n dryRun?: boolean;\n force?: boolean;\n}\n\nfunction isNewerVersion(candidateVersion: string, currentVersion: string): boolean {\n if (candidateVersion === currentVersion) {\n return false;\n }\n\n return resolve('*', [candidateVersion, currentVersion]) === candidateVersion;\n}\n\nfunction resolveCurrentBinary(): string {\n try {\n return fs.realpathSync(process.argv[1]);\n } catch {\n return process.execPath;\n }\n}\n\nexport async function upgradeCommand(opts?: UpgradeOptions): Promise<void> {\n const currentBinaryPath = resolveCurrentBinary();\n if (\n process.platform !== 'win32' &&\n (currentBinaryPath.includes('/Cellar/') || currentBinaryPath.includes('/homebrew/'))\n ) {\n console.log(chalk.yellow('Tank was installed via Homebrew. Run `brew upgrade tank` instead.'));\n return;\n }\n\n if (\n currentBinaryPath.includes('node_modules') ||\n currentBinaryPath.endsWith('.js') ||\n currentBinaryPath.endsWith('.mjs')\n ) {\n console.log(chalk.yellow('Tank was installed via npm/npx. Run `npm update -g @tankpkg/cli` to upgrade instead.'));\n return;\n }\n\n let targetVersion: string;\n\n if (opts?.version) {\n targetVersion = opts.version;\n } else {\n const res = await fetch('https://api.github.com/repos/tankpkg/tank/releases/latest', {\n headers: { 'User-Agent': USER_AGENT }\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch latest release: ${res.status} ${res.statusText}`);\n }\n const data = (await res.json()) as { tag_name: string };\n targetVersion = data.tag_name.replace(/^v/, '');\n }\n\n if (!isNewerVersion(targetVersion, VERSION) && !opts?.force) {\n console.log(chalk.green(`✓ Already on latest version: ${VERSION}`));\n return;\n }\n\n const platform = process.platform === 'win32' ? 'windows' : process.platform === 'darwin' ? 'darwin' : 'linux';\n const arch = process.arch === 'arm64' ? 'arm64' : 'x64';\n const binaryName = `tank-${platform}-${arch}${process.platform === 'win32' ? '.exe' : ''}`;\n\n if (opts?.dryRun) {\n console.log(`Would upgrade tank ${VERSION} → ${targetVersion}`);\n return;\n }\n\n console.log(chalk.cyan(`Upgrading tank ${VERSION} → ${targetVersion}...`));\n\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tank-upgrade-'));\n\n try {\n const binaryUrl = `https://github.com/tankpkg/tank/releases/download/v${targetVersion}/${binaryName}`;\n const tmpBin = path.join(tmpDir, binaryName);\n\n const binRes = await fetch(binaryUrl, {\n headers: { 'User-Agent': USER_AGENT }\n });\n if (!binRes.ok) {\n throw new Error(`Failed to download binary: ${binRes.status} ${binRes.statusText}`);\n }\n if (!binRes.body) {\n throw new Error('Empty response body when downloading binary');\n }\n\n const binBuffer = Buffer.from(await binRes.arrayBuffer());\n fs.writeFileSync(tmpBin, binBuffer);\n\n const sumsUrl = `https://github.com/tankpkg/tank/releases/download/v${targetVersion}/SHA256SUMS`;\n const sumsRes = await fetch(sumsUrl, {\n headers: { 'User-Agent': USER_AGENT }\n });\n if (!sumsRes.ok) {\n throw new Error(`Failed to download SHA256SUMS: ${sumsRes.status} ${sumsRes.statusText}`);\n }\n const sumsText = await sumsRes.text();\n\n let expectedHash: string | undefined;\n for (const line of sumsText.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n const parts = trimmed.split(/\\s+/);\n if (parts.length >= 2 && parts[1] === binaryName) {\n expectedHash = parts[0];\n break;\n }\n }\n if (!expectedHash) {\n throw new Error(`No checksum found for ${binaryName} in SHA256SUMS`);\n }\n\n const fileBuffer = fs.readFileSync(tmpBin);\n const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex');\n\n if (actualHash !== expectedHash) {\n console.log(chalk.red('Checksum mismatch. Aborting for security.'));\n return;\n }\n\n if (process.platform !== 'win32') {\n fs.chmodSync(tmpBin, 0o755);\n }\n\n fs.copyFileSync(tmpBin, currentBinaryPath);\n if (process.platform !== 'win32') {\n fs.chmodSync(currentBinaryPath, 0o755);\n }\n\n console.log(chalk.green(`✓ Upgraded tank ${VERSION} → ${targetVersion}`));\n console.log(chalk.gray(`Release notes: https://github.com/tankpkg/tank/releases/tag/v${targetVersion}`));\n } finally {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { LOCKFILE_FILENAME } from '@internals/schemas';\n\nimport { readLockfile } from '~/lib/lockfile.js';\nimport { logger } from '~/lib/logger.js';\n\nexport interface VerifyOptions {\n directory?: string;\n}\n\nexport interface VerifyResult {\n valid: boolean;\n mismatches: Array<{\n key: string;\n expected: string;\n actual: string | null; // null if file missing\n }>;\n}\n\n/**\n * Verify that installed skills match the lockfile.\n *\n * For each entry in skills.lock:\n * 1. Parse the skill name from the lock key\n * 2. Check that the skill directory exists in .tank/skills/\n * 3. Check that the directory is not empty\n *\n * Throws on failure (exit code 1). Prints success message on pass.\n */\nexport async function verifyCommand(options?: VerifyOptions): Promise<void> {\n const directory = options?.directory ?? process.cwd();\n\n const lock = readLockfile(directory);\n if (!lock) {\n throw new Error(`No ${LOCKFILE_FILENAME} found in ${directory}. Run: tank install`);\n }\n\n const entries = Object.entries(lock.skills);\n if (entries.length === 0) {\n logger.success('No skills to verify (lockfile is empty)');\n return;\n }\n\n const issues: string[] = [];\n\n for (const [key] of entries) {\n const skillName = parseLockKey(key);\n const skillDir = getExtractDir(directory, skillName);\n\n if (!fs.existsSync(skillDir)) {\n issues.push(`${key}: directory missing at ${skillDir}`);\n continue;\n }\n\n // Check directory is not empty\n const contents = fs.readdirSync(skillDir);\n if (contents.length === 0) {\n issues.push(`${key}: directory exists but is empty`);\n }\n }\n\n if (issues.length > 0) {\n for (const issue of issues) {\n logger.error(issue);\n }\n throw new Error(`Verification failed: ${issues.length} issue${issues.length === 1 ? '' : 's'} found`);\n }\n\n const count = entries.length;\n logger.success(`All ${count} skill${count === 1 ? '' : 's'} verified`);\n}\n\nfunction parseLockKey(key: string): string {\n const lastAt = key.lastIndexOf('@');\n if (lastAt <= 0) {\n throw new Error(`Invalid lockfile key: ${key}`);\n }\n return key.slice(0, lastAt);\n}\n\nfunction getExtractDir(projectDir: string, skillName: string): string {\n if (skillName.startsWith('@')) {\n const [scope, name] = skillName.split('/');\n return path.join(projectDir, '.tank', 'skills', scope, name);\n }\n return path.join(projectDir, '.tank', 'skills', skillName);\n}\n","import { getConfig } from '~/lib/config.js';\nimport { logger } from '~/lib/logger.js';\nimport { USER_AGENT } from '~/version.js';\n\nexport interface WhoamiOptions {\n configDir?: string;\n}\n\nexport async function whoamiCommand(options: WhoamiOptions = {}): Promise<void> {\n const { configDir } = options;\n const config = getConfig(configDir);\n\n if (!config.token) {\n logger.warn('Not logged in. Run: tank login');\n return;\n }\n\n try {\n const res = await fetch(`${config.registry}/api/v1/auth/whoami`, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${config.token}`,\n 'User-Agent': USER_AGENT\n }\n });\n\n if (res.status === 401) {\n logger.error('Token is invalid or expired. Run: tank login');\n return;\n }\n\n if (!res.ok) {\n if (config.user) {\n printUserInfo(config.user);\n logger.warn('Could not verify token with server. Run: tank login');\n } else {\n logger.error('Could not verify token. Server returned an error. Run: tank login');\n }\n process.exitCode = 1;\n return;\n }\n\n if (config.user) {\n printUserInfo(config.user);\n } else {\n logger.info('Logged in (token verified).');\n }\n } catch {\n if (config.user) {\n logger.info(`Logged in as: ${config.user.name ?? 'unknown'} (offline)`);\n logger.info(`Email: ${config.user.email ?? 'unknown'}`);\n logger.warn('Could not reach server to verify token. Run: tank login');\n } else {\n logger.error('Could not verify token. Check your network connection.');\n }\n process.exitCode = 1;\n }\n}\n\nfunction printUserInfo(user: { name: string; email: string }): void {\n logger.info(`Logged in as: ${user.name ?? 'unknown'}`);\n logger.info(`Email: ${user.email ?? 'unknown'}`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { resolve } from '@internals/helpers';\nimport chalk from 'chalk';\n\nimport { VERSION } from '~/version.js';\n\nimport { getConfigDir } from './config.js';\n\ninterface UpgradeCache {\n lastCheck: number;\n latestVersion: string;\n}\n\nfunction isNewerVersion(candidateVersion: string, currentVersion: string): boolean {\n if (candidateVersion === currentVersion) {\n return false;\n }\n\n return resolve('*', [candidateVersion, currentVersion]) === candidateVersion;\n}\n\nexport async function checkForUpgrade(configDir?: string): Promise<void> {\n try {\n if (process.env.TANK_NO_UPDATE_CHECK || process.env.CI) {\n return;\n }\n\n const cacheDir = getConfigDir(configDir);\n const cachePath = path.join(cacheDir, 'upgrade_check.json');\n\n let cache: UpgradeCache | null = null;\n try {\n const raw = fs.readFileSync(cachePath, 'utf-8');\n cache = JSON.parse(raw) as UpgradeCache;\n } catch {\n // File doesn't exist or invalid JSON — treat as stale\n }\n\n const isFresh = cache !== null && Date.now() - cache.lastCheck < 24 * 60 * 60 * 1000;\n\n if (isFresh && cache !== null) {\n if (isNewerVersion(cache.latestVersion, VERSION)) {\n console.error(\n `\\n ${chalk.cyan('ℹ')} New version available: ${chalk.gray(VERSION)} → ${chalk.green(cache.latestVersion)}`\n );\n console.error(` Run ${chalk.cyan('`tank upgrade`')} to update.\\n`);\n }\n return;\n }\n\n const res = await fetch('https://api.github.com/repos/tankpkg/tank/releases/latest', {\n headers: { 'User-Agent': `tank-cli/${VERSION}` },\n signal: AbortSignal.timeout(3000)\n });\n if (!res.ok) {\n return;\n }\n const data = (await res.json()) as { tag_name: string };\n const latestVersion = data.tag_name.replace(/^v/, '');\n\n if (!fs.existsSync(cacheDir)) {\n fs.mkdirSync(cacheDir, { recursive: true });\n }\n const newCache: UpgradeCache = { lastCheck: Date.now(), latestVersion };\n fs.writeFileSync(cachePath, `${JSON.stringify(newCache, null, 2)}\\n`);\n\n if (isNewerVersion(latestVersion, VERSION)) {\n console.error(\n `\\n ${chalk.cyan('ℹ')} New version available: ${chalk.gray(VERSION)} → ${chalk.green(latestVersion)}`\n );\n console.error(` Run ${chalk.cyan('`tank upgrade`')} to update.\\n`);\n }\n } catch {\n // Silently swallow ALL errors — background check must never cause CLI to fail\n }\n}\n","#!/usr/bin/env node\nimport { Command } from 'commander';\n\nimport { auditCommand } from '~/commands/audit.js';\nimport { doctorCommand } from '~/commands/doctor.js';\nimport { infoCommand } from '~/commands/info.js';\nimport { initCommand } from '~/commands/init.js';\nimport { installAll, installCommand } from '~/commands/install.js';\nimport { linkCommand } from '~/commands/link.js';\nimport { loginCommand } from '~/commands/login.js';\nimport { logoutCommand } from '~/commands/logout.js';\nimport { migrateCommand } from '~/commands/migrate.js';\nimport { permissionsCommand } from '~/commands/permissions.js';\nimport { publishCommand } from '~/commands/publish.js';\nimport { removeCommand } from '~/commands/remove.js';\nimport { scanCommand } from '~/commands/scan.js';\nimport { searchCommand } from '~/commands/search.js';\nimport { unlinkCommand } from '~/commands/unlink.js';\nimport { updateCommand } from '~/commands/update.js';\nimport { upgradeCommand } from '~/commands/upgrade.js';\nimport { verifyCommand } from '~/commands/verify.js';\nimport { whoamiCommand } from '~/commands/whoami.js';\nimport { flushLogs } from '~/lib/debug-logger.js';\nimport { checkForUpgrade } from '~/lib/upgrade-check.js';\nimport { VERSION } from '~/version.js';\n\nconst program = new Command();\n\nprogram.name('tank').description('Security-first package manager for AI agent skills').version(VERSION);\n\nprogram\n .command('init')\n .description('Create a new tank.json in the current directory')\n .option('-y, --yes', 'Skip prompts, use defaults')\n .option('--name <name>', 'Skill name')\n .option('--skill-version <version>', 'Skill version (default: 0.1.0)')\n .option('--description <desc>', 'Skill description')\n .option('--private', 'Make skill private')\n .option('--force', 'Overwrite existing tank.json')\n .action(\n async (opts: {\n yes?: boolean;\n name?: string;\n skillVersion?: string;\n description?: string;\n private?: boolean;\n force?: boolean;\n }) => {\n try {\n await initCommand({\n yes: opts.yes,\n name: opts.name,\n version: opts.skillVersion,\n description: opts.description,\n private: opts.private,\n force: opts.force\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Init failed: ${msg}`);\n process.exit(1);\n }\n }\n );\n\nprogram\n .command('login')\n .description('Authenticate with the Tank registry via browser')\n .action(async () => {\n try {\n await loginCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Login failed: ${msg}`);\n await flushLogs();\n process.exit(1);\n }\n await flushLogs();\n });\n\nprogram\n .command('whoami')\n .description('Show the currently logged-in user')\n .action(async () => {\n try {\n await whoamiCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('logout')\n .description('Remove authentication token from config')\n .action(async () => {\n try {\n await logoutCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Logout failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('publish')\n .alias('pub')\n .description('Pack and publish a skill to the Tank registry')\n .option('--dry-run', 'Validate and pack without uploading')\n .option('--private', 'Publish skill as private')\n .option('--visibility <mode>', 'Skill visibility (public|private)')\n .action(async (opts: { dryRun?: boolean; private?: boolean; visibility?: string }) => {\n try {\n await publishCommand({\n dryRun: opts.dryRun,\n private: opts.private,\n visibility: opts.visibility\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Publish failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('install')\n .alias('i')\n .description('Install a skill from the Tank registry, or all skills from lockfile')\n .argument('[name]', 'Skill name (e.g., @org/skill-name). Omit to install from lockfile.')\n .argument('[version-range]', 'Semver range (default: *)', '*')\n .option('-g, --global', 'Install skill globally (available to all projects)')\n .action(async (name: string | undefined, versionRange: string, opts: { global?: boolean }) => {\n try {\n if (name) {\n await installCommand({ name, versionRange, global: opts.global });\n } else {\n await installAll({ global: opts.global });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Install failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('remove')\n .aliases(['rm', 'r'])\n .description('Remove an installed skill')\n .argument('<name>', 'Skill name (e.g., @org/skill-name)')\n .option('-g, --global', 'Remove a globally installed skill')\n .action(async (name: string, opts: { global?: boolean }) => {\n try {\n await removeCommand({ name, global: opts.global });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Remove failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('update')\n .alias('up')\n .description('Update skills to latest versions within their ranges')\n .argument('[name]', 'Skill name to update (omit to update all)')\n .option('-g, --global', 'Update globally installed skills')\n .action(async (name: string | undefined, opts: { global?: boolean }) => {\n try {\n await updateCommand({ name, global: opts.global });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Update failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('verify')\n .description('Verify installed skills match the lockfile')\n .action(async () => {\n try {\n await verifyCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Verify failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('permissions')\n .alias('perms')\n .description('Display resolved permission summary for installed skills')\n .action(async () => {\n try {\n await permissionsCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Error: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('search')\n .alias('s')\n .description('Search for skills in the Tank registry')\n .argument('<query>', 'Search query')\n .action(async (query: string) => {\n try {\n await searchCommand({ query });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Search failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('info')\n .alias('show')\n .description('Show detailed information about a skill')\n .argument('<name>', 'Skill name (e.g., @org/skill-name)')\n .action(async (name: string) => {\n try {\n await infoCommand({ name });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Info failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('audit')\n .description('Display security audit results for installed skills')\n .argument('[name]', 'Skill name to audit (omit to audit all)')\n .action(async (name: string | undefined) => {\n try {\n await auditCommand({ name });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Audit failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('scan')\n .description('Scan a local skill for security issues without publishing')\n .option('-d, --directory <path>', 'Directory to scan (default: current directory)')\n .action(async (opts: { directory?: string }) => {\n try {\n await scanCommand({ directory: opts.directory });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Scan failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('link')\n .alias('ln')\n .description('Link current skill directory to AI agent directories (for development)')\n .action(async () => {\n try {\n await linkCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Link failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('unlink')\n .description('Remove skill symlinks from AI agent directories')\n .action(async () => {\n try {\n await unlinkCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Unlink failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('doctor')\n .description('Diagnose agent integration health')\n .action(async () => {\n try {\n await doctorCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Doctor failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('migrate')\n .description('Migrate skills.json → tank.json and skills.lock → tank.lock')\n .action(async () => {\n try {\n await migrateCommand();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Migration failed: ${msg}`);\n process.exit(1);\n }\n });\n\nprogram\n .command('upgrade')\n .description('Update tank to the latest version')\n .argument('[version]', 'Target version (default: latest)')\n .option('--dry-run', 'Check for updates without installing')\n .option('--force', 'Reinstall even if already on the target version')\n .action(async (version: string | undefined, opts: { dryRun?: boolean; force?: boolean }) => {\n try {\n await upgradeCommand({ version, dryRun: opts.dryRun, force: opts.force });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`Upgrade failed: ${msg}`);\n await flushLogs();\n process.exit(1);\n }\n await flushLogs();\n });\n\ncheckForUpgrade().catch(() => {});\n\nprogram.parse();\n"],"mappings":"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"}
|