hngr 0.5.1 → 0.6.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.
package/README.md CHANGED
@@ -59,7 +59,10 @@ install replaces the existing target; `--force` is not needed.
59
59
 
60
60
  Interactive runs end with a borderless summary on stderr (canonical path, universal
61
61
  agents, symlinked/copied agents); stdout carries the JSON payload only when piped or when
62
- `--json` is passed.
62
+ `--json` is passed. After a successful interactive install the CLI asks once whether to
63
+ also install `system/find-capabilities` (the catalog's discovery skill, which finds both
64
+ Skills and MCP servers) for the same scope and agents; it is skipped silently when a copy
65
+ already exists in either scope, and a failed follow-up install only prints a warning.
63
66
 
64
67
  MCP installs ask for the same scope and then for the MCP targets to write, with an
65
68
  `All targets` entry; targets whose config already exists are preselected, and Claude
package/dist/index.mjs CHANGED
@@ -5279,7 +5279,7 @@ function isYargsInstance(y) {
5279
5279
  // ../../node_modules/.pnpm/yargs@18.2.0/node_modules/yargs/index.mjs
5280
5280
  const Yargs = YargsFactory(esm_default);
5281
5281
  // package.json
5282
- var version = "0.5.1";
5282
+ var version = "0.6.0";
5283
5283
  // src/generated/completion.ts
5284
5284
  function isCompletionShell(value) {
5285
5285
  return value === "bash" || value === "zsh" || value === "fish";
@@ -6429,6 +6429,23 @@ var y = class {
6429
6429
  }
6430
6430
  }
6431
6431
  };
