@tankpkg/cli 0.4.2

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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/dist/bin/tank.d.ts +2 -0
  3. package/dist/bin/tank.js +279 -0
  4. package/dist/bin/tank.js.map +1 -0
  5. package/dist/commands/audit.d.ts +5 -0
  6. package/dist/commands/audit.js +185 -0
  7. package/dist/commands/audit.js.map +1 -0
  8. package/dist/commands/doctor.d.ts +5 -0
  9. package/dist/commands/doctor.js +164 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/info.d.ts +5 -0
  12. package/dist/commands/info.js +102 -0
  13. package/dist/commands/info.js.map +1 -0
  14. package/dist/commands/init.d.ts +1 -0
  15. package/dist/commands/init.js +92 -0
  16. package/dist/commands/init.js.map +1 -0
  17. package/dist/commands/install.d.ts +39 -0
  18. package/dist/commands/install.js +550 -0
  19. package/dist/commands/install.js.map +1 -0
  20. package/dist/commands/link.d.ts +5 -0
  21. package/dist/commands/link.js +79 -0
  22. package/dist/commands/link.js.map +1 -0
  23. package/dist/commands/login.d.ts +14 -0
  24. package/dist/commands/login.js +87 -0
  25. package/dist/commands/login.js.map +1 -0
  26. package/dist/commands/logout.d.ts +9 -0
  27. package/dist/commands/logout.js +20 -0
  28. package/dist/commands/logout.js.map +1 -0
  29. package/dist/commands/permissions.d.ts +4 -0
  30. package/dist/commands/permissions.js +199 -0
  31. package/dist/commands/permissions.js.map +1 -0
  32. package/dist/commands/publish.d.ts +25 -0
  33. package/dist/commands/publish.js +166 -0
  34. package/dist/commands/publish.js.map +1 -0
  35. package/dist/commands/remove.d.ts +7 -0
  36. package/dist/commands/remove.js +163 -0
  37. package/dist/commands/remove.js.map +1 -0
  38. package/dist/commands/search.d.ts +5 -0
  39. package/dist/commands/search.js +67 -0
  40. package/dist/commands/search.js.map +1 -0
  41. package/dist/commands/unlink.d.ts +5 -0
  42. package/dist/commands/unlink.js +42 -0
  43. package/dist/commands/unlink.js.map +1 -0
  44. package/dist/commands/update.d.ts +8 -0
  45. package/dist/commands/update.js +337 -0
  46. package/dist/commands/update.js.map +1 -0
  47. package/dist/commands/upgrade.d.ts +6 -0
  48. package/dist/commands/upgrade.js +100 -0
  49. package/dist/commands/upgrade.js.map +1 -0
  50. package/dist/commands/verify.d.ts +22 -0
  51. package/dist/commands/verify.js +63 -0
  52. package/dist/commands/verify.js.map +1 -0
  53. package/dist/commands/whoami.d.ts +4 -0
  54. package/dist/commands/whoami.js +57 -0
  55. package/dist/commands/whoami.js.map +1 -0
  56. package/dist/index.d.ts +5 -0
  57. package/dist/index.js +5 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/lib/agents.d.ts +19 -0
  60. package/dist/lib/agents.js +84 -0
  61. package/dist/lib/agents.js.map +1 -0
  62. package/dist/lib/api-client.d.ts +14 -0
  63. package/dist/lib/api-client.js +63 -0
  64. package/dist/lib/api-client.js.map +1 -0
  65. package/dist/lib/config.d.ts +29 -0
  66. package/dist/lib/config.js +66 -0
  67. package/dist/lib/config.js.map +1 -0
  68. package/dist/lib/debug-logger.d.ts +9 -0
  69. package/dist/lib/debug-logger.js +77 -0
  70. package/dist/lib/debug-logger.js.map +1 -0
  71. package/dist/lib/frontmatter.d.ts +11 -0
  72. package/dist/lib/frontmatter.js +89 -0
  73. package/dist/lib/frontmatter.js.map +1 -0
  74. package/dist/lib/linker.d.ts +45 -0
  75. package/dist/lib/linker.js +137 -0
  76. package/dist/lib/linker.js.map +1 -0
  77. package/dist/lib/links.d.ts +20 -0
  78. package/dist/lib/links.js +105 -0
  79. package/dist/lib/links.js.map +1 -0
  80. package/dist/lib/lockfile.d.ts +24 -0
  81. package/dist/lib/lockfile.js +135 -0
  82. package/dist/lib/lockfile.js.map +1 -0
  83. package/dist/lib/logger.d.ts +6 -0
  84. package/dist/lib/logger.js +8 -0
  85. package/dist/lib/logger.js.map +1 -0
  86. package/dist/lib/packer.d.ts +21 -0
  87. package/dist/lib/packer.js +210 -0
  88. package/dist/lib/packer.js.map +1 -0
  89. package/dist/lib/upgrade-check.d.ts +1 -0
  90. package/dist/lib/upgrade-check.js +52 -0
  91. package/dist/lib/upgrade-check.js.map +1 -0
  92. package/dist/version.d.ts +2 -0
  93. package/dist/version.js +4 -0
  94. package/dist/version.js.map +1 -0
  95. package/package.json +40 -0
