@skillguard/cli 0.4.0 → 0.5.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.0
4
+
5
+ - Added `skillguard auth` command group:
6
+ - `auth login` (store key),
7
+ - `auth status` (show effective auth source),
8
+ - `auth logout` (remove local config key)
9
+ - Added auth help/man coverage in CLI help pages
10
+
3
11
  ## 0.4.0
4
12
 
5
13
  - Added `skillguard workflow` command to generate ready-to-use GitHub Actions CI gate workflow
package/README.md CHANGED
@@ -34,6 +34,12 @@ npx @skillguard/cli help scan
34
34
  npx @skillguard/cli init
35
35
  ```
36
36
 
37
+ Or via auth command:
38
+
39
+ ```bash
40
+ npx @skillguard/cli auth login
41
+ ```
42
+
37
43
  You can also provide the key via env:
38
44
 
39
45
  ```bash
@@ -72,6 +78,13 @@ npx @skillguard/cli gate ./skills
72
78
  npx @skillguard/cli limits
73
79
  ```
74
80
 
81
+ ### Auth status and logout
82
+
83
+ ```bash
84
+ npx @skillguard/cli auth status
85
+ npx @skillguard/cli auth logout
86
+ ```
87
+
75
88
  ### Generate and push workflow in one command
76
89
 
77
90
  ```bash
@@ -119,6 +132,12 @@ npx @skillguard/cli verify ./SKILL.md
119
132
  - `--api-key <key>` (optional)
120
133
  - `--no-color`
121
134
 
135
+ ### auth
136
+
137
+ - `auth login [--api-key <key>] [--base-url <url>]`
138
+ - `auth status [--json] [--api-key <key>] [--base-url <url>]`
139
+ - `auth logout`
140
+
122
141
  ### workflow
123
142
 
124
143
  - `--path <file>` (default: `.github/workflows/skillguard.yml`)
@@ -158,6 +177,8 @@ npx @skillguard/cli verify ./SKILL.md
158
177
  - `1` threshold exceeded
159
178
  - `workflow`:
160
179
  - `0` success
180
+ - `auth`:
181
+ - `0` success
161
182
  - all commands:
162
183
  - `4` usage/input/config error
163
184
  - `5` network/API/runtime external failure
package/dist/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { parseArgs } from 'node:util';
3
+ import { runAuth } from './commands/auth.js';
3
4
  import { runInit } from './commands/init.js';
4
5
  import { runGate } from './commands/gate.js';
5
6
  import { runLimits } from './commands/limits.js';
@@ -115,6 +116,15 @@ async function run() {
115
116
  baseUrl: typeof options['base-url'] === 'string' ? options['base-url'] : undefined,
116
117
  });
117
118
  }
