@nexusbloom/cli 0.1.7 → 0.1.8

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +111 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexusbloom/cli",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "NexusBloom CLI — run tools and workflows from your terminal",
5
5
  "type": "module",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -159,7 +159,7 @@ program
159
159
  .argument("<slug>", "Tool slug")
160
160
  .argument("[args...]", "Key=value arguments (e.g. domain=example.com)")
161
161
  .option("-k, --key <key>", "API key (or set NEXUSBLOOM_API_KEY env var)")
162
- .option("-i, --input <json>", "Inline JSON input (e.g. '{\"domain\":\"example.com\"}')")
162
+ .option("-i, --input <json>", "Inline JSON input or @- for stdin (e.g. '{\"domain\":\"example.com\"}' or @-)")
163
163
  .option("-f, --file <path>", "Read input from a JSON file")
164
164
  .option("--local", "Execute coreLogic locally (fetches tool source, caches it)")
165
165
  .action(async (slug, args, opts, cmd) => {
@@ -182,8 +182,23 @@ program
182
182
  // ── Build input body ────────────────────────────────────────────────
183
183
  let body = {};
184
184
 
185
- // 1. --input flag takes highest priority
186
- if (opts.input) {
185
+ // 0. Detect stdin pipe (auto-detect or explicit @-)
186
+ const isPiped = opts.input === "@-" || opts.input === "@stdin" || (!opts.input && !opts.file && !process.stdin.isTTY);
187
+ if (isPiped) {
188
+ try {
189
+ const stdin = await readStdin();
190
+ if (stdin && stdin.trim()) {
191
+ body = JSON.parse(stdin);
192
+ }
193
+ } catch {
194
+ if (jsonMode) return console.log(JSON.stringify({ error: "Invalid JSON from stdin" }));
195
+ console.error(chalk.red("Error: Invalid JSON received from stdin"));
196
+ process.exit(1);
197
+ }
198
+ }
199
+
200
+ // 1. --input flag takes highest priority (skip if @- was used)
201
+ if (opts.input && opts.input !== "@-" && opts.input !== "@stdin") {
187
202
  try {
188
203
  body = JSON.parse(opts.input);
189
204
  } catch {
@@ -409,9 +424,21 @@ program
409
424
  program
410
425
  .command("info")
411
426
  .description("Show tool schema and details")
412
- .argument("<slug>", "Tool slug")
427
+ .argument("<slug>", "Tool slug or abbreviation")
413
428
  .action(async (slug, opts, cmd) => {
414
429
  const jsonMode = (cmd?.parent?.opts().json) || false;
430
+
431
+ // Resolve abbreviation
432
+ try {
433
+ const res = await fetch(`${API_BASE}/api/tools`);
434
+ const { tools } = await res.json();
435
+ const slugs = (tools || []).map((t) => t.slug);
436
+ const abbrMap = generateAbbreviations(slugs);
437
+ if (abbrMap[slug] && slugs.includes(abbrMap[slug])) {
438
+ if (!jsonMode) console.log(chalk.dim(` Resolved: ${slug} → ${abbrMap[slug]}`));
439
+ slug = abbrMap[slug];
440
+ }
441
+ } catch { /* ignore */ }
415
442
  try {
416
443
  const res = await fetch(`${API_BASE}/api/run/${slug}`);
417
444
  if (!res.ok) {
@@ -611,8 +638,6 @@ cacheCmd
611
638
  console.log();
612
639
  });
613
640
 
614
- program.parse(process.argv);
615
-
616
641
  // ─── Abbreviation generator ──────────────────────────────────────────────────
617
642
 
618
643
  function generateAbbreviations(slugs) {
@@ -645,16 +670,32 @@ function generateAbbreviations(slugs) {
645
670
  program
646
671
  .command("abbr")
647
672
  .description("Show tool abbreviations")
648
- .action(async () => {
673
+ .argument("[query]", "Search/filter abbreviations (e.g. 'ip' shows all IP-related tools)")
674
+ .action(async (query, opts, cmd) => {
649
675
  try {
650
676
  const res = await fetch(`${API_BASE}/api/tools`);
651
677
  const { tools } = await res.json();
652
678
  const slugs = (tools || []).map((t) => t.slug);
653
679
  const abbrMap = generateAbbreviations(slugs);
654
680
 
655
- console.log(chalk.bold("\n Tool Abbreviations:\n"));
656
- slugs.forEach((slug) => {
657
- console.log(` ${chalk.cyan(abbrMap[slug].padEnd(15))} → ${chalk.dim(slug)}`);
681
+ // Filter by query if provided
682
+ let filtered = slugs;
683
+ if (query) {
684
+ const q = query.toLowerCase();
685
+ filtered = slugs.filter(
686
+ (slug) => slug.includes(q) || abbrMap[slug].includes(q) || (tools || []).find(t => t.slug === slug)?.name?.toLowerCase().includes(q)
687
+ );
688
+ if (filtered.length === 0) {
689
+ console.log(chalk.yellow(` No abbreviations matching "${query}"`));
690
+ return;
691
+ }
692
+ }
693
+
694
+ console.log(chalk.bold(`\n Tool Abbreviations${query ? ` matching "${query}"` : ""}:\n`));
695
+ filtered.forEach((slug) => {
696
+ const tool = (tools || []).find(t => t.slug === slug);
697
+ const name = tool?.name || slug;
698
+ console.log(` ${chalk.cyan(abbrMap[slug].padEnd(15))} → ${chalk.dim(slug.padEnd(30))} ${chalk.gray(name)}`);
658
699
  });
659
700
  console.log();
660
701
  } catch (err) {
@@ -669,6 +710,8 @@ program
669
710
  .description("Create JSON input for tools (pipe into nxb run)")
670
711
  .option("-t, --template <slug>", "Generate JSON template for a specific tool")
671
712
  .option("-i, --interactive", "Interactive mode - prompts for each field")
713
+ .option("-s, --set <key=value...>", "Set specific field values (e.g. -s count=10 format=csv)")
714
+ .option("-a, --add <json>", "Merge additional JSON into the output (e.g. -a '{\"count\":20}')")
672
715
  .action(async (opts, cmd) => {
673
716
  const jsonMode = cmd.parent.opts().json;
674
717
 
@@ -683,11 +726,12 @@ program
683
726
  const manifest = data.data?.manifest || data.manifest || data;
684
727
  const schema = manifest.input_schema || {};
685
728
 
729
+ let result = {};
730
+
686
731
  if (opts.interactive) {
687
732
  // Interactive mode
688
733
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
689
734
  const ask = (q) => new Promise((r) => rl.question(q, r));
690
- const result = {};
691
735
 
692
736
  if (schema.properties) {
693
737
  for (const [key, prop] of Object.entries(schema.properties)) {
@@ -696,35 +740,75 @@ program
696
740
  const def = prop.default ? ` [${prop.default}]` : "";
697
741
  const answer = await ask(chalk.cyan(` ${key}${hint}${def}: `));
698
742
  const val = answer || prop.default;
699
- if (val) result[key] = prop.type === "number" ? Number(val) : val;
743
+ if (val !== undefined && val !== "") {
744
+ result[key] = prop.type === "number" || prop.type === "integer" ? Number(val) : val;
745
+ }
700
746
  }
701
747
  }
702
748
  rl.close();
703
- if (jsonMode) return console.log(JSON.stringify(result, null, 2));
704
- console.log(JSON.stringify(result));
705
749
  } else {
706
750
  // Generate template with defaults
707
- const template = {};
708
751
  if (schema.properties) {
709
752
  for (const [key, prop] of Object.entries(schema.properties)) {
710
753
  if (prop.default !== undefined) {
711
- template[key] = prop.default;
754
+ result[key] = prop.default;
712
755
  } else if (prop.type === "string") {
713
- template[key] = prop.enum ? prop.enum[0] : "";
756
+ result[key] = prop.enum ? prop.enum[0] : "";
714
757
  } else if (prop.type === "number" || prop.type === "integer") {
715
- template[key] = 0;
758
+ result[key] = 0;
716
759
  } else if (prop.type === "boolean") {
717
- template[key] = false;
760
+ result[key] = false;
761
+ } else if (prop.type === "array") {
762
+ result[key] = [];
763
+ } else if (prop.type === "object") {
764
+ result[key] = {};
718
765
  }
719
766
  }
720
767
  }
721
- if (jsonMode) return console.log(JSON.stringify(template, null, 2));
722
- console.log(JSON.stringify(template, null, 2));
723
768
  }
769
+
770
+ // ── Apply --set overrides ──────────────────────────────────────
771
+ if (opts.set) {
772
+ const sets = Array.isArray(opts.set) ? opts.set : [opts.set];
773
+ for (const pair of sets) {
774
+ const eqIdx = pair.indexOf("=");
775
+ if (eqIdx > 0) {
776
+ const k = pair.slice(0, eqIdx);
777
+ let v = pair.slice(eqIdx + 1);
778
+ // Try parsing as number or boolean
779
+ if (v === "true") v = true;
780
+ else if (v === "false") v = false;
781
+ else if (!isNaN(v) && v.trim() !== "") v = Number(v);
782
+ result[k] = v;
783
+ }
784
+ }
785
+ }
786
+
787
+ // ── Apply --add merge ──────────────────────────────────────────
788
+ if (opts.add) {
789
+ try {
790
+ const extra = typeof opts.add === "string" ? JSON.parse(opts.add) : opts.add;
791
+ result = { ...result, ...extra };
792
+ } catch {
793
+ console.error(chalk.red("Error: --add must be valid JSON"));
794
+ process.exit(1);
795
+ }
796
+ }
797
+
798
+ const output = JSON.stringify(result, null, jsonMode ? 2 : 0);
799
+ if (jsonMode) return console.log(output);
800
+ console.log(output);
724
801
  } else {
725
- console.log(chalk.yellow("Usage: nxb json --template <slug> [--interactive]"));
726
- console.log(chalk.dim(" Generate JSON input for a tool that can be piped into nxb run"));
727
- console.log(chalk.dim(" Example: nxb json --template summarize | nxb run summarize --input @-"));
802
+ console.log(chalk.yellow("Usage:"));
803
+ console.log(chalk.dim(" nxb json --template <slug> Generate JSON template with defaults"));
804
+ console.log(chalk.dim(" nxb json --template <slug> -i Interactive mode (prompts for fields)"));
805
+ console.log(chalk.dim(" nxb json --template <slug> -s count=10 Set specific field values"));
806
+ console.log(chalk.dim(" nxb json --template <slug> -a '{\"x\":1}' Merge additional JSON"));
807
+ console.log(chalk.dim(""));
808
+ console.log(chalk.dim(" Examples:"));
809
+ console.log(chalk.dim(" nxb json --template data-generator | nxb run data-generator --input @-"));
810
+ console.log(chalk.dim(" nxb run data-generator --input \"$(nxb json --template dg -s count=5)\""));
811
+ console.log(chalk.dim(" nxb json --template color-palette -s color=#4A5D23 --json"));
728
812
  }
729
813
  });
730
814
 
@@ -738,4 +822,6 @@ function readStdin() {
738
822
  process.stdin.on("end", () => resolve(chunks.join("")));
739
823
  if (process.stdin.isTTY) resolve("");
740
824
  });
741
- }
825
+ }
826
+
827
+ program.parse(process.argv);