@@ -0,0 +1,550 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import os from 'node:os';
5
+ import ora from 'ora';
6
+ import { extract } from 'tar';
7
+ import { resolve, LOCKFILE_VERSION } from '@tank/shared';
8
+ import { getConfig } from '../lib/config.js';
9
+ import { logger } from '../lib/logger.js';
10
+ import { prepareAgentSkillDir } from '../lib/frontmatter.js';
11
+ import { linkSkillToAgents } from '../lib/linker.js';
12
+ import { detectInstalledAgents, getGlobalSkillsDir, getGlobalAgentSkillsDir } from '../lib/agents.js';
13
+ import { USER_AGENT } from '../version.js';
14
+ const MAX_UNCOMPRESSED_SIZE = 100 * 1024 * 1024; // 100MB
15
+ /**
16
+ * Install a skill from the Tank registry.
17
+ *
18
+ * Flow:
19
+ * 1. Read skills.json from directory (must exist)
20
+ * 2. Fetch available versions
21
+ * 3. Resolve best version using semver
22
+ * 4. Check if already installed (skip if same version in lockfile)
23
+ * 5. Fetch version metadata + download URL
24
+ * 6. Check permission budget
25
+ * 7. Download tarball
26
+ * 8. Verify integrity (sha512)
27
+ * 9. Extract tarball safely
28
+ * 10. Update skills.json
29
+ * 11. Update skills.lock
30
+ */
31
+ export async function installCommand(options) {
32
+ const { name, versionRange = '*', directory = process.cwd(), configDir, global = false, homedir, } = options;
33
+ const config = getConfig(configDir);
34
+ const resolvedHome = homedir ?? os.homedir();
35
+ const requestHeaders = { 'User-Agent': USER_AGENT };
36
+ if (config.token) {
37
+ requestHeaders.Authorization = `Bearer ${config.token}`;
38
+ }
39
+ // 1. Read or create skills.json
40
+ const skillsJsonPath = path.join(directory, 'skills.json');
41
+ let skillsJson = { skills: {} };
42
+ if (!global) {
43
+ if (!fs.existsSync(skillsJsonPath)) {
44
+ skillsJson = { skills: {} };
45
+ fs.writeFileSync(skillsJsonPath, JSON.stringify(skillsJson, null, 2) + '\n');
46
+ logger.info('Created skills.json');
47
+ }
48
+ else {
49
+ try {
50
+ const raw = fs.readFileSync(skillsJsonPath, 'utf-8');
51
+ skillsJson = JSON.parse(raw);
52
+ }
53
+ catch {
54
+ throw new Error('Failed to read or parse skills.json');
55
+ }
56
+ }
57
+ }
58
+ // Read existing lockfile if present
59
+ const lockPath = global
60
+ ? path.join(resolvedHome, '.tank', 'skills.lock')
61
+ : path.join(directory, 'skills.lock');
62
+ let lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };
63
+ if (fs.existsSync(lockPath)) {
64
+ try {
65
+ const raw = fs.readFileSync(lockPath, 'utf-8');
66
+ lock = JSON.parse(raw);
67
+ }
68
+ catch {
69
+ // If lockfile is corrupt, start fresh
70
+ lock = { lockfileVersion: LOCKFILE_VERSION, skills: {} };
71
+ }
72
+ }
73
+ const spinner = ora('Resolving versions...').start();
74
+ // 2. Fetch available versions
75
+ const encodedName = encodeURIComponent(name);
76
+ const versionsUrl = `${config.registry}/api/v1/skills/${encodedName}/versions`;
77
+ let versionsRes;
78
+ try {
79
+ versionsRes = await fetch(versionsUrl, {
80
+ headers: requestHeaders,
81
+ });
82
+ }
83
+ catch (err) {
84
+ spinner.fail('Failed to fetch versions');
85
+ throw new Error(`Network error fetching versions: ${err instanceof Error ? err.message : String(err)}`);
86
+ }
87
+ if (!versionsRes.ok) {
88
+ spinner.fail('Failed to fetch versions');
89
+ if (versionsRes.status === 403) {
90
+ throw new Error('Token lacks required scope: skills:read');
91
+ }
92
+ if (versionsRes.status === 404) {
93
+ throw new Error(`Skill not found or no access: ${name}`);
94
+ }
95
+ const body = await versionsRes.json().catch(() => ({}));
96
+ throw new Error(body.error ?? versionsRes.statusText);
97
+ }
98
+ const versionsData = await versionsRes.json();
99
+ const availableVersions = versionsData.versions.map((v) => v.version);
100
+ // 3. Resolve best version
101
+ const resolved = resolve(versionRange, availableVersions);
102
+ if (!resolved) {
103
+ spinner.fail('Version resolution failed');
104
+ throw new Error(`No version of ${name} satisfies range "${versionRange}". Available: ${availableVersions.join(', ')}`);
105
+ }
106
+ // 4. Check if already installed
107
+ const lockKey = `${name}@${resolved}`;
108
+ if (lock.skills[lockKey]) {
109
+ spinner.stop();
110
+ logger.info(`${name}@${resolved} is already installed`);
111
+ return;
112
+ }
113
+ // 5. Fetch version metadata
114
+ spinner.text = `Fetching ${name}@${resolved}...`;
115
+ const metaUrl = `${config.registry}/api/v1/skills/${encodedName}/${resolved}`;
116
+ let metaRes;
117
+ try {
118
+ metaRes = await fetch(metaUrl, {
119
+ headers: requestHeaders,
120
+ });
121
+ }
122
+ catch (err) {
123
+ spinner.fail('Failed to fetch version metadata');
124
+ throw new Error(`Network error fetching metadata: ${err instanceof Error ? err.message : String(err)}`);
125
+ }
126
+ if (!metaRes.ok) {
127
+ spinner.fail('Failed to fetch version metadata');
128
+ if (metaRes.status === 403) {
129
+ throw new Error('Token lacks required scope: skills:read');
130
+ }
131
+ if (metaRes.status === 404) {
132
+ throw new Error(`Skill not found or no access: ${name}@${resolved}`);
133
+ }
134
+ const body = await metaRes.json().catch(() => ({}));
135
+ throw new Error(body.error ?? metaRes.statusText);
136
+ }
137
+ const metadata = await metaRes.json();
138
+ // 6. Check permission budget
139
+ const projectPermissions = global ? undefined : skillsJson.permissions;
140
+ const skillPermissions = metadata.permissions;
141
+ if (!global) {
142
+ if (!projectPermissions) {
143
+ logger.warn('No permission budget defined in skills.json. Install proceeding without permission checks.');
144
+ }
145
+ else {
146
+ checkPermissionBudget(projectPermissions, skillPermissions, name);
147
+ }
148
+ }
149
+ // 6.5. Check audit score threshold
150
+ const auditMinScore = global ? undefined : skillsJson.audit?.min_score;
151
+ if (!global && auditMinScore !== undefined) {
152
+ if (metadata.auditScore === null || metadata.auditScore === undefined) {
153
+ logger.warn(`Audit score not yet available for ${name}. Install proceeding without audit score check.`);
154
+ }
155
+ else if (metadata.auditScore < auditMinScore) {
156
+ throw new Error(`Audit score ${metadata.auditScore} for ${name} is below minimum threshold ${auditMinScore} defined in skills.json`);
157
+ }
158
+ }
159
+ // 7. Download tarball
160
+ spinner.text = `Downloading ${name}@${resolved}...`;
161
+ let downloadRes;
162
+ try {
163
+ downloadRes = await fetch(metadata.downloadUrl);
164
+ }
165
+ catch (err) {
166
+ spinner.fail('Download failed');
167
+ throw new Error(`Network error downloading tarball: ${err instanceof Error ? err.message : String(err)}`);
168
+ }
169
+ if (!downloadRes.ok) {
170
+ spinner.fail('Download failed');
171
+ throw new Error(`Failed to download tarball: ${downloadRes.status} ${downloadRes.statusText}`);
172
+ }
173
+ const tarballBuffer = Buffer.from(await downloadRes.arrayBuffer());
174
+ // 8. Verify integrity
175
+ spinner.text = 'Verifying integrity...';
176
+ const hash = crypto.createHash('sha512').update(tarballBuffer).digest('base64');
177
+ const computedIntegrity = `sha512-${hash}`;
178
+ if (computedIntegrity !== metadata.integrity) {
179
+ spinner.fail('Integrity check failed');
180
+ throw new Error(`Integrity mismatch for ${name}@${resolved}. Expected: ${metadata.integrity}, Got: ${computedIntegrity}`);
181
+ }
182
+ // 9. Extract tarball safely
183
+ spinner.text = `Extracting ${name}@${resolved}...`;
184
+ const extractDir = global
185
+ ? getGlobalExtractDir(resolvedHome, name)
186
+ : getExtractDir(directory, name);
187
+ // Create extraction directory
188
+ fs.mkdirSync(extractDir, { recursive: true });
189
+ // Extract with safety checks
190
+ await extractSafely(tarballBuffer, extractDir);
191
+ // 10. Update skills.json
192
+ if (!global) {
193
+ const skills = (skillsJson.skills ?? {});
194
+ // Use the provided range if explicit, otherwise use ^{resolved}
195
+ skills[name] = versionRange === '*' ? `^${resolved}` : versionRange;
196
+ skillsJson.skills = skills;
197
+ fs.writeFileSync(skillsJsonPath, JSON.stringify(skillsJson, null, 2) + '\n');
198
+ }
199
+ // 11. Update skills.lock
200
+ lock.skills[lockKey] = {
201
+ resolved: metadata.downloadUrl,
202
+ integrity: computedIntegrity,
203
+ permissions: skillPermissions ?? {},
204
+ audit_score: metadata.auditScore ?? null,
205
+ };
206
+ // Sort keys alphabetically
207
+ const sortedSkills = {};
208
+ for (const key of Object.keys(lock.skills).sort()) {
209
+ sortedSkills[key] = lock.skills[key];
210
+ }
211
+ lock.skills = sortedSkills;
212
+ fs.mkdirSync(path.dirname(lockPath), { recursive: true });
213
+ fs.writeFileSync(lockPath, JSON.stringify(lock, null, 2) + '\n');
214
+ // 12. Agent linking (always-on, failures are warnings)
215
+ try {
216
+ const agentSkillsBaseDir = global
217
+ ? getGlobalAgentSkillsDir(resolvedHome)
218
+ : path.join(directory, '.tank', 'agent-skills');
219
+ const agentSkillDir = prepareAgentSkillDir({
220
+ skillName: name,
221
+ extractDir,
222
+ agentSkillsBaseDir,
223
+ description: metadata.description,
224
+ });
225
+ const linkResult = linkSkillToAgents({
226
+ skillName: name,
227
+ sourceDir: agentSkillDir,
228
+ linksDir: global ? path.join(resolvedHome, '.tank') : path.join(directory, '.tank'),
229
+ source: global ? 'global' : 'local',
230
+ homedir: options.homedir,
231
+ });
232
+ const detectedAgents = detectInstalledAgents(options.homedir);
233
+ if (detectedAgents.length === 0) {
234
+ logger.warn('No agents detected for linking');
235
+ }
236
+ if (linkResult.linked.length > 0) {
237
+ logger.info(`Linked to ${linkResult.linked.length} agent(s)`);
238
+ }
239
+ if (linkResult.failed.length > 0) {
240
+ for (const f of linkResult.failed) {
241
+ logger.warn(`Failed to link to ${f.agentId}: ${f.error}`);
242
+ }
243
+ }
244
+ }
245
+ catch {
246
+ logger.warn('Agent linking skipped (non-fatal)');
247
+ }
248
+ spinner.succeed(`Installed ${name}@${resolved}`);
249
+ }
250
+ export async function installFromLockfile(options) {
251
+ const { directory = process.cwd(), configDir, global = false, homedir } = options;
252
+ const resolvedHome = homedir ?? os.homedir();
253
+ const config = getConfig(configDir);
254
+ const requestHeaders = { 'User-Agent': USER_AGENT };
255
+ if (config.token) {
256
+ requestHeaders.Authorization = `Bearer ${config.token}`;
257
+ }
258
+ const lockPath = global
259
+ ? path.join(resolvedHome, '.tank', 'skills.lock')
260
+ : path.join(directory, 'skills.lock');
261
+ if (!fs.existsSync(lockPath)) {
262
+ throw new Error(`No skills.lock found in ${directory}`);
263
+ }
264
+ let lock;
265
+ try {
266
+ const raw = fs.readFileSync(lockPath, 'utf-8');
267
+ lock = JSON.parse(raw);
268
+ }
269
+ catch {
270
+ throw new Error('Failed to read or parse skills.lock');
271
+ }
272
+ const entries = Object.entries(lock.skills);
273
+ if (entries.length === 0) {
274
+ logger.info('No skills in lockfile');
275
+ return;
276
+ }
277
+ const spinner = ora('Installing from lockfile...').start();
278
+ const skillsDir = global
279
+ ? getGlobalSkillsDir(resolvedHome)
280
+ : path.join(directory, '.tank', 'skills');
281
+ try {
282
+ for (const [key, entry] of entries) {
283
+ const skillName = parseLockKey(key);
284
+ const version = parseVersionFromLockKey(key);
285
+ spinner.text = `Installing ${key}...`;
286
+ // Fetch metadata from API to record download and get fresh signed URL
287
+ const encodedName = encodeURIComponent(skillName);
288
+ const metaUrl = `${config.registry}/api/v1/skills/${encodedName}/${version}`;
289
+ let metaRes;
290
+ try {
291
+ metaRes = await fetch(metaUrl, {
292
+ headers: requestHeaders,
293
+ });
294
+ }
295
+ catch (err) {
296
+ throw new Error(`Network error fetching ${key}: ${err instanceof Error ? err.message : String(err)}`);
297
+ }
298
+ if (!metaRes.ok) {
299
+ if (metaRes.status === 404) {
300
+ throw new Error(`Skill or version not found: ${key}`);
301
+ }
302
+ const body = await metaRes.json().catch(() => ({}));
303
+ throw new Error(`Failed to fetch ${key}: ${body.error ?? metaRes.statusText}`);
304
+ }
305
+ const metadata = await metaRes.json();
306
+ const downloadUrl = metadata.downloadUrl;
307
+ const downloadRes = await fetch(downloadUrl);
308
+ if (!downloadRes.ok) {
309
+ throw new Error(`Failed to download ${key}: ${downloadRes.status} ${downloadRes.statusText}`);
310
+ }
311
+ const tarballBuffer = Buffer.from(await downloadRes.arrayBuffer());
312
+ const hash = crypto.createHash('sha512').update(tarballBuffer).digest('base64');
313
+ const computedIntegrity = `sha512-${hash}`;
314
+ if (computedIntegrity !== entry.integrity) {
315
+ throw new Error(`Integrity mismatch for ${key}. Expected: ${entry.integrity}, Got: ${computedIntegrity}`);
316
+ }
317
+ const extractDir = global
318
+ ? getGlobalExtractDir(resolvedHome, skillName)
319
+ : getExtractDir(directory, skillName);
320
+ if (fs.existsSync(extractDir)) {
321
+ fs.rmSync(extractDir, { recursive: true, force: true });
322
+ }
323
+ fs.mkdirSync(extractDir, { recursive: true });
324
+ await extractSafely(tarballBuffer, extractDir);
325
+ if (global) {
326
+ try {
327
+ const agentSkillsBaseDir = getGlobalAgentSkillsDir(resolvedHome);
328
+ const agentSkillDir = prepareAgentSkillDir({
329
+ skillName,
330
+ extractDir,
331
+ agentSkillsBaseDir,
332
+ });
333
+ const linkResult = linkSkillToAgents({
334
+ skillName,
335
+ sourceDir: agentSkillDir,
336
+ linksDir: path.join(resolvedHome, '.tank'),
337
+ source: 'global',
338
+ homedir,
339
+ });
340
+ const detectedAgents = detectInstalledAgents(homedir);
341
+ if (detectedAgents.length === 0) {
342
+ logger.warn('No agents detected for linking');
343
+ }
344
+ if (linkResult.linked.length > 0) {
345
+ logger.info(`Linked to ${linkResult.linked.length} agent(s)`);
346
+ }
347
+ if (linkResult.failed.length > 0) {
348
+ for (const f of linkResult.failed) {
349
+ logger.warn(`Failed to link to ${f.agentId}: ${f.error}`);
350
+ }
351
+ }
352
+ }
353
+ catch {
354
+ logger.warn('Agent linking skipped (non-fatal)');
355
+ }
356
+ }
357
+ }
358
+ spinner.succeed(`Installed ${entries.length} skill${entries.length === 1 ? '' : 's'} from lockfile`);
359
+ }
360
+ catch (err) {
361
+ spinner.fail('Install from lockfile failed');
362
+ if (fs.existsSync(skillsDir)) {
363
+ fs.rmSync(skillsDir, { recursive: true, force: true });
364
+ }
365
+ throw err;
366
+ }
367
+ }
368
+ export async function installAll(options) {
369
+ const { directory = process.cwd(), configDir, global = false, homedir } = options;
370
+ const resolvedHome = homedir ?? os.homedir();
371
+ const lockPath = global
372
+ ? path.join(resolvedHome, '.tank', 'skills.lock')
373
+ : path.join(directory, 'skills.lock');
374
+ const skillsJsonPath = path.join(directory, 'skills.json');
375
+ if (fs.existsSync(lockPath)) {
376
+ return installFromLockfile({ directory, configDir, global, homedir });
377
+ }
378
+ if (global) {
379
+ logger.info('No skills.lock found — nothing to install');
380
+ return;
381
+ }
382
+ if (!fs.existsSync(skillsJsonPath)) {
383
+ logger.info('No skills.json found — nothing to install');
384
+ return;
385
+ }
386
+ let skillsJson;
387
+ try {
388
+ const raw = fs.readFileSync(skillsJsonPath, 'utf-8');
389
+ skillsJson = JSON.parse(raw);
390
+ }
391
+ catch {
392
+ throw new Error('Failed to read or parse skills.json');
393
+ }
394
+ const skills = (skillsJson.skills ?? {});
395
+ const skillEntries = Object.entries(skills);
396
+ if (skillEntries.length === 0) {
397
+ logger.info('No skills defined in skills.json');
398
+ return;
399
+ }
400
+ for (const [name, versionRange] of skillEntries) {
401
+ await installCommand({ name, versionRange, directory, configDir, global, homedir });
402
+ }
403
+ }
404
+ function parseLockKey(key) {
405
+ const lastAt = key.lastIndexOf('@');
406
+ if (lastAt <= 0) {
407
+ throw new Error(`Invalid lockfile key: ${key}`);
408
+ }
409
+ return key.slice(0, lastAt);
410
+ }
411
+ function parseVersionFromLockKey(key) {
412
+ const lastAt = key.lastIndexOf('@');
413
+ if (lastAt <= 0 || lastAt === key.length - 1) {
414
+ throw new Error(`Invalid lockfile key: ${key}`);
415
+ }
416
+ return key.slice(lastAt + 1);
417
+ }
418
+ function getExtractDir(projectDir, skillName) {
419
+ if (skillName.startsWith('@')) {
420
+ const [scope, name] = skillName.split('/');
421
+ return path.join(projectDir, '.tank', 'skills', scope, name);
422
+ }
423
+ return path.join(projectDir, '.tank', 'skills', skillName);
424
+ }
425
+ function getGlobalExtractDir(homedir, skillName) {
426
+ const globalDir = path.join(homedir, '.tank', 'skills');
427
+ if (skillName.startsWith('@')) {
428
+ const [scope, name] = skillName.split('/');
429
+ return path.join(globalDir, scope, name);
430
+ }
431
+ return path.join(globalDir, skillName);
432
+ }
433
+ /**
434
+ * Extract a tarball safely with security checks.
435
+ * Rejects: absolute paths, path traversal (..), symlinks/hardlinks.
436
+ * Enforces max uncompressed size.
437
+ */
438
+ async function extractSafely(tarball, destDir) {
439
+ // Write tarball to a temp file for extraction
440
+ const tmpTarball = path.join(destDir, '.tmp-tarball.tgz');
441
+ fs.writeFileSync(tmpTarball, tarball);
442
+ try {
443
+ await extract({
444
+ file: tmpTarball,
445
+ cwd: destDir,
446
+ // Safety: reject entries that try to escape the extraction directory
447
+ filter: (entryPath) => {
448
+ // Reject absolute paths
449
+ if (path.isAbsolute(entryPath)) {
450
+ throw new Error(`Absolute path in tarball: ${entryPath}`);
451
+ }
452
+ // Reject path traversal
453
+ if (entryPath.split('/').includes('..') || entryPath.split(path.sep).includes('..')) {
454
+ throw new Error(`Path traversal in tarball: ${entryPath}`);
455
+ }
456
+ return true;
457
+ },
458
+ onReadEntry: (entry) => {
459
+ // Reject symlinks and hardlinks
460
+ if (entry.type === 'SymbolicLink' || entry.type === 'Link') {
461
+ throw new Error(`Symlink/hardlink in tarball: ${entry.path}`);
462
+ }
463
+ },
464
+ });
465
+ }
466
+ finally {
467
+ // Clean up temp tarball
468
+ if (fs.existsSync(tmpTarball)) {
469
+ fs.unlinkSync(tmpTarball);
470
+ }
471
+ }
472
+ }
473
+ /**
474
+ * Check if a skill's permissions fit within the project's permission budget.
475
+ * Throws if any permission exceeds the budget.
476
+ */
477
+ function checkPermissionBudget(budget, skillPerms, skillName) {
478
+ if (!skillPerms)
479
+ return;
480
+ // Check subprocess
481
+ if (skillPerms.subprocess === true && budget.subprocess !== true) {
482
+ throw new Error(`Permission denied: ${skillName} requires subprocess access, but project budget does not allow it`);
483
+ }
484
+ // Check network outbound
485
+ if (skillPerms.network?.outbound && skillPerms.network.outbound.length > 0) {
486
+ const budgetDomains = budget.network?.outbound ?? [];
487
+ for (const domain of skillPerms.network.outbound) {
488
+ if (!isDomainAllowed(domain, budgetDomains)) {
489
+ throw new Error(`Permission denied: ${skillName} requests network access to "${domain}", which is not in the project's permission budget`);
490
+ }
491
+ }
492
+ }
493
+ // Check filesystem read
494
+ if (skillPerms.filesystem?.read && skillPerms.filesystem.read.length > 0) {
495
+ const budgetPaths = budget.filesystem?.read ?? [];
496
+ for (const p of skillPerms.filesystem.read) {
497
+ if (!isPathAllowed(p, budgetPaths)) {
498
+ throw new Error(`Permission denied: ${skillName} requests filesystem read access to "${p}", which is not in the project's permission budget`);
499
+ }
500
+ }
501
+ }
502
+ // Check filesystem write
503
+ if (skillPerms.filesystem?.write && skillPerms.filesystem.write.length > 0) {
504
+ const budgetPaths = budget.filesystem?.write ?? [];
505
+ for (const p of skillPerms.filesystem.write) {
506
+ if (!isPathAllowed(p, budgetPaths)) {
507
+ throw new Error(`Permission denied: ${skillName} requests filesystem write access to "${p}", which is not in the project's permission budget`);
508
+ }
509
+ }
510
+ }
511
+ }
512
+ /**
513
+ * Check if a domain is allowed by the budget's domain list.
514
+ * Supports wildcard matching: *.example.com matches sub.example.com
515
+ */
516
+ function isDomainAllowed(domain, allowedDomains) {
517
+ for (const allowed of allowedDomains) {
518
+ if (allowed === domain)
519
+ return true;
520
+ // Wildcard matching: *.example.com
521
+ if (allowed.startsWith('*.')) {
522
+ const suffix = allowed.slice(1); // .example.com
523
+ if (domain.endsWith(suffix) || domain === allowed.slice(2)) {
524
+ return true;
525
+ }
526
+ // Also match if the skill requests the same wildcard pattern
527
+ if (domain === allowed)
528
+ return true;
529
+ }
530
+ }
531
+ return false;
532
+ }
533
+ /**
534
+ * Check if a path is allowed by the budget's path list.
535
+ * Simple subset check: skill path must match one of the budget paths.
536
+ */
537
+ function isPathAllowed(requestedPath, allowedPaths) {
538
+ for (const allowed of allowedPaths) {
539
+ if (allowed === requestedPath)
540
+ return true;
541
+ // If budget allows ./src/** and skill requests ./src/foo, it's allowed
542
+ if (allowed.endsWith('/**')) {
543
+ const prefix = allowed.slice(0, -3); // ./src
544
+ if (requestedPath.startsWith(prefix))
545
+ return true;
546
+ }
547
+ }
548
+ return false;
549
+ }
550
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface LinkOptions {
2
+ directory?: string;
3
+ homedir?: string;
4
+ }
5
+ export declare function linkCommand(options?: LinkOptions): Promise<void>;
@@ -0,0 +1,79 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import { detectInstalledAgents, getGlobalAgentSkillsDir } from '../lib/agents.js';
5
+ import { linkSkillToAgents } from '../lib/linker.js';
6
+ import { prepareAgentSkillDir, hasFrontmatter } from '../lib/frontmatter.js';
7
+ import { readGlobalLinks } from '../lib/links.js';
8
+ import { logger } from '../lib/logger.js';
9
+ export async function linkCommand(options = {}) {
10
+ const workDir = options.directory ?? process.cwd();
11
+ const homedir = options.homedir ?? os.homedir();
12
+ const skillsJsonPath = path.join(workDir, 'skills.json');
13
+ if (!fs.existsSync(skillsJsonPath)) {
14
+ throw new Error('No skills.json found. Run this command from a skill directory.');
15
+ }
16
+ let skillsJson;
17
+ try {
18
+ const raw = fs.readFileSync(skillsJsonPath, 'utf-8');
19
+ skillsJson = JSON.parse(raw);
20
+ }
21
+ catch {
22
+ throw new Error('Failed to read or parse skills.json');
23
+ }
24
+ const skillName = skillsJson.name;
25
+ if (typeof skillName !== 'string' || skillName.trim().length === 0) {
26
+ throw new Error("Missing 'name' in skills.json");
27
+ }
28
+ const description = typeof skillsJson.description === 'string'
29
+ ? skillsJson.description
30
+ : undefined;
31
+ const agents = detectInstalledAgents(options.homedir);
32
+ if (agents.length === 0) {
33
+ logger.info('No AI agents detected. Skills linked to agents will be available once agents are installed.');
34
+ return;
35
+ }
36
+ const skillMdPath = path.join(workDir, 'SKILL.md');
37
+ let sourceDir = workDir;
38
+ if (fs.existsSync(skillMdPath)) {
39
+ const content = fs.readFileSync(skillMdPath, 'utf-8');
40
+ if (!hasFrontmatter(content)) {
41
+ sourceDir = prepareAgentSkillDir({
42
+ skillName,
43
+ extractDir: workDir,
44
+ agentSkillsBaseDir: getGlobalAgentSkillsDir(homedir),
45
+ description,
46
+ });
47
+ }
48
+ }
49
+ else {
50
+ sourceDir = prepareAgentSkillDir({
51
+ skillName,
52
+ extractDir: workDir,
53
+ agentSkillsBaseDir: getGlobalAgentSkillsDir(homedir),
54
+ description,
55
+ });
56
+ }
57
+ void readGlobalLinks(homedir);
58
+ const result = linkSkillToAgents({
59
+ skillName,
60
+ sourceDir,
61
+ linksDir: path.join(homedir, '.tank'),
62
+ source: 'dev',
63
+ homedir: options.homedir,
64
+ });
65
+ const agentNames = new Map(agents.map((agent) => [agent.id, agent.name]));
66
+ for (const agentId of result.linked) {
67
+ logger.success(agentNames.get(agentId) ?? agentId);
68
+ }
69
+ for (const agentId of result.skipped) {
70
+ const name = agentNames.get(agentId) ?? agentId;
71
+ logger.warn(`- ${name} (already linked)`);
72
+ }
73
+ for (const failure of result.failed) {
74
+ const name = agentNames.get(failure.agentId) ?? failure.agentId;
75
+ logger.error(`${name}: ${failure.error}`);
76
+ }
77
+ logger.success(`Linked ${skillName} to ${result.linked.length} agent(s)`);
78
+ }
79
+ //# sourceMappingURL=link.js.map