@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.
Files changed (168) hide show
  1. package/README.md +163 -0
  2. package/bin/skhema.js +2 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +92 -0
  6. package/dist/commands/agent.d.ts +3 -0
  7. package/dist/commands/agent.d.ts.map +1 -0
  8. package/dist/commands/agent.js +154 -0
  9. package/dist/commands/api.d.ts +19 -0
  10. package/dist/commands/api.d.ts.map +1 -0
  11. package/dist/commands/api.js +97 -0
  12. package/dist/commands/auth.d.ts +3 -0
  13. package/dist/commands/auth.d.ts.map +1 -0
  14. package/dist/commands/auth.js +403 -0
  15. package/dist/commands/component.d.ts +3 -0
  16. package/dist/commands/component.d.ts.map +1 -0
  17. package/dist/commands/component.js +221 -0
  18. package/dist/commands/contribute.d.ts +3 -0
  19. package/dist/commands/contribute.d.ts.map +1 -0
  20. package/dist/commands/contribute.js +318 -0
  21. package/dist/commands/element.d.ts +7 -0
  22. package/dist/commands/element.d.ts.map +1 -0
  23. package/dist/commands/element.js +167 -0
  24. package/dist/commands/export.d.ts +9 -0
  25. package/dist/commands/export.d.ts.map +1 -0
  26. package/dist/commands/export.js +42 -0
  27. package/dist/commands/init.d.ts +49 -0
  28. package/dist/commands/init.d.ts.map +1 -0
  29. package/dist/commands/init.js +275 -0
  30. package/dist/commands/link.d.ts +8 -0
  31. package/dist/commands/link.d.ts.map +1 -0
  32. package/dist/commands/link.js +70 -0
  33. package/dist/commands/resource.d.ts +3 -0
  34. package/dist/commands/resource.d.ts.map +1 -0
  35. package/dist/commands/resource.js +88 -0
  36. package/dist/commands/skills.d.ts +3 -0
  37. package/dist/commands/skills.d.ts.map +1 -0
  38. package/dist/commands/skills.js +113 -0
  39. package/dist/commands/strategy.d.ts +3 -0
  40. package/dist/commands/strategy.d.ts.map +1 -0
  41. package/dist/commands/strategy.js +143 -0
  42. package/dist/commands/webhook.d.ts +3 -0
  43. package/dist/commands/webhook.d.ts.map +1 -0
  44. package/dist/commands/webhook.js +77 -0
  45. package/dist/commands/workspace.d.ts +8 -0
  46. package/dist/commands/workspace.d.ts.map +1 -0
  47. package/dist/commands/workspace.js +219 -0
  48. package/dist/index.d.ts +2 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +1 -0
  51. package/dist/lib/api/client.d.ts +27 -0
  52. package/dist/lib/api/client.d.ts.map +1 -0
  53. package/dist/lib/api/client.js +71 -0
  54. package/dist/lib/api/command-helpers.d.ts +27 -0
  55. package/dist/lib/api/command-helpers.d.ts.map +1 -0
  56. package/dist/lib/api/command-helpers.js +77 -0
  57. package/dist/lib/api/credential-context.d.ts +3 -0
  58. package/dist/lib/api/credential-context.d.ts.map +1 -0
  59. package/dist/lib/api/credential-context.js +13 -0
  60. package/dist/lib/api/export-cli.d.ts +14 -0
  61. package/dist/lib/api/export-cli.d.ts.map +1 -0
  62. package/dist/lib/api/export-cli.js +26 -0
  63. package/dist/lib/api/formats.d.ts +12 -0
  64. package/dist/lib/api/formats.d.ts.map +1 -0
  65. package/dist/lib/api/formats.js +18 -0
  66. package/dist/lib/api/passthrough.d.ts +34 -0
  67. package/dist/lib/api/passthrough.d.ts.map +1 -0
  68. package/dist/lib/api/passthrough.js +106 -0
  69. package/dist/lib/api/payload.d.ts +16 -0
  70. package/dist/lib/api/payload.d.ts.map +1 -0
  71. package/dist/lib/api/payload.js +53 -0
  72. package/dist/lib/api/run-command.d.ts +69 -0
  73. package/dist/lib/api/run-command.d.ts.map +1 -0
  74. package/dist/lib/api/run-command.js +127 -0
  75. package/dist/lib/api/scope.d.ts +8 -0
  76. package/dist/lib/api/scope.d.ts.map +1 -0
  77. package/dist/lib/api/scope.js +16 -0
  78. package/dist/lib/api/transfer.d.ts +57 -0
  79. package/dist/lib/api/transfer.d.ts.map +1 -0
  80. package/dist/lib/api/transfer.js +153 -0
  81. package/dist/lib/auth/api-key-admin.d.ts +39 -0
  82. package/dist/lib/auth/api-key-admin.d.ts.map +1 -0
  83. package/dist/lib/auth/api-key-admin.js +66 -0
  84. package/dist/lib/auth/api-key-store.d.ts +8 -0
  85. package/dist/lib/auth/api-key-store.d.ts.map +1 -0
  86. package/dist/lib/auth/api-key-store.js +109 -0
  87. package/dist/lib/auth/device-flow.d.ts +4 -0
  88. package/dist/lib/auth/device-flow.d.ts.map +1 -0
  89. package/dist/lib/auth/device-flow.js +109 -0
  90. package/dist/lib/auth/entitlements.d.ts +16 -0
  91. package/dist/lib/auth/entitlements.d.ts.map +1 -0
  92. package/dist/lib/auth/entitlements.js +89 -0
  93. package/dist/lib/auth/localhost-flow.d.ts +4 -0
  94. package/dist/lib/auth/localhost-flow.d.ts.map +1 -0
  95. package/dist/lib/auth/localhost-flow.js +134 -0
  96. package/dist/lib/auth/oauth-client.d.ts +33 -0
  97. package/dist/lib/auth/oauth-client.d.ts.map +1 -0
  98. package/dist/lib/auth/oauth-client.js +96 -0
  99. package/dist/lib/auth/token-store.d.ts +6 -0
  100. package/dist/lib/auth/token-store.d.ts.map +1 -0
  101. package/dist/lib/auth/token-store.js +136 -0
  102. package/dist/lib/auth/types.d.ts +49 -0
  103. package/dist/lib/auth/types.d.ts.map +1 -0
  104. package/dist/lib/auth/types.js +1 -0
  105. package/dist/lib/banner.d.ts +2 -0
  106. package/dist/lib/banner.d.ts.map +1 -0
  107. package/dist/lib/banner.js +82 -0
  108. package/dist/lib/config-store.d.ts +23 -0
  109. package/dist/lib/config-store.d.ts.map +1 -0
  110. package/dist/lib/config-store.js +53 -0
  111. package/dist/lib/config.d.ts +20 -0
  112. package/dist/lib/config.d.ts.map +1 -0
  113. package/dist/lib/config.js +33 -0
  114. package/dist/lib/contribute/author-resolver.d.ts +13 -0
  115. package/dist/lib/contribute/author-resolver.d.ts.map +1 -0
  116. package/dist/lib/contribute/author-resolver.js +63 -0
  117. package/dist/lib/contribute/embed-generator.d.ts +4 -0
  118. package/dist/lib/contribute/embed-generator.d.ts.map +1 -0
  119. package/dist/lib/contribute/embed-generator.js +108 -0
  120. package/dist/lib/contribute/link-generator.d.ts +11 -0
  121. package/dist/lib/contribute/link-generator.d.ts.map +1 -0
  122. package/dist/lib/contribute/link-generator.js +119 -0
  123. package/dist/lib/contribute/method-schema.d.ts +239 -0
  124. package/dist/lib/contribute/method-schema.d.ts.map +1 -0
  125. package/dist/lib/contribute/method-schema.js +172 -0
  126. package/dist/lib/contribute/profile-resolver.d.ts +2 -0
  127. package/dist/lib/contribute/profile-resolver.d.ts.map +1 -0
  128. package/dist/lib/contribute/profile-resolver.js +25 -0
  129. package/dist/lib/contribute/types.d.ts +91 -0
  130. package/dist/lib/contribute/types.d.ts.map +1 -0
  131. package/dist/lib/contribute/types.js +1 -0
  132. package/dist/lib/mcp/registrar.d.ts +26 -0
  133. package/dist/lib/mcp/registrar.d.ts.map +1 -0
  134. package/dist/lib/mcp/registrar.js +69 -0
  135. package/dist/lib/mcp/registry.d.ts +54 -0
  136. package/dist/lib/mcp/registry.d.ts.map +1 -0
  137. package/dist/lib/mcp/registry.js +105 -0
  138. package/dist/lib/open.d.ts +2 -0
  139. package/dist/lib/open.d.ts.map +1 -0
  140. package/dist/lib/open.js +24 -0
  141. package/dist/lib/output.d.ts +25 -0
  142. package/dist/lib/output.d.ts.map +1 -0
  143. package/dist/lib/output.js +74 -0
  144. package/dist/lib/skills/installer.d.ts +4 -0
  145. package/dist/lib/skills/installer.d.ts.map +1 -0
  146. package/dist/lib/skills/installer.js +100 -0
  147. package/dist/lib/skills/lock.d.ts +4 -0
  148. package/dist/lib/skills/lock.d.ts.map +1 -0
  149. package/dist/lib/skills/lock.js +28 -0
  150. package/dist/lib/skills/platform-detect.d.ts +4 -0
  151. package/dist/lib/skills/platform-detect.d.ts.map +1 -0
  152. package/dist/lib/skills/platform-detect.js +89 -0
  153. package/dist/lib/skills/types.d.ts +29 -0
  154. package/dist/lib/skills/types.d.ts.map +1 -0
  155. package/dist/lib/skills/types.js +1 -0
  156. package/dist/postinstall.d.ts +2 -0
  157. package/dist/postinstall.d.ts.map +1 -0
  158. package/dist/postinstall.js +11 -0
  159. package/package.json +67 -0
  160. package/skills/.manifest.json +15 -0
  161. package/skills/skhema-challenge-framing/SKILL.md +28 -0
  162. package/skills/skhema-coherence-check/SKILL.md +28 -0
  163. package/skills/skhema-element-decomposition/SKILL.md +28 -0
  164. package/skills/skhema-element-writer/SKILL.md +20 -0
  165. package/skills/skhema-judgment-audit/SKILL.md +28 -0
  166. package/skills/skhema-semantic-sharpening/SKILL.md +28 -0
  167. package/skills/skhema-strategy-advisor/SKILL.md +20 -0
  168. 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"}