@alavida/agentpack 0.1.5 → 0.1.7

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/bin/intent.js CHANGED
@@ -1,10 +1,35 @@
1
1
  #!/usr/bin/env node
2
- // Auto-generated by @tanstack/intent setup
3
- // Exposes the intent end-user CLI for consumers of this library.
4
- // Commit this file, then add to your package.json:
5
- // "bin": { "intent": "./bin/intent.js" }
2
+ import { spawnSync } from 'node:child_process';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
8
+ const intentPackageRoot = join(packageRoot, 'node_modules', '@tanstack', 'intent');
9
+
6
10
  try {
7
- await import('@tanstack/intent/intent-library')
11
+ const packageJsonPath = join(intentPackageRoot, 'package.json');
12
+ if (!existsSync(packageJsonPath)) {
13
+ const error = new Error('@tanstack/intent is not installed');
14
+ error.code = 'MODULE_NOT_FOUND';
15
+ throw error;
16
+ }
17
+
18
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
19
+ const cliRelativePath = typeof packageJson.bin === 'string'
20
+ ? packageJson.bin
21
+ : packageJson.bin?.intent;
22
+
23
+ if (!cliRelativePath) {
24
+ throw new Error('@tanstack/intent does not expose an intent cli binary');
25
+ }
26
+
27
+ const result = spawnSync(process.execPath, [join(intentPackageRoot, cliRelativePath), ...process.argv.slice(2)], {
28
+ stdio: 'inherit',
29
+ });
30
+
31
+ if (result.error) throw result.error;
32
+ process.exit(result.status ?? 0);
8
33
  } catch (e) {
9
34
  if (e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND') {
10
35
  console.error('@tanstack/intent is not installed.')
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@alavida/agentpack",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Package-backed skills lifecycle CLI for agent skills and plugins",
5
5
  "type": "module",
6
+ "workspaces": [
7
+ "packages/*"
8
+ ],
6
9
  "bin": {
7
10
  "agentpack": "bin/agentpack.js",
8
11
  "intent": "bin/intent.js"
@@ -20,8 +23,8 @@
20
23
  "smoke:monorepo": "node scripts/smoke-monorepo.mjs",
21
24
  "docs:dev": "cd docs && mint dev",
22
25
  "changeset": "changeset",
23
- "version-packages": "changeset version",
24
- "release": "changeset publish",
26
+ "version-packages": "node scripts/version-packages.mjs",
27
+ "release": "node scripts/release.mjs",
25
28
  "intent:validate": "npx @tanstack/intent validate",
26
29
  "build:dashboard": "node scripts/build-dashboard.mjs"
27
30
  },
@@ -0,0 +1,97 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { readUserConfig } from '../../infrastructure/fs/user-config-repository.js';
4
+ import { readUserCredentials } from '../../infrastructure/fs/user-credentials-repository.js';
5
+ import { getUserNpmrcPath, parseNpmrc, readUserNpmrc } from '../../infrastructure/fs/user-npmrc-repository.js';
6
+ import { resolveRegistryConfig } from '../../domain/auth/registry-resolution.js';
7
+ import { verifyAuth } from './verify-auth.js';
8
+
9
+ function findRepoNpmrc(cwd) {
10
+ let current = cwd;
11
+
12
+ while (true) {
13
+ const npmrcPath = join(current, '.npmrc');
14
+ if (existsSync(npmrcPath)) {
15
+ return {
16
+ path: npmrcPath,
17
+ config: parseNpmrc(readFileSync(npmrcPath, 'utf-8')),
18
+ };
19
+ }
20
+
21
+ const gitPath = join(current, '.git');
22
+ if (existsSync(gitPath)) break;
23
+
24
+ const parent = join(current, '..');
25
+ if (parent === current) break;
26
+ current = parent;
27
+ }
28
+
29
+ return {
30
+ path: null,
31
+ config: {},
32
+ };
33
+ }
34
+
35
+ export async function getAuthStatus({
36
+ cwd = process.cwd(),
37
+ env = process.env,
38
+ verify = false,
39
+ } = {}) {
40
+ const config = readUserConfig({ env });
41
+ const credentials = readUserCredentials({ env });
42
+ const userNpmrc = readUserNpmrc({ env });
43
+ const repoNpmrc = findRepoNpmrc(cwd);
44
+
45
+ const resolved = resolveRegistryConfig({
46
+ scope: config.scope,
47
+ defaults: {
48
+ registry: config.registry,
49
+ verificationPackage: config.verificationPackage,
50
+ },
51
+ userNpmrc,
52
+ repoNpmrc: repoNpmrc.config,
53
+ });
54
+
55
+ const userNpmrcPath = getUserNpmrcPath({ env });
56
+ const requiredRegistryKey = `${config.scope}:registry`;
57
+ const requiredTokenKey = resolved.registry
58
+ ? `//${new URL(resolved.registry).host}/:_authToken`
59
+ : null;
60
+
61
+ const npmWired = Boolean(
62
+ userNpmrc[requiredRegistryKey]
63
+ && requiredTokenKey
64
+ && userNpmrc[requiredTokenKey]
65
+ );
66
+
67
+ const result = {
68
+ provider: config.provider,
69
+ configured: Boolean(credentials?.token && npmWired),
70
+ scope: config.scope,
71
+ registry: resolved.registry,
72
+ storage: {
73
+ mode: credentials?.token ? 'file' : 'missing',
74
+ },
75
+ npmConfig: {
76
+ path: userNpmrcPath,
77
+ wired: npmWired,
78
+ source: resolved.source,
79
+ repoOverridePath: repoNpmrc.path,
80
+ },
81
+ verification: {
82
+ status: 'not_checked',
83
+ },
84
+ };
85
+
86
+ if (!verify) {
87
+ return result;
88
+ }
89
+
90
+ result.verification = await verifyAuth({
91
+ registry: resolved.registry,
92
+ authToken: credentials?.token || null,
93
+ verificationPackage: resolved.verificationPackage,
94
+ });
95
+
96
+ return result;
97
+ }
@@ -0,0 +1,110 @@
1
+ import readline from 'node:readline/promises';
2
+ import { stdin as input, stdout as output } from 'node:process';
3
+ import { readUserConfig, writeUserConfig } from '../../infrastructure/fs/user-config-repository.js';
4
+ import { writeUserCredentials } from '../../infrastructure/fs/user-credentials-repository.js';
5
+ import { writeManagedNpmrcEntries } from '../../infrastructure/fs/user-npmrc-repository.js';
6
+ import { openBrowser } from '../../infrastructure/runtime/open-browser.js';
7
+ import { verifyAuth } from './verify-auth.js';
8
+ import { AgentpackError, EXIT_CODES } from '../../utils/errors.js';
9
+
10
+ const GITHUB_TOKEN_URL = 'https://github.com/settings/tokens';
11
+
12
+ function buildVerificationFailure(verification) {
13
+ if (verification.status === 'invalid') {
14
+ return new AgentpackError('The GitHub personal access token was rejected by GitHub Packages', {
15
+ code: 'auth_verification_failed',
16
+ exitCode: EXIT_CODES.GENERAL,
17
+ });
18
+ }
19
+
20
+ if (verification.status === 'insufficient_permissions') {
21
+ return new AgentpackError('The GitHub personal access token does not have package read access', {
22
+ code: 'auth_verification_failed',
23
+ exitCode: EXIT_CODES.GENERAL,
24
+ });
25
+ }
26
+
27
+ if (verification.status === 'unreachable') {
28
+ return new AgentpackError('GitHub Packages could not be reached during verification', {
29
+ code: 'auth_verification_failed',
30
+ exitCode: EXIT_CODES.GENERAL,
31
+ });
32
+ }
33
+
34
+ if (verification.status === 'not_configured') {
35
+ return new AgentpackError('Authentication verification is not configured for this machine', {
36
+ code: 'auth_verification_not_configured',
37
+ exitCode: EXIT_CODES.GENERAL,
38
+ });
39
+ }
40
+
41
+ return new AgentpackError('The saved credential was rejected by the configured registry', {
42
+ code: 'auth_verification_failed',
43
+ exitCode: EXIT_CODES.GENERAL,
44
+ });
45
+ }
46
+
47
+ export async function login({
48
+ env = process.env,
49
+ scope = null,
50
+ registry = null,
51
+ verificationPackage = null,
52
+ } = {}) {
53
+ const current = readUserConfig({ env });
54
+ const nextConfig = {
55
+ ...current,
56
+ scope: scope || current.scope,
57
+ registry: registry || current.registry,
58
+ verificationPackage: verificationPackage || current.verificationPackage,
59
+ };
60
+
61
+ openBrowser(GITHUB_TOKEN_URL);
62
+ output.write(`Configuring GitHub Packages auth for ${nextConfig.scope}`);
63
+ output.write('Use a GitHub personal access token with package read access.');
64
+
65
+ const rl = readline.createInterface({ input, output });
66
+ try {
67
+ const token = (await rl.question('Token: ')).trim();
68
+ if (!token) {
69
+ throw new AgentpackError('A GitHub credential is required to continue', {
70
+ code: 'auth_token_missing',
71
+ exitCode: EXIT_CODES.GENERAL,
72
+ });
73
+ }
74
+
75
+ const verification = await verifyAuth({
76
+ registry: nextConfig.registry,
77
+ authToken: token,
78
+ verificationPackage: nextConfig.verificationPackage,
79
+ });
80
+
81
+ if (verification.status !== 'valid') {
82
+ throw buildVerificationFailure(verification);
83
+ }
84
+
85
+ const managedEntries = {
86
+ [`${nextConfig.scope}:registry`]: nextConfig.registry,
87
+ [`//${new URL(nextConfig.registry).host}/:_authToken`]: token,
88
+ };
89
+
90
+ writeManagedNpmrcEntries({ entries: managedEntries, env });
91
+ writeUserCredentials({ token }, { env });
92
+ writeUserConfig({
93
+ ...nextConfig,
94
+ managedNpmKeys: Object.keys(managedEntries),
95
+ }, { env });
96
+
97
+ return {
98
+ configured: true,
99
+ provider: nextConfig.provider,
100
+ scope: nextConfig.scope,
101
+ registry: nextConfig.registry,
102
+ verificationPackage: nextConfig.verificationPackage,
103
+ storage: {
104
+ mode: 'file',
105
+ },
106
+ };
107
+ } finally {
108
+ rl.close();
109
+ }
110
+ }
@@ -0,0 +1,16 @@
1
+ import { deleteUserCredentials } from '../../infrastructure/fs/user-credentials-repository.js';
2
+ import { removeManagedNpmrcEntries } from '../../infrastructure/fs/user-npmrc-repository.js';
3
+ import { readUserConfig } from '../../infrastructure/fs/user-config-repository.js';
4
+
5
+ export function logout({ env = process.env } = {}) {
6
+ const config = readUserConfig({ env });
7
+ const keys = config.managedNpmKeys || [];
8
+
9
+ deleteUserCredentials({ env });
10
+ removeManagedNpmrcEntries({ keys, env });
11
+
12
+ return {
13
+ removedCredentials: true,
14
+ removedNpmKeys: keys.length,
15
+ };
16
+ }
@@ -0,0 +1,37 @@
1
+ export async function verifyAuth({
2
+ registry,
3
+ authToken,
4
+ verificationPackage,
5
+ } = {}) {
6
+ if (!registry || !authToken || !verificationPackage) {
7
+ return { status: 'not_configured' };
8
+ }
9
+
10
+ const url = `${registry.replace(/\/+$/, '')}/${encodeURIComponent(verificationPackage)}`;
11
+
12
+ let response;
13
+ try {
14
+ response = await fetch(url, {
15
+ headers: {
16
+ accept: 'application/json',
17
+ authorization: `Bearer ${authToken}`,
18
+ },
19
+ });
20
+ } catch {
21
+ return { status: 'unreachable' };
22
+ }
23
+
24
+ if (response.ok) {
25
+ return { status: 'valid' };
26
+ }
27
+
28
+ if (response.status === 401) {
29
+ return { status: 'invalid' };
30
+ }
31
+
32
+ if (response.status === 403) {
33
+ return { status: 'insufficient_permissions' };
34
+ }
35
+
36
+ return { status: 'unreachable' };
37
+ }
package/src/cli.js CHANGED
@@ -2,6 +2,7 @@ import { Command } from 'commander';
2
2
  import { createRequire } from 'node:module';
3
3
  import { formatError, AgentpackError, EXIT_CODES } from './utils/errors.js';
4
4
  import { output } from './utils/output.js';
5
+ import { authCommand } from './commands/auth.js';
5
6
  import { skillsCommand } from './commands/skills.js';
6
7
  import { pluginCommand } from './commands/plugin.js';
7
8
 
@@ -21,6 +22,7 @@ export function createProgram() {
21
22
  .option('--workbench <path>', 'Override workbench context (name or path)');
22
23
 
23
24
  program.addCommand(skillsCommand());
25
+ program.addCommand(authCommand());
24
26
  program.addCommand(pluginCommand());
25
27
 
26
28
  program.addHelpText('after', `
@@ -0,0 +1,78 @@
1
+ import { Command } from 'commander';
2
+ import { login } from '../application/auth/login.js';
3
+ import { logout } from '../application/auth/logout.js';
4
+ import { output } from '../utils/output.js';
5
+ import { getAuthStatus } from '../application/auth/get-auth-status.js';
6
+
7
+ export function authCommand() {
8
+ const cmd = new Command('auth')
9
+ .description('Configure and inspect package registry authentication');
10
+
11
+ cmd.addHelpText('after', `
12
+ Defaults:
13
+ Scope: @alavida-ai
14
+ Registry: https://npm.pkg.github.com
15
+ Token: GitHub personal access token with package read access
16
+ `);
17
+
18
+ cmd
19
+ .command('login')
20
+ .description('Configure GitHub Packages authentication for this machine')
21
+ .option('--scope <scope>', 'Override the package scope to configure')
22
+ .option('--registry <url>', 'Override the package registry URL')
23
+ .option('--verify-package <packageName>', 'Override the package used for live verification')
24
+ .action(async (opts, command) => {
25
+ const globalOpts = command.optsWithGlobals();
26
+ const result = await login({
27
+ scope: opts.scope || null,
28
+ registry: opts.registry || null,
29
+ verificationPackage: opts.verifyPackage || null,
30
+ });
31
+
32
+ if (globalOpts.json) {
33
+ output.json(result);
34
+ return;
35
+ }
36
+
37
+ output.write(`Configured auth for ${result.scope}`);
38
+ output.write(`Registry: ${result.registry}`);
39
+ output.write(`Storage: ${result.storage.mode}`);
40
+ });
41
+
42
+ cmd
43
+ .command('status')
44
+ .description('Show authentication status for the configured package registry')
45
+ .option('--verify', 'Check whether the stored credential works against the configured registry')
46
+ .action(async (opts, command) => {
47
+ const globalOpts = command.optsWithGlobals();
48
+ const result = await getAuthStatus({ verify: opts.verify });
49
+
50
+ if (globalOpts.json) {
51
+ output.json(result);
52
+ return;
53
+ }
54
+
55
+ output.write(`Provider: ${result.provider}`);
56
+ output.write(`Configured: ${result.configured}`);
57
+ output.write(`Storage: ${result.storage.mode}`);
58
+ output.write(`Verification: ${result.verification.status}`);
59
+ });
60
+
61
+ cmd
62
+ .command('logout')
63
+ .description('Remove configured package registry authentication')
64
+ .action((opts, command) => {
65
+ const globalOpts = command.optsWithGlobals();
66
+ const result = logout();
67
+
68
+ if (globalOpts.json) {
69
+ output.json(result);
70
+ return;
71
+ }
72
+
73
+ output.write(`Removed Credentials: ${result.removedCredentials}`);
74
+ output.write(`Removed npm Keys: ${result.removedNpmKeys}`);
75
+ });
76
+
77
+ return cmd;
78
+ }
@@ -136,6 +136,8 @@ export function skillsCommand() {
136
136
  output.write(`Outdated Skills: ${result.outdatedCount}`);
137
137
  output.write(`Deprecated Skills: ${result.deprecatedCount}`);
138
138
  output.write(`Incomplete Skills: ${result.incompleteCount}`);
139
+ output.write(`Runtime Drifted Skills: ${result.runtimeDriftCount}`);
140
+ output.write(`Orphaned Materializations: ${result.orphanedMaterializationCount}`);
139
141
  output.write(`Registry Configured: ${result.registry.configured}`);
140
142
 
141
143
  if (result.outdated.length > 0) {
@@ -170,6 +172,28 @@ export function skillsCommand() {
170
172
  }
171
173
  }
172
174
  }
175
+
176
+ if (result.runtimeDrift.length > 0) {
177
+ output.write('');
178
+ output.write('Runtime Drift:');
179
+ for (const install of result.runtimeDrift) {
180
+ output.write(`- ${install.packageName}`);
181
+ for (const issue of install.issues) {
182
+ output.write(` issue: ${issue.code}`);
183
+ output.write(` target: ${issue.target}`);
184
+ if (issue.runtimeName) output.write(` runtime: ${issue.runtimeName}`);
185
+ }
186
+ }
187
+ }
188
+
189
+ if (result.orphanedMaterializations.length > 0) {
190
+ output.write('');
191
+ output.write('Orphaned Materializations:');
192
+ for (const entry of result.orphanedMaterializations) {
193
+ output.write(`- ${entry.target}`);
194
+ output.write(` issue: ${entry.code}`);
195
+ }
196
+ }
173
197
  });
174
198
 
175
199
  cmd
@@ -280,6 +304,19 @@ export function skillsCommand() {
280
304
  return;
281
305
  }
282
306
 
307
+ if (result.kind === 'package') {
308
+ output.write(`Package: ${result.packageName}`);
309
+ if (result.packageVersion) output.write(`Version: ${result.packageVersion}`);
310
+ output.write(`Path: ${result.packagePath}`);
311
+ output.write('');
312
+ output.write('Exports:');
313
+ for (const entry of result.exports) {
314
+ output.write(`- ${entry.name}`);
315
+ output.write(` path: ${entry.skillFile}`);
316
+ }
317
+ return;
318
+ }
319
+
283
320
  output.write(`Skill: ${result.name}`);
284
321
  if (result.description) output.write(`Description: ${result.description}`);
285
322
  if (result.packageName) output.write(`Package: ${result.packageName}`);
@@ -287,6 +324,7 @@ export function skillsCommand() {
287
324
  if (result.status) output.write(`Status: ${result.status}`);
288
325
  if (result.replacement) output.write(`Replacement: ${result.replacement}`);
289
326
  if (result.message) output.write(`Message: ${result.message}`);
327
+ if (result.wraps) output.write(`Wraps: ${result.wraps}`);
290
328
  output.write(`Path: ${result.skillFile}`);
291
329
 
292
330
  output.write('');
@@ -304,6 +342,12 @@ export function skillsCommand() {
304
342
  } else {
305
343
  for (const requirement of result.requires) output.write(`- ${requirement}`);
306
344
  }
345
+
346
+ if (result.overrides?.length) {
347
+ output.write('');
348
+ output.write('Overrides:');
349
+ for (const override of result.overrides) output.write(`- ${override}`);
350
+ }
307
351
  });
308
352
 
309
353
  cmd
@@ -363,16 +407,26 @@ export function skillsCommand() {
363
407
  const result = validateSkillsUseCase(target);
364
408
 
365
409
  if (globalOpts.json) {
366
- output.json(
367
- target
368
- ? result.skills[0]
369
- : result
370
- );
410
+ output.json(target && result.count === 1 ? result.skills[0] : result);
371
411
  if (!result.valid) process.exitCode = EXIT_CODES.VALIDATION;
372
412
  return;
373
413
  }
374
414
 
375
415
  if (target) {
416
+ if (result.count > 1) {
417
+ output.write(`Validated Skills: ${result.count}`);
418
+ output.write(`Valid Skills: ${result.validCount}`);
419
+ output.write(`Invalid Skills: ${result.invalidCount}`);
420
+ for (const skill of result.skills) {
421
+ output.write('');
422
+ output.write(`- ${skill.name || skill.packageName || skill.packagePath}`);
423
+ output.write(` status: ${skill.valid ? 'valid' : 'invalid'}`);
424
+ output.write(` path: ${skill.skillFile}`);
425
+ }
426
+ if (!result.valid) process.exitCode = EXIT_CODES.VALIDATION;
427
+ return;
428
+ }
429
+
376
430
  const skill = result.skills[0];
377
431
  output.write(`Skill: ${skill.packageName || skill.packagePath}`);
378
432
  output.write(`Status: ${skill.valid ? 'valid' : 'invalid'}`);
@@ -459,6 +513,9 @@ export function skillsCommand() {
459
513
  output.write(` direct: ${install.direct}`);
460
514
  output.write(` version: ${install.packageVersion}`);
461
515
  output.write(` source: ${install.sourcePackagePath}`);
516
+ if (install.skills?.length > 0) {
517
+ output.write(` skills: ${install.skills.map((skill) => skill.name).join(', ')}`);
518
+ }
462
519
  for (const materialization of install.materializations) {
463
520
  output.write(` materialized: ${materialization.target} (${materialization.mode})`);
464
521
  }
@@ -0,0 +1,55 @@
1
+ function getScopedRegistry(config, scope) {
2
+ return config?.[`${scope}:registry`] || null;
3
+ }
4
+
5
+ function getRegistryHostKey(registry) {
6
+ if (!registry) return null;
7
+ try {
8
+ const url = new URL(registry);
9
+ return `//${url.host}/:_authToken`;
10
+ } catch {
11
+ return null;
12
+ }
13
+ }
14
+
15
+ function getAuthToken(config, registry) {
16
+ const hostKey = getRegistryHostKey(registry);
17
+ return hostKey ? (config?.[hostKey] || null) : null;
18
+ }
19
+
20
+ export function resolveRegistryConfig({
21
+ scope,
22
+ defaults = {},
23
+ userNpmrc = {},
24
+ repoNpmrc = {},
25
+ } = {}) {
26
+ const repoRegistry = getScopedRegistry(repoNpmrc, scope);
27
+ if (repoRegistry) {
28
+ return {
29
+ scope,
30
+ registry: repoRegistry,
31
+ authToken: getAuthToken(repoNpmrc, repoRegistry),
32
+ verificationPackage: defaults.verificationPackage || null,
33
+ source: 'repo',
34
+ };
35
+ }
36
+
37
+ const userRegistry = getScopedRegistry(userNpmrc, scope);
38
+ if (userRegistry) {
39
+ return {
40
+ scope,
41
+ registry: userRegistry,
42
+ authToken: getAuthToken(userNpmrc, userRegistry),
43
+ verificationPackage: defaults.verificationPackage || null,
44
+ source: 'user',
45
+ };
46
+ }
47
+
48
+ return {
49
+ scope,
50
+ registry: defaults.registry || null,
51
+ authToken: null,
52
+ verificationPackage: defaults.verificationPackage || null,
53
+ source: 'default',
54
+ };
55
+ }