superacli 1.0.0

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 (69) hide show
  1. package/.env.example +14 -0
  2. package/README.md +173 -0
  3. package/cli/adapters/http.js +72 -0
  4. package/cli/adapters/mcp.js +193 -0
  5. package/cli/adapters/openapi.js +160 -0
  6. package/cli/ask.js +208 -0
  7. package/cli/config.js +133 -0
  8. package/cli/executor.js +117 -0
  9. package/cli/help-json.js +46 -0
  10. package/cli/mcp-local.js +72 -0
  11. package/cli/plan-runtime.js +32 -0
  12. package/cli/planner.js +67 -0
  13. package/cli/skills.js +240 -0
  14. package/cli/supercli.js +704 -0
  15. package/docs/features/adapters.md +25 -0
  16. package/docs/features/agent-friendly.md +28 -0
  17. package/docs/features/ask.md +32 -0
  18. package/docs/features/config-sync.md +22 -0
  19. package/docs/features/execution-plans.md +25 -0
  20. package/docs/features/observability.md +22 -0
  21. package/docs/features/skills.md +25 -0
  22. package/docs/features/storage.md +25 -0
  23. package/docs/features/workflows.md +33 -0
  24. package/docs/initial/AGENTS_FRIENDLY_TOOLS.md +553 -0
  25. package/docs/initial/agent-friendly.md +447 -0
  26. package/docs/initial/architecture.md +436 -0
  27. package/docs/initial/built-in-mcp-server.md +64 -0
  28. package/docs/initial/command-plan.md +532 -0
  29. package/docs/initial/core-features-2.md +428 -0
  30. package/docs/initial/core-features.md +366 -0
  31. package/docs/initial/dag.md +20 -0
  32. package/docs/initial/description.txt +9 -0
  33. package/docs/initial/idea.txt +564 -0
  34. package/docs/initial/initial-spec-details.md +726 -0
  35. package/docs/initial/initial-spec.md +731 -0
  36. package/docs/initial/mcp-local-mode.md +53 -0
  37. package/docs/initial/mcp-sse-mode.md +54 -0
  38. package/docs/initial/skills-support.md +246 -0
  39. package/docs/initial/storage-adapter-example.md +155 -0
  40. package/docs/initial/supercli-vs-gwc.md +109 -0
  41. package/examples/mcp-sse/install-demo.js +86 -0
  42. package/examples/mcp-sse/server.js +81 -0
  43. package/examples/mcp-stdio/install-demo.js +78 -0
  44. package/examples/mcp-stdio/server.js +50 -0
  45. package/package.json +21 -0
  46. package/server/app.js +59 -0
  47. package/server/public/app.js +18 -0
  48. package/server/routes/ask.js +92 -0
  49. package/server/routes/commands.js +126 -0
  50. package/server/routes/config.js +58 -0
  51. package/server/routes/jobs.js +122 -0
  52. package/server/routes/mcp.js +79 -0
  53. package/server/routes/plans.js +134 -0
  54. package/server/routes/specs.js +79 -0
  55. package/server/services/configService.js +88 -0
  56. package/server/storage/adapter.js +32 -0
  57. package/server/storage/file.js +64 -0
  58. package/server/storage/mongo.js +55 -0
  59. package/server/views/command-edit.ejs +110 -0
  60. package/server/views/commands.ejs +49 -0
  61. package/server/views/jobs.ejs +72 -0
  62. package/server/views/layout.ejs +42 -0
  63. package/server/views/mcp.ejs +80 -0
  64. package/server/views/partials/foot.ejs +5 -0
  65. package/server/views/partials/head.ejs +27 -0
  66. package/server/views/specs.ejs +91 -0
  67. package/tests/test-cli.js +367 -0
  68. package/tests/test-mcp.js +189 -0
  69. package/tests/test-openapi.js +101 -0