119
+ if (command === 'auth') {
120
+ const authOptions = {
121
+ subcommand: positionals[0],
122
+ json: options.json === true,
123
+ baseUrl: typeof options['base-url'] === 'string' ? options['base-url'] : undefined,
124
+ apiKey: typeof options['api-key'] === 'string' ? options['api-key'] : undefined,
125
+ };
126
+ return await runAuth(authOptions);
127
+ }
118
128
  if (command === 'scan') {
119
129
  const pathArg = positionals[0];
120
130
  let parsedTimeout;
@@ -0,0 +1,7 @@
1
+ export interface AuthOptions {
2
+ subcommand?: string;
3
+ apiKey?: string;
4
+ baseUrl?: string;
5
+ json: boolean;
6
+ }
7
+ export declare function runAuth(options: AuthOptions): Promise<number>;
@@ -0,0 +1,103 @@
1
+ import { clearConfig, DEFAULT_BASE_URL, getConfigPath, normalizeBaseUrl, readConfig, resolveApiKey } from '../lib/config.js';
2
+ import { runInit } from './init.js';
3
+ function toAuthSubcommand(raw) {
4
+ if (!raw) {
5
+ return null;
6
+ }
7
+ const value = raw.trim().toLowerCase();
8
+ if (value === 'login')
9
+ return 'login';
10
+ if (value === 'status')
11
+ return 'status';
12
+ if (value === 'logout')
13
+ return 'logout';
14
+ return null;
15
+ }
16
+ function renderStatusHuman(input) {
17
+ const lines = [
18
+ `Auth: ${input.authenticated ? 'configured' : 'not configured'}`,
19
+ `Source: ${input.source}`,
20
+ `Base URL: ${input.baseUrl}`,
21
+ `Config file: ${input.configPath}`,
22
+ ];
23
+ if (!input.authenticated) {
24
+ lines.push('No API key found. Use "skillguard auth login" or set SKILLGUARD_API_KEY.');
25
+ }
26
+ if (input.hasEnvKey) {
27
+ lines.push('Env key detected: SKILLGUARD_API_KEY');
28
+ }
29
+ if (input.hasConfigKey) {
30
+ lines.push('Config key detected.');
31
+ }
32
+ return lines.join('\n');
33
+ }
34
+ async function runStatus(options) {
35
+ const configPath = getConfigPath();
36
+ let config = null;
37
+ try {
38
+ config = await readConfig(configPath);
39
+ }
40
+ catch (error) {
41
+ console.error(error.message);
42
+ return 4;
43
+ }
44
+ const resolvedApiKey = resolveApiKey({
45
+ apiKeyFlag: options.apiKey,
46
+ env: process.env,
47
+ config,
48
+ });
49
+ let baseUrl;
50
+ try {
51
+ baseUrl = normalizeBaseUrl(options.baseUrl || config?.apiUrl || DEFAULT_BASE_URL);
52
+ }
53
+ catch (error) {
54
+ console.error(error.message);
55
+ return 4;
56
+ }
57
+ const payload = {
58
+ authenticated: Boolean(resolvedApiKey),
59
+ source: (resolvedApiKey?.source || 'none'),
60
+ baseUrl,
61
+ configPath,
62
+ hasConfigKey: typeof config?.apiKey === 'string' && config.apiKey.trim().length > 0,
63
+ hasEnvKey: typeof process.env.SKILLGUARD_API_KEY === 'string' && process.env.SKILLGUARD_API_KEY.trim().length > 0,
64
+ };
65
+ if (options.json) {
66
+ console.log(JSON.stringify(payload, null, 2));
67
+ return 0;
68
+ }
69
+ console.log(renderStatusHuman(payload));
70
+ return 0;
71
+ }
72
+ async function runLogout() {
73
+ const configPath = getConfigPath();
74
+ try {
75
+ const removed = await clearConfig(configPath);
76
+ console.log(removed ? `Logged out. Removed ${configPath}` : `No config found at ${configPath}`);
77
+ }
78
+ catch (error) {
79
+ console.error(error.message);
80
+ return 4;
81
+ }
82
+ if (typeof process.env.SKILLGUARD_API_KEY === 'string' && process.env.SKILLGUARD_API_KEY.trim().length > 0) {
83
+ console.log('Note: SKILLGUARD_API_KEY is set in environment and will still be used by this shell.');
84
+ }
85
+ return 0;
86
+ }
87
+ export async function runAuth(options) {
88
+ const subcommand = toAuthSubcommand(options.subcommand);
89
+ if (!subcommand) {
90
+ console.error('Auth subcommand is required: login | status | logout');
91
+ return 4;
92
+ }
93
+ if (subcommand === 'login') {
94
+ return runInit({
95
+ apiKey: options.apiKey,
96
+ baseUrl: options.baseUrl,
97
+ });
98
+ }
99
+ if (subcommand === 'status') {
100
+ return runStatus(options);
101
+ }
102
+ return runLogout();
103
+ }
@@ -15,5 +15,6 @@ export interface ApiKeyResolutionInput {
15
15
  export declare function getConfigPath(home?: string): string;
16
16
  export declare function readConfig(configPath?: string): Promise<SkillguardCliConfig | null>;
17
17
  export declare function writeConfig(config: SkillguardCliConfig, configPath?: string): Promise<void>;
18
+ export declare function clearConfig(configPath?: string): Promise<boolean>;
18
19
  export declare function normalizeBaseUrl(value?: string): string;
19
20
  export declare function resolveApiKey(input: ApiKeyResolutionInput): ResolvedApiKey | null;
@@ -1,4 +1,4 @@
1
- import { mkdir, readFile, chmod, writeFile } from 'node:fs/promises';
1
+ import { access, chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
2
  import { homedir } from 'node:os';
3
3
  import { dirname, join } from 'node:path';
4
4
  export const DEFAULT_BASE_URL = 'https://skillguard.ai';
@@ -39,6 +39,25 @@ export async function writeConfig(config, configPath = getConfigPath()) {
39
39
  await writeFile(configPath, `${body}\n`, { encoding: 'utf8', mode: 0o600 });
40
40
  await chmod(configPath, 0o600);
41
41
  }
42
+ export async function clearConfig(configPath = getConfigPath()) {
43
+ try {
44
+ await access(configPath);
45
+ }
46
+ catch (error) {
47
+ const typed = error;
48
+ if (typed?.code === 'ENOENT') {
49
+ return false;
50
+ }
51
+ throw new Error('Failed to clear CLI config.');
52
+ }
53
+ try {
54
+ await rm(configPath, { force: true });
55
+ return true;
56
+ }
57
+ catch {
58
+ throw new Error('Failed to clear CLI config.');
59
+ }
60
+ }
42
61
  export function normalizeBaseUrl(value) {
43
62
  const raw = (value || DEFAULT_BASE_URL).trim();
44
63
  const withoutTrailingSlash = raw.replace(/\/+$/, '');
package/dist/lib/help.js CHANGED
@@ -8,6 +8,7 @@ function normalizeTopic(topic) {
8
8
  const normalized = topic.trim().toLowerCase();
9
9
  const aliased = COMMAND_ALIASES[normalized] || normalized;
10
10
  if (aliased === 'help' ||
11
+ aliased === 'auth' ||
11
12
  aliased === 'init' ||
12
13
  aliased === 'scan' ||
13
14
  aliased === 'gate' ||
@@ -25,6 +26,7 @@ Usage:
25
26
  skillguard <command> [options]
26
27
 
27
28
  Core commands:
29
+ auth API-key auth commands (login/status/logout)
28
30
  init Save API key in local CLI config
29
31
  scan Scan one file or recursively scan a directory
30
32
  gate CI gate mode (0 pass, 1 fail)
@@ -38,6 +40,7 @@ Help commands:
38
40
 
39
41
  Quick start:
40
42
  skillguard scan SKILL.md
43
+ skillguard auth status
41
44
  skillguard gate .
42
45
  skillguard workflow
43
46
  skillguard workflow --auto-gh-push
@@ -80,6 +83,28 @@ OPTIONS
80
83
  --api-key <key> API key to persist
81
84
  --base-url <url> API base URL (default: https://skillguard.ai)
82
85
 
86
+ EXIT CODES
87
+ 0 success
88
+ 4 usage/input/config error
89
+ 5 external runtime error`;
90
+ }
91
+ function renderAuthHelp() {
92
+ return `NAME
93
+ skillguard auth - manage CLI authentication state
94
+
95
+ SYNOPSIS
96
+ skillguard auth login [--api-key <key>] [--base-url <url>]
97
+ skillguard auth status [--json] [--api-key <key>] [--base-url <url>]
98
+ skillguard auth logout
99
+
100
+ DESCRIPTION
101
+ login Save API key in local config (interactive if key omitted)
102
+ status Show effective auth source (flag/env/config/none)
103
+ logout Remove local CLI config key
104
+
105
+ NOTES
106
+ logout cannot unset SKILLGUARD_API_KEY environment variables from parent shell.
107
+
83
108
  EXIT CODES
84
109
  0 success
85
110
  4 usage/input/config error
@@ -222,6 +247,8 @@ export function renderTopicHelp(command) {
222
247
  switch (topic) {
223
248
  case 'help':
224
249
  return renderHelpCommandHelp();
250
+ case 'auth':
251
+ return renderAuthHelp();
225
252
  case 'init':
226
253
  return renderInitHelp();
227
254
  case 'scan':
@@ -1 +1 @@
1
- export declare const CLI_VERSION = "0.4.0";
1
+ export declare const CLI_VERSION = "0.5.0";
@@ -1 +1 @@
1
- export const CLI_VERSION = '0.4.0';
1
+ export const CLI_VERSION = '0.5.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillguard/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Security scanner for AI agent skill files",
5
5
  "type": "module",
6
6
  "bin": {