@skhema/cli 0.4.2 → 0.4.3
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/cli.js +3 -55
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +64 -3
- package/dist/lib/docs/reference-generator.d.ts +2 -0
- package/dist/lib/docs/reference-generator.d.ts.map +1 -0
- package/dist/lib/docs/reference-generator.js +185 -0
- package/dist/lib/skills/bundled.d.ts +16 -0
- package/dist/lib/skills/bundled.d.ts.map +1 -0
- package/dist/lib/skills/bundled.js +66 -0
- package/dist/lib/skills/installer.d.ts.map +1 -1
- package/dist/lib/skills/installer.js +2 -17
- package/dist/program.d.ts +8 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +63 -0
- package/package.json +9 -6
package/dist/cli.js
CHANGED
|
@@ -1,63 +1,11 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
1
|
import { createRequire } from 'module';
|
|
3
|
-
import { registerAgentCommands } from './commands/agent.js';
|
|
4
|
-
import { registerApiCommands } from './commands/api.js';
|
|
5
|
-
import { registerAuthCommands } from './commands/auth.js';
|
|
6
|
-
import { registerComponentCommands } from './commands/component.js';
|
|
7
|
-
import { registerContributeCommands } from './commands/contribute.js';
|
|
8
|
-
import { registerElementCommands } from './commands/element.js';
|
|
9
|
-
import { registerWorkspaceExportCommand } from './commands/export.js';
|
|
10
|
-
import { registerInitCommand } from './commands/init.js';
|
|
11
|
-
import { registerLinkCommands } from './commands/link.js';
|
|
12
|
-
import { registerResourceCommands } from './commands/resource.js';
|
|
13
|
-
import { registerSkillsCommands } from './commands/skills.js';
|
|
14
|
-
import { registerStrategyCommands } from './commands/strategy.js';
|
|
15
|
-
import { registerWebhookCommands } from './commands/webhook.js';
|
|
16
|
-
import { registerWorkspaceCommands } from './commands/workspace.js';
|
|
17
|
-
import { setApiKeyFlag } from './lib/api/credential-context.js';
|
|
18
2
|
import { isApprovedContributor } from './lib/auth/entitlements.js';
|
|
19
3
|
import { getStoredCredentials } from './lib/auth/token-store.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
4
|
+
import { outputError } from './lib/output.js';
|
|
5
|
+
import { buildProgram } from './program.js';
|
|
22
6
|
const require = createRequire(import.meta.url);
|
|
23
7
|
const pkg = require('../package.json');
|
|
24
|
-
const program =
|
|
25
|
-
program
|
|
26
|
-
.name('skhema')
|
|
27
|
-
.description('Skhema CLI — drive the Skhema Public API from your terminal, CI, or agent')
|
|
28
|
-
.version(pkg.version)
|
|
29
|
-
.option('--json', 'Output structured JSON')
|
|
30
|
-
.option('--verbose', 'Verbose output')
|
|
31
|
-
.option('--quiet', 'Suppress non-essential output')
|
|
32
|
-
.option('--api-key <key>', 'Use this API key for this invocation (highest credential precedence)')
|
|
33
|
-
.hook('preAction', (thisCommand) => {
|
|
34
|
-
const opts = thisCommand.opts();
|
|
35
|
-
setGlobalOptions({
|
|
36
|
-
json: opts.json ?? false,
|
|
37
|
-
verbose: opts.verbose ?? false,
|
|
38
|
-
quiet: opts.quiet ?? false,
|
|
39
|
-
});
|
|
40
|
-
setApiKeyFlag(opts.apiKey);
|
|
41
|
-
})
|
|
42
|
-
.action(() => {
|
|
43
|
-
// Show banner when run with no subcommand
|
|
44
|
-
showBanner(pkg.version);
|
|
45
|
-
program.help();
|
|
46
|
-
});
|
|
47
|
-
registerInitCommand(program);
|
|
48
|
-
registerAuthCommands(program);
|
|
49
|
-
registerWorkspaceCommands(program);
|
|
50
|
-
registerElementCommands(program);
|
|
51
|
-
registerLinkCommands(program);
|
|
52
|
-
registerWorkspaceExportCommand(program);
|
|
53
|
-
registerComponentCommands(program);
|
|
54
|
-
registerStrategyCommands(program);
|
|
55
|
-
registerResourceCommands(program);
|
|
56
|
-
registerWebhookCommands(program);
|
|
57
|
-
registerAgentCommands(program);
|
|
58
|
-
registerApiCommands(program);
|
|
59
|
-
registerSkillsCommands(program);
|
|
60
|
-
registerContributeCommands(program);
|
|
8
|
+
const program = buildProgram(pkg.version);
|
|
61
9
|
async function main() {
|
|
62
10
|
// Gate all contribute invocations (including --help) before Commander parses.
|
|
63
11
|
// Commander handles --help internally before preAction fires, so we check early.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAenC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgO7D"}
|
package/dist/commands/skills.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import { getGlobalOptions, log, logHeader, logTable, outputError, outputSuccess, } from '../lib/output.js';
|
|
4
|
+
import { listBundledSkills, readBundledSkill } from '../lib/skills/bundled.js';
|
|
4
5
|
import { installSkills, updateSkills } from '../lib/skills/installer.js';
|
|
6
|
+
import { readLock } from '../lib/skills/lock.js';
|
|
5
7
|
import { detectPlatforms } from '../lib/skills/platform-detect.js';
|
|
6
8
|
export function registerSkillsCommands(program) {
|
|
7
9
|
const skills = program.command('skills').description('AI skills management');
|
|
@@ -57,12 +59,71 @@ export function registerSkillsCommands(program) {
|
|
|
57
59
|
});
|
|
58
60
|
skills
|
|
59
61
|
.command('list')
|
|
60
|
-
.description('
|
|
62
|
+
.description('List the Skhema skills bundled with this CLI')
|
|
63
|
+
.action(async () => {
|
|
64
|
+
try {
|
|
65
|
+
const bundled = listBundledSkills();
|
|
66
|
+
const detected = detectPlatforms().filter((p) => p.detected);
|
|
67
|
+
const withInstalls = bundled.map((skill) => ({
|
|
68
|
+
name: skill.name,
|
|
69
|
+
description: skill.description,
|
|
70
|
+
installedOn: detected
|
|
71
|
+
.filter((p) => readLock(p).skills[skill.name] !== undefined)
|
|
72
|
+
.map((p) => p.id),
|
|
73
|
+
}));
|
|
74
|
+
if (getGlobalOptions().json) {
|
|
75
|
+
outputSuccess({ skills: withInstalls, detectedPlatforms: detected.length }, 'skills list');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
logHeader(`Skhema Skills (${withInstalls.length})`);
|
|
79
|
+
const truncate = (text, max) => text.length > max ? `${text.slice(0, max - 1)}…` : text;
|
|
80
|
+
logTable(['Name', 'Description', 'Installed'], withInstalls.map((skill) => [
|
|
81
|
+
skill.name,
|
|
82
|
+
truncate(skill.description, 60),
|
|
83
|
+
skill.installedOn.length === 0
|
|
84
|
+
? chalk.dim('not installed')
|
|
85
|
+
: `${skill.installedOn.length}/${detected.length} platforms`,
|
|
86
|
+
]));
|
|
87
|
+
log('');
|
|
88
|
+
log(chalk.dim('Run `skhema skills show <name>` to read a skill, `skhema skills platforms` for install locations.'));
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
outputError(error instanceof Error ? error.message : 'Unknown error', 'skills list');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
skills
|
|
96
|
+
.command('show <name>')
|
|
97
|
+
.description('Print a bundled skill (frontmatter and documentation)')
|
|
98
|
+
.action(async (name) => {
|
|
99
|
+
try {
|
|
100
|
+
const skill = readBundledSkill(name);
|
|
101
|
+
if (!skill) {
|
|
102
|
+
const available = listBundledSkills()
|
|
103
|
+
.map((s) => s.name)
|
|
104
|
+
.join(', ');
|
|
105
|
+
outputError(`Skill "${name}" is not bundled with this CLI. Available: ${available}`, 'skills show');
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
if (getGlobalOptions().json) {
|
|
109
|
+
outputSuccess(skill, 'skills show');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
log(skill.content);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
outputError(error instanceof Error ? error.message : 'Unknown error', 'skills show');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
skills
|
|
120
|
+
.command('platforms')
|
|
121
|
+
.description('Show detected agent platforms and their skills directories')
|
|
61
122
|
.action(async () => {
|
|
62
123
|
try {
|
|
63
124
|
const platforms = detectPlatforms();
|
|
64
125
|
if (getGlobalOptions().json) {
|
|
65
|
-
outputSuccess(platforms, 'skills
|
|
126
|
+
outputSuccess(platforms, 'skills platforms');
|
|
66
127
|
return;
|
|
67
128
|
}
|
|
68
129
|
logHeader('Agent Platforms');
|
|
@@ -74,7 +135,7 @@ export function registerSkillsCommands(program) {
|
|
|
74
135
|
logTable(['Platform', 'Status', 'Skills Directory'], rows);
|
|
75
136
|
}
|
|
76
137
|
catch (error) {
|
|
77
|
-
outputError(error instanceof Error ? error.message : 'Unknown error', 'skills
|
|
138
|
+
outputError(error instanceof Error ? error.message : 'Unknown error', 'skills platforms');
|
|
78
139
|
process.exit(1);
|
|
79
140
|
}
|
|
80
141
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference-generator.d.ts","sourceRoot":"","sources":["../../../src/lib/docs/reference-generator.ts"],"names":[],"mappings":"AA6IA,wBAAgB,oBAAoB,IAAI,MAAM,CAyE7C"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { buildProgram } from '../../program.js';
|
|
2
|
+
import { ExitCode } from '../api/run-command.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generate the public CLI reference as MDX, by walking the same commander
|
|
5
|
+
* program tree the runtime executes (src/program.ts). Deterministic and
|
|
6
|
+
* version-independent: the output changes only when commands, arguments,
|
|
7
|
+
* options, or exit codes change — never on a plain version bump.
|
|
8
|
+
*
|
|
9
|
+
* The committed copy lives at docs/cli-reference.mdx and is drift-tested
|
|
10
|
+
* (src/__tests__/cli-reference-drift.test.ts). The deployed copy is
|
|
11
|
+
* skhema-web content/docs/api/cli.mdx — sync it when this file changes.
|
|
12
|
+
*/
|
|
13
|
+
/** Human meanings for the stable exit-code contract (run-command.ts). */
|
|
14
|
+
const EXIT_CODE_MEANINGS = {
|
|
15
|
+
Ok: 'Success',
|
|
16
|
+
Error: 'Generic or API error (non-specific 4xx/5xx, unexpected failure)',
|
|
17
|
+
Usage: 'Usage error — bad or missing arguments, caught before any API call',
|
|
18
|
+
AuthRequired: 'No usable credential — authenticate first',
|
|
19
|
+
Denied: 'Permission or plan denied (HTTP 403 or 402)',
|
|
20
|
+
RateLimited: 'Rate limited (HTTP 429)',
|
|
21
|
+
};
|
|
22
|
+
/** Escape plain text for MDX prose and table cells. */
|
|
23
|
+
function esc(text) {
|
|
24
|
+
return text
|
|
25
|
+
.replace(/\\/g, '\\\\')
|
|
26
|
+
.replace(/</g, '\\<')
|
|
27
|
+
.replace(/\{/g, '\\{')
|
|
28
|
+
.replace(/\}/g, '\\}')
|
|
29
|
+
.replace(/\|/g, '\\|')
|
|
30
|
+
.replace(/\n/g, ' ');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Escape a string destined for an inline-code span INSIDE a GFM table cell.
|
|
34
|
+
* Table cell-splitting on `|` happens before inline-code parsing, so a pipe
|
|
35
|
+
* inside backticks still breaks the row (and can leak `<` into JSX position —
|
|
36
|
+
* this took down /docs/api/cli via `--data <json|@file|->`). GFM honours
|
|
37
|
+
* `\|` everywhere in a table row, including within code spans.
|
|
38
|
+
*/
|
|
39
|
+
function escCode(text) {
|
|
40
|
+
return text.replace(/\|/g, '\\|');
|
|
41
|
+
}
|
|
42
|
+
/** Render an argument the way commander's help does: <required> / [optional]. */
|
|
43
|
+
function argName(arg) {
|
|
44
|
+
const name = arg.name() + (arg.variadic ? '...' : '');
|
|
45
|
+
return arg.required ? `<${name}>` : `[${name}]`;
|
|
46
|
+
}
|
|
47
|
+
/** Full invocation path from the root program down to this command. */
|
|
48
|
+
function commandPath(cmd) {
|
|
49
|
+
const parts = [];
|
|
50
|
+
let current = cmd;
|
|
51
|
+
while (current) {
|
|
52
|
+
parts.unshift(current.name());
|
|
53
|
+
current = current.parent;
|
|
54
|
+
}
|
|
55
|
+
return parts.join(' ');
|
|
56
|
+
}
|
|
57
|
+
function usageLine(cmd) {
|
|
58
|
+
const pieces = [commandPath(cmd)];
|
|
59
|
+
const args = cmd.registeredArguments.map(argName);
|
|
60
|
+
if (args.length > 0)
|
|
61
|
+
pieces.push(...args);
|
|
62
|
+
const visibleOptions = cmd.options.filter((option) => !option.hidden);
|
|
63
|
+
if (visibleOptions.length > 0)
|
|
64
|
+
pieces.push('[options]');
|
|
65
|
+
if (cmd.commands.length > 0)
|
|
66
|
+
pieces.push('<command>');
|
|
67
|
+
return pieces.join(' ');
|
|
68
|
+
}
|
|
69
|
+
function renderDefault(option) {
|
|
70
|
+
if (option.defaultValue === undefined)
|
|
71
|
+
return '';
|
|
72
|
+
return `\`${JSON.stringify(option.defaultValue)}\``;
|
|
73
|
+
}
|
|
74
|
+
function renderOptionsTable(cmd) {
|
|
75
|
+
const visible = cmd.options.filter((option) => !option.hidden);
|
|
76
|
+
if (visible.length === 0)
|
|
77
|
+
return [];
|
|
78
|
+
const hasDefaults = visible.some((option) => option.defaultValue !== undefined);
|
|
79
|
+
const lines = [];
|
|
80
|
+
if (hasDefaults) {
|
|
81
|
+
lines.push('| Option | Description | Default |');
|
|
82
|
+
lines.push('| --- | --- | --- |');
|
|
83
|
+
for (const option of visible) {
|
|
84
|
+
lines.push(`| \`${escCode(option.flags)}\` | ${esc(option.description ?? '')} | ${renderDefault(option)} |`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
lines.push('| Option | Description |');
|
|
89
|
+
lines.push('| --- | --- |');
|
|
90
|
+
for (const option of visible) {
|
|
91
|
+
lines.push(`| \`${escCode(option.flags)}\` | ${esc(option.description ?? '')} |`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
lines.push('');
|
|
95
|
+
return lines;
|
|
96
|
+
}
|
|
97
|
+
function renderArgumentsTable(cmd) {
|
|
98
|
+
const args = cmd.registeredArguments.filter((arg) => arg.description);
|
|
99
|
+
if (args.length === 0)
|
|
100
|
+
return [];
|
|
101
|
+
const lines = ['| Argument | Description |', '| --- | --- |'];
|
|
102
|
+
for (const arg of args) {
|
|
103
|
+
lines.push(`| \`${argName(arg)}\` | ${esc(arg.description)} |`);
|
|
104
|
+
}
|
|
105
|
+
lines.push('');
|
|
106
|
+
return lines;
|
|
107
|
+
}
|
|
108
|
+
function renderCommand(cmd, depth) {
|
|
109
|
+
if (cmd.name() === 'help')
|
|
110
|
+
return [];
|
|
111
|
+
const lines = [];
|
|
112
|
+
const heading = '#'.repeat(Math.min(depth + 2, 6));
|
|
113
|
+
lines.push(`${heading} \`${commandPath(cmd)}\``);
|
|
114
|
+
lines.push('');
|
|
115
|
+
lines.push('```sh');
|
|
116
|
+
lines.push(usageLine(cmd));
|
|
117
|
+
lines.push('```');
|
|
118
|
+
lines.push('');
|
|
119
|
+
const description = cmd.description();
|
|
120
|
+
if (description) {
|
|
121
|
+
lines.push(esc(description));
|
|
122
|
+
lines.push('');
|
|
123
|
+
}
|
|
124
|
+
lines.push(...renderArgumentsTable(cmd));
|
|
125
|
+
lines.push(...renderOptionsTable(cmd));
|
|
126
|
+
for (const sub of cmd.commands) {
|
|
127
|
+
lines.push(...renderCommand(sub, depth + 1));
|
|
128
|
+
}
|
|
129
|
+
return lines;
|
|
130
|
+
}
|
|
131
|
+
export function generateCliReference() {
|
|
132
|
+
const program = buildProgram('0.0.0-reference');
|
|
133
|
+
const lines = [];
|
|
134
|
+
lines.push('---');
|
|
135
|
+
lines.push('title: CLI Reference');
|
|
136
|
+
lines.push('description: Every skhema command — usage, arguments, options, and the exit-code contract. Generated from the CLI itself.');
|
|
137
|
+
lines.push('---');
|
|
138
|
+
lines.push('');
|
|
139
|
+
lines.push('{/* AUTO-GENERATED from the @skhema/cli command tree — do not edit by hand.');
|
|
140
|
+
lines.push(' Regenerate with `npm run generate:cli-reference` in skhema-packages/skhema-cli. */}');
|
|
141
|
+
lines.push('');
|
|
142
|
+
lines.push('This page is generated from the CLI itself, so it always matches the installed command surface. For a guided tour, start with [Using the CLI](/docs/guides/using-the-cli).');
|
|
143
|
+
lines.push('');
|
|
144
|
+
lines.push('```sh');
|
|
145
|
+
lines.push('npm install -g @skhema/cli');
|
|
146
|
+
lines.push('```');
|
|
147
|
+
lines.push('');
|
|
148
|
+
lines.push('## `skhema`');
|
|
149
|
+
lines.push('');
|
|
150
|
+
lines.push('```sh');
|
|
151
|
+
lines.push('skhema [options] <command>');
|
|
152
|
+
lines.push('```');
|
|
153
|
+
lines.push('');
|
|
154
|
+
lines.push(esc(program.description()));
|
|
155
|
+
lines.push('');
|
|
156
|
+
lines.push('### Global options');
|
|
157
|
+
lines.push('');
|
|
158
|
+
lines.push('Available on every command.');
|
|
159
|
+
lines.push('');
|
|
160
|
+
lines.push('| Option | Description |');
|
|
161
|
+
lines.push('| --- | --- |');
|
|
162
|
+
for (const option of program.options.filter((o) => !o.hidden)) {
|
|
163
|
+
lines.push(`| \`${escCode(option.flags)}\` | ${esc(option.description ?? '')} |`);
|
|
164
|
+
}
|
|
165
|
+
lines.push('');
|
|
166
|
+
lines.push('### Exit codes');
|
|
167
|
+
lines.push('');
|
|
168
|
+
lines.push('Exit codes are a stable contract — scripts, CI steps, and agents can branch on them.');
|
|
169
|
+
lines.push('');
|
|
170
|
+
lines.push('| Code | Meaning |');
|
|
171
|
+
lines.push('| --- | --- |');
|
|
172
|
+
for (const [key, meaning] of Object.entries(EXIT_CODE_MEANINGS)) {
|
|
173
|
+
lines.push(`| \`${ExitCode[key]}\` | ${meaning} |`);
|
|
174
|
+
}
|
|
175
|
+
lines.push('');
|
|
176
|
+
lines.push('## Commands');
|
|
177
|
+
lines.push('');
|
|
178
|
+
for (const cmd of program.commands) {
|
|
179
|
+
lines.push(...renderCommand(cmd, 1));
|
|
180
|
+
}
|
|
181
|
+
return (lines
|
|
182
|
+
.join('\n')
|
|
183
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
184
|
+
.trimEnd() + '\n');
|
|
185
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** A skill shipped inside the @skhema/cli package (skills/, synced at build). */
|
|
2
|
+
export interface BundledSkill {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
dir: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function getBundledSkillsDir(): string;
|
|
8
|
+
/** Enumerate the bundled skills with their frontmatter metadata. */
|
|
9
|
+
export declare function listBundledSkills(): BundledSkill[];
|
|
10
|
+
/** Read a bundled skill's full SKILL.md, or null if it is not bundled. */
|
|
11
|
+
export declare function readBundledSkill(name: string): {
|
|
12
|
+
name: string;
|
|
13
|
+
description: string;
|
|
14
|
+
content: string;
|
|
15
|
+
} | null;
|
|
16
|
+
//# sourceMappingURL=bundled.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundled.d.ts","sourceRoot":"","sources":["../../../src/lib/skills/bundled.ts"],"names":[],"mappings":"AAOA,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AA6BD,oEAAoE;AACpE,wBAAgB,iBAAiB,IAAI,YAAY,EAAE,CAmBlD;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GACX;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAM/D"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
export function getBundledSkillsDir() {
|
|
7
|
+
// Navigate from dist/lib/skills/bundled.js (or src/… under tsx) to skills/
|
|
8
|
+
return path.resolve(__dirname, '..', '..', '..', 'skills');
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse the SKILL.md frontmatter block. The bundled skills use simple
|
|
12
|
+
* single-line `key: value` pairs between `---` fences.
|
|
13
|
+
*/
|
|
14
|
+
function parseFrontmatter(content) {
|
|
15
|
+
const result = {};
|
|
16
|
+
const lines = content.split('\n');
|
|
17
|
+
if (lines[0]?.trim() !== '---')
|
|
18
|
+
return result;
|
|
19
|
+
for (let i = 1; i < lines.length; i++) {
|
|
20
|
+
const line = lines[i];
|
|
21
|
+
if (line.trim() === '---')
|
|
22
|
+
break;
|
|
23
|
+
const colon = line.indexOf(':');
|
|
24
|
+
if (colon === -1)
|
|
25
|
+
continue;
|
|
26
|
+
let value = line.slice(colon + 1).trim();
|
|
27
|
+
// Unwrap YAML-style quoting ("…" or '…') around single-line values.
|
|
28
|
+
if (value.length >= 2 &&
|
|
29
|
+
((value.startsWith('"') && value.endsWith('"')) ||
|
|
30
|
+
(value.startsWith("'") && value.endsWith("'")))) {
|
|
31
|
+
value = value.slice(1, -1);
|
|
32
|
+
}
|
|
33
|
+
result[line.slice(0, colon).trim()] = value;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
/** Enumerate the bundled skills with their frontmatter metadata. */
|
|
38
|
+
export function listBundledSkills() {
|
|
39
|
+
const skillsDir = getBundledSkillsDir();
|
|
40
|
+
if (!fs.existsSync(skillsDir))
|
|
41
|
+
return [];
|
|
42
|
+
return fs
|
|
43
|
+
.readdirSync(skillsDir, { withFileTypes: true })
|
|
44
|
+
.filter((entry) => entry.isDirectory())
|
|
45
|
+
.map((entry) => {
|
|
46
|
+
const dir = path.join(skillsDir, entry.name);
|
|
47
|
+
const skillFile = path.join(dir, 'SKILL.md');
|
|
48
|
+
let description = '';
|
|
49
|
+
if (fs.existsSync(skillFile)) {
|
|
50
|
+
const meta = parseFrontmatter(fs.readFileSync(skillFile, 'utf-8'));
|
|
51
|
+
description = meta.description ?? '';
|
|
52
|
+
}
|
|
53
|
+
return { name: entry.name, description, dir };
|
|
54
|
+
})
|
|
55
|
+
.filter((skill) => fs.existsSync(path.join(skill.dir, 'SKILL.md')))
|
|
56
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
57
|
+
}
|
|
58
|
+
/** Read a bundled skill's full SKILL.md, or null if it is not bundled. */
|
|
59
|
+
export function readBundledSkill(name) {
|
|
60
|
+
const skillFile = path.join(getBundledSkillsDir(), name, 'SKILL.md');
|
|
61
|
+
if (!fs.existsSync(skillFile))
|
|
62
|
+
return null;
|
|
63
|
+
const content = fs.readFileSync(skillFile, 'utf-8');
|
|
64
|
+
const meta = parseFrontmatter(content);
|
|
65
|
+
return { name, description: meta.description ?? '', content };
|
|
66
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../../src/lib/skills/installer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../../src/lib/skills/installer.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AA+DjE,wBAAsB,aAAa,CACjC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,OAAO,CAAC,aAAa,EAAE,CAAC,CA0C1B;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,gBAAgB,EAAE,GAC5B,OAAO,CAAC,aAAa,EAAE,CAAC,CAG1B"}
|
|
@@ -1,28 +1,13 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import {
|
|
4
|
+
import { listBundledSkills } from './bundled.js';
|
|
5
5
|
import { readLock, writeLock } from './lock.js';
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
function getBundledSkillsDir() {
|
|
9
|
-
// Navigate from dist/lib/skills/installer.js to skills/
|
|
10
|
-
return path.resolve(__dirname, '..', '..', '..', 'skills');
|
|
11
|
-
}
|
|
12
6
|
function getFileChecksum(content) {
|
|
13
7
|
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
14
8
|
}
|
|
15
9
|
function getBundledSkills() {
|
|
16
|
-
|
|
17
|
-
if (!fs.existsSync(skillsDir))
|
|
18
|
-
return [];
|
|
19
|
-
return fs
|
|
20
|
-
.readdirSync(skillsDir, { withFileTypes: true })
|
|
21
|
-
.filter((entry) => entry.isDirectory())
|
|
22
|
-
.map((entry) => ({
|
|
23
|
-
name: entry.name,
|
|
24
|
-
dir: path.join(skillsDir, entry.name),
|
|
25
|
-
}));
|
|
10
|
+
return listBundledSkills();
|
|
26
11
|
}
|
|
27
12
|
function copySkillToPlatform(skillDir, skillName, platform) {
|
|
28
13
|
const targetDir = path.join(platform.skillsDir, skillName);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Assemble the full skhema CLI program. The runtime entry (cli.ts) and the
|
|
4
|
+
* docs reference generator both build from here, so every registered command
|
|
5
|
+
* is — structurally — part of the generated reference.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildProgram(version: string): Command;
|
|
8
|
+
//# sourceMappingURL=program.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAmBnC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CA+CrD"}
|
package/dist/program.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { registerAgentCommands } from './commands/agent.js';
|
|
3
|
+
import { registerApiCommands } from './commands/api.js';
|
|
4
|
+
import { registerAuthCommands } from './commands/auth.js';
|
|
5
|
+
import { registerComponentCommands } from './commands/component.js';
|
|
6
|
+
import { registerContributeCommands } from './commands/contribute.js';
|
|
7
|
+
import { registerElementCommands } from './commands/element.js';
|
|
8
|
+
import { registerWorkspaceExportCommand } from './commands/export.js';
|
|
9
|
+
import { registerInitCommand } from './commands/init.js';
|
|
10
|
+
import { registerLinkCommands } from './commands/link.js';
|
|
11
|
+
import { registerResourceCommands } from './commands/resource.js';
|
|
12
|
+
import { registerSkillsCommands } from './commands/skills.js';
|
|
13
|
+
import { registerStrategyCommands } from './commands/strategy.js';
|
|
14
|
+
import { registerWebhookCommands } from './commands/webhook.js';
|
|
15
|
+
import { registerWorkspaceCommands } from './commands/workspace.js';
|
|
16
|
+
import { setApiKeyFlag } from './lib/api/credential-context.js';
|
|
17
|
+
import { showBanner } from './lib/banner.js';
|
|
18
|
+
import { setGlobalOptions } from './lib/output.js';
|
|
19
|
+
/**
|
|
20
|
+
* Assemble the full skhema CLI program. The runtime entry (cli.ts) and the
|
|
21
|
+
* docs reference generator both build from here, so every registered command
|
|
22
|
+
* is — structurally — part of the generated reference.
|
|
23
|
+
*/
|
|
24
|
+
export function buildProgram(version) {
|
|
25
|
+
const program = new Command();
|
|
26
|
+
program
|
|
27
|
+
.name('skhema')
|
|
28
|
+
.description('Skhema CLI — drive the Skhema Public API from your terminal, CI, or agent')
|
|
29
|
+
.version(version)
|
|
30
|
+
.option('--json', 'Output structured JSON')
|
|
31
|
+
.option('--verbose', 'Verbose output')
|
|
32
|
+
.option('--quiet', 'Suppress non-essential output')
|
|
33
|
+
.option('--api-key <key>', 'Use this API key for this invocation (highest credential precedence)')
|
|
34
|
+
.hook('preAction', (thisCommand) => {
|
|
35
|
+
const opts = thisCommand.opts();
|
|
36
|
+
setGlobalOptions({
|
|
37
|
+
json: opts.json ?? false,
|
|
38
|
+
verbose: opts.verbose ?? false,
|
|
39
|
+
quiet: opts.quiet ?? false,
|
|
40
|
+
});
|
|
41
|
+
setApiKeyFlag(opts.apiKey);
|
|
42
|
+
})
|
|
43
|
+
.action(() => {
|
|
44
|
+
// Show banner when run with no subcommand
|
|
45
|
+
showBanner(version);
|
|
46
|
+
program.help();
|
|
47
|
+
});
|
|
48
|
+
registerInitCommand(program);
|
|
49
|
+
registerAuthCommands(program);
|
|
50
|
+
registerWorkspaceCommands(program);
|
|
51
|
+
registerElementCommands(program);
|
|
52
|
+
registerLinkCommands(program);
|
|
53
|
+
registerWorkspaceExportCommand(program);
|
|
54
|
+
registerComponentCommands(program);
|
|
55
|
+
registerStrategyCommands(program);
|
|
56
|
+
registerResourceCommands(program);
|
|
57
|
+
registerWebhookCommands(program);
|
|
58
|
+
registerAgentCommands(program);
|
|
59
|
+
registerApiCommands(program);
|
|
60
|
+
registerSkillsCommands(program);
|
|
61
|
+
registerContributeCommands(program);
|
|
62
|
+
return program;
|
|
63
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skhema/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Skhema CLI - Authentication and AI skills management for agent platforms",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,23 +37,26 @@
|
|
|
37
37
|
"check": "npm run typecheck && npm run lint && npm run format:check",
|
|
38
38
|
"check:fix": "npm run format && npm run lint:fix",
|
|
39
39
|
"prepublishOnly": "npm run build",
|
|
40
|
-
"postinstall": "node dist/postinstall.js || true"
|
|
40
|
+
"postinstall": "node dist/postinstall.js || true",
|
|
41
|
+
"generate:cli-reference": "tsx scripts/generate-cli-reference.ts"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"chalk": "^5.3.0",
|
|
44
|
-
"commander": "^12.0.0",
|
|
45
|
-
"ora": "^8.0.0",
|
|
46
44
|
"@skhema/agent-sdk": "0.1.4",
|
|
47
45
|
"@skhema/method": "0.3.0",
|
|
48
|
-
"@skhema/sdk": "0.2.1"
|
|
46
|
+
"@skhema/sdk": "0.2.1",
|
|
47
|
+
"chalk": "^5.3.0",
|
|
48
|
+
"commander": "^12.0.0",
|
|
49
|
+
"ora": "^8.0.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
52
|
+
"@mdx-js/mdx": "3.1.1",
|
|
51
53
|
"@skhema/linter": "2.2.0",
|
|
52
54
|
"@skhema/prettier-config": "1.0.0",
|
|
53
55
|
"@types/node": "^22.0.0",
|
|
54
56
|
"eslint": "^9.0.0",
|
|
55
57
|
"prettier": "^3.0.0",
|
|
56
58
|
"prettier-plugin-organize-imports": "^4.3.0",
|
|
59
|
+
"remark-gfm": "4.0.1",
|
|
57
60
|
"tsx": "^4.7.0",
|
|
58
61
|
"typescript": "^5.0.0",
|
|
59
62
|
"vitest": "^4.0.0"
|