@skhema/cli 0.4.2 → 0.4.4
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/document.d.ts +10 -0
- package/dist/commands/document.d.ts.map +1 -0
- package/dist/commands/document.js +122 -0
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +64 -3
- package/dist/lib/api/command-helpers.d.ts.map +1 -1
- package/dist/lib/api/command-helpers.js +2 -1
- package/dist/lib/api/passthrough.d.ts.map +1 -1
- package/dist/lib/api/passthrough.js +12 -3
- package/dist/lib/api/payload.d.ts.map +1 -1
- package/dist/lib/api/payload.js +2 -1
- 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 +65 -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.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Document commands — workspace-authored narrative artifacts (board updates,
|
|
4
|
+
* team briefs, decision briefs, custom docs). All are workspace-scoped via
|
|
5
|
+
* `--workspace` (falling back to the default set by `skhema workspace use`).
|
|
6
|
+
* The routes carry the workspace id in the path, so — unlike `workspace list`
|
|
7
|
+
* — no organizationId is forwarded; the gateway authorizes via workspace access.
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerDocumentCommands(program: Command): void;
|
|
10
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/commands/document.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAqBnC;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgK/D"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { resolveClient } from '../lib/api/client.js';
|
|
3
|
+
import { asRecord, buildBody, coerceList, readPayloadFile, resolveWorkspaceId, } from '../lib/api/command-helpers.js';
|
|
4
|
+
import { runCommand, UsageError } from '../lib/api/run-command.js';
|
|
5
|
+
import { getGlobalOptions, log, logHeader, logTable } from '../lib/output.js';
|
|
6
|
+
/** Print a single document as a label block (human output only). */
|
|
7
|
+
function printDocument(document) {
|
|
8
|
+
const d = asRecord(document);
|
|
9
|
+
logHeader(String(d.title ?? d.id ?? 'Document'));
|
|
10
|
+
log(` Id: ${chalk.cyan(String(d.id ?? '—'))}`);
|
|
11
|
+
if (d.docType)
|
|
12
|
+
log(` Type: ${String(d.docType)}`);
|
|
13
|
+
if (d.updatedAt)
|
|
14
|
+
log(` Updated: ${String(d.updatedAt)}`);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Document commands — workspace-authored narrative artifacts (board updates,
|
|
18
|
+
* team briefs, decision briefs, custom docs). All are workspace-scoped via
|
|
19
|
+
* `--workspace` (falling back to the default set by `skhema workspace use`).
|
|
20
|
+
* The routes carry the workspace id in the path, so — unlike `workspace list`
|
|
21
|
+
* — no organizationId is forwarded; the gateway authorizes via workspace access.
|
|
22
|
+
*/
|
|
23
|
+
export function registerDocumentCommands(program) {
|
|
24
|
+
const document = program.command('document').description('Document commands');
|
|
25
|
+
const withWorkspace = (cmd) => cmd.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)');
|
|
26
|
+
withWorkspace(document.command('list').description('List documents')).action(async (options) => {
|
|
27
|
+
await runCommand('document list', async () => {
|
|
28
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
29
|
+
const { client } = await resolveClient();
|
|
30
|
+
const data = await client.documents.list(workspaceId);
|
|
31
|
+
const rows = coerceList(data, 'documents');
|
|
32
|
+
if (!getGlobalOptions().json) {
|
|
33
|
+
logHeader(`Documents (${rows.length})`);
|
|
34
|
+
if (rows.length === 0)
|
|
35
|
+
log(chalk.dim(' No documents.'));
|
|
36
|
+
else
|
|
37
|
+
logTable(['Title', 'Type', 'Updated', 'Id'], rows.map((r) => {
|
|
38
|
+
const d = asRecord(r);
|
|
39
|
+
return [
|
|
40
|
+
String(d.title ?? '—'),
|
|
41
|
+
String(d.docType ?? '—'),
|
|
42
|
+
String(d.updatedAt ?? '—'),
|
|
43
|
+
String(d.id ?? '—'),
|
|
44
|
+
];
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
return data;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
withWorkspace(document.command('get <document-id>').description('Get a document by id')).action(async (documentId, options) => {
|
|
51
|
+
await runCommand('document get', async () => {
|
|
52
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
53
|
+
const { client } = await resolveClient();
|
|
54
|
+
const data = await client.documents.get(workspaceId, documentId);
|
|
55
|
+
if (!getGlobalOptions().json)
|
|
56
|
+
printDocument(asRecord(data).document ?? data);
|
|
57
|
+
return data;
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
withWorkspace(document
|
|
61
|
+
.command('create')
|
|
62
|
+
.description('Create a document')
|
|
63
|
+
.option('--title <title>', 'Document title')
|
|
64
|
+
.option('--doc-type <type>', 'Document type (board_update | team_brief | decision_brief | custom)')
|
|
65
|
+
.option('--content <content>', 'Document content')
|
|
66
|
+
.option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')).action(async (options) => {
|
|
67
|
+
await runCommand('document create', async () => {
|
|
68
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
69
|
+
const body = buildBody(readPayloadFile(options.file), {
|
|
70
|
+
title: options.title,
|
|
71
|
+
docType: options.docType,
|
|
72
|
+
content: options.content,
|
|
73
|
+
});
|
|
74
|
+
if (Object.keys(body).length === 0) {
|
|
75
|
+
throw new UsageError('Nothing to create. Pass document fields as flags or a --file payload.');
|
|
76
|
+
}
|
|
77
|
+
const { client } = await resolveClient();
|
|
78
|
+
const data = await client.documents.create(workspaceId, body);
|
|
79
|
+
if (!getGlobalOptions().json) {
|
|
80
|
+
log(chalk.green('Document created.'));
|
|
81
|
+
printDocument(asRecord(data).document ?? data);
|
|
82
|
+
}
|
|
83
|
+
return data;
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
withWorkspace(document
|
|
87
|
+
.command('update <document-id>')
|
|
88
|
+
.description('Update a document')
|
|
89
|
+
.option('--title <title>', 'Document title')
|
|
90
|
+
.option('--doc-type <type>', 'Document type (board_update | team_brief | decision_brief | custom)')
|
|
91
|
+
.option('--content <content>', 'Document content')
|
|
92
|
+
.option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')).action(async (documentId, options) => {
|
|
93
|
+
await runCommand('document update', async () => {
|
|
94
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
95
|
+
const body = buildBody(readPayloadFile(options.file), {
|
|
96
|
+
title: options.title,
|
|
97
|
+
docType: options.docType,
|
|
98
|
+
content: options.content,
|
|
99
|
+
});
|
|
100
|
+
if (Object.keys(body).length === 0) {
|
|
101
|
+
throw new UsageError('Nothing to update. Pass document fields as flags or a --file payload.');
|
|
102
|
+
}
|
|
103
|
+
const { client } = await resolveClient();
|
|
104
|
+
const data = await client.documents.update(workspaceId, documentId, body);
|
|
105
|
+
if (!getGlobalOptions().json) {
|
|
106
|
+
log(chalk.green('Document updated.'));
|
|
107
|
+
printDocument(asRecord(data).document ?? data);
|
|
108
|
+
}
|
|
109
|
+
return data;
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
withWorkspace(document.command('delete <document-id>').description('Delete a document')).action(async (documentId, options) => {
|
|
113
|
+
await runCommand('document delete', async () => {
|
|
114
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
115
|
+
const { client } = await resolveClient();
|
|
116
|
+
const data = await client.documents.delete(workspaceId, documentId);
|
|
117
|
+
if (!getGlobalOptions().json)
|
|
118
|
+
log(chalk.green(`Deleted document ${documentId}.`));
|
|
119
|
+
return data;
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
@@ -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
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/command-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAKzC;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"command-helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/api/command-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAKzC;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAuB1E;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,WAAW,CAMb;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAIhE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,EAAE,CAQlE"}
|
|
@@ -29,7 +29,8 @@ export function readPayloadFile(filePath) {
|
|
|
29
29
|
throw new UsageError(`Could not read payload file "${filePath}": ${err instanceof Error ? err.message : String(err)}`);
|
|
30
30
|
}
|
|
31
31
|
try {
|
|
32
|
-
|
|
32
|
+
// Strip a leading UTF-8 BOM so a byte-identical-looking file parses.
|
|
33
|
+
const parsed = JSON.parse(raw.replace(/^\uFEFF/, ''));
|
|
33
34
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
34
35
|
throw new Error('expected a JSON object');
|
|
35
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passthrough.d.ts","sourceRoot":"","sources":["../../../src/lib/api/passthrough.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAW7C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAQvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAyBrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,EAAE,EAAE;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACjC,GACA,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"passthrough.d.ts","sourceRoot":"","sources":["../../../src/lib/api/passthrough.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAW7C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAQvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAyBrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,EAAE,EAAE;IACF,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3C,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CACjC,GACA,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CA2C9C;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EAAE,GAAG,SAAS,GACxB,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CASzB"}
|
|
@@ -79,10 +79,19 @@ export async function resolveDataBody(raw, io) {
|
|
|
79
79
|
}
|
|
80
80
|
let parsed;
|
|
81
81
|
try {
|
|
82
|
-
|
|
82
|
+
// Strip a leading UTF-8 BOM (editors and some `echo`/redirect toolchains
|
|
83
|
+
// prepend one); JSON.parse chokes on it, which surfaced as the opaque
|
|
84
|
+
// "--data is not valid JSON" even for a well-formed @file. Mirrors curl.
|
|
85
|
+
parsed = JSON.parse(text.replace(/^\uFEFF/, ''));
|
|
83
86
|
}
|
|
84
|
-
catch {
|
|
85
|
-
|
|
87
|
+
catch (err) {
|
|
88
|
+
// Name the source so a failed @file is not mistaken for the literal form.
|
|
89
|
+
const label = raw === '-'
|
|
90
|
+
? 'from stdin'
|
|
91
|
+
: raw.startsWith('@')
|
|
92
|
+
? `file "${raw.slice(1)}"`
|
|
93
|
+
: 'value';
|
|
94
|
+
throw new UsageError(`--data ${label} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
86
95
|
}
|
|
87
96
|
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
88
97
|
throw new UsageError('--data must be a JSON object.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/lib/api/payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAI9C,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../../src/lib/api/payload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAI9C,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAezD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACnC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,WAAW,CAad"}
|
package/dist/lib/api/payload.js
CHANGED
|
@@ -22,7 +22,8 @@ export function parseJsonFile(source) {
|
|
|
22
22
|
const raw = readFileOrStdin(source);
|
|
23
23
|
let parsed;
|
|
24
24
|
try {
|
|
25
|
-
|
|
25
|
+
// Strip a leading UTF-8 BOM so a byte-identical-looking file parses.
|
|
26
|
+
parsed = JSON.parse(raw.replace(/^\uFEFF/, ''));
|
|
26
27
|
}
|
|
27
28
|
catch {
|
|
28
29
|
throw new UsageError(`Payload in ${source === '-' ? 'stdin' : source} is not valid JSON.`);
|
|
@@ -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;AAoBnC;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAgDrD"}
|
package/dist/program.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
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 { registerDocumentCommands } from './commands/document.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
|
+
import { showBanner } from './lib/banner.js';
|
|
19
|
+
import { setGlobalOptions } from './lib/output.js';
|
|
20
|
+
/**
|
|
21
|
+
* Assemble the full skhema CLI program. The runtime entry (cli.ts) and the
|
|
22
|
+
* docs reference generator both build from here, so every registered command
|
|
23
|
+
* is — structurally — part of the generated reference.
|
|
24
|
+
*/
|
|
25
|
+
export function buildProgram(version) {
|
|
26
|
+
const program = new Command();
|
|
27
|
+
program
|
|
28
|
+
.name('skhema')
|
|
29
|
+
.description('Skhema CLI — drive the Skhema Public API from your terminal, CI, or agent')
|
|
30
|
+
.version(version)
|
|
31
|
+
.option('--json', 'Output structured JSON')
|
|
32
|
+
.option('--verbose', 'Verbose output')
|
|
33
|
+
.option('--quiet', 'Suppress non-essential output')
|
|
34
|
+
.option('--api-key <key>', 'Use this API key for this invocation (highest credential precedence)')
|
|
35
|
+
.hook('preAction', (thisCommand) => {
|
|
36
|
+
const opts = thisCommand.opts();
|
|
37
|
+
setGlobalOptions({
|
|
38
|
+
json: opts.json ?? false,
|
|
39
|
+
verbose: opts.verbose ?? false,
|
|
40
|
+
quiet: opts.quiet ?? false,
|
|
41
|
+
});
|
|
42
|
+
setApiKeyFlag(opts.apiKey);
|
|
43
|
+
})
|
|
44
|
+
.action(() => {
|
|
45
|
+
// Show banner when run with no subcommand
|
|
46
|
+
showBanner(version);
|
|
47
|
+
program.help();
|
|
48
|
+
});
|
|
49
|
+
registerInitCommand(program);
|
|
50
|
+
registerAuthCommands(program);
|
|
51
|
+
registerWorkspaceCommands(program);
|
|
52
|
+
registerElementCommands(program);
|
|
53
|
+
registerLinkCommands(program);
|
|
54
|
+
registerWorkspaceExportCommand(program);
|
|
55
|
+
registerComponentCommands(program);
|
|
56
|
+
registerStrategyCommands(program);
|
|
57
|
+
registerDocumentCommands(program);
|
|
58
|
+
registerResourceCommands(program);
|
|
59
|
+
registerWebhookCommands(program);
|
|
60
|
+
registerAgentCommands(program);
|
|
61
|
+
registerApiCommands(program);
|
|
62
|
+
registerSkillsCommands(program);
|
|
63
|
+
registerContributeCommands(program);
|
|
64
|
+
return program;
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skhema/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
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.
|
|
46
|
+
"@skhema/sdk": "0.2.2",
|
|
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"
|