hngr 0.4.0 → 0.5.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
|
@@ -47,12 +47,19 @@ hngr skills add team-a/pdf-tools --agent claude-code
|
|
|
47
47
|
hngr skills add pdf-tools@2026.10.25.1 --agent claude-code
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
terminal, omitting `--global` first asks whether to
|
|
52
|
-
the
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
The shared `.agents/skills` store is always installed, so every universal agent reads it
|
|
51
|
+
without extra work. In an interactive terminal, omitting `--global` first asks whether to
|
|
52
|
+
install for the current project or the user, then shows a multi-select of the agents that
|
|
53
|
+
need their own directory (`claude-code`, `cline`, `junie` or `kiro-cli`), with an
|
|
54
|
+
`All agents` entry; detected agents are preselected, and pressing Enter with none selected
|
|
55
|
+
installs only `.agents/skills`. Without a terminal, pass `--agent` (repeatable) or `--all`,
|
|
56
|
+
and the scope defaults to the project. Use `--copy` to write copies instead of the
|
|
57
|
+
canonical store plus links, and `--dry-run` to preview without writing. Re-running an
|
|
58
|
+
install replaces the existing target; `--force` is not needed.
|
|
59
|
+
|
|
60
|
+
Interactive runs end with a borderless summary on stderr (canonical path, universal
|
|
61
|
+
agents, symlinked/copied agents); stdout carries the JSON payload only when piped or when
|
|
62
|
+
`--json` is passed.
|
|
56
63
|
|
|
57
64
|
## Publish capabilities
|
|
58
65
|
|
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.
|
|
5282
|
+
var version = "0.5.0";
|
|
5283
5283
|
// src/generated/completion.ts
|
|
5284
5284
|
function isCompletionShell(value) {
|
|
5285
5285
|
return value === "bash" || value === "zsh" || value === "fish";
|
|
@@ -6721,6 +6721,13 @@ ${h}
|
|
|
6721
6721
|
}
|
|
6722
6722
|
}).prompt();
|
|
6723
6723
|
};
|
|
6724
|
+
const outro = (o = "", t) => {
|
|
6725
|
+
const i = t?.output ?? process.stdout, e = t?.withGuide ?? settings.withGuide ? `${styleText("gray", S_BAR)}
|
|
6726
|
+
${styleText("gray", S_BAR_END)} ` : "";
|
|
6727
|
+
i.write(`${e}${o}
|
|
6728
|
+
|
|
6729
|
+
`);
|
|
6730
|
+
};
|
|
6724
6731
|
const password = (e) => new u$1({
|
|
6725
6732
|
validate: e.validate,
|
|
6726
6733
|
mask: e.mask ?? S_PASSWORD_MASK,
|
|
@@ -6898,15 +6905,24 @@ function clackPrompter(streams = {}) {
|
|
|
6898
6905
|
initialValue
|
|
6899
6906
|
}));
|
|
6900
6907
|
},
|
|
6901
|
-
async multiselect(message, options, initialValues = []) {
|
|
6908
|
+
async multiselect(message, options, initialValues = [], hint) {
|
|
6909
|
+
if (hint) output.write(`${import_picocolors.default.dim(hint)}\n`);
|
|
6902
6910
|
return unwrapPrompt(await multiselect({
|
|
6903
6911
|
message,
|
|
6904
6912
|
input,
|
|
6905
6913
|
output,
|
|
6906
6914
|
options,
|
|
6907
6915
|
initialValues,
|
|
6908
|
-
required:
|
|
6916
|
+
required: false
|
|
6909
6917
|
}));
|
|
6918
|
+
},
|
|
6919
|
+
note(message, title) {
|
|
6920
|
+
output.write(`${import_picocolors.default.green("◇")} ${title}\n`);
|
|
6921
|
+
output.write(`${import_picocolors.default.dim("│")}\n`);
|
|
6922
|
+
for (const line of message.split("\n")) output.write(`${import_picocolors.default.dim("│")} ${line}\n`);
|
|
6923
|
+
},
|
|
6924
|
+
outro(message) {
|
|
6925
|
+
outro(message, { output });
|
|
6910
6926
|
}
|
|
6911
6927
|
};
|
|
6912
6928
|
}
|
|
@@ -6922,7 +6938,7 @@ const GLOBAL_OPTIONS = {
|
|
|
6922
6938
|
json: {
|
|
6923
6939
|
type: "boolean",
|
|
6924
6940
|
default: false,
|
|
6925
|
-
describe: "Machine mode:
|
|
6941
|
+
describe: "Machine mode: print the JSON payload even on a TTY, and errors as JSON",
|
|
6926
6942
|
global: true
|
|
6927
6943
|
},
|
|
6928
6944
|
quiet: {
|
|
@@ -7085,7 +7101,7 @@ function buildParser(onMatched) {
|
|
|
7085
7101
|
}), (argv) => {
|
|
7086
7102
|
onMatched({
|
|
7087
7103
|
argv,
|
|
7088
|
-
run: async (ctx, matchedArgv) => (await import("./skills-
|
|
7104
|
+
run: async (ctx, matchedArgv) => (await import("./skills-ZFmFyZtw.mjs")).add(ctx, matchedArgv)
|
|
7089
7105
|
});
|
|
7090
7106
|
});
|
|
7091
7107
|
skills.command("publish <paths..>", "Publish a local skill directory as a capability or a new version", (publish) => publish.option("namespace", {
|
|
@@ -7113,7 +7129,7 @@ function buildParser(onMatched) {
|
|
|
7113
7129
|
}), (argv) => {
|
|
7114
7130
|
onMatched({
|
|
7115
7131
|
argv,
|
|
7116
|
-
run: async (ctx, matchedArgv) => (await import("./skills-
|
|
7132
|
+
run: async (ctx, matchedArgv) => (await import("./skills-ZFmFyZtw.mjs")).publish(ctx, matchedArgv)
|
|
7117
7133
|
});
|
|
7118
7134
|
});
|
|
7119
7135
|
return skills.demandCommand(1);
|
|
@@ -7135,7 +7151,7 @@ function buildParser(onMatched) {
|
|
|
7135
7151
|
}), (argv) => {
|
|
7136
7152
|
onMatched({
|
|
7137
7153
|
argv,
|
|
7138
|
-
run: async (ctx, matchedArgv) => (await import("./mcp-
|
|
7154
|
+
run: async (ctx, matchedArgv) => (await import("./mcp-B0sbQABp.mjs")).add(ctx, matchedArgv)
|
|
7139
7155
|
});
|
|
7140
7156
|
});
|
|
7141
7157
|
mcp.command("publish <paths..>", "Publish an MCP snapshot as a capability or a new version", (publish) => publish.option("namespace", {
|
|
@@ -7163,7 +7179,7 @@ function buildParser(onMatched) {
|
|
|
7163
7179
|
}), (argv) => {
|
|
7164
7180
|
onMatched({
|
|
7165
7181
|
argv,
|
|
7166
|
-
run: async (ctx, matchedArgv) => (await import("./mcp-
|
|
7182
|
+
run: async (ctx, matchedArgv) => (await import("./mcp-B0sbQABp.mjs")).publish(ctx, matchedArgv)
|
|
7167
7183
|
});
|
|
7168
7184
|
});
|
|
7169
7185
|
return mcp.demandCommand(1);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
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-
|
|
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-DVfDeQi2.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";
|
|
@@ -391,8 +391,6 @@ async function addOne(ctx, connection, parsed, targets, state) {
|
|
|
391
391
|
const snapshot = await downloadMcpSnapshot(connection, capability.id, version);
|
|
392
392
|
if (snapshot.ignoredKeys.length > 0) ctx.summary(`warning: ignoring unsupported top-level keys in the MCP snapshot: ${snapshot.ignoredKeys.join(", ")}`);
|
|
393
393
|
const serverNames = Object.keys(snapshot.servers);
|
|
394
|
-
const etag = snapshot.etag ? ` (ETag ${snapshot.etag})` : "";
|
|
395
|
-
ctx.summary(`resolved ${parsed.namespace}/${parsed.name} to version ${version}${etag}`);
|
|
396
394
|
if (ctx.flags.dryRun) {
|
|
397
395
|
const dryTargets = [];
|
|
398
396
|
let failure = null;
|
|
@@ -583,7 +581,7 @@ async function add(ctx, argv) {
|
|
|
583
581
|
}
|
|
584
582
|
}
|
|
585
583
|
});
|
|
586
|
-
ctx.writePayload({ results });
|
|
584
|
+
if (ctx.flags.json || !ctx.io.stdout.isTTY) ctx.writePayload({ results });
|
|
587
585
|
return failed ? 1 : 0;
|
|
588
586
|
}
|
|
589
587
|
export { add, publish };
|
|
@@ -172,6 +172,10 @@ function claudeConfigDir(env) {
|
|
|
172
172
|
function xdgConfigDir(env) {
|
|
173
173
|
return env.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME !== "" ? env.XDG_CONFIG_HOME : join(homeDir(env), ".config");
|
|
174
174
|
}
|
|
175
|
+
/** Pi's agent directory: `PI_CODING_AGENT_DIR` or `~/.pi/agent`. */
|
|
176
|
+
function piAgentDir(env) {
|
|
177
|
+
return env.PI_CODING_AGENT_DIR && env.PI_CODING_AGENT_DIR !== "" ? env.PI_CODING_AGENT_DIR : join(homeDir(env), ".pi", "agent");
|
|
178
|
+
}
|
|
175
179
|
const universalSkills = {
|
|
176
180
|
projectDir: null,
|
|
177
181
|
globalDir: null,
|
|
@@ -220,6 +224,15 @@ const AGENTS = [
|
|
|
220
224
|
reason: "OpenCode uses a top-level `mcp` object with local/remote entries."
|
|
221
225
|
}
|
|
222
226
|
},
|
|
227
|
+
{
|
|
228
|
+
id: "pi",
|
|
229
|
+
skills: universalSkills,
|
|
230
|
+
mcp: {
|
|
231
|
+
kind: "json",
|
|
232
|
+
project: ".pi/mcp.json",
|
|
233
|
+
global: (env) => join(piAgentDir(env), "mcp.json")
|
|
234
|
+
}
|
|
235
|
+
},
|
|
223
236
|
{
|
|
224
237
|
id: "amp",
|
|
225
238
|
skills: universalSkills,
|
|
@@ -557,7 +570,7 @@ const SCOPE_OPTIONS = [{
|
|
|
557
570
|
async function resolveInstallScope(ctx, global) {
|
|
558
571
|
if (global) return "global";
|
|
559
572
|
const select = ctx.io.prompts?.select;
|
|
560
|
-
if (!select || !ctx.io.stdin.isTTY || !ctx.io.stdout.isTTY) return "project";
|
|
573
|
+
if (!select || ctx.flags.json || !ctx.io.stdin.isTTY || !ctx.io.stdout.isTTY) return "project";
|
|
561
574
|
return await select("Install scope", SCOPE_OPTIONS, "project") === "global" ? "global" : "project";
|
|
562
575
|
}
|
|
563
576
|
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
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-
|
|
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-DVfDeQi2.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";
|
|
@@ -552,17 +552,19 @@ function failureResult(parsed, err, version) {
|
|
|
552
552
|
canonicalPath: null
|
|
553
553
|
};
|
|
554
554
|
}
|
|
555
|
-
/**
|
|
555
|
+
/** Agent prompts need a TTY on both ends, a multi-select prompter, and human output mode. */
|
|
556
556
|
function canPrompt(ctx) {
|
|
557
|
-
return ctx.io.stdin.isTTY && ctx.io.stdout.isTTY && ctx.io.prompts?.multiselect !== void 0;
|
|
557
|
+
return !ctx.flags.json && ctx.io.stdin.isTTY && ctx.io.stdout.isTTY && ctx.io.prompts?.multiselect !== void 0;
|
|
558
558
|
}
|
|
559
559
|
function noAgentSelectionError(examplePath, global) {
|
|
560
560
|
return usage("No agent target selected", `Pass --agent <id> (repeatable) or --all. For example: hngr skills add ${examplePath} --agent claude-code${global ? " --global" : ""}`);
|
|
561
561
|
}
|
|
562
|
+
const ALL_AGENTS = "__all__";
|
|
562
563
|
/**
|
|
563
|
-
* One multi-select for
|
|
564
|
-
*
|
|
565
|
-
*
|
|
564
|
+
* One multi-select for the agents that keep their own skills directory.
|
|
565
|
+
* `.agents` is always installed, so universal agents never appear here.
|
|
566
|
+
* Detection only preselects; a non-TTY invocation never consults the
|
|
567
|
+
* filesystem and must pass --agent or --all.
|
|
566
568
|
*/
|
|
567
569
|
async function selectAgents(ctx, argv, examplePath, global) {
|
|
568
570
|
const requested = stringValues(argv.agent);
|
|
@@ -572,26 +574,28 @@ async function selectAgents(ctx, argv, examplePath, global) {
|
|
|
572
574
|
return AGENT_TARGETS.filter((agent) => requested.includes(agent.id));
|
|
573
575
|
}
|
|
574
576
|
if (argv.all === true) return [...AGENT_TARGETS];
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
577
|
+
const multiselect = ctx.io.prompts?.multiselect;
|
|
578
|
+
if (!canPrompt(ctx) || !multiselect) throw noAgentSelectionError(examplePath, global);
|
|
579
|
+
const cwd = process.cwd();
|
|
580
|
+
const linkAgents = AGENT_TARGETS.filter((agent) => agent.link);
|
|
581
|
+
const options = [{
|
|
582
|
+
value: ALL_AGENTS,
|
|
583
|
+
label: "All agents",
|
|
584
|
+
hint: "select every agent below"
|
|
585
|
+
}];
|
|
586
|
+
const initialValues = [];
|
|
587
|
+
for (const agent of linkAgents) {
|
|
588
|
+
const dir = targetSkillsDir(agent, cwd, ctx.env, global);
|
|
589
|
+
options.push({
|
|
590
|
+
value: agent.id,
|
|
591
|
+
label: agent.id,
|
|
592
|
+
hint: displayPath(dir, cwd)
|
|
593
|
+
});
|
|
594
|
+
if (await pathExists(dirname(dir))) initialValues.push(agent.id);
|
|
593
595
|
}
|
|
594
|
-
|
|
596
|
+
const selected = await multiselect("Install for another agent", options, initialValues, ".agents/skills is always installed; press Enter to skip");
|
|
597
|
+
const chosen = selected.includes(ALL_AGENTS) ? new Set(linkAgents.map((agent) => agent.id)) : new Set(selected);
|
|
598
|
+
return linkAgents.filter((agent) => chosen.has(agent.id));
|
|
595
599
|
}
|
|
596
600
|
function frontmatterName(manifest) {
|
|
597
601
|
const text = manifest.toString("utf8");
|
|
@@ -683,6 +687,26 @@ async function install(ctx, plan, entries) {
|
|
|
683
687
|
failure
|
|
684
688
|
};
|
|
685
689
|
}
|
|
690
|
+
function withDot(path) {
|
|
691
|
+
return path.startsWith("/") ? path : `./${path}`;
|
|
692
|
+
}
|
|
693
|
+
/** The boxed result shown after an interactive install. */
|
|
694
|
+
function installSummary(outcome, canonicalPath) {
|
|
695
|
+
const lines = [];
|
|
696
|
+
const installed = outcome.targets.filter((target) => target.status === "installed");
|
|
697
|
+
if (canonicalPath) {
|
|
698
|
+
lines.push(`✓ ${withDot(canonicalPath)}`);
|
|
699
|
+
const universal = AGENT_TARGETS.filter((agent) => !agent.link).map((agent) => agent.id);
|
|
700
|
+
if (universal.length > 0) lines.push(` universal: ${universal.join(", ")}`);
|
|
701
|
+
} else for (const target of installed) lines.push(`✓ ${withDot(target.path)}`);
|
|
702
|
+
const symlinked = installed.filter((target) => target.mode === "symlink" && target.agent).map((target) => target.agent);
|
|
703
|
+
const copied = installed.filter((target) => target.mode === "copy" && target.agent).map((target) => target.agent);
|
|
704
|
+
if (symlinked.length > 0) lines.push(` symlinked: ${symlinked.join(", ")}`);
|
|
705
|
+
if (copied.length > 0) lines.push(` copied: ${copied.join(", ")}`);
|
|
706
|
+
const failed = outcome.targets.filter((target) => target.status === "failed");
|
|
707
|
+
if (failed.length > 0) lines.push(` failed: ${failed.map((target) => target.path).join(", ")}`);
|
|
708
|
+
return lines.join("\n");
|
|
709
|
+
}
|
|
686
710
|
async function addOne(ctx, connection, parsed, destName, agents, dir, global, copy, state) {
|
|
687
711
|
const capability = await resolveSkill(connection, parsed);
|
|
688
712
|
const version = resolveVersion(capability, parsed);
|
|
@@ -694,7 +718,9 @@ async function addOne(ctx, connection, parsed, destName, agents, dir, global, co
|
|
|
694
718
|
});
|
|
695
719
|
const canonicalPath = plan.canonical ? displayPath(plan.canonical, process.cwd()) : null;
|
|
696
720
|
if (ctx.flags.dryRun) {
|
|
697
|
-
ctx.
|
|
721
|
+
ctx.detail(`dry-run: would install ${parsed.namespace}/${parsed.name}@${version}`);
|
|
722
|
+
if (canonicalPath) ctx.detail(` ${canonicalPath}`);
|
|
723
|
+
for (const target of plan.targets) ctx.detail(` ${displayPath(target.path, process.cwd())} (${target.mode})`);
|
|
698
724
|
return { result: {
|
|
699
725
|
name: capability.name,
|
|
700
726
|
namespace: capability.namespace,
|
|
@@ -710,19 +736,18 @@ async function addOne(ctx, connection, parsed, destName, agents, dir, global, co
|
|
|
710
736
|
error: null
|
|
711
737
|
} };
|
|
712
738
|
}
|
|
713
|
-
const
|
|
714
|
-
const pkg = readSkillPackage(download.bytes);
|
|
715
|
-
ctx.summary(`resolved ${parsed.namespace}/${parsed.name} to version ${version}`);
|
|
739
|
+
const pkg = readSkillPackage((await downloadSkillPackage(connection, capability.id, version)).bytes);
|
|
716
740
|
const declaredName = frontmatterName(pkg.manifest);
|
|
717
741
|
if (declaredName !== void 0 && declaredName !== destName) ctx.summary(`warning: SKILL.md declares name ${JSON.stringify(declaredName)} but the directory is ${JSON.stringify(destName)}`);
|
|
718
742
|
const outcome = await install(ctx, plan, pkg.entries);
|
|
719
|
-
const
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
743
|
+
const verb = outcome.failure ? "Partially installed" : "Installed";
|
|
744
|
+
const note = ctx.io.prompts?.note;
|
|
745
|
+
if (!ctx.flags.json && !ctx.flags.quiet && ctx.io.stderr.isTTY && note) note(installSummary(outcome, canonicalPath), `${verb} ${capability.namespace}/${capability.name}@${version}`);
|
|
746
|
+
else {
|
|
747
|
+
ctx.progress(`${verb.toLowerCase()} ${capability.namespace}/${capability.name}@${version}`);
|
|
748
|
+
if (canonicalPath) ctx.progress(` ${canonicalPath}`);
|
|
749
|
+
for (const target of outcome.targets) ctx.progress(target.status === "failed" ? ` failed ${target.path}` : ` ${target.path} (${target.mode})`);
|
|
750
|
+
}
|
|
726
751
|
return { result: {
|
|
727
752
|
name: capability.name,
|
|
728
753
|
namespace: capability.namespace,
|
|
@@ -849,7 +874,8 @@ async function add(ctx, argv) {
|
|
|
849
874
|
},
|
|
850
875
|
failure: failureResult
|
|
851
876
|
});
|
|
852
|
-
ctx.writePayload({ results });
|
|
877
|
+
if (ctx.flags.json || !ctx.io.stdout.isTTY) ctx.writePayload({ results });
|
|
878
|
+
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.");
|
|
853
879
|
return failed ? 1 : 0;
|
|
854
880
|
}
|
|
855
881
|
export { add, publish };
|