6432
+ var r = class extends y {
6433
+ get cursor() {
6434
+ return this.value ? 0 : 1;
6435
+ }
6436
+ get _value() {
6437
+ return this.cursor === 0;
6438
+ }
6439
+ constructor(t) {
6440
+ super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
6441
+ this.value = this._value;
6442
+ }), this.on("confirm", (i) => {
6443
+ this.output.write(import_src.cursor.move(0, -1)), this.value = i, this.state = "submit", this.close();
6444
+ }), this.on("cursor", () => {
6445
+ this.value = !this.value;
6446
+ });
6447
+ }
6448
+ };
6432
6449
  var a = class extends y {
6433
6450
  options;
6434
6451
  cursor = 0;
@@ -6633,6 +6650,35 @@ const limitOptions = ({ cursor: l, options: e, style: w, output: p = process.std
6633
6650
  for (const t of s) for (const n of t) x.push(n);
6634
6651
  return c && x.push(M), x;
6635
6652
  };
6653
+ const confirm = (e) => {
6654
+ const a = e.active ?? "Yes", o = e.inactive ?? "No";
6655
+ return new r({
6656
+ active: a,
6657
+ inactive: o,
6658
+ signal: e.signal,
6659
+ input: e.input,
6660
+ output: e.output,
6661
+ initialValue: e.initialValue ?? true,
6662
+ render() {
6663
+ const i = e.withGuide ?? settings.withGuide, u = `${symbol(this.state)} `, l = i ? `${styleText("gray", S_BAR)} ` : "", f = wrapTextWithPrefix(e.output, e.message, l, u), s = `${i ? `${styleText("gray", S_BAR)}
6664
+ ` : ""}${f}
6665
+ `, c = this.value ? a : o;
6666
+ switch (this.state) {
6667
+ case "submit": return `${s}${i ? `${styleText("gray", S_BAR)} ` : ""}${styleText("dim", c)}`;
6668
+ case "cancel": return `${s}${i ? `${styleText("gray", S_BAR)} ` : ""}${styleText(["strikethrough", "dim"], c)}${i ? `
6669
+ ${styleText("gray", S_BAR)}` : ""}`;
6670
+ default: {
6671
+ const r = i ? `${styleText("cyan", S_BAR)} ` : "", g = i ? styleText("cyan", S_BAR_END) : "";
6672
+ return `${s}${r}${this.value ? `${styleText("green", S_RADIO_ACTIVE)} ${a}` : `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", a)}`}${e.vertical ? i ? `
6673
+ ${styleText("cyan", S_BAR)} ` : `
6674
+ ` : ` ${styleText("dim", "/")} `}${this.value ? `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", o)}` : `${styleText("green", S_RADIO_ACTIVE)} ${o}`}
6675
+ ${g}
6676
+ `;
6677
+ }
6678
+ }
6679
+ }
6680
+ }).prompt();
6681
+ };
6636
6682
  const MULTISELECT_INSTRUCTIONS = [
6637
6683
  `${styleText("dim", "↑/↓")} to navigate`,
6638
6684
  `${styleText("dim", "Space:")} select`,
@@ -6905,6 +6951,14 @@ function clackPrompter(streams = {}) {
6905
6951
  initialValue
6906
6952
  }));
6907
6953
  },
6954
+ async confirm(message, initialValue = true) {
6955
+ return unwrapPrompt(await confirm({
6956
+ message,
6957
+ input,
6958
+ output,
6959
+ initialValue
6960
+ }));
6961
+ },
6908
6962
  async multiselect(message, options, initialValues = [], hint) {
6909
6963
  if (hint) output.write(`${import_picocolors.default.dim(hint)}\n`);
6910
6964
  return unwrapPrompt(await multiselect({
@@ -7101,7 +7155,7 @@ function buildParser(onMatched) {
7101
7155
  }), (argv) => {
7102
7156
  onMatched({
7103
7157
  argv,
7104
- run: async (ctx, matchedArgv) => (await import("./skills-C3cphIAl.mjs")).add(ctx, matchedArgv)
7158
+ run: async (ctx, matchedArgv) => (await import("./skills-B92ip3mJ.mjs")).add(ctx, matchedArgv)
7105
7159
  });
7106
7160
  });
7107
7161
  skills.command("publish <paths..>", "Publish a local skill directory as a capability or a new version", (publish) => publish.option("namespace", {
@@ -7129,7 +7183,7 @@ function buildParser(onMatched) {
7129
7183
  }), (argv) => {
7130
7184
  onMatched({
7131
7185
  argv,
7132
- run: async (ctx, matchedArgv) => (await import("./skills-C3cphIAl.mjs")).publish(ctx, matchedArgv)
7186
+ run: async (ctx, matchedArgv) => (await import("./skills-B92ip3mJ.mjs")).publish(ctx, matchedArgv)
7133
7187
  });
7134
7188
  });
7135
7189
  return skills.demandCommand(1);
@@ -787,6 +787,47 @@ async function publish(ctx, argv) {
787
787
  items
788
788
  });
789
789
  }
790
+ const FIND_CAPABILITIES = "find-capabilities";
791
+ /** The follow-up question needs a yes/no prompter on both ends of a terminal. */
792
+ function canConfirm(ctx) {
793
+ return ctx.io.stdin.isTTY && ctx.io.stdout.isTTY && ctx.io.prompts?.confirm !== void 0;
794
+ }
795
+ /**
796
+ * Whether any scope the caller would install into already holds find-capabilities.
797
+ * A project install does not ask again when the global store has it, and the
798
+ * reverse, so the offer appears only when nothing is installed yet.
799
+ */
800
+ async function findCapabilitiesInstalled(ctx, agents, options) {
801
+ const scopes = options.dir ? [options.global] : [false, true];
802
+ for (const global of scopes) {
803
+ const plan = planInstall(process.cwd(), ctx.env, FIND_CAPABILITIES, agents, {
804
+ ...options,
805
+ global
806
+ });
807
+ const paths = [...plan.canonical ? [plan.canonical] : [], ...plan.targets.map((target) => target.path)];
808
+ for (const path of paths) if (await pathExists(path)) return true;
809
+ }
810
+ return false;
811
+ }
812
+ /**
813
+ * Offers the catalog's capability-discovery skill after a successful install so
814
+ * an agent that just gained one capability can find the rest, Skills and MCP
815
+ * servers alike. It is a convenience, not part of the request: an
816
+ * already-installed copy or a failed download never makes the add itself fail
817
+ * (a cancelled prompt still exits 130).
818
+ */
819
+ async function offerFindCapabilities(ctx, connection, destNames, agents, options) {
820
+ const confirm = ctx.io.prompts?.confirm;
821
+ if (!confirm || destNames.includes(FIND_CAPABILITIES)) return;
822
+ if (await findCapabilitiesInstalled(ctx, agents, options)) return;
823
+ if (!await confirm(`Also install ${FIND_CAPABILITIES} (discover more capabilities)?`, true)) return;
824
+ try {
825
+ await addOne(ctx, connection, parseSkillPath(FIND_CAPABILITIES), FIND_CAPABILITIES, agents, options.dir, options.global, options.copy, { version: null });
826
+ } catch (err) {
827
+ if (err instanceof HngrError && err.exitCode !== 1) throw err;
828
+ ctx.summary(`warning: could not install ${FIND_CAPABILITIES}: ${err instanceof Error ? err.message : String(err)}`);
829
+ }
830
+ }
790
831
  async function add(ctx, argv) {
791
832
  const rawPaths = positionalPaths(argv.paths);
792
833
  if (rawPaths.length === 0) throw usage("No skill path given", "Pass one or more `<name>` or `<namespace>/<name>` arguments.");
@@ -812,6 +853,11 @@ async function add(ctx, argv) {
812
853
  failure: failureResult
813
854
  });
814
855
  if (ctx.flags.json || !ctx.io.stdout.isTTY) ctx.writePayload({ results });
856
+ if (!ctx.flags.json && !ctx.flags.dryRun && !ctx.flags.quiet && !failed && canConfirm(ctx)) await offerFindCapabilities(ctx, connection, parsedPaths.map((parsed) => as ?? parsed.name), agents, {
857
+ dir,
858
+ global,
859
+ copy
860
+ });
815
861
  if (!ctx.flags.json && !ctx.flags.dryRun && !ctx.flags.quiet && !failed && ctx.io.stderr.isTTY) ctx.io.prompts?.outro?.("Done! Review skills before use; they run with full agent permissions.");
816
862
  return failed ? 1 : 0;
817
863
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hngr",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "The Hangar CLI: install skills and MCP servers into coding agents, and drive the Management API.",
5
5
  "keywords": [
6
6
  "hangar",