@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,275 @@
1
+ import chalk from 'chalk';
2
+ import readline from 'readline';
3
+ import { resolveClient } from '../lib/api/client.js';
4
+ import { setApiKeyFlag } from '../lib/api/credential-context.js';
5
+ import { AuthRequiredError, runCommand } from '../lib/api/run-command.js';
6
+ import { getStoredApiKey, storeApiKey } from '../lib/auth/api-key-store.js';
7
+ import { startLocalhostFlow } from '../lib/auth/localhost-flow.js';
8
+ import { MCP_SERVER_URL } from '../lib/config.js';
9
+ import { inspectTarget, registerTarget, } from '../lib/mcp/registrar.js';
10
+ import { detectMcpTargets, MCP_SERVER_NAME } from '../lib/mcp/registry.js';
11
+ import { getGlobalOptions, log, logHeader } from '../lib/output.js';
12
+ import { installSkills } from '../lib/skills/installer.js';
13
+ import { detectPlatforms } from '../lib/skills/platform-detect.js';
14
+ const QUICKSTART = [
15
+ 'skhema auth status',
16
+ 'skhema api routes',
17
+ 'skhema api GET /workspaces',
18
+ ];
19
+ function isInteractive() {
20
+ const opts = getGlobalOptions();
21
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY) && !opts.json;
22
+ }
23
+ async function confirm(question) {
24
+ const rl = readline.createInterface({
25
+ input: process.stdin,
26
+ output: process.stdout,
27
+ });
28
+ const answer = await new Promise((resolve) => {
29
+ rl.question(`${question} [y/N]: `, (a) => {
30
+ rl.close();
31
+ resolve(a.trim().toLowerCase());
32
+ });
33
+ });
34
+ return answer === 'y' || answer === 'yes';
35
+ }
36
+ /** Step 1 — resolve, verify, and (for an explicit --api-key) persist a credential. */
37
+ async function runCredentialStep(options) {
38
+ if (options.apiKey)
39
+ setApiKeyFlag(options.apiKey);
40
+ let resolved;
41
+ try {
42
+ resolved = await resolveClient();
43
+ }
44
+ catch (err) {
45
+ if (err instanceof AuthRequiredError) {
46
+ // Interactive and not --yes: offer to log in, then retry once.
47
+ if (!options.yes && isInteractive()) {
48
+ log(chalk.yellow('No credential found.'));
49
+ if (await confirm('Log in to Skhema in your browser now?')) {
50
+ await startLocalhostFlow();
51
+ try {
52
+ resolved = await resolveClient();
53
+ }
54
+ catch {
55
+ /* fall through to disconnected */
56
+ }
57
+ }
58
+ }
59
+ if (!resolved) {
60
+ return {
61
+ connected: false,
62
+ reason: 'No credential. Set SKHEMA_API_KEY / pass --api-key, or run `skhema auth login`.',
63
+ };
64
+ }
65
+ }
66
+ else {
67
+ throw err;
68
+ }
69
+ }
70
+ const { client, credential } = resolved;
71
+ // health() forces the API-key exchange (verifying the key) and is a cheap probe.
72
+ let verified = false;
73
+ try {
74
+ await client.meta.health();
75
+ verified = true;
76
+ }
77
+ catch {
78
+ verified = false;
79
+ }
80
+ const ctx = client.getContext();
81
+ const organizationId = ctx.organizationId ?? credential.organizationId;
82
+ let workspaceCount;
83
+ if (verified) {
84
+ try {
85
+ const res = await client.workspaces.list();
86
+ workspaceCount = Array.isArray(res?.workspaces)
87
+ ? res.workspaces.length
88
+ : undefined;
89
+ }
90
+ catch {
91
+ // A read may be denied (e.g. write-only key) — leave the count undefined.
92
+ }
93
+ }
94
+ // Persist only an explicitly-provided --api-key, and never clobber a different
95
+ // stored key without --force (spec: no silent credential overwrite).
96
+ let stored;
97
+ if (options.apiKey && verified) {
98
+ const existing = getStoredApiKey();
99
+ if (!existing || existing === options.apiKey || options.force) {
100
+ storeApiKey(options.apiKey);
101
+ stored = true;
102
+ }
103
+ else {
104
+ stored = false;
105
+ }
106
+ }
107
+ return {
108
+ connected: true,
109
+ lane: credential.lane,
110
+ source: credential.source,
111
+ verified,
112
+ organizationId,
113
+ permission: ctx.permission,
114
+ workspaceCount,
115
+ stored,
116
+ };
117
+ }
118
+ /** Step 2 — install bundled skills into every detected agent platform. */
119
+ async function runSkillsStep(options) {
120
+ if (options.skipSkills)
121
+ return { skipped: true };
122
+ const platforms = detectPlatforms().filter((p) => p.detected);
123
+ if (platforms.length === 0) {
124
+ return { platforms: [], installed: 0, updated: 0, unchanged: 0 };
125
+ }
126
+ const results = await installSkills(platforms);
127
+ return {
128
+ platforms: platforms.map((p) => p.name),
129
+ installed: results.filter((r) => r.action === 'installed').length,
130
+ updated: results.filter((r) => r.action === 'updated').length,
131
+ unchanged: results.filter((r) => r.action === 'skipped').length,
132
+ };
133
+ }
134
+ /** Step 3 — register the MCP server, or report what a run would do (safe by default). */
135
+ async function runMcpStep(options) {
136
+ const targets = detectMcpTargets();
137
+ const detected = targets.filter((t) => t.detected);
138
+ if (options.skipMcp || detected.length === 0) {
139
+ return {
140
+ serverUrl: MCP_SERVER_URL,
141
+ mutated: false,
142
+ targets: detected.map((t) => ({
143
+ id: t.id,
144
+ name: t.name,
145
+ status: options.skipMcp ? 'skipped' : inspectTarget(t),
146
+ configPath: t.configPath ?? undefined,
147
+ })),
148
+ };
149
+ }
150
+ // Mutate only with --yes, or after an interactive confirmation. Otherwise we
151
+ // report the inspected status without touching any file.
152
+ let willWrite = Boolean(options.yes);
153
+ if (!willWrite && isInteractive()) {
154
+ willWrite = await confirm(`Register the Skhema MCP server in ${detected.length} detected client(s)?`);
155
+ }
156
+ const targetStates = detected.map((t) => {
157
+ if (!willWrite) {
158
+ return {
159
+ id: t.id,
160
+ name: t.name,
161
+ status: inspectTarget(t),
162
+ configPath: t.configPath ?? undefined,
163
+ instructions: t.instructions,
164
+ };
165
+ }
166
+ const outcome = registerTarget(t, {
167
+ force: options.force,
168
+ });
169
+ return {
170
+ id: t.id,
171
+ name: t.name,
172
+ status: outcome.status,
173
+ configPath: outcome.configPath,
174
+ backupPath: outcome.backupPath,
175
+ instructions: outcome.instructions,
176
+ };
177
+ });
178
+ return {
179
+ serverUrl: MCP_SERVER_URL,
180
+ mutated: willWrite && targetStates.some((s) => s.status === 'registered'),
181
+ targets: targetStates,
182
+ };
183
+ }
184
+ /** Orchestrate all init steps and return the machine-readable state. */
185
+ export async function runInit(options) {
186
+ const credential = await runCredentialStep(options);
187
+ const skills = await runSkillsStep(options);
188
+ const mcp = await runMcpStep(options);
189
+ const state = { credential, skills, mcp, quickstart: QUICKSTART };
190
+ renderHuman(state);
191
+ return state;
192
+ }
193
+ function renderHuman(state) {
194
+ if (getGlobalOptions().json)
195
+ return;
196
+ logHeader('Skhema init');
197
+ // Credential
198
+ const c = state.credential;
199
+ if (c.connected) {
200
+ log(` Credential: ${chalk.green('connected')} (${c.source})${c.verified ? '' : chalk.yellow(' — could not verify')}`);
201
+ if (c.organizationId)
202
+ log(` Organization: ${chalk.cyan(c.organizationId)}`);
203
+ if (c.permission)
204
+ log(` Permission: ${c.permission}`);
205
+ if (c.stored === true)
206
+ log(chalk.dim(' Stored API key for future commands.'));
207
+ if (c.stored === false) {
208
+ log(chalk.yellow(' A different API key is already stored; kept it (use --force to replace).'));
209
+ }
210
+ }
211
+ else {
212
+ log(` Credential: ${chalk.yellow('not connected')}`);
213
+ log(chalk.dim(` ${c.reason}`));
214
+ }
215
+ // Skills
216
+ const s = state.skills;
217
+ if (s.skipped) {
218
+ log(` Skills: ${chalk.dim('skipped')}`);
219
+ }
220
+ else if ((s.platforms?.length ?? 0) === 0) {
221
+ log(` Skills: ${chalk.dim('no agent platforms detected')}`);
222
+ }
223
+ else {
224
+ log(` Skills: ${chalk.green('installed')} to ${s.platforms.join(', ')} ` +
225
+ chalk.dim(`(+${s.installed} new, ${s.updated} updated, ${s.unchanged} unchanged)`));
226
+ }
227
+ // MCP
228
+ const m = state.mcp;
229
+ log(` MCP server: ${chalk.cyan(m.serverUrl)}`);
230
+ if (m.targets.length === 0) {
231
+ log(chalk.dim(' No MCP clients detected.'));
232
+ }
233
+ for (const t of m.targets) {
234
+ const label = t.status === 'registered'
235
+ ? chalk.green('registered')
236
+ : t.status === 'already'
237
+ ? chalk.dim('already registered')
238
+ : t.status === 'writable'
239
+ ? chalk.yellow('pending (run with --yes to register)')
240
+ : t.status === 'conflict'
241
+ ? chalk.yellow('conflict — left untouched')
242
+ : chalk.yellow(t.status);
243
+ log(` ${t.name}: ${label}`);
244
+ if (t.backupPath)
245
+ log(chalk.dim(` backup: ${t.backupPath}`));
246
+ if ((t.status === 'manual' ||
247
+ t.status === 'conflict' ||
248
+ t.status === 'writable') &&
249
+ t.instructions) {
250
+ log(chalk.dim(` ${t.instructions}`));
251
+ }
252
+ }
253
+ // Quickstart
254
+ log('');
255
+ logHeader('Try these');
256
+ for (const cmd of state.quickstart)
257
+ log(` ${chalk.cyan(cmd)}`);
258
+ if (state.credential.connected) {
259
+ log('');
260
+ log(chalk.dim(` MCP server "${MCP_SERVER_NAME}" authorizes in-browser on first use.`));
261
+ }
262
+ }
263
+ export function registerInitCommand(program) {
264
+ program
265
+ .command('init')
266
+ .description('One-command onboarding: verify a credential, install skills, register the MCP server')
267
+ .option('--api-key <key>', 'Use (and store) this API key as the credential')
268
+ .option('--yes', 'Non-interactive: do everything possible, never prompt')
269
+ .option('--force', 'Overwrite an existing stored key / conflicting MCP entry')
270
+ .option('--skip-skills', 'Do not install skills')
271
+ .option('--skip-mcp', 'Do not register the MCP server')
272
+ .action(async (options) => {
273
+ await runCommand('init', () => runInit(options));
274
+ });
275
+ }
@@ -0,0 +1,8 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Element link commands — typed relationships between elements. All are
4
+ * workspace-scoped via `--workspace`. There is no `list` verb: links come back
5
+ * embedded in element/workspace payloads, not on a dedicated route.
6
+ */
7
+ export declare function registerLinkCommands(program: Command): void;
8
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/commands/link.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAWnC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8F3D"}
@@ -0,0 +1,70 @@
1
+ import chalk from 'chalk';
2
+ import { resolveClient } from '../lib/api/client.js';
3
+ import { asRecord, buildBody, readPayloadFile, resolveWorkspaceId, } from '../lib/api/command-helpers.js';
4
+ import { runCommand, UsageError } from '../lib/api/run-command.js';
5
+ import { getGlobalOptions, log } from '../lib/output.js';
6
+ /**
7
+ * Element link commands — typed relationships between elements. All are
8
+ * workspace-scoped via `--workspace`. There is no `list` verb: links come back
9
+ * embedded in element/workspace payloads, not on a dedicated route.
10
+ */
11
+ export function registerLinkCommands(program) {
12
+ const link = program.command('link').description('Element link commands');
13
+ const withWorkspace = (cmd) => cmd.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)');
14
+ withWorkspace(link
15
+ .command('create')
16
+ .description('Create an element link')
17
+ .option('--source <element-id>', 'Source element id')
18
+ .option('--target <element-id>', 'Target element id')
19
+ .option('--type <link-type>', 'Link type')
20
+ .option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')).action(async (options) => {
21
+ await runCommand('link create', async () => {
22
+ const workspaceId = resolveWorkspaceId(options.workspace);
23
+ const body = buildBody(readPayloadFile(options.file), {
24
+ sourceElementId: options.source,
25
+ targetElementId: options.target,
26
+ linkType: options.type,
27
+ });
28
+ if (Object.keys(body).length === 0) {
29
+ throw new UsageError('Nothing to create. Pass --source/--target/--type or a --file payload.');
30
+ }
31
+ const { client } = await resolveClient();
32
+ const data = await client.links.create(workspaceId, body);
33
+ if (!getGlobalOptions().json) {
34
+ const l = asRecord(asRecord(data).link ?? data);
35
+ log(chalk.green(`Link created${l.id ? ` (${String(l.id)})` : ''}.`));
36
+ }
37
+ return data;
38
+ });
39
+ });
40
+ withWorkspace(link
41
+ .command('update <link-id>')
42
+ .description('Update an element link')
43
+ .option('--type <link-type>', 'Link type')
44
+ .option('--file <path>', 'JSON payload file for the full body ("-" for stdin)')).action(async (linkId, options) => {
45
+ await runCommand('link update', async () => {
46
+ const workspaceId = resolveWorkspaceId(options.workspace);
47
+ const body = buildBody(readPayloadFile(options.file), {
48
+ linkType: options.type,
49
+ });
50
+ if (Object.keys(body).length === 0) {
51
+ throw new UsageError('Nothing to update. Pass --type or a --file payload.');
52
+ }
53
+ const { client } = await resolveClient();
54
+ const data = await client.links.update(workspaceId, linkId, body);
55
+ if (!getGlobalOptions().json)
56
+ log(chalk.green('Link updated.'));
57
+ return data;
58
+ });
59
+ });
60
+ withWorkspace(link.command('delete <link-id>').description('Delete an element link')).action(async (linkId, options) => {
61
+ await runCommand('link delete', async () => {
62
+ const workspaceId = resolveWorkspaceId(options.workspace);
63
+ const { client } = await resolveClient();
64
+ const data = await client.links.delete(workspaceId, linkId);
65
+ if (!getGlobalOptions().json)
66
+ log(chalk.green(`Deleted link ${linkId}.`));
67
+ return data;
68
+ });
69
+ });
70
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerResourceCommands(program: Command): void;
3
+ //# sourceMappingURL=resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/commands/resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAuCnC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoF/D"}
@@ -0,0 +1,88 @@
1
+ import chalk from 'chalk';
2
+ import { resolveClient } from '../lib/api/client.js';
3
+ import { assertExportOutput } from '../lib/api/export-cli.js';
4
+ import { runCommand } from '../lib/api/run-command.js';
5
+ import { resolveWorkspaceId } from '../lib/api/scope.js';
6
+ import { downloadResource, uploadResource } from '../lib/api/transfer.js';
7
+ import { log, logHeader, logTable } from '../lib/output.js';
8
+ function withWorkspace(cmd) {
9
+ return cmd.option('--workspace <id>', 'Workspace id (defaults to `skhema workspace use`)');
10
+ }
11
+ function renderResources(resources = []) {
12
+ if (resources.length === 0) {
13
+ log(chalk.dim('No resources.'));
14
+ return;
15
+ }
16
+ logHeader(`Resources (${resources.length})`);
17
+ logTable(['id', 'kind', 'ref'], resources.map((r) => {
18
+ const kind = r.filePath ? 'file' : r.url ? 'url' : 'citation';
19
+ const ref = r.filePath ?? r.url ?? (r.citation ? '(citation)' : '');
20
+ return [r.id, kind, String(ref ?? '')];
21
+ }));
22
+ }
23
+ export function registerResourceCommands(program) {
24
+ const resource = program
25
+ .command('resource')
26
+ .description('Element resource commands (files, URLs, citations)');
27
+ withWorkspace(resource.command('list'))
28
+ .description('List resources on an element')
29
+ .requiredOption('--element <id>', 'Element id')
30
+ .action(async (opts) => {
31
+ await runCommand('resource list', async () => {
32
+ const { client } = await resolveClient();
33
+ const ws = resolveWorkspaceId(opts.workspace);
34
+ const data = await client.elements.resources.list(ws, opts.element);
35
+ renderResources(data.resources);
36
+ return data;
37
+ });
38
+ });
39
+ withWorkspace(resource.command('upload <file>'))
40
+ .description('Upload a file as a resource on an element')
41
+ .requiredOption('--element <id>', 'Element id to attach the file to')
42
+ .option('--attribution <text>', 'Attribution note')
43
+ .action(async (file, opts) => {
44
+ await runCommand('resource upload', async () => {
45
+ const { client } = await resolveClient();
46
+ const ws = resolveWorkspaceId(opts.workspace);
47
+ const data = await uploadResource({
48
+ client,
49
+ workspaceId: ws,
50
+ elementId: opts.element,
51
+ filePath: file,
52
+ attribution: opts.attribution,
53
+ });
54
+ log(chalk.green(`Uploaded ${file}`));
55
+ return data;
56
+ });
57
+ });
58
+ withWorkspace(resource.command('download <resourceId>'))
59
+ .description('Download a resource file')
60
+ .option('--out <path>', 'Write to this path (- for stdout; default: original filename in cwd)')
61
+ .action(async (resourceId, opts) => {
62
+ await runCommand('resource download', async () => {
63
+ // `--out -` streams binary; incompatible with --json.
64
+ assertExportOutput({ out: opts.out });
65
+ const { client } = await resolveClient();
66
+ const ws = resolveWorkspaceId(opts.workspace);
67
+ const result = await downloadResource({
68
+ client,
69
+ workspaceId: ws,
70
+ resourceId,
71
+ out: opts.out,
72
+ });
73
+ log(chalk.green(`Wrote ${result.bytes} bytes to ${result.path}`));
74
+ return result;
75
+ });
76
+ });
77
+ withWorkspace(resource.command('delete <resourceId>'))
78
+ .description('Delete a resource')
79
+ .action(async (resourceId, opts) => {
80
+ await runCommand('resource delete', async () => {
81
+ const { client } = await resolveClient();
82
+ const ws = resolveWorkspaceId(opts.workspace);
83
+ const data = await client.elements.resources.delete(ws, resourceId);
84
+ log(chalk.green(`Deleted resource ${chalk.cyan(resourceId)}`));
85
+ return data;
86
+ });
87
+ });
88
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerSkillsCommands(program: Command): void;
3
+ //# sourceMappingURL=skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAanC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4I7D"}
@@ -0,0 +1,113 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { getGlobalOptions, log, logHeader, logTable, outputError, outputSuccess, } from '../lib/output.js';
4
+ import { installSkills, updateSkills } from '../lib/skills/installer.js';
5
+ import { detectPlatforms } from '../lib/skills/platform-detect.js';
6
+ export function registerSkillsCommands(program) {
7
+ const skills = program.command('skills').description('AI skills management');
8
+ skills
9
+ .command('install')
10
+ .description('Install Skhema skills to detected agent platforms')
11
+ .option('--platform <id>', 'Install to a specific platform only')
12
+ .action(async (options) => {
13
+ const opts = getGlobalOptions();
14
+ const spinner = opts.json || opts.quiet
15
+ ? null
16
+ : ora('Detecting agent platforms...').start();
17
+ try {
18
+ const platforms = detectPlatforms();
19
+ const detected = platforms.filter((p) => p.detected);
20
+ if (detected.length === 0) {
21
+ spinner?.warn('No agent platforms detected.');
22
+ log(chalk.dim('Supported platforms: Claude Code, Codex, Warp, Antigravity'));
23
+ return;
24
+ }
25
+ // Filter to specific platform if requested
26
+ const targets = options.platform
27
+ ? detected.filter((p) => p.id === options.platform)
28
+ : detected;
29
+ if (targets.length === 0) {
30
+ spinner?.fail(`Platform "${options.platform}" not detected.`);
31
+ return;
32
+ }
33
+ spinner?.succeed(`Detected ${targets.length} platform(s): ${targets.map((p) => p.name).join(', ')}`);
34
+ const results = await installSkills(targets);
35
+ if (opts.json) {
36
+ outputSuccess(results, 'skills install');
37
+ return;
38
+ }
39
+ // Display results as table
40
+ const rows = results.map((r) => [
41
+ r.platform.name,
42
+ r.skill,
43
+ r.action === 'installed'
44
+ ? chalk.green(r.action)
45
+ : r.action === 'updated'
46
+ ? chalk.yellow(r.action)
47
+ : chalk.dim(r.action),
48
+ ]);
49
+ logHeader('Installation Results');
50
+ logTable(['Platform', 'Skill', 'Status'], rows);
51
+ }
52
+ catch (error) {
53
+ spinner?.fail('Installation failed');
54
+ outputError(error instanceof Error ? error.message : 'Unknown error', 'skills install');
55
+ process.exit(1);
56
+ }
57
+ });
58
+ skills
59
+ .command('list')
60
+ .description('Show detected platforms and installed skills')
61
+ .action(async () => {
62
+ try {
63
+ const platforms = detectPlatforms();
64
+ if (getGlobalOptions().json) {
65
+ outputSuccess(platforms, 'skills list');
66
+ return;
67
+ }
68
+ logHeader('Agent Platforms');
69
+ const rows = platforms.map((p) => [
70
+ p.name,
71
+ p.detected ? chalk.green('Detected') : chalk.dim('Not found'),
72
+ p.detected ? p.skillsDir : '-',
73
+ ]);
74
+ logTable(['Platform', 'Status', 'Skills Directory'], rows);
75
+ }
76
+ catch (error) {
77
+ outputError(error instanceof Error ? error.message : 'Unknown error', 'skills list');
78
+ process.exit(1);
79
+ }
80
+ });
81
+ skills
82
+ .command('update')
83
+ .description('Update installed skills to latest bundled versions')
84
+ .action(async () => {
85
+ const opts = getGlobalOptions();
86
+ const spinner = opts.json || opts.quiet ? null : ora('Checking for updates...').start();
87
+ try {
88
+ const platforms = detectPlatforms().filter((p) => p.detected);
89
+ if (platforms.length === 0) {
90
+ spinner?.warn('No agent platforms detected.');
91
+ return;
92
+ }
93
+ spinner?.succeed(`Found ${platforms.length} platform(s)`);
94
+ const results = await updateSkills(platforms);
95
+ if (opts.json) {
96
+ outputSuccess(results, 'skills update');
97
+ return;
98
+ }
99
+ const rows = results.map((r) => [
100
+ r.platform.name,
101
+ r.skill,
102
+ r.action === 'updated' ? chalk.yellow(r.action) : chalk.dim(r.action),
103
+ ]);
104
+ logHeader('Update Results');
105
+ logTable(['Platform', 'Skill', 'Status'], rows);
106
+ }
107
+ catch (error) {
108
+ spinner?.fail('Update failed');
109
+ outputError(error instanceof Error ? error.message : 'Unknown error', 'skills update');
110
+ process.exit(1);
111
+ }
112
+ });
113
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerStrategyCommands(program: Command): void;
3
+ //# sourceMappingURL=strategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../../src/commands/strategy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAyCnC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+K/D"}