@@ -0,0 +1,704 @@
1
+ #!/usr/bin/env node
2
+
3
+ require("dotenv").config();
4
+ const {
5
+ loadConfig,
6
+ syncConfig,
7
+ showConfig,
8
+ setMcpServer,
9
+ removeMcpServer,
10
+ listMcpServers,
11
+ } = require("./config");
12
+ const { execute } = require("./executor");
13
+ const { buildCapabilities } = require("./help-json");
14
+ const { handleMcpRegistryCommand } = require("./mcp-local");
15
+ const {
16
+ buildLocalPlan,
17
+ annotateServerPlan,
18
+ outputHumanPlan,
19
+ } = require("./plan-runtime");
20
+ const { handleAskCommand } = require("./ask");
21
+ const { handleSkillsCommand } = require("./skills");
22
+
23
+ const SERVER = process.env.SUPERCLI_SERVER;
24
+ const hasServer = !!SERVER;
25
+ const isTTY = process.stdout.isTTY;
26
+ const rawArgs = process.argv.slice(2);
27
+
28
+ const flags = {};
29
+ const positional = [];
30
+ for (let i = 0; i < rawArgs.length; i++) {
31
+ if (rawArgs[i].startsWith("--")) {
32
+ const key = rawArgs[i].slice(2);
33
+ if (i + 1 < rawArgs.length && !rawArgs[i + 1].startsWith("--")) {
34
+ flags[key] = rawArgs[++i];
35
+ } else {
36
+ flags[key] = true;
37
+ }
38
+ } else {
39
+ positional.push(rawArgs[i]);
40
+ }
41
+ }
42
+
43
+ const humanMode =
44
+ flags.human ||
45
+ (isTTY &&
46
+ !flags.json &&
47
+ !flags.compact &&
48
+ !flags.schema &&
49
+ !flags["help-json"]);
50
+ const compactMode = !!flags.compact;
51
+ const RESERVED_FLAGS = [
52
+ "human",
53
+ "json",
54
+ "compact",
55
+ "schema",
56
+ "help-json",
57
+ "no-color",
58
+ "show-dag",
59
+ "format",
60
+ ];
61
+
62
+ function compactKeys(obj) {
63
+ if (Array.isArray(obj)) return obj.map(compactKeys);
64
+ if (obj && typeof obj === "object") {
65
+ const map = {
66
+ version: "v",
67
+ command: "c",
68
+ duration_ms: "ms",
69
+ data: "d",
70
+ namespace: "ns",
71
+ resource: "r",
72
+ action: "a",
73
+ description: "desc",
74
+ adapter: "ad",
75
+ commands: "cmds",
76
+ error: "err",
77
+ message: "msg",
78
+ suggestions: "sug",
79
+ };
80
+ const out = {};
81
+ for (const [k, v] of Object.entries(obj)) {
82
+ out[map[k] || k] = compactKeys(v);
83
+ }
84
+ return out;
85
+ }
86
+ return obj;
87
+ }
88
+
89
+ function output(data) {
90
+ const str = compactMode
91
+ ? JSON.stringify(compactKeys(data))
92
+ : humanMode
93
+ ? typeof data === "string"
94
+ ? data
95
+ : JSON.stringify(data, null, 2)
96
+ : JSON.stringify(data);
97
+ console.log(str);
98
+ }
99
+
100
+ function outputHumanTable(rows, columns) {
101
+ if (!humanMode) return false;
102
+ if (!rows || rows.length === 0) {
103
+ console.log(" (empty)");
104
+ return true;
105
+ }
106
+ // Calculate column widths
107
+ const widths = columns.map((col) =>
108
+ Math.max(
109
+ col.label.length,
110
+ ...rows.map((r) => String(r[col.key] || "").length),
111
+ ),
112
+ );
113
+ const header = columns
114
+ .map((col, i) => col.label.padEnd(widths[i]))
115
+ .join(" ");
116
+ const sep = columns.map((_, i) => "─".repeat(widths[i])).join("──");
117
+ console.log(` ${header}`);
118
+ console.log(` ${sep}`);
119
+ rows.forEach((row) => {
120
+ const line = columns
121
+ .map((col, i) => String(row[col.key] || "").padEnd(widths[i]))
122
+ .join(" ");
123
+ console.log(` ${line}`);
124
+ });
125
+ return true;
126
+ }
127
+
128
+ function outputError(error) {
129
+ const envelope = {
130
+ error: {
131
+ code: error.code || 110,
132
+ type: error.type || "internal_error",
133
+ message: error.message,
134
+ recoverable: error.recoverable || false,
135
+ suggestions: error.suggestions || [],
136
+ },
137
+ };
138
+ if (humanMode) {
139
+ process.stderr.write(`${envelope.error.type}: ${envelope.error.message}\n`);
140
+ if (envelope.error.suggestions.length) {
141
+ envelope.error.suggestions.forEach((s) =>
142
+ process.stderr.write(` → ${s}\n`),
143
+ );
144
+ }
145
+ } else {
146
+ console.log(JSON.stringify(compactMode ? compactKeys(envelope) : envelope));
147
+ }
148
+ process.exit(envelope.error.code);
149
+ }
150
+
151
+ function requireServer(message) {
152
+ if (hasServer) return true;
153
+ outputError({
154
+ code: 85,
155
+ type: "invalid_argument",
156
+ message:
157
+ message ||
158
+ "This command requires SUPERCLI_SERVER. Export SUPERCLI_SERVER and run: supercli sync",
159
+ recoverable: false,
160
+ });
161
+ return false;
162
+ }
163
+
164
+ function userFlags() {
165
+ const f = {};
166
+ for (const [k, v] of Object.entries(flags)) {
167
+ if (!RESERVED_FLAGS.includes(k)) f[k] = v;
168
+ }
169
+ return f;
170
+ }
171
+
172
+ async function readStdin() {
173
+ // Only read stdin if data is actually being piped
174
+ if (process.stdin.isTTY) return null;
175
+ return new Promise((resolve) => {
176
+ let data = "";
177
+ let resolved = false;
178
+ process.stdin.setEncoding("utf-8");
179
+ process.stdin.on("data", (chunk) => {
180
+ data += chunk;
181
+ });
182
+ process.stdin.on("end", () => {
183
+ if (resolved) return;
184
+ resolved = true;
185
+ if (!data.trim()) return resolve(null);
186
+ try {
187
+ resolve(JSON.parse(data));
188
+ } catch {
189
+ resolve({ _stdin: data.trim() });
190
+ }
191
+ });
192
+ process.stdin.on("error", () => {
193
+ if (!resolved) {
194
+ resolved = true;
195
+ resolve(null);
196
+ }
197
+ });
198
+ // Short timeout — if no data arrives, stdin is not being piped
199
+ setTimeout(() => {
200
+ if (!resolved) {
201
+ resolved = true;
202
+ process.stdin.destroy();
203
+ resolve(null);
204
+ }
205
+ }, 50);
206
+ });
207
+ }
208
+
209
+ async function main() {
210
+ try {
211
+ if (flags.server) {
212
+ require("../server/app.js");
213
+ return;
214
+ }
215
+
216
+ // Read stdin if piped
217
+ const stdinData = await readStdin();
218
+ if (stdinData) {
219
+ for (const [k, v] of Object.entries(stdinData)) {
220
+ if (!flags[k] && k !== "_stdin") flags[k] = v;
221
+ }
222
+ }
223
+
224
+ if (flags["help-json"]) {
225
+ const config = await loadConfig(SERVER);
226
+ output(buildCapabilities(config, hasServer));
227
+ return;
228
+ }
229
+
230
+ if (positional.length === 0 || positional[0] === "help") {
231
+ const config = await loadConfig(SERVER);
232
+ const namespaces = [...new Set(config.commands.map((c) => c.namespace))];
233
+ if (humanMode) {
234
+ console.log("\n ⚡ SuperCLI\n");
235
+ console.log(" Namespaces:\n");
236
+ namespaces.forEach((ns) => {
237
+ const resources = [
238
+ ...new Set(
239
+ config.commands
240
+ .filter((c) => c.namespace === ns)
241
+ .map((c) => c.resource),
242
+ ),
243
+ ];
244
+ console.log(` ${ns}`);
245
+ resources.forEach((r) => {
246
+ const actions = config.commands
247
+ .filter((c) => c.namespace === ns && c.resource === r)
248
+ .map((c) => c.action);
249
+ console.log(` └─ ${r}: ${actions.join(", ")}`);
250
+ });
251
+ });
252
+ console.log(
253
+ "\n Usage: supercli <namespace> <resource> <action> [--args]",
254
+ );
255
+ if (hasServer) console.log(" Sync: supercli sync");
256
+ console.log(
257
+ " MCP: supercli mcp list | supercli mcp add <name> --url <url> | supercli mcp remove <name>",
258
+ );
259
+ console.log(
260
+ " Skills: supercli skills list | supercli skills get <ns.res.act> | supercli skills teach",
261
+ );
262
+ if (config.features?.ask || process.env.OPENAI_BASE_URL) {
263
+ console.log(' AI: supercli ask "<your natural language query>"');
264
+ }
265
+ console.log(" Server: supercli --server");
266
+ console.log(
267
+ " Flags: --json | --human | --compact | --schema | --help-json | --server\n",
268
+ );
269
+ } else {
270
+ output({
271
+ version: "1.0",
272
+ namespaces: namespaces.map((ns) => ({
273
+ name: ns,
274
+ resources: [
275
+ ...new Set(
276
+ config.commands
277
+ .filter((c) => c.namespace === ns)
278
+ .map((c) => c.resource),
279
+ ),
280
+ ].map((r) => ({
281
+ name: r,
282
+ actions: config.commands
283
+ .filter((c) => c.namespace === ns && c.resource === r)
284
+ .map((c) => c.action),
285
+ })),
286
+ })),
287
+ });
288
+ }
289
+ return;
290
+ }
291
+
292
+ if (positional[0] === "config") {
293
+ if (positional[1] === "show") {
294
+ const info = await showConfig();
295
+ output(info);
296
+ return;
297
+ }
298
+ outputError({
299
+ code: 85,
300
+ type: "invalid_argument",
301
+ message: "Unknown config subcommand. Use: show",
302
+ recoverable: false,
303
+ });
304
+ return;
305
+ }
306
+
307
+ if (positional[0] === "ask") {
308
+ const config = await loadConfig(SERVER);
309
+ await handleAskCommand({
310
+ positional,
311
+ config,
312
+ flags,
313
+ context: { server: SERVER || "", config },
314
+ humanMode,
315
+ output,
316
+ outputError,
317
+ });
318
+ return;
319
+ }
320
+
321
+ if (hasServer && positional[0] === "sync") {
322
+ await syncConfig(SERVER);
323
+ output({ ok: true, message: "Config synced" });
324
+ return;
325
+ }
326
+
327
+ if (positional[0] === "mcp") {
328
+ await handleMcpRegistryCommand({
329
+ positional,
330
+ flags,
331
+ humanMode,
332
+ output,
333
+ outputHumanTable,
334
+ outputError,
335
+ setMcpServer,
336
+ removeMcpServer,
337
+ listMcpServers,
338
+ });
339
+ return;
340
+ }
341
+
342
+ if (positional[0] === "skills") {
343
+ const config = await loadConfig(SERVER);
344
+ handleSkillsCommand({
345
+ positional,
346
+ flags,
347
+ config,
348
+ humanMode,
349
+ output,
350
+ outputHumanTable,
351
+ outputError,
352
+ });
353
+ return;
354
+ }
355
+
356
+ if (positional[0] === "commands") {
357
+ const config = await loadConfig(SERVER);
358
+ const rows = config.commands.map((c) => ({
359
+ command: `${c.namespace} ${c.resource} ${c.action}`,
360
+ description: c.description || "",
361
+ adapter: c.adapter,
362
+ args: (c.args || [])
363
+ .map((a) => `--${a.name}${a.required ? "*" : ""}`)
364
+ .join(" "),
365
+ }));
366
+ if (humanMode) {
367
+ console.log("\n ⚡ Commands\n");
368
+ outputHumanTable(rows, [
369
+ { key: "command", label: "Command" },
370
+ { key: "adapter", label: "Adapter" },
371
+ { key: "args", label: "Args" },
372
+ { key: "description", label: "Description" },
373
+ ]);
374
+ console.log("");
375
+ } else {
376
+ output({ version: "1.0", commands: rows });
377
+ }
378
+ return;
379
+ }
380
+
381
+ if (positional[0] === "inspect") {
382
+ if (positional.length < 4) {
383
+ outputError({
384
+ code: 85,
385
+ type: "invalid_argument",
386
+ message: "Usage: supercli inspect <namespace> <resource> <action>",
387
+ recoverable: false,
388
+ });
389
+ return;
390
+ }
391
+ const config = await loadConfig(SERVER);
392
+ const cmd = config.commands.find(
393
+ (c) =>
394
+ c.namespace === positional[1] &&
395
+ c.resource === positional[2] &&
396
+ c.action === positional[3],
397
+ );
398
+ if (!cmd) {
399
+ outputError({
400
+ code: 92,
401
+ type: "resource_not_found",
402
+ message: `Command ${positional[1]}.${positional[2]}.${positional[3]} not found`,
403
+ suggestions: ["Run: supercli commands"],
404
+ });
405
+ return;
406
+ }
407
+ const spec = {
408
+ version: "1.0",
409
+ command: `${cmd.namespace}.${cmd.resource}.${cmd.action}`,
410
+ description: cmd.description,
411
+ adapter: cmd.adapter,
412
+ adapterConfig: cmd.adapterConfig,
413
+ args: cmd.args,
414
+ input_schema: {
415
+ type: "object",
416
+ properties: (cmd.args || []).reduce((acc, a) => {
417
+ acc[a.name] = { type: a.type || "string" };
418
+ return acc;
419
+ }, {}),
420
+ required: (cmd.args || [])
421
+ .filter((a) => a.required)
422
+ .map((a) => a.name),
423
+ },
424
+ side_effects: !!cmd.mutation,
425
+ risk_level: cmd.risk_level || "safe",
426
+ };
427
+ if (humanMode) {
428
+ console.log(`\n ⚡ ${spec.command}\n`);
429
+ console.log(` Description: ${spec.description || "(none)"}`);
430
+ console.log(` Adapter: ${spec.adapter}`);
431
+ console.log(` Risk: ${spec.risk_level}`);
432
+ console.log(` Side effects: ${spec.side_effects ? "yes" : "no"}`);
433
+ if (cmd.args && cmd.args.length) {
434
+ console.log("\n Arguments:");
435
+ cmd.args.forEach((a) =>
436
+ console.log(
437
+ ` --${a.name} (${a.type || "string"})${a.required ? " [required]" : ""}`,
438
+ ),
439
+ );
440
+ }
441
+ console.log("");
442
+ } else {
443
+ output(spec);
444
+ }
445
+ return;
446
+ }
447
+
448
+ if (positional[0] === "plan") {
449
+ if (positional.length < 4) {
450
+ outputError({
451
+ code: 85,
452
+ type: "invalid_argument",
453
+ message:
454
+ "Usage: supercli plan <namespace> <resource> <action> [--args]",
455
+ recoverable: false,
456
+ });
457
+ return;
458
+ }
459
+ const config = await loadConfig(SERVER);
460
+ const cmd = config.commands.find(
461
+ (c) =>
462
+ c.namespace === positional[1] &&
463
+ c.resource === positional[2] &&
464
+ c.action === positional[3],
465
+ );
466
+ if (!cmd) {
467
+ outputError({
468
+ code: 92,
469
+ type: "resource_not_found",
470
+ message: `Command ${positional[1]}.${positional[2]}.${positional[3]} not found`,
471
+ suggestions: ["Run: supercli commands"],
472
+ });
473
+ return;
474
+ }
475
+ const args = userFlags();
476
+ if (!hasServer) {
477
+ const localPlan = buildLocalPlan(cmd, args);
478
+ if (humanMode) outputHumanPlan(localPlan);
479
+ else output(localPlan);
480
+ return;
481
+ }
482
+ try {
483
+ const r = await fetch(`${SERVER}/api/plans`, {
484
+ method: "POST",
485
+ headers: { "Content-Type": "application/json" },
486
+ body: JSON.stringify({
487
+ command: `${cmd.namespace}.${cmd.resource}.${cmd.action}`,
488
+ args,
489
+ cmd,
490
+ }),
491
+ });
492
+ const plan = annotateServerPlan(await r.json());
493
+ if (humanMode) outputHumanPlan(plan);
494
+ else output(plan);
495
+ } catch (err) {
496
+ outputError({
497
+ code: 105,
498
+ type: "integration_error",
499
+ message: `Failed to create plan: ${err.message}`,
500
+ recoverable: true,
501
+ });
502
+ }
503
+ return;
504
+ }
505
+
506
+ if (positional[0] === "execute" && positional.length === 2) {
507
+ if (
508
+ !requireServer(
509
+ "This command requires SUPERCLI_SERVER and a persisted plan. Local plans from `supercli plan` are preview-only.",
510
+ )
511
+ )
512
+ return;
513
+ const planId = positional[1];
514
+ try {
515
+ const r = await fetch(`${SERVER}/api/plans/${planId}/execute`, {
516
+ method: "POST",
517
+ });
518
+ const result = await r.json();
519
+ output(result);
520
+ } catch (err) {
521
+ outputError({
522
+ code: 105,
523
+ type: "integration_error",
524
+ message: `Failed to execute plan: ${err.message}`,
525
+ recoverable: true,
526
+ });
527
+ }
528
+ return;
529
+ }
530
+
531
+ if (positional.length === 1) {
532
+ const config = await loadConfig(SERVER);
533
+ const cmds = config.commands.filter((c) => c.namespace === positional[0]);
534
+ const resources = [...new Set(cmds.map((c) => c.resource))];
535
+ if (resources.length === 0) {
536
+ outputError({
537
+ code: 92,
538
+ type: "resource_not_found",
539
+ message: `Namespace '${positional[0]}' not found`,
540
+ suggestions: ["Run: supercli help"],
541
+ });
542
+ return;
543
+ }
544
+ if (humanMode) {
545
+ console.log(`\n ⚡ ${positional[0]}\n`);
546
+ console.log(" Resources:\n");
547
+ resources.forEach((r) => {
548
+ const actions = cmds
549
+ .filter((c) => c.resource === r)
550
+ .map((c) => c.action);
551
+ console.log(` ${r}: ${actions.join(", ")}`);
552
+ });
553
+ console.log("");
554
+ } else {
555
+ output({ namespace: positional[0], resources });
556
+ }
557
+ return;
558
+ }
559
+
560
+ if (positional.length === 2) {
561
+ const config = await loadConfig(SERVER);
562
+ const actions = config.commands
563
+ .filter(
564
+ (c) => c.namespace === positional[0] && c.resource === positional[1],
565
+ )
566
+ .map((c) => c.action);
567
+ if (actions.length === 0) {
568
+ outputError({
569
+ code: 92,
570
+ type: "resource_not_found",
571
+ message: `Resource '${positional[0]}.${positional[1]}' not found`,
572
+ suggestions: [`Run: supercli ${positional[0]}`],
573
+ });
574
+ return;
575
+ }
576
+ if (humanMode) {
577
+ console.log(`\n ⚡ ${positional[0]}.${positional[1]}\n`);
578
+ console.log(" Actions:\n");
579
+ actions.forEach((a) => console.log(` ${a}`));
580
+ console.log("");
581
+ } else {
582
+ output({ namespace: positional[0], resource: positional[1], actions });
583
+ }
584
+ return;
585
+ }
586
+
587
+ const [namespace, resource, action] = positional;
588
+ const config = await loadConfig(SERVER);
589
+ const cmd = config.commands.find(
590
+ (c) =>
591
+ c.namespace === namespace &&
592
+ c.resource === resource &&
593
+ c.action === action,
594
+ );
595
+ if (!cmd) {
596
+ outputError({
597
+ code: 92,
598
+ type: "resource_not_found",
599
+ message: `Command ${namespace}.${resource}.${action} not found`,
600
+ suggestions: [
601
+ "Run: supercli commands",
602
+ `Run: supercli ${namespace} ${resource}`,
603
+ ],
604
+ });
605
+ return;
606
+ }
607
+
608
+ if (flags.schema) {
609
+ output({
610
+ version: "1.0",
611
+ command: `${namespace}.${resource}.${action}`,
612
+ input_schema: {
613
+ type: "object",
614
+ properties: (cmd.args || []).reduce((acc, a) => {
615
+ acc[a.name] = { type: a.type || "string" };
616
+ return acc;
617
+ }, {}),
618
+ required: (cmd.args || [])
619
+ .filter((a) => a.required)
620
+ .map((a) => a.name),
621
+ },
622
+ output_schema: cmd.output || { type: "object" },
623
+ });
624
+ return;
625
+ }
626
+
627
+ // Validate required args
628
+ const uFlags = userFlags();
629
+ const missingArgs = (cmd.args || []).filter(
630
+ (a) => a.required && !uFlags[a.name],
631
+ );
632
+ if (missingArgs.length > 0) {
633
+ outputError({
634
+ code: 82,
635
+ type: "validation_error",
636
+ message: `Missing required arguments: ${missingArgs.map((a) => "--" + a.name).join(", ")}`,
637
+ suggestions: [
638
+ `Run: supercli inspect ${namespace} ${resource} ${action}`,
639
+ ],
640
+ });
641
+ return;
642
+ }
643
+
644
+ const start = Date.now();
645
+ const result = await execute(cmd, uFlags, { server: SERVER || "", config });
646
+ const duration = Date.now() - start;
647
+
648
+ const envelope = {
649
+ version: "1.0",
650
+ command: `${namespace}.${resource}.${action}`,
651
+ duration_ms: duration,
652
+ data: result,
653
+ };
654
+
655
+ // Post job record (async, non-blocking)
656
+ if (hasServer) {
657
+ fetch(`${SERVER}/api/jobs`, {
658
+ method: "POST",
659
+ headers: { "Content-Type": "application/json" },
660
+ body: JSON.stringify({
661
+ command: `${namespace}.${resource}.${action}`,
662
+ args: uFlags,
663
+ status: "success",
664
+ duration_ms: duration,
665
+ timestamp: new Date().toISOString(),
666
+ }),
667
+ }).catch(() => {}); // silent fail
668
+ }
669
+
670
+ if (humanMode) {
671
+ process.stderr.write(
672
+ ` ⚡ ${namespace}.${resource}.${action} (${duration}ms)\n`,
673
+ );
674
+ if (Array.isArray(result)) {
675
+ outputHumanTable(
676
+ result.slice(0, 20),
677
+ Object.keys(result[0] || {})
678
+ .slice(0, 6)
679
+ .map((k) => ({ key: k, label: k })),
680
+ );
681
+ if (result.length > 20)
682
+ console.log(` ... and ${result.length - 20} more`);
683
+ } else if (typeof result === "object") {
684
+ for (const [k, v] of Object.entries(result)) {
685
+ const val = typeof v === "object" ? JSON.stringify(v) : v;
686
+ console.log(` ${k}: ${val}`);
687
+ }
688
+ }
689
+ console.log("");
690
+ } else {
691
+ output(envelope);
692
+ }
693
+ } catch (err) {
694
+ outputError({
695
+ code: err.code || 110,
696
+ type: err.type || "internal_error",
697
+ message: err.message,
698
+ recoverable: !!err.recoverable,
699
+ suggestions: err.suggestions || [],
700
+ });
701
+ }
702
+ }
703
+
704
+ main();