@skhema/cli 0.3.1
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/README.md +163 -0
- package/bin/skhema.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +92 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +154 -0
- package/dist/commands/api.d.ts +19 -0
- package/dist/commands/api.d.ts.map +1 -0
- package/dist/commands/api.js +97 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +403 -0
- package/dist/commands/component.d.ts +3 -0
- package/dist/commands/component.d.ts.map +1 -0
- package/dist/commands/component.js +221 -0
- package/dist/commands/contribute.d.ts +3 -0
- package/dist/commands/contribute.d.ts.map +1 -0
- package/dist/commands/contribute.js +318 -0
- package/dist/commands/element.d.ts +7 -0
- package/dist/commands/element.d.ts.map +1 -0
- package/dist/commands/element.js +167 -0
- package/dist/commands/export.d.ts +9 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +42 -0
- package/dist/commands/init.d.ts +49 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +275 -0
- package/dist/commands/link.d.ts +8 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +70 -0
- package/dist/commands/resource.d.ts +3 -0
- package/dist/commands/resource.d.ts.map +1 -0
- package/dist/commands/resource.js +88 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +113 -0
- package/dist/commands/strategy.d.ts +3 -0
- package/dist/commands/strategy.d.ts.map +1 -0
- package/dist/commands/strategy.js +143 -0
- package/dist/commands/webhook.d.ts +3 -0
- package/dist/commands/webhook.d.ts.map +1 -0
- package/dist/commands/webhook.js +77 -0
- package/dist/commands/workspace.d.ts +8 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +219 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api/client.d.ts +27 -0
- package/dist/lib/api/client.d.ts.map +1 -0
- package/dist/lib/api/client.js +71 -0
- package/dist/lib/api/command-helpers.d.ts +27 -0
- package/dist/lib/api/command-helpers.d.ts.map +1 -0
- package/dist/lib/api/command-helpers.js +77 -0
- package/dist/lib/api/credential-context.d.ts +3 -0
- package/dist/lib/api/credential-context.d.ts.map +1 -0
- package/dist/lib/api/credential-context.js +13 -0
- package/dist/lib/api/export-cli.d.ts +14 -0
- package/dist/lib/api/export-cli.d.ts.map +1 -0
- package/dist/lib/api/export-cli.js +26 -0
- package/dist/lib/api/formats.d.ts +12 -0
- package/dist/lib/api/formats.d.ts.map +1 -0
- package/dist/lib/api/formats.js +18 -0
- package/dist/lib/api/passthrough.d.ts +34 -0
- package/dist/lib/api/passthrough.d.ts.map +1 -0
- package/dist/lib/api/passthrough.js +106 -0
- package/dist/lib/api/payload.d.ts +16 -0
- package/dist/lib/api/payload.d.ts.map +1 -0
- package/dist/lib/api/payload.js +53 -0
- package/dist/lib/api/run-command.d.ts +69 -0
- package/dist/lib/api/run-command.d.ts.map +1 -0
- package/dist/lib/api/run-command.js +127 -0
- package/dist/lib/api/scope.d.ts +8 -0
- package/dist/lib/api/scope.d.ts.map +1 -0
- package/dist/lib/api/scope.js +16 -0
- package/dist/lib/api/transfer.d.ts +57 -0
- package/dist/lib/api/transfer.d.ts.map +1 -0
- package/dist/lib/api/transfer.js +153 -0
- package/dist/lib/auth/api-key-admin.d.ts +39 -0
- package/dist/lib/auth/api-key-admin.d.ts.map +1 -0
- package/dist/lib/auth/api-key-admin.js +66 -0
- package/dist/lib/auth/api-key-store.d.ts +8 -0
- package/dist/lib/auth/api-key-store.d.ts.map +1 -0
- package/dist/lib/auth/api-key-store.js +109 -0
- package/dist/lib/auth/device-flow.d.ts +4 -0
- package/dist/lib/auth/device-flow.d.ts.map +1 -0
- package/dist/lib/auth/device-flow.js +109 -0
- package/dist/lib/auth/entitlements.d.ts +16 -0
- package/dist/lib/auth/entitlements.d.ts.map +1 -0
- package/dist/lib/auth/entitlements.js +89 -0
- package/dist/lib/auth/localhost-flow.d.ts +4 -0
- package/dist/lib/auth/localhost-flow.d.ts.map +1 -0
- package/dist/lib/auth/localhost-flow.js +134 -0
- package/dist/lib/auth/oauth-client.d.ts +33 -0
- package/dist/lib/auth/oauth-client.d.ts.map +1 -0
- package/dist/lib/auth/oauth-client.js +96 -0
- package/dist/lib/auth/token-store.d.ts +6 -0
- package/dist/lib/auth/token-store.d.ts.map +1 -0
- package/dist/lib/auth/token-store.js +136 -0
- package/dist/lib/auth/types.d.ts +49 -0
- package/dist/lib/auth/types.d.ts.map +1 -0
- package/dist/lib/auth/types.js +1 -0
- package/dist/lib/banner.d.ts +2 -0
- package/dist/lib/banner.d.ts.map +1 -0
- package/dist/lib/banner.js +82 -0
- package/dist/lib/config-store.d.ts +23 -0
- package/dist/lib/config-store.d.ts.map +1 -0
- package/dist/lib/config-store.js +53 -0
- package/dist/lib/config.d.ts +20 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +33 -0
- package/dist/lib/contribute/author-resolver.d.ts +13 -0
- package/dist/lib/contribute/author-resolver.d.ts.map +1 -0
- package/dist/lib/contribute/author-resolver.js +63 -0
- package/dist/lib/contribute/embed-generator.d.ts +4 -0
- package/dist/lib/contribute/embed-generator.d.ts.map +1 -0
- package/dist/lib/contribute/embed-generator.js +108 -0
- package/dist/lib/contribute/link-generator.d.ts +11 -0
- package/dist/lib/contribute/link-generator.d.ts.map +1 -0
- package/dist/lib/contribute/link-generator.js +119 -0
- package/dist/lib/contribute/method-schema.d.ts +239 -0
- package/dist/lib/contribute/method-schema.d.ts.map +1 -0
- package/dist/lib/contribute/method-schema.js +172 -0
- package/dist/lib/contribute/profile-resolver.d.ts +2 -0
- package/dist/lib/contribute/profile-resolver.d.ts.map +1 -0
- package/dist/lib/contribute/profile-resolver.js +25 -0
- package/dist/lib/contribute/types.d.ts +91 -0
- package/dist/lib/contribute/types.d.ts.map +1 -0
- package/dist/lib/contribute/types.js +1 -0
- package/dist/lib/mcp/registrar.d.ts +26 -0
- package/dist/lib/mcp/registrar.d.ts.map +1 -0
- package/dist/lib/mcp/registrar.js +69 -0
- package/dist/lib/mcp/registry.d.ts +54 -0
- package/dist/lib/mcp/registry.d.ts.map +1 -0
- package/dist/lib/mcp/registry.js +105 -0
- package/dist/lib/open.d.ts +2 -0
- package/dist/lib/open.d.ts.map +1 -0
- package/dist/lib/open.js +24 -0
- package/dist/lib/output.d.ts +25 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +74 -0
- package/dist/lib/skills/installer.d.ts +4 -0
- package/dist/lib/skills/installer.d.ts.map +1 -0
- package/dist/lib/skills/installer.js +100 -0
- package/dist/lib/skills/lock.d.ts +4 -0
- package/dist/lib/skills/lock.d.ts.map +1 -0
- package/dist/lib/skills/lock.js +28 -0
- package/dist/lib/skills/platform-detect.d.ts +4 -0
- package/dist/lib/skills/platform-detect.d.ts.map +1 -0
- package/dist/lib/skills/platform-detect.js +89 -0
- package/dist/lib/skills/types.d.ts +29 -0
- package/dist/lib/skills/types.d.ts.map +1 -0
- package/dist/lib/skills/types.js +1 -0
- package/dist/postinstall.d.ts +2 -0
- package/dist/postinstall.d.ts.map +1 -0
- package/dist/postinstall.js +11 -0
- package/package.json +67 -0
- package/skills/.manifest.json +15 -0
- package/skills/skhema-challenge-framing/SKILL.md +28 -0
- package/skills/skhema-coherence-check/SKILL.md +28 -0
- package/skills/skhema-element-decomposition/SKILL.md +28 -0
- package/skills/skhema-element-writer/SKILL.md +20 -0
- package/skills/skhema-judgment-audit/SKILL.md +28 -0
- package/skills/skhema-semantic-sharpening/SKILL.md +28 -0
- package/skills/skhema-strategy-advisor/SKILL.md +20 -0
- package/skills/skhema-workspace-navigator/SKILL.md +20 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { isApprovedContributor } from '../lib/auth/entitlements.js';
|
|
4
|
+
import { getStoredCredentials } from '../lib/auth/token-store.js';
|
|
5
|
+
import { resolveComponentAuthor, resolveElementAuthor, } from '../lib/contribute/author-resolver.js';
|
|
6
|
+
import { generateComponentEmbed, generateEmbed, } from '../lib/contribute/embed-generator.js';
|
|
7
|
+
import { generateSaveComponentLink, generateSaveElementLink, publishAndGenerateComponentShareLink, publishAndGenerateElementShareLink, } from '../lib/contribute/link-generator.js';
|
|
8
|
+
import { getGlobalOptions, log, outputError, outputSuccess, } from '../lib/output.js';
|
|
9
|
+
function parseElementArgs(elementArgs) {
|
|
10
|
+
return elementArgs.map((arg) => {
|
|
11
|
+
const colonIndex = arg.indexOf(':');
|
|
12
|
+
if (colonIndex === -1) {
|
|
13
|
+
throw new Error(`Invalid element format "${arg}". Expected "element_type:content text"`);
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
elementType: arg.substring(0, colonIndex),
|
|
17
|
+
content: arg.substring(colonIndex + 1),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function loadElementsFromFile(filePath) {
|
|
22
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
23
|
+
const parsed = JSON.parse(raw);
|
|
24
|
+
if (!Array.isArray(parsed)) {
|
|
25
|
+
throw new Error('--from-file must contain a JSON array');
|
|
26
|
+
}
|
|
27
|
+
for (const item of parsed) {
|
|
28
|
+
if (typeof item.elementType !== 'string' ||
|
|
29
|
+
typeof item.content !== 'string') {
|
|
30
|
+
throw new Error('Each element in --from-file must have "elementType" and "content" string fields');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return parsed;
|
|
34
|
+
}
|
|
35
|
+
async function requireContributor() {
|
|
36
|
+
const approved = await isApprovedContributor();
|
|
37
|
+
if (!approved) {
|
|
38
|
+
const credentials = await getStoredCredentials();
|
|
39
|
+
if (!credentials?.access_token) {
|
|
40
|
+
outputError('Authentication required. Run "skhema auth login" first.', 'contribute');
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
outputError('Contributor access required. Apply at https://skhema.com/contribute', 'contribute');
|
|
44
|
+
}
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function registerContributeCommands(program) {
|
|
49
|
+
const contribute = program
|
|
50
|
+
.command('contribute')
|
|
51
|
+
.description('Contributor tools — requires approved contributor status')
|
|
52
|
+
.hook('preAction', requireContributor);
|
|
53
|
+
const generate = contribute
|
|
54
|
+
.command('generate')
|
|
55
|
+
.description('Generate embed snippets and shareable links');
|
|
56
|
+
generate
|
|
57
|
+
.command('embed')
|
|
58
|
+
.description('Generate an HTML embed snippet for an element or component')
|
|
59
|
+
.option('--element-type <type>', 'Element type (e.g. key_challenge)')
|
|
60
|
+
.option('--content <text>', 'Element content text')
|
|
61
|
+
.option('--component-type <type>', 'Component type (e.g. diagnosis)')
|
|
62
|
+
.option('--element <elements...>', 'Elements as type:content')
|
|
63
|
+
.option('--from-file <path>', 'Path to JSON file with elements array')
|
|
64
|
+
.option('--title <title>', 'Component title')
|
|
65
|
+
.option('--contributor-id <id>', 'Your contributor ID')
|
|
66
|
+
.option('--element-id <id>', 'Published sysauth element ID — resolves author from sysauthElementAuthor')
|
|
67
|
+
.option('--component-id <id>', 'Published sysauth component ID — resolves component-level author from view')
|
|
68
|
+
.option('--theme <theme>', 'Theme: light, dark, or auto', 'auto')
|
|
69
|
+
.option('--no-track-analytics', 'Disable analytics tracking')
|
|
70
|
+
.option('--source-url <url>', 'Source URL for attribution')
|
|
71
|
+
.action(async (options) => {
|
|
72
|
+
try {
|
|
73
|
+
if (options.elementType && options.componentType) {
|
|
74
|
+
outputError('--element-type and --component-type are mutually exclusive. Use one or the other.', 'contribute generate embed');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
if (!options.contributorId) {
|
|
78
|
+
outputError('--contributor-id is required', 'contribute generate embed');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
let authorName;
|
|
82
|
+
let authorSlug;
|
|
83
|
+
const credentials = await getStoredCredentials();
|
|
84
|
+
if (credentials?.access_token) {
|
|
85
|
+
if (options.elementId) {
|
|
86
|
+
const resolved = await resolveElementAuthor(options.elementId, credentials.access_token);
|
|
87
|
+
if (resolved) {
|
|
88
|
+
authorName = resolved.authorName;
|
|
89
|
+
authorSlug = resolved.authorSlug;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (options.componentId) {
|
|
93
|
+
const resolved = await resolveComponentAuthor(options.componentId, credentials.access_token);
|
|
94
|
+
if (resolved) {
|
|
95
|
+
authorName = resolved.authorName;
|
|
96
|
+
authorSlug = resolved.authorSlug;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (options.componentType) {
|
|
101
|
+
// Component mode
|
|
102
|
+
let elements;
|
|
103
|
+
if (options.fromFile) {
|
|
104
|
+
elements = loadElementsFromFile(options.fromFile);
|
|
105
|
+
}
|
|
106
|
+
else if (options.element) {
|
|
107
|
+
elements = parseElementArgs(options.element);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
outputError('Component mode requires --element or --from-file', 'contribute generate embed');
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
const result = generateComponentEmbed({
|
|
114
|
+
componentType: options.componentType,
|
|
115
|
+
contributorId: options.contributorId,
|
|
116
|
+
authorName,
|
|
117
|
+
authorSlug,
|
|
118
|
+
elements,
|
|
119
|
+
title: options.title,
|
|
120
|
+
theme: options.theme,
|
|
121
|
+
trackAnalytics: options.trackAnalytics,
|
|
122
|
+
sourceUrl: options.sourceUrl,
|
|
123
|
+
});
|
|
124
|
+
if (getGlobalOptions().json) {
|
|
125
|
+
outputSuccess(result, 'contribute generate embed');
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
log(result.snippet);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Element mode
|
|
133
|
+
if (!options.elementType) {
|
|
134
|
+
outputError('--element-type is required for element mode', 'contribute generate embed');
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
if (!options.content) {
|
|
138
|
+
outputError('--content is required for element mode', 'contribute generate embed');
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
const result = generateEmbed({
|
|
142
|
+
elementType: options.elementType,
|
|
143
|
+
contributorId: options.contributorId,
|
|
144
|
+
authorName,
|
|
145
|
+
authorSlug,
|
|
146
|
+
content: options.content,
|
|
147
|
+
theme: options.theme,
|
|
148
|
+
trackAnalytics: options.trackAnalytics,
|
|
149
|
+
sourceUrl: options.sourceUrl,
|
|
150
|
+
});
|
|
151
|
+
if (getGlobalOptions().json) {
|
|
152
|
+
outputSuccess(result, 'contribute generate embed');
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
log(result.snippet);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
outputError(error instanceof Error ? error.message : 'Failed to generate embed', 'contribute generate embed');
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
generate
|
|
165
|
+
.command('link')
|
|
166
|
+
.description('Generate a shareable link for an element or component')
|
|
167
|
+
.option('--element-type <type>', 'Element type (e.g. key_challenge)')
|
|
168
|
+
.option('--content <text>', 'Element content text')
|
|
169
|
+
.option('--component-type <type>', 'Component type (e.g. diagnosis)')
|
|
170
|
+
.option('--element <elements...>', 'Elements as type:content')
|
|
171
|
+
.option('--from-file <path>', 'Path to JSON file with elements array')
|
|
172
|
+
.option('--title <title>', 'Component title')
|
|
173
|
+
.option('--contributor-id <id>', 'Your contributor ID')
|
|
174
|
+
.option('--source-url <url>', 'Source URL for attribution')
|
|
175
|
+
.option('--utm-source <source>', 'UTM source parameter')
|
|
176
|
+
.option('--utm-medium <medium>', 'UTM medium parameter')
|
|
177
|
+
.option('--utm-campaign <campaign>', 'UTM campaign parameter')
|
|
178
|
+
.option('--save-only', 'Only generate the save link (skip share link, works offline)')
|
|
179
|
+
.action(async (options) => {
|
|
180
|
+
try {
|
|
181
|
+
if (options.elementType && options.componentType) {
|
|
182
|
+
outputError('--element-type and --component-type are mutually exclusive. Use one or the other.', 'contribute generate link');
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
if (!options.contributorId) {
|
|
186
|
+
outputError('--contributor-id is required', 'contribute generate link');
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
if (options.componentType) {
|
|
190
|
+
// Component mode
|
|
191
|
+
let elements;
|
|
192
|
+
if (options.fromFile) {
|
|
193
|
+
elements = loadElementsFromFile(options.fromFile);
|
|
194
|
+
}
|
|
195
|
+
else if (options.element) {
|
|
196
|
+
elements = parseElementArgs(options.element);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
outputError('Component mode requires --element or --from-file', 'contribute generate link');
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
const saveResult = generateSaveComponentLink({
|
|
203
|
+
componentType: options.componentType,
|
|
204
|
+
contributorId: options.contributorId,
|
|
205
|
+
elements,
|
|
206
|
+
title: options.title,
|
|
207
|
+
sourceUrl: options.sourceUrl,
|
|
208
|
+
utmSource: options.utmSource,
|
|
209
|
+
utmMedium: options.utmMedium,
|
|
210
|
+
utmCampaign: options.utmCampaign,
|
|
211
|
+
});
|
|
212
|
+
let shareResult = null;
|
|
213
|
+
if (!options.saveOnly) {
|
|
214
|
+
const credentials = await getStoredCredentials();
|
|
215
|
+
if (!credentials?.access_token) {
|
|
216
|
+
outputError('Authentication required for share link. Run "sk auth login" first, or use --save-only.', 'contribute generate link');
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
const spinner = ora('Publishing component for share link...').start();
|
|
220
|
+
try {
|
|
221
|
+
shareResult = await publishAndGenerateComponentShareLink({
|
|
222
|
+
contributorId: options.contributorId,
|
|
223
|
+
componentType: options.componentType,
|
|
224
|
+
title: options.title,
|
|
225
|
+
elements,
|
|
226
|
+
accessToken: credentials.access_token,
|
|
227
|
+
});
|
|
228
|
+
spinner.succeed('Published');
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
spinner.fail('Failed to publish');
|
|
232
|
+
outputError(err instanceof Error
|
|
233
|
+
? err.message
|
|
234
|
+
: 'Failed to generate share link', 'contribute generate link');
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (getGlobalOptions().json) {
|
|
239
|
+
outputSuccess({
|
|
240
|
+
saveLink: saveResult.url,
|
|
241
|
+
shareLink: shareResult?.shareUrl || null,
|
|
242
|
+
componentHash: saveResult.componentHash,
|
|
243
|
+
componentId: shareResult?.componentId || null,
|
|
244
|
+
}, 'contribute generate link');
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
log(`Save link: ${saveResult.url}`);
|
|
248
|
+
if (shareResult) {
|
|
249
|
+
log(`Share link: ${shareResult.shareUrl}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
// Element mode
|
|
255
|
+
if (!options.elementType) {
|
|
256
|
+
outputError('--element-type is required for element mode', 'contribute generate link');
|
|
257
|
+
process.exit(1);
|
|
258
|
+
}
|
|
259
|
+
if (!options.content) {
|
|
260
|
+
outputError('--content is required for element mode', 'contribute generate link');
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
const saveResult = generateSaveElementLink({
|
|
264
|
+
elementType: options.elementType,
|
|
265
|
+
contributorId: options.contributorId,
|
|
266
|
+
content: options.content,
|
|
267
|
+
sourceUrl: options.sourceUrl,
|
|
268
|
+
utmSource: options.utmSource,
|
|
269
|
+
utmMedium: options.utmMedium,
|
|
270
|
+
utmCampaign: options.utmCampaign,
|
|
271
|
+
});
|
|
272
|
+
let shareResult = null;
|
|
273
|
+
if (!options.saveOnly) {
|
|
274
|
+
const credentials = await getStoredCredentials();
|
|
275
|
+
if (!credentials?.access_token) {
|
|
276
|
+
outputError('Authentication required for share link. Run "sk auth login" first, or use --save-only.', 'contribute generate link');
|
|
277
|
+
process.exit(1);
|
|
278
|
+
}
|
|
279
|
+
const spinner = ora('Publishing element for share link...').start();
|
|
280
|
+
try {
|
|
281
|
+
shareResult = await publishAndGenerateElementShareLink({
|
|
282
|
+
contributorId: options.contributorId,
|
|
283
|
+
elementType: options.elementType,
|
|
284
|
+
content: options.content,
|
|
285
|
+
accessToken: credentials.access_token,
|
|
286
|
+
});
|
|
287
|
+
spinner.succeed('Published');
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
spinner.fail('Failed to publish');
|
|
291
|
+
outputError(err instanceof Error
|
|
292
|
+
? err.message
|
|
293
|
+
: 'Failed to generate share link', 'contribute generate link');
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (getGlobalOptions().json) {
|
|
298
|
+
outputSuccess({
|
|
299
|
+
saveLink: saveResult.url,
|
|
300
|
+
shareLink: shareResult?.shareUrl || null,
|
|
301
|
+
contentHash: saveResult.contentHash,
|
|
302
|
+
elementId: shareResult?.elementId || null,
|
|
303
|
+
}, 'contribute generate link');
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
log(`Save link: ${saveResult.url}`);
|
|
307
|
+
if (shareResult) {
|
|
308
|
+
log(`Share link: ${shareResult.shareUrl}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
outputError(error instanceof Error ? error.message : 'Failed to generate link', 'contribute generate link');
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Element commands — the strategic building blocks in a workspace. All are
|
|
4
|
+
* workspace-scoped via `--workspace` (falling back to the default workspace).
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerElementCommands(program: Command): void;
|
|
7
|
+
//# sourceMappingURL=element.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../../src/commands/element.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkBnC;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+P9D"}
|
|
@@ -0,0 +1,167 @@
|
|
|
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
|
+
/** Truncate free-text element content for table display. */
|
|
7
|
+
function summarize(content) {
|
|
8
|
+
const text = typeof content === 'string' ? content : '';
|
|
9
|
+
return text.length > 48 ? `${text.slice(0, 47)}…` : text || '—';
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Element commands — the strategic building blocks in a workspace. All are
|
|
13
|
+
* workspace-scoped via `--workspace` (falling back to the default workspace).
|
|
14
|
+
*/
|
|
15
|
+
export function registerElementCommands(program) {
|
|
16
|
+
const element = program.command('element').description('Element commands');
|
|
17
|
+
const withWorkspace = (cmd) => cmd.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)');
|
|
18
|
+
withWorkspace(element.command('list').description('List elements')).action(async (options) => {
|
|
19
|
+
await runCommand('element list', async () => {
|
|
20
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
21
|
+
const { client } = await resolveClient();
|
|
22
|
+
const data = await client.elements.list(workspaceId);
|
|
23
|
+
const rows = coerceList(data, 'elements');
|
|
24
|
+
if (!getGlobalOptions().json) {
|
|
25
|
+
logHeader(`Elements (${rows.length})`);
|
|
26
|
+
if (rows.length === 0)
|
|
27
|
+
log(chalk.dim(' No elements.'));
|
|
28
|
+
else
|
|
29
|
+
logTable(['Id', 'Component', 'Type', 'Content'], rows.map((r) => {
|
|
30
|
+
const e = asRecord(r);
|
|
31
|
+
return [
|
|
32
|
+
String(e.id ?? '—'),
|
|
33
|
+
String(e.componentType ?? '—'),
|
|
34
|
+
String(e.elementType ?? '—'),
|
|
35
|
+
summarize(e.content),
|
|
36
|
+
];
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
return data;
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
// No `GET /v1/.../elements/:id` route exists on the gateway, so `element get`
|
|
43
|
+
// fetches the workspace's element list and filters it client-side by id.
|
|
44
|
+
withWorkspace(element.command('get <element-id>').description('Get an element by id')).action(async (elementId, options) => {
|
|
45
|
+
await runCommand('element get', async () => {
|
|
46
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
47
|
+
const { client } = await resolveClient();
|
|
48
|
+
const data = await client.elements.list(workspaceId);
|
|
49
|
+
const match = coerceList(data, 'elements').find((r) => asRecord(r).id === elementId);
|
|
50
|
+
if (!match) {
|
|
51
|
+
throw new Error(`Element "${elementId}" not found in workspace ${workspaceId}.`);
|
|
52
|
+
}
|
|
53
|
+
if (!getGlobalOptions().json) {
|
|
54
|
+
const e = asRecord(match);
|
|
55
|
+
logHeader(String(e.id));
|
|
56
|
+
log(` Component: ${String(e.componentType ?? '—')}`);
|
|
57
|
+
log(` Type: ${String(e.elementType ?? '—')}`);
|
|
58
|
+
log(` Content: ${String(e.content ?? '—')}`);
|
|
59
|
+
}
|
|
60
|
+
return match;
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
withWorkspace(element
|
|
64
|
+
.command('create')
|
|
65
|
+
.description('Create an element')
|
|
66
|
+
.option('--content <content>', 'Element content')
|
|
67
|
+
.option('--component-type <type>', 'Component type')
|
|
68
|
+
.option('--element-type <type>', 'Element type')
|
|
69
|
+
.option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')).action(async (options) => {
|
|
70
|
+
await runCommand('element create', async () => {
|
|
71
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
72
|
+
const body = buildBody(readPayloadFile(options.file), {
|
|
73
|
+
content: options.content,
|
|
74
|
+
componentType: options.componentType,
|
|
75
|
+
elementType: options.elementType,
|
|
76
|
+
});
|
|
77
|
+
if (Object.keys(body).length === 0) {
|
|
78
|
+
throw new UsageError('Nothing to create. Pass element fields as flags or a --file payload.');
|
|
79
|
+
}
|
|
80
|
+
const { client } = await resolveClient();
|
|
81
|
+
const data = await client.elements.create(workspaceId, body);
|
|
82
|
+
if (!getGlobalOptions().json) {
|
|
83
|
+
const e = asRecord(asRecord(data).element ?? data);
|
|
84
|
+
log(chalk.green(`Element created${e.id ? ` (${String(e.id)})` : ''}.`));
|
|
85
|
+
}
|
|
86
|
+
return data;
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
withWorkspace(element
|
|
90
|
+
.command('update <element-id>')
|
|
91
|
+
.description('Update an element')
|
|
92
|
+
.option('--content <content>', 'Element content')
|
|
93
|
+
.option('--component-type <type>', 'Component type')
|
|
94
|
+
.option('--element-type <type>', 'Element type')
|
|
95
|
+
.option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')).action(async (elementId, options) => {
|
|
96
|
+
await runCommand('element update', async () => {
|
|
97
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
98
|
+
const body = buildBody(readPayloadFile(options.file), {
|
|
99
|
+
content: options.content,
|
|
100
|
+
componentType: options.componentType,
|
|
101
|
+
elementType: options.elementType,
|
|
102
|
+
});
|
|
103
|
+
if (Object.keys(body).length === 0) {
|
|
104
|
+
throw new UsageError('Nothing to update. Pass element fields as flags or a --file payload.');
|
|
105
|
+
}
|
|
106
|
+
const { client } = await resolveClient();
|
|
107
|
+
const data = await client.elements.update(workspaceId, elementId, body);
|
|
108
|
+
if (!getGlobalOptions().json)
|
|
109
|
+
log(chalk.green('Element updated.'));
|
|
110
|
+
return data;
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
withWorkspace(element.command('delete <element-id>').description('Delete an element')).action(async (elementId, options) => {
|
|
114
|
+
await runCommand('element delete', async () => {
|
|
115
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
116
|
+
const { client } = await resolveClient();
|
|
117
|
+
const data = await client.elements.delete(workspaceId, elementId);
|
|
118
|
+
if (!getGlobalOptions().json)
|
|
119
|
+
log(chalk.green(`Deleted element ${elementId}.`));
|
|
120
|
+
return data;
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
withWorkspace(element
|
|
124
|
+
.command('validate <element-id>')
|
|
125
|
+
.description('Run element validation (completeness/coherence)')
|
|
126
|
+
.option('--file <path>', 'JSON payload file for the request body ("-" for stdin)')).action(async (elementId, options) => {
|
|
127
|
+
await runCommand('element validate', async () => {
|
|
128
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
129
|
+
const { client } = await resolveClient();
|
|
130
|
+
const data = await client.elements.validate(workspaceId, elementId, readPayloadFile(options.file) ?? {});
|
|
131
|
+
if (!getGlobalOptions().json)
|
|
132
|
+
log(chalk.green('Validation complete.'));
|
|
133
|
+
return data;
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
withWorkspace(element
|
|
137
|
+
.command('rephrase <element-id>')
|
|
138
|
+
.description('Rephrase an element')
|
|
139
|
+
.option('--file <path>', 'JSON payload file for the request body ("-" for stdin)')).action(async (elementId, options) => {
|
|
140
|
+
await runCommand('element rephrase', async () => {
|
|
141
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
142
|
+
const { client } = await resolveClient();
|
|
143
|
+
const data = await client.elements.rephrase(workspaceId, elementId, readPayloadFile(options.file) ?? {});
|
|
144
|
+
if (!getGlobalOptions().json)
|
|
145
|
+
log(chalk.green('Rephrase complete.'));
|
|
146
|
+
return data;
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
// `element fork <public-element-id> --workspace <target>` forks a published
|
|
150
|
+
// public element into the target workspace (the workspace in the path is the
|
|
151
|
+
// destination).
|
|
152
|
+
withWorkspace(element
|
|
153
|
+
.command('fork <element-id>')
|
|
154
|
+
.description('Fork a published element into a workspace')
|
|
155
|
+
.option('--file <path>', 'JSON payload file for the request body ("-" for stdin)')).action(async (elementId, options) => {
|
|
156
|
+
await runCommand('element fork', async () => {
|
|
157
|
+
const workspaceId = resolveWorkspaceId(options.workspace);
|
|
158
|
+
const { client } = await resolveClient();
|
|
159
|
+
const data = await client.elements.fork(workspaceId, elementId, readPayloadFile(options.file) ?? {});
|
|
160
|
+
if (!getGlobalOptions().json) {
|
|
161
|
+
const e = asRecord(asRecord(data).element ?? data);
|
|
162
|
+
log(chalk.green(`Forked into ${workspaceId}${e.id ? ` (${String(e.id)})` : ''}.`));
|
|
163
|
+
}
|
|
164
|
+
return data;
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* `skhema workspace export` — full-portability export of a whole workspace
|
|
4
|
+
* (xlsx/csv/json). It hangs off the `workspace` command group owned by the
|
|
5
|
+
* workspace module, so this registration must run after that group exists; it
|
|
6
|
+
* finds (or, defensively, creates) the group rather than editing that file.
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerWorkspaceExportCommand(program: Command): void;
|
|
9
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQnC;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgDrE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { resolveClient } from '../lib/api/client.js';
|
|
2
|
+
import { assertExportOutput, reportExport } from '../lib/api/export-cli.js';
|
|
3
|
+
import { assertFormat, WORKSPACE_FORMATS } from '../lib/api/formats.js';
|
|
4
|
+
import { runCommand } from '../lib/api/run-command.js';
|
|
5
|
+
import { resolveWorkspaceId } from '../lib/api/scope.js';
|
|
6
|
+
import { performExport } from '../lib/api/transfer.js';
|
|
7
|
+
/**
|
|
8
|
+
* `skhema workspace export` — full-portability export of a whole workspace
|
|
9
|
+
* (xlsx/csv/json). It hangs off the `workspace` command group owned by the
|
|
10
|
+
* workspace module, so this registration must run after that group exists; it
|
|
11
|
+
* finds (or, defensively, creates) the group rather than editing that file.
|
|
12
|
+
*/
|
|
13
|
+
export function registerWorkspaceExportCommand(program) {
|
|
14
|
+
const workspace = program.commands.find((c) => c.name() === 'workspace') ??
|
|
15
|
+
program.command('workspace').description('Workspace commands');
|
|
16
|
+
workspace
|
|
17
|
+
.command('export')
|
|
18
|
+
.description('Export a whole workspace (xlsx/csv/json full portability)')
|
|
19
|
+
.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)')
|
|
20
|
+
.requiredOption('--format <format>', 'xlsx | csv | json')
|
|
21
|
+
.option('--out <path>', 'Write to this path (- for stdout; default: server filename in cwd)')
|
|
22
|
+
.option('--email <recipient>', 'Email the export to an org member instead of downloading')
|
|
23
|
+
.action(async (opts) => {
|
|
24
|
+
await runCommand('workspace export', async () => {
|
|
25
|
+
const { client } = await resolveClient();
|
|
26
|
+
const ws = resolveWorkspaceId(opts.workspace);
|
|
27
|
+
const format = assertFormat(opts.format, WORKSPACE_FORMATS);
|
|
28
|
+
assertExportOutput(opts);
|
|
29
|
+
const result = await performExport({
|
|
30
|
+
out: opts.email ? undefined : opts.out,
|
|
31
|
+
exec: () => client.exports.workspace(ws, {
|
|
32
|
+
format,
|
|
33
|
+
...(opts.email
|
|
34
|
+
? { delivery: 'email', recipientEmail: opts.email }
|
|
35
|
+
: {}),
|
|
36
|
+
}),
|
|
37
|
+
});
|
|
38
|
+
reportExport(result);
|
|
39
|
+
return result;
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface InitOptions {
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
yes?: boolean;
|
|
5
|
+
force?: boolean;
|
|
6
|
+
skipSkills?: boolean;
|
|
7
|
+
skipMcp?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface CredentialState {
|
|
10
|
+
connected: boolean;
|
|
11
|
+
lane?: string;
|
|
12
|
+
source?: string;
|
|
13
|
+
verified?: boolean;
|
|
14
|
+
organizationId?: string;
|
|
15
|
+
permission?: string;
|
|
16
|
+
workspaceCount?: number;
|
|
17
|
+
stored?: boolean;
|
|
18
|
+
reason?: string;
|
|
19
|
+
}
|
|
20
|
+
interface SkillsState {
|
|
21
|
+
skipped?: boolean;
|
|
22
|
+
platforms?: string[];
|
|
23
|
+
installed?: number;
|
|
24
|
+
updated?: number;
|
|
25
|
+
unchanged?: number;
|
|
26
|
+
}
|
|
27
|
+
interface McpState {
|
|
28
|
+
serverUrl: string;
|
|
29
|
+
mutated: boolean;
|
|
30
|
+
targets: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
status: string;
|
|
34
|
+
configPath?: string;
|
|
35
|
+
backupPath?: string;
|
|
36
|
+
instructions?: string;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
export interface InitState {
|
|
40
|
+
credential: CredentialState;
|
|
41
|
+
skills: SkillsState;
|
|
42
|
+
mcp: McpState;
|
|
43
|
+
quickstart: string[];
|
|
44
|
+
}
|
|
45
|
+
/** Orchestrate all init steps and return the machine-readable state. */
|
|
46
|
+
export declare function runInit(options: InitOptions): Promise<InitState>;
|
|
47
|
+
export declare function registerInitCommand(program: Command): void;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAkBnC,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,UAAU,eAAe;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,UAAU,QAAQ;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,KAAK,CAAC;QACb,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAC,CAAA;CACH;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,eAAe,CAAA;IAC3B,MAAM,EAAE,WAAW,CAAA;IACnB,GAAG,EAAE,QAAQ,CAAA;IACb,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AA2LD,wEAAwE;AACxE,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAQtE;AA0FD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiB1D"}
|