hngr 0.3.0 → 0.4.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 +6 -3
- package/dist/{auth-CgBUQUQn.mjs → auth-ByGgd_Yn.mjs} +49 -11
- package/dist/{catalog-DbGLqQ8a.mjs → catalog-Bv304SXJ.mjs} +412 -20
- package/dist/{commands-BHXSZ04F.mjs → commands-CLqLwQHY.mjs} +1 -1
- package/dist/{http-2HWxLIOq.mjs → http-DgHX7Fux.mjs} +13 -7
- package/dist/index.mjs +117 -18
- package/dist/{mcp-b6bE3ZRt.mjs → mcp-DWF_ELDU.mjs} +26 -47
- package/dist/{pagination-BbKLKSw3.mjs → pagination-BXyFxGYA.mjs} +1 -1
- package/dist/{publish-EUqgdp0G.mjs → scope-Ch6wZCgE.mjs} +24 -3
- package/dist/{skills-XDbBCC5f.mjs → skills-CxMeJ5PF.mjs} +18 -34
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as kebabCase, i as normalizeCommandName, n as COMMANDS, o as COMMAND_ALIASES, r as findCommand, t as commandCatalog } from "./catalog-
|
|
2
|
-
import { C as
|
|
3
|
-
import { t as collectPages } from "./pagination-
|
|
1
|
+
import { a as kebabCase, i as normalizeCommandName, n as COMMANDS, o as COMMAND_ALIASES, r as findCommand, t as commandCatalog } from "./catalog-Bv304SXJ.mjs";
|
|
2
|
+
import { C as HngrError, S as CancelledError, c as redactUrlForDisplay, g as resolveConnection, i as buildRequestUrl, o as redactQuery, r as apiRequest, s as redactSensitive, u as DEFAULT_SERVER_URL, w as renderError } from "./http-DgHX7Fux.mjs";
|
|
3
|
+
import { t as collectPages } from "./pagination-BXyFxGYA.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { notStrictEqual, strictEqual } from "assert";
|
|
6
6
|
import { basename, dirname, extname, join, normalize, relative, resolve } from "path";
|
|
@@ -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.
|
|
5282
|
+
var version = "0.4.0";
|
|
5283
5283
|
// src/generated/completion.ts
|
|
5284
5284
|
function isCompletionShell(value) {
|
|
5285
5285
|
return value === "bash" || value === "zsh" || value === "fish";
|
|
@@ -6499,6 +6499,32 @@ let u$1 = class u extends y {
|
|
|
6499
6499
|
});
|
|
6500
6500
|
}
|
|
6501
6501
|
};
|
|
6502
|
+
let n$1 = class n extends y {
|
|
6503
|
+
options;
|
|
6504
|
+
cursor = 0;
|
|
6505
|
+
get _selectedValue() {
|
|
6506
|
+
return this.options[this.cursor];
|
|
6507
|
+
}
|
|
6508
|
+
changeValue() {
|
|
6509
|
+
const e = this._selectedValue;
|
|
6510
|
+
this.value = e === void 0 ? void 0 : e.value;
|
|
6511
|
+
}
|
|
6512
|
+
constructor(e) {
|
|
6513
|
+
super(e, false), this.options = e.options;
|
|
6514
|
+
const o = this.options.findIndex(({ value: s }) => s === e.initialValue), t = o === -1 ? 0 : o;
|
|
6515
|
+
this.cursor = this.options[t]?.disabled ? findCursor(t, 1, this.options) : t, this.changeValue(), this.on("cursor", (s) => {
|
|
6516
|
+
switch (s) {
|
|
6517
|
+
case "left":
|
|
6518
|
+
case "up":
|
|
6519
|
+
this.cursor = findCursor(this.cursor, -1, this.options);
|
|
6520
|
+
break;
|
|
6521
|
+
case "down":
|
|
6522
|
+
case "right": this.cursor = findCursor(this.cursor, 1, this.options);
|
|
6523
|
+
}
|
|
6524
|
+
this.changeValue();
|
|
6525
|
+
});
|
|
6526
|
+
}
|
|
6527
|
+
};
|
|
6502
6528
|
var n = class extends y {
|
|
6503
6529
|
get userInputWithCursor() {
|
|
6504
6530
|
if (this.state === "submit") return this.userInput;
|
|
@@ -6534,6 +6560,8 @@ const S_STEP_ERROR = unicodeOr("▲", "x");
|
|
|
6534
6560
|
const S_STEP_SUBMIT = unicodeOr("◇", "o");
|
|
6535
6561
|
const S_BAR = unicodeOr("│", "|");
|
|
6536
6562
|
const S_BAR_END = unicodeOr("└", "—");
|
|
6563
|
+
const S_RADIO_ACTIVE = unicodeOr("●", ">");
|
|
6564
|
+
const S_RADIO_INACTIVE = unicodeOr("○", " ");
|
|
6537
6565
|
const S_CHECKBOX_ACTIVE = unicodeOr("◻", "[•]");
|
|
6538
6566
|
const S_CHECKBOX_SELECTED = unicodeOr("◼", "[+]");
|
|
6539
6567
|
const S_CHECKBOX_INACTIVE = unicodeOr("◻", "[ ]");
|
|
@@ -6720,7 +6748,64 @@ ${r ? styleText("cyan", S_BAR_END) : ""}
|
|
|
6720
6748
|
}
|
|
6721
6749
|
}
|
|
6722
6750
|
}).prompt();
|
|
6723
|
-
`${styleText("dim", "↑/↓")}`, `${styleText("dim", "Enter:")}
|
|
6751
|
+
const SELECT_INSTRUCTIONS = [`${styleText("dim", "↑/↓")} to navigate`, `${styleText("dim", "Enter:")} confirm`];
|
|
6752
|
+
const c = (t, o) => t.includes(`
|
|
6753
|
+
`) ? t.split(`
|
|
6754
|
+
`).map((d) => o(d)).join(`
|
|
6755
|
+
`) : o(t);
|
|
6756
|
+
const select = (t) => {
|
|
6757
|
+
const o = (n, m) => {
|
|
6758
|
+
if (n === void 0) return "";
|
|
6759
|
+
const s = n.label ?? String(n.value);
|
|
6760
|
+
switch (m) {
|
|
6761
|
+
case "disabled": return `${styleText("gray", S_RADIO_INACTIVE)} ${c(s, (i) => styleText("gray", i))}${n.hint ? ` ${styleText("dim", `(${n.hint ?? "disabled"})`)}` : ""}`;
|
|
6762
|
+
case "selected": return `${c(s, (i) => styleText("dim", i))}`;
|
|
6763
|
+
case "active": return `${styleText("green", S_RADIO_ACTIVE)} ${s}${n.hint ? ` ${styleText("dim", `(${n.hint})`)}` : ""}`;
|
|
6764
|
+
case "cancelled": return `${c(s, (i) => styleText(["strikethrough", "dim"], i))}`;
|
|
6765
|
+
default: return `${styleText("dim", S_RADIO_INACTIVE)} ${c(s, (i) => styleText("dim", i))}`;
|
|
6766
|
+
}
|
|
6767
|
+
}, d = t.showInstructions ?? true;
|
|
6768
|
+
return new n$1({
|
|
6769
|
+
options: t.options,
|
|
6770
|
+
signal: t.signal,
|
|
6771
|
+
input: t.input,
|
|
6772
|
+
output: t.output,
|
|
6773
|
+
initialValue: t.initialValue,
|
|
6774
|
+
render() {
|
|
6775
|
+
const n = t.withGuide ?? settings.withGuide, m = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, i = wrapTextWithPrefix(t.output, t.message, s, m), u = `${n ? `${styleText("gray", S_BAR)}
|
|
6776
|
+
` : ""}${i}
|
|
6777
|
+
`;
|
|
6778
|
+
switch (this.state) {
|
|
6779
|
+
case "submit": {
|
|
6780
|
+
const r = n ? `${styleText("gray", S_BAR)} ` : "";
|
|
6781
|
+
return `${u}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "selected"), r)}`;
|
|
6782
|
+
}
|
|
6783
|
+
case "cancel": {
|
|
6784
|
+
const r = n ? `${styleText("gray", S_BAR)} ` : "";
|
|
6785
|
+
return `${u}${wrapTextWithPrefix(t.output, o(this.options[this.cursor], "cancelled"), r)}${n ? `
|
|
6786
|
+
${styleText("gray", S_BAR)}` : ""}`;
|
|
6787
|
+
}
|
|
6788
|
+
default: {
|
|
6789
|
+
const r = n ? `${styleText("cyan", S_BAR)} ` : "", a = u.split(`
|
|
6790
|
+
`).length, p = d ? formatInstructionFooter(SELECT_INSTRUCTIONS, n) : n ? [styleText("cyan", S_BAR_END)] : [], f = p.join(`
|
|
6791
|
+
`), b = p.length + 1;
|
|
6792
|
+
return `${u}${r}${limitOptions({
|
|
6793
|
+
output: t.output,
|
|
6794
|
+
cursor: this.cursor,
|
|
6795
|
+
options: this.options,
|
|
6796
|
+
maxItems: t.maxItems,
|
|
6797
|
+
columnPadding: r.length,
|
|
6798
|
+
rowPadding: a + b,
|
|
6799
|
+
style: (g, x) => o(g, g.disabled ? "disabled" : x ? "active" : "inactive")
|
|
6800
|
+
}).join(`
|
|
6801
|
+
${r}`)}
|
|
6802
|
+
${f}
|
|
6803
|
+
`;
|
|
6804
|
+
}
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
}).prompt();
|
|
6808
|
+
};
|
|
6724
6809
|
`${styleText("gray", S_BAR)}`;
|
|
6725
6810
|
const text = (t) => new n({
|
|
6726
6811
|
validate: t.validate,
|
|
@@ -6804,6 +6889,15 @@ function clackPrompter(streams = {}) {
|
|
|
6804
6889
|
}
|
|
6805
6890
|
}));
|
|
6806
6891
|
},
|
|
6892
|
+
async select(message, options, initialValue) {
|
|
6893
|
+
return unwrapPrompt(await select({
|
|
6894
|
+
message,
|
|
6895
|
+
input,
|
|
6896
|
+
output,
|
|
6897
|
+
options,
|
|
6898
|
+
initialValue
|
|
6899
|
+
}));
|
|
6900
|
+
},
|
|
6807
6901
|
async multiselect(message, options, initialValues = []) {
|
|
6808
6902
|
return unwrapPrompt(await multiselect({
|
|
6809
6903
|
message,
|
|
@@ -6846,7 +6940,7 @@ const GLOBAL_OPTIONS = {
|
|
|
6846
6940
|
force: {
|
|
6847
6941
|
type: "boolean",
|
|
6848
6942
|
default: false,
|
|
6849
|
-
describe: "
|
|
6943
|
+
describe: "Skip destructive confirmations",
|
|
6850
6944
|
global: true
|
|
6851
6945
|
},
|
|
6852
6946
|
"dry-run": {
|
|
@@ -6911,7 +7005,7 @@ function buildParser(onMatched) {
|
|
|
6911
7005
|
parser.command("commands", "Print the command catalog", () => {}, (argv) => {
|
|
6912
7006
|
onMatched({
|
|
6913
7007
|
argv,
|
|
6914
|
-
run: async (ctx) => (await import("./commands-
|
|
7008
|
+
run: async (ctx) => (await import("./commands-CLqLwQHY.mjs")).run(ctx)
|
|
6915
7009
|
});
|
|
6916
7010
|
});
|
|
6917
7011
|
parser.command("auth", "Manage credentials", (auth) => {
|
|
@@ -6924,35 +7018,40 @@ function buildParser(onMatched) {
|
|
|
6924
7018
|
}).conflicts("password", "token"), (argv) => {
|
|
6925
7019
|
onMatched({
|
|
6926
7020
|
argv,
|
|
6927
|
-
run: async (ctx, matchedArgv) => (await import("./auth-
|
|
7021
|
+
run: async (ctx, matchedArgv) => (await import("./auth-ByGgd_Yn.mjs")).login(ctx, matchedArgv)
|
|
6928
7022
|
});
|
|
6929
7023
|
});
|
|
6930
7024
|
auth.command("logout", "Revoke the stored token and delete the local config", () => {}, (argv) => {
|
|
6931
7025
|
onMatched({
|
|
6932
7026
|
argv,
|
|
6933
|
-
run: async (ctx) => (await import("./auth-
|
|
7027
|
+
run: async (ctx) => (await import("./auth-ByGgd_Yn.mjs")).logout(ctx)
|
|
6934
7028
|
});
|
|
6935
7029
|
});
|
|
6936
|
-
auth.command("status", "Show credential source, server, principal
|
|
7030
|
+
auth.command("status", "Show credential source, server, principal, token prefix and audience", (status) => status.option("offline", {
|
|
6937
7031
|
type: "boolean",
|
|
6938
7032
|
default: false,
|
|
6939
7033
|
describe: "Skip the live GET /me check"
|
|
6940
7034
|
}), (argv) => {
|
|
6941
7035
|
onMatched({
|
|
6942
7036
|
argv,
|
|
6943
|
-
run: async (ctx, matchedArgv) => (await import("./auth-
|
|
7037
|
+
run: async (ctx, matchedArgv) => (await import("./auth-ByGgd_Yn.mjs")).status(ctx, matchedArgv)
|
|
6944
7038
|
});
|
|
6945
7039
|
});
|
|
6946
|
-
auth.command("token", "Print the access token to stdout and nothing else", () =>
|
|
7040
|
+
auth.command("token", "Print the access token to stdout and nothing else", (token) => token.option("audience", {
|
|
7041
|
+
type: "string",
|
|
7042
|
+
choices: ["management", "mcp"],
|
|
7043
|
+
default: "management",
|
|
7044
|
+
describe: "management prints the stored token; mcp mints a new MCP token that can read and write the catalog content you have access to, without changing the stored credential"
|
|
7045
|
+
}), (argv) => {
|
|
6947
7046
|
onMatched({
|
|
6948
7047
|
argv,
|
|
6949
|
-
run: async (ctx) => (await import("./auth-
|
|
7048
|
+
run: async (ctx, matchedArgv) => (await import("./auth-ByGgd_Yn.mjs")).token(ctx, matchedArgv)
|
|
6950
7049
|
});
|
|
6951
7050
|
});
|
|
6952
7051
|
auth.command("whoami", "Print the GET /me payload", () => {}, (argv) => {
|
|
6953
7052
|
onMatched({
|
|
6954
7053
|
argv,
|
|
6955
|
-
run: async (ctx) => (await import("./auth-
|
|
7054
|
+
run: async (ctx) => (await import("./auth-ByGgd_Yn.mjs")).whoami(ctx)
|
|
6956
7055
|
});
|
|
6957
7056
|
});
|
|
6958
7057
|
registerGeneratedAuth(auth, onMatched);
|
|
@@ -6986,7 +7085,7 @@ function buildParser(onMatched) {
|
|
|
6986
7085
|
}), (argv) => {
|
|
6987
7086
|
onMatched({
|
|
6988
7087
|
argv,
|
|
6989
|
-
run: async (ctx, matchedArgv) => (await import("./skills-
|
|
7088
|
+
run: async (ctx, matchedArgv) => (await import("./skills-CxMeJ5PF.mjs")).add(ctx, matchedArgv)
|
|
6990
7089
|
});
|
|
6991
7090
|
});
|
|
6992
7091
|
skills.command("publish <paths..>", "Publish a local skill directory as a capability or a new version", (publish) => publish.option("namespace", {
|
|
@@ -7014,7 +7113,7 @@ function buildParser(onMatched) {
|
|
|
7014
7113
|
}), (argv) => {
|
|
7015
7114
|
onMatched({
|
|
7016
7115
|
argv,
|
|
7017
|
-
run: async (ctx, matchedArgv) => (await import("./skills-
|
|
7116
|
+
run: async (ctx, matchedArgv) => (await import("./skills-CxMeJ5PF.mjs")).publish(ctx, matchedArgv)
|
|
7018
7117
|
});
|
|
7019
7118
|
});
|
|
7020
7119
|
return skills.demandCommand(1);
|
|
@@ -7036,7 +7135,7 @@ function buildParser(onMatched) {
|
|
|
7036
7135
|
}), (argv) => {
|
|
7037
7136
|
onMatched({
|
|
7038
7137
|
argv,
|
|
7039
|
-
run: async (ctx, matchedArgv) => (await import("./mcp-
|
|
7138
|
+
run: async (ctx, matchedArgv) => (await import("./mcp-DWF_ELDU.mjs")).add(ctx, matchedArgv)
|
|
7040
7139
|
});
|
|
7041
7140
|
});
|
|
7042
7141
|
mcp.command("publish <paths..>", "Publish an MCP snapshot as a capability or a new version", (publish) => publish.option("namespace", {
|
|
@@ -7064,7 +7163,7 @@ function buildParser(onMatched) {
|
|
|
7064
7163
|
}), (argv) => {
|
|
7065
7164
|
onMatched({
|
|
7066
7165
|
argv,
|
|
7067
|
-
run: async (ctx, matchedArgv) => (await import("./mcp-
|
|
7166
|
+
run: async (ctx, matchedArgv) => (await import("./mcp-DWF_ELDU.mjs")).publish(ctx, matchedArgv)
|
|
7068
7167
|
});
|
|
7069
7168
|
});
|
|
7070
7169
|
return mcp.demandCommand(1);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { _ as
|
|
1
|
+
import { C as HngrError, g as resolveConnection, s as redactSensitive, t as apiFetch, x as writeFileAtomically } from "./http-DgHX7Fux.mjs";
|
|
2
|
+
import { _ as resolveCapability, a as errorBody, c as runBatch, d as findAgentDefinition, g as parseCapabilityPath, i as runPublish, l as stringValues, m as MCP_KIND, n as explicitName, o as failedResult, p as downloadContent, r as readPublishFlags, s as positionalPaths, t as resolveInstallScope, u as AGENTS, v as resolveCapabilityVersion } from "./scope-Ch6wZCgE.mjs";
|
|
3
3
|
import { constants } from "node:fs";
|
|
4
4
|
import { access, readFile, stat } from "node:fs/promises";
|
|
5
5
|
import { basename, delimiter, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -98,31 +98,24 @@ async function userServerState(executable, name, env) {
|
|
|
98
98
|
/**
|
|
99
99
|
* Installs one user-scope server through Claude Code itself.
|
|
100
100
|
* `~/.claude.json` is never edited directly: Claude Code owns that file and may
|
|
101
|
-
* hold live session state.
|
|
102
|
-
*
|
|
103
|
-
*
|
|
101
|
+
* hold live session state. Re-installing replaces an existing user-scope
|
|
102
|
+
* server through the CLI before the new entry is added, so every write still
|
|
103
|
+
* goes through Claude Code.
|
|
104
104
|
*/
|
|
105
|
-
async function addJsonWithClaude(env, name, config
|
|
105
|
+
async function addJsonWithClaude(env, name, config) {
|
|
106
106
|
const executable = await findExecutable(EXECUTABLE, env);
|
|
107
107
|
if (!executable) throw new HngrError({
|
|
108
108
|
type: "config",
|
|
109
109
|
title: "Claude Code CLI not found",
|
|
110
110
|
detail: "`claude` must be on PATH to install user-level MCP servers; install Claude Code or use project-level (no --global) installation."
|
|
111
111
|
});
|
|
112
|
-
if (await userServerState(executable, name, env) === "present")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
"mcp",
|
|
120
|
-
"remove",
|
|
121
|
-
"--scope",
|
|
122
|
-
"user",
|
|
123
|
-
name
|
|
124
|
-
], env, name);
|
|
125
|
-
}
|
|
112
|
+
if (await userServerState(executable, name, env) === "present") await runChecked(executable, [
|
|
113
|
+
"mcp",
|
|
114
|
+
"remove",
|
|
115
|
+
"--scope",
|
|
116
|
+
"user",
|
|
117
|
+
name
|
|
118
|
+
], env, name);
|
|
126
119
|
await runChecked(executable, [
|
|
127
120
|
"mcp",
|
|
128
121
|
"add-json",
|
|
@@ -202,8 +195,8 @@ function readTargetDocument(raw, label) {
|
|
|
202
195
|
}
|
|
203
196
|
/**
|
|
204
197
|
* Merges the incoming servers into the document's `mcpServers`. Existing
|
|
205
|
-
* servers and other top-level keys survive
|
|
206
|
-
*
|
|
198
|
+
* servers and other top-level keys survive; a name collision is replaced, so
|
|
199
|
+
* re-installing a capability is idempotent.
|
|
207
200
|
*/
|
|
208
201
|
function mergeSnapshot(document, incoming, options) {
|
|
209
202
|
const existing = document.mcpServers;
|
|
@@ -211,21 +204,13 @@ function mergeSnapshot(document, incoming, options) {
|
|
|
211
204
|
if (existing === null || typeof existing !== "object" || Array.isArray(existing)) throw ioError(`"mcpServers" in ${options.label} is not an object`, "Fix the target file before installing MCP servers.");
|
|
212
205
|
}
|
|
213
206
|
const servers = { ...existing ?? {} };
|
|
214
|
-
const conflicts = [];
|
|
215
207
|
const added = [];
|
|
216
208
|
const replaced = [];
|
|
217
|
-
for (const [name, entry] of Object.entries(incoming))
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
209
|
+
for (const [name, entry] of Object.entries(incoming)) {
|
|
210
|
+
if (Object.hasOwn(servers, name)) replaced.push(name);
|
|
211
|
+
else added.push(name);
|
|
222
212
|
servers[name] = entry;
|
|
223
|
-
replaced.push(name);
|
|
224
|
-
} else {
|
|
225
|
-
servers[name] = entry;
|
|
226
|
-
added.push(name);
|
|
227
213
|
}
|
|
228
|
-
if (conflicts.length > 0) throw ioError(`MCP ${conflicts.length === 1 ? "server" : "servers"} already configured in ${options.label}: ${conflicts.join(", ")}`, "Pass --force to replace only the conflicting entries.");
|
|
229
214
|
return {
|
|
230
215
|
document: {
|
|
231
216
|
...document,
|
|
@@ -381,11 +366,8 @@ async function readExisting(path, label) {
|
|
|
381
366
|
* redacted. The file is never written by dry-run, so this only affects the
|
|
382
367
|
* preview.
|
|
383
368
|
*/
|
|
384
|
-
function previewJsonTarget(plan, existing, servers
|
|
385
|
-
const merged = mergeSnapshot(readTargetDocument(existing, plan.path), servers, {
|
|
386
|
-
force,
|
|
387
|
-
label: plan.path
|
|
388
|
-
});
|
|
369
|
+
function previewJsonTarget(plan, existing, servers) {
|
|
370
|
+
const merged = mergeSnapshot(readTargetDocument(existing, plan.path), servers, { label: plan.path });
|
|
389
371
|
return serializeTargetDocument(redactSensitive(merged.document));
|
|
390
372
|
}
|
|
391
373
|
/** Reports one target's failure without aborting the remaining targets. */
|
|
@@ -393,16 +375,13 @@ function reportTargetFailure(ctx, plan, err) {
|
|
|
393
375
|
const body = errorBody(err);
|
|
394
376
|
ctx.summary(`error: ${plan.agent} ${plan.path}: ${body.title}`);
|
|
395
377
|
}
|
|
396
|
-
async function installJsonTarget(plan, servers
|
|
378
|
+
async function installJsonTarget(plan, servers) {
|
|
397
379
|
const filePath = plan.filePath;
|
|
398
380
|
if (!filePath) throw new HngrError({
|
|
399
381
|
type: "io",
|
|
400
382
|
title: `No file path configured for ${plan.agent}`
|
|
401
383
|
});
|
|
402
|
-
const merged = mergeSnapshot(readTargetDocument(await readExisting(filePath, plan.path), plan.path), servers, {
|
|
403
|
-
force,
|
|
404
|
-
label: plan.path
|
|
405
|
-
});
|
|
384
|
+
const merged = mergeSnapshot(readTargetDocument(await readExisting(filePath, plan.path), plan.path), servers, { label: plan.path });
|
|
406
385
|
await writeFileAtomically(filePath, serializeTargetDocument(merged.document), { label: plan.path });
|
|
407
386
|
}
|
|
408
387
|
async function addOne(ctx, connection, parsed, targets, state) {
|
|
@@ -419,7 +398,7 @@ async function addOne(ctx, connection, parsed, targets, state) {
|
|
|
419
398
|
let failure = null;
|
|
420
399
|
for (const plan of targets) try {
|
|
421
400
|
if (plan.kind === "json") {
|
|
422
|
-
const preview = previewJsonTarget(plan, plan.filePath ? await readExisting(plan.filePath, plan.path) : void 0, snapshot.servers
|
|
401
|
+
const preview = previewJsonTarget(plan, plan.filePath ? await readExisting(plan.filePath, plan.path) : void 0, snapshot.servers);
|
|
423
402
|
ctx.detail(`dry-run: would write ${plan.path}\n${preview}`);
|
|
424
403
|
} else for (const [name, entry] of Object.entries(snapshot.servers)) ctx.detail(`dry-run: claude mcp add-json --scope user ${name} ${JSON.stringify(redactSensitive(entry))}`);
|
|
425
404
|
dryTargets.push({
|
|
@@ -449,8 +428,8 @@ async function addOne(ctx, connection, parsed, targets, state) {
|
|
|
449
428
|
const installedTargets = [];
|
|
450
429
|
let failure = null;
|
|
451
430
|
for (const plan of targets) try {
|
|
452
|
-
if (plan.kind === "json") await installJsonTarget(plan, snapshot.servers
|
|
453
|
-
else for (const [name, entry] of Object.entries(snapshot.servers)) await addJsonWithClaude(ctx.env, name, entry
|
|
431
|
+
if (plan.kind === "json") await installJsonTarget(plan, snapshot.servers);
|
|
432
|
+
else for (const [name, entry] of Object.entries(snapshot.servers)) await addJsonWithClaude(ctx.env, name, entry);
|
|
454
433
|
installedTargets.push({
|
|
455
434
|
agent: plan.agent,
|
|
456
435
|
path: plan.path,
|
|
@@ -587,7 +566,7 @@ async function add(ctx, argv) {
|
|
|
587
566
|
const rawPaths = positionalPaths(argv.paths);
|
|
588
567
|
if (rawPaths.length === 0) throw usage("No capability path given", "Pass one or more `<name>` or `<namespace>/<name>` arguments.");
|
|
589
568
|
const parsedPaths = rawPaths.map(parseMcpPath);
|
|
590
|
-
const scope = argv.global === true
|
|
569
|
+
const scope = await resolveInstallScope(ctx, argv.global === true);
|
|
591
570
|
const targets = selectMcpTargets(stringValues(argv.agent), argv.all === true, scope, process.cwd(), ctx.env);
|
|
592
571
|
const connection = await resolveConnection(ctx.flags, ctx.env);
|
|
593
572
|
let approvalHintShown = false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as HngrError } from "./http-DgHX7Fux.mjs";
|
|
2
2
|
// src/pagination.ts
|
|
3
3
|
function asCursorPage(body, path) {
|
|
4
4
|
if (body === null || typeof body !== "object" || !Array.isArray(body.items) || !("next_cursor" in body)) throw new HngrError({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as collectPages } from "./pagination-
|
|
1
|
+
import { C as HngrError, _ as scrubCredentials, a as toHngrError, g as resolveConnection, h as redactUrl, i as buildRequestUrl, l as apiErrorFromResponse, n as apiGet } from "./http-DgHX7Fux.mjs";
|
|
2
|
+
import { t as collectPages } from "./pagination-BXyFxGYA.mjs";
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
@@ -539,4 +539,25 @@ async function runPublish(options) {
|
|
|
539
539
|
ctx.writePayload({ results });
|
|
540
540
|
return results.some((result) => result.status === "failed") ? 1 : 0;
|
|
541
541
|
}
|
|
542
|
-
|
|
542
|
+
// src/commands/scope.ts
|
|
543
|
+
const SCOPE_OPTIONS = [{
|
|
544
|
+
value: "project",
|
|
545
|
+
label: "Project",
|
|
546
|
+
hint: "install into the current directory"
|
|
547
|
+
}, {
|
|
548
|
+
value: "global",
|
|
549
|
+
label: "Global",
|
|
550
|
+
hint: "install for the current user"
|
|
551
|
+
}];
|
|
552
|
+
/**
|
|
553
|
+
* Resolves project vs global install scope. `--global` wins without asking.
|
|
554
|
+
* Without an interactive prompter the current-directory default is kept, so
|
|
555
|
+
* scripts, pipes and `--json` runs never block on a prompt.
|
|
556
|
+
*/
|
|
557
|
+
async function resolveInstallScope(ctx, global) {
|
|
558
|
+
if (global) return "global";
|
|
559
|
+
const select = ctx.io.prompts?.select;
|
|
560
|
+
if (!select || !ctx.io.stdin.isTTY || !ctx.io.stdout.isTTY) return "project";
|
|
561
|
+
return await select("Install scope", SCOPE_OPTIONS, "project") === "global" ? "global" : "project";
|
|
562
|
+
}
|
|
563
|
+
export { resolveCapability as _, errorBody as a, runBatch as c, findAgentDefinition as d, homeDir as f, parseCapabilityPath as g, SKILL_KIND as h, runPublish as i, stringValues as l, MCP_KIND as m, explicitName as n, failedResult as o, downloadContent as p, readPublishFlags as r, positionalPaths as s, resolveInstallScope as t, AGENTS as u, resolveCapabilityVersion as v };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { _ as
|
|
1
|
+
import { C as HngrError, b as writeDirectoryAtomically, g as resolveConnection, r as apiRequest } from "./http-DgHX7Fux.mjs";
|
|
2
|
+
import { _ as resolveCapability, a as errorBody, c as runBatch, f as homeDir, g as parseCapabilityPath, h as SKILL_KIND, i as runPublish, l as stringValues, n as explicitName, o as failedResult, p as downloadContent, r as readPublishFlags, s as positionalPaths, t as resolveInstallScope, u as AGENTS, v as resolveCapabilityVersion } from "./scope-Ch6wZCgE.mjs";
|
|
3
3
|
import { lstat, mkdir, readFile, readdir, rename, rm, symlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
@@ -92,13 +92,6 @@ function downloadSkillPackage(connection, capabilityId, version, signal) {
|
|
|
92
92
|
async function pathExists(path) {
|
|
93
93
|
return lstat(path).then(() => true, () => false);
|
|
94
94
|
}
|
|
95
|
-
function targetExistsError(path) {
|
|
96
|
-
return new HngrError({
|
|
97
|
-
type: "io",
|
|
98
|
-
title: `Target already exists: ${path}`,
|
|
99
|
-
detail: "Pass --force to replace it."
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
95
|
async function writeEntries(dest, entries) {
|
|
103
96
|
await mkdir(dest, { recursive: true });
|
|
104
97
|
for (const entry of entries) {
|
|
@@ -114,25 +107,22 @@ async function writeEntries(dest, entries) {
|
|
|
114
107
|
}
|
|
115
108
|
/**
|
|
116
109
|
* Writes a skill through "temp directory → validate → rename": the target is
|
|
117
|
-
* either absent or complete, never half-written.
|
|
118
|
-
* directory
|
|
110
|
+
* either absent or complete, never half-written. Re-installing replaces the
|
|
111
|
+
* previous directory, with the old one moved aside so a failed swap puts it
|
|
112
|
+
* back.
|
|
119
113
|
*/
|
|
120
|
-
async function installAtomically(dest, entries
|
|
121
|
-
await writeDirectoryAtomically(dest, (temp) => writeEntries(temp, entries)
|
|
122
|
-
force,
|
|
123
|
-
existsError: () => targetExistsError(dest)
|
|
124
|
-
});
|
|
114
|
+
async function installAtomically(dest, entries) {
|
|
115
|
+
await writeDirectoryAtomically(dest, (temp) => writeEntries(temp, entries));
|
|
125
116
|
}
|
|
126
117
|
/**
|
|
127
118
|
* Creates a relative symlink to the canonical directory, falling back to a
|
|
128
119
|
* copy when the platform refuses the link (Windows without Developer Mode).
|
|
129
120
|
* Windows prefers a junction, which does not need elevated privileges; its
|
|
130
121
|
* target must be absolute. A temp link plus rename replaces an existing link
|
|
131
|
-
* atomically.
|
|
122
|
+
* atomically; re-installing replaces an existing directory or link.
|
|
132
123
|
*/
|
|
133
|
-
async function linkAtomically(canonical, target, entries,
|
|
124
|
+
async function linkAtomically(canonical, target, entries, options = {}) {
|
|
134
125
|
const exists = await pathExists(target);
|
|
135
|
-
if (exists && !force) throw targetExistsError(target);
|
|
136
126
|
await mkdir(dirname(target), { recursive: true });
|
|
137
127
|
const temp = resolve(dirname(target), `.hngr-${basename(target)}-${randomUUID()}.link`);
|
|
138
128
|
const windows = (options.platform ?? process.platform) === "win32";
|
|
@@ -150,7 +140,7 @@ async function linkAtomically(canonical, target, entries, force, options = {}) {
|
|
|
150
140
|
return { mode: "symlink" };
|
|
151
141
|
} catch {
|
|
152
142
|
await rm(temp, { force: true }).catch(() => {});
|
|
153
|
-
await installAtomically(target, entries
|
|
143
|
+
await installAtomically(target, entries);
|
|
154
144
|
return {
|
|
155
145
|
mode: "copy",
|
|
156
146
|
warning: `warning: could not create a link at ${target}; copied instead`
|
|
@@ -158,8 +148,8 @@ async function linkAtomically(canonical, target, entries, force, options = {}) {
|
|
|
158
148
|
}
|
|
159
149
|
}
|
|
160
150
|
/** Copies one target directory atomically, without a canonical store. */
|
|
161
|
-
async function copyAtomically(target, entries
|
|
162
|
-
await installAtomically(target, entries
|
|
151
|
+
async function copyAtomically(target, entries) {
|
|
152
|
+
await installAtomically(target, entries);
|
|
163
153
|
}
|
|
164
154
|
// src/skills/resolve.ts
|
|
165
155
|
/** Parses `<name>` or `<namespace>/<name>`, with an optional `@YYYY.MM.DD.N`. */
|
|
@@ -657,16 +647,12 @@ function planInstall(cwd, env, destName, agents, options) {
|
|
|
657
647
|
async function install(ctx, plan, entries) {
|
|
658
648
|
const cwd = process.cwd();
|
|
659
649
|
const display = (path) => displayPath(path, cwd);
|
|
660
|
-
|
|
661
|
-
if (!ctx.flags.force) {
|
|
662
|
-
for (const path of destinations) if (await pathExists(path)) throw targetExistsError(display(path));
|
|
663
|
-
}
|
|
664
|
-
if (plan.canonical) await installAtomically(plan.canonical, entries, ctx.flags.force);
|
|
650
|
+
if (plan.canonical) await installAtomically(plan.canonical, entries);
|
|
665
651
|
const installed = [];
|
|
666
652
|
let failure = null;
|
|
667
653
|
for (const target of plan.targets) try {
|
|
668
654
|
if (target.mode === "copy" || !plan.canonical) {
|
|
669
|
-
await copyAtomically(target.path, entries
|
|
655
|
+
await copyAtomically(target.path, entries);
|
|
670
656
|
installed.push({
|
|
671
657
|
agent: target.agent,
|
|
672
658
|
path: display(target.path),
|
|
@@ -675,7 +661,7 @@ async function install(ctx, plan, entries) {
|
|
|
675
661
|
});
|
|
676
662
|
continue;
|
|
677
663
|
}
|
|
678
|
-
const result = await linkAtomically(plan.canonical, target.path, entries
|
|
664
|
+
const result = await linkAtomically(plan.canonical, target.path, entries);
|
|
679
665
|
if (result.warning) ctx.summary(result.warning);
|
|
680
666
|
installed.push({
|
|
681
667
|
agent: target.agent,
|
|
@@ -708,9 +694,6 @@ async function addOne(ctx, connection, parsed, destName, agents, dir, global, co
|
|
|
708
694
|
});
|
|
709
695
|
const canonicalPath = plan.canonical ? displayPath(plan.canonical, process.cwd()) : null;
|
|
710
696
|
if (ctx.flags.dryRun) {
|
|
711
|
-
if (!ctx.flags.force) {
|
|
712
|
-
for (const path of [plan.canonical, ...plan.targets.map((target) => target.path)]) if (path && await pathExists(path)) throw targetExistsError(displayPath(path, process.cwd()));
|
|
713
|
-
}
|
|
714
697
|
ctx.summary(`resolved ${parsed.namespace}/${parsed.name} to version ${version}`);
|
|
715
698
|
return { result: {
|
|
716
699
|
name: capability.name,
|
|
@@ -849,12 +832,13 @@ async function add(ctx, argv) {
|
|
|
849
832
|
const dir = stringFlag(argv, "dir");
|
|
850
833
|
const as = stringFlag(argv, "as");
|
|
851
834
|
if (as !== void 0 && (as.includes("/") || as.includes("\\") || as === "." || as === "..")) throw usage(`Invalid --as name ${JSON.stringify(as)}`, "Pass a single directory name.");
|
|
852
|
-
const
|
|
835
|
+
const requestedGlobal = argv.global === true;
|
|
853
836
|
const copy = argv.copy === true;
|
|
854
|
-
if (dir !== void 0 &&
|
|
837
|
+
if (dir !== void 0 && requestedGlobal) throw usage("--dir cannot be combined with --global");
|
|
855
838
|
if (dir !== void 0 && (stringValues(argv.agent).length > 0 || argv.all === true)) throw usage("--dir cannot be combined with --agent or --all");
|
|
856
839
|
if (as !== void 0) assertNotReserved(as);
|
|
857
840
|
for (const parsed of parsedPaths) assertNotReserved(as ?? parsed.name);
|
|
841
|
+
const global = dir === void 0 ? await resolveInstallScope(ctx, requestedGlobal) === "global" : false;
|
|
858
842
|
const agents = dir === void 0 ? await selectAgents(ctx, argv, rawPaths[0] ?? "", global) : [];
|
|
859
843
|
const connection = await resolveConnection(ctx.flags, ctx.env);
|
|
860
844
|
const { results, failed } = await runBatch({
|