@sechroom/cli 2026.6.229-rc.0d108cee → 2026.6.230-rc.326bc4de
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/dist/index.js +142 -16
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4697,6 +4697,10 @@ Examples:
|
|
|
4697
4697
|
});
|
|
4698
4698
|
}
|
|
4699
4699
|
|
|
4700
|
+
// src/commands/skills.ts
|
|
4701
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync9, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
4702
|
+
import { join as join13 } from "path";
|
|
4703
|
+
|
|
4700
4704
|
// src/commands/lane.ts
|
|
4701
4705
|
var LANE_KEYS = ["code-lane", "design-lane"];
|
|
4702
4706
|
function showLane(json) {
|
|
@@ -4905,6 +4909,51 @@ function runClean(spec, cmd, opts, slugArg) {
|
|
|
4905
4909
|
}
|
|
4906
4910
|
|
|
4907
4911
|
// src/commands/skills.ts
|
|
4912
|
+
function filenameFromDisposition(header) {
|
|
4913
|
+
if (!header) return void 0;
|
|
4914
|
+
const m = /filename\*?=(?:UTF-8'')?"?([^";]+)"?/i.exec(header);
|
|
4915
|
+
return m?.[1];
|
|
4916
|
+
}
|
|
4917
|
+
function resolveOutputPath(output, serverFilename) {
|
|
4918
|
+
const filename = serverFilename || "skills.zip";
|
|
4919
|
+
if (!output) return join13(process.cwd(), filename);
|
|
4920
|
+
const looksLikeDir = output.endsWith("/") || existsSync11(output) && statSync4(output).isDirectory();
|
|
4921
|
+
if (looksLikeDir) {
|
|
4922
|
+
mkdirSync9(output, { recursive: true });
|
|
4923
|
+
return join13(output, filename);
|
|
4924
|
+
}
|
|
4925
|
+
return output;
|
|
4926
|
+
}
|
|
4927
|
+
async function downloadZip(label, call, output) {
|
|
4928
|
+
const s = spinner(label);
|
|
4929
|
+
let res;
|
|
4930
|
+
try {
|
|
4931
|
+
res = await call();
|
|
4932
|
+
} catch (err2) {
|
|
4933
|
+
s.fail();
|
|
4934
|
+
fail(err2);
|
|
4935
|
+
}
|
|
4936
|
+
if (!res.response || !res.response.ok) {
|
|
4937
|
+
s.fail();
|
|
4938
|
+
let detail = res.response ? `HTTP ${res.response.status} ${res.response.statusText}`.trim() : "request failed";
|
|
4939
|
+
const e = res.error;
|
|
4940
|
+
if (e && typeof e === "object" && "title" in e) detail = String(e.title);
|
|
4941
|
+
else if (e instanceof ArrayBuffer) {
|
|
4942
|
+
try {
|
|
4943
|
+
const j = JSON.parse(new TextDecoder().decode(e));
|
|
4944
|
+
detail = j.title ?? j.detail ?? detail;
|
|
4945
|
+
} catch {
|
|
4946
|
+
}
|
|
4947
|
+
}
|
|
4948
|
+
fail(detail);
|
|
4949
|
+
}
|
|
4950
|
+
s.succeed();
|
|
4951
|
+
const buf = Buffer.from(res.data);
|
|
4952
|
+
const filename = filenameFromDisposition(res.response.headers.get("content-disposition")) ?? "skills.zip";
|
|
4953
|
+
const path = resolveOutputPath(output, filename);
|
|
4954
|
+
writeFileSync9(path, buf);
|
|
4955
|
+
return { path, bytes: buf.length, filename };
|
|
4956
|
+
}
|
|
4908
4957
|
function registerSkills(program2) {
|
|
4909
4958
|
const skills = program2.command("skills").description("Manage operator skills (install to disk, list, clean)");
|
|
4910
4959
|
skills.addHelpText(
|
|
@@ -4915,6 +4964,9 @@ Examples:
|
|
|
4915
4964
|
$ sechroom skills install --scope project write them to ./.claude/skills instead
|
|
4916
4965
|
$ sechroom skills list what's materialised on disk
|
|
4917
4966
|
$ sechroom skills clean remove the materialised skill files
|
|
4967
|
+
$ sechroom skills preview --workspace wsp_abc render a draft bundle from source (no install)
|
|
4968
|
+
$ sechroom skills package my-bundle download the installed bundle's skills as a zip
|
|
4969
|
+
$ sechroom skills package --from-source --workspace wsp_abc -o ./dist zip a draft from source
|
|
4918
4970
|
$ sechroom skills set-lane --code-lane claude-code-chris --design-lane claude-design-chris
|
|
4919
4971
|
|
|
4920
4972
|
`
|
|
@@ -4922,6 +4974,80 @@ Examples:
|
|
|
4922
4974
|
skills.command("install").description("Materialise your installed skills to disk (the already-installed bundle \u2014 no server install)").option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--dry-run", "print what would be written; write nothing").option("--json", "machine output").action((opts, cmd) => runInstall(SKILL_SPEC, cmd, opts));
|
|
4923
4975
|
skills.command("list").description("List the skills materialised on disk (per resolved config dir)").option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--json", "machine output").action((opts, cmd) => runList(SKILL_SPEC, cmd, opts));
|
|
4924
4976
|
skills.command("clean [slug]").description(`Remove skill files materialised to disk (default ${DEFAULT_SKILLS_SLUG})`).option("--scope <scope>", "global (config dir / CLAUDE_CONFIG_DIR) or project (<cwd>/.claude) \u2014 default global").option("--local", "alias for --scope project").option("--json", "machine output").action((slugArg, opts, cmd) => runClean(SKILL_SPEC, cmd, opts, slugArg));
|
|
4977
|
+
skills.command("preview").description("Render a workspace's draft bundle from source (no publish/install) and report the components").requiredOption("--workspace <id>", "workspace holding the draft bundle sources (wsp_\u2026)").option("--slug <slug>", "override the derived bundle slug").option("--title <title>", "override the derived bundle title").option("--version <version>", "override the derived bundle version").option("--default-install-parent <path>", "override the derived default install parent").option("--json", "machine output (the full RenderBundlePreviewResponse)").action(async (opts, cmd) => {
|
|
4978
|
+
const json = Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json);
|
|
4979
|
+
const client = await makeClient(resolveConfig(cmd.optsWithGlobals()));
|
|
4980
|
+
const data = await runApi(
|
|
4981
|
+
"rendering bundle preview",
|
|
4982
|
+
() => client.POST("/workspaces/{workspaceId}/render-bundle-preview", {
|
|
4983
|
+
params: { path: { workspaceId: opts.workspace } },
|
|
4984
|
+
body: {
|
|
4985
|
+
slug: opts.slug ?? null,
|
|
4986
|
+
title: opts.title ?? null,
|
|
4987
|
+
version: opts.version ?? null,
|
|
4988
|
+
defaultInstallParent: opts.defaultInstallParent ?? null
|
|
4989
|
+
}
|
|
4990
|
+
})
|
|
4991
|
+
);
|
|
4992
|
+
if (json) return emit(data, true);
|
|
4993
|
+
console.log(
|
|
4994
|
+
style.bold(data.bundleSlug) + style.dim("@") + (data.bundleVersion || style.dim("(no version)"))
|
|
4995
|
+
);
|
|
4996
|
+
const components = data.components ?? [];
|
|
4997
|
+
if (components.length === 0) console.log(" " + style.dim("(no components rendered)"));
|
|
4998
|
+
for (const c of components) {
|
|
4999
|
+
const bits = [c.role, c.skillName ? style.dim("skill=") + c.skillName : null, c.target ? style.dim("target=") + c.target : null].filter(Boolean).join(" ");
|
|
5000
|
+
const flags = [
|
|
5001
|
+
c.intakeBlocked ? style.yellow("intake-blocked") : null,
|
|
5002
|
+
c.unresolvedTokens?.length ? style.yellow(`${c.unresolvedTokens.length} unresolved`) : null
|
|
5003
|
+
].filter(Boolean).join(" ");
|
|
5004
|
+
console.log(" " + style.green("\u2022") + " " + style.bold(c.slug) + " " + bits + (flags ? " " + flags : ""));
|
|
5005
|
+
}
|
|
5006
|
+
const skipped = data.skipped ?? [];
|
|
5007
|
+
if (skipped.length) {
|
|
5008
|
+
console.log(style.dim(` skipped ${skipped.length}:`));
|
|
5009
|
+
for (const sk of skipped) console.log(" " + style.dim(`- ${sk.slug ?? sk.reason ?? JSON.stringify(sk)}`));
|
|
5010
|
+
}
|
|
5011
|
+
});
|
|
5012
|
+
skills.command("package [slug]").description("Download a bundle's skill-templates as a zip to disk (installed bundle, or --from-source draft)").option("--from-source", "package a workspace's DRAFT from source (requires --workspace) instead of an installed bundle").option("--workspace <id>", "with --from-source: the workspace holding the draft (wsp_\u2026)").option("--target <surface>", "surface to render (server default: claude-web installed, claude-code from-source)").option("--slug <slug>", "with --from-source: override the derived bundle slug").option("--title <title>", "with --from-source: override the derived bundle title").option("--version <version>", "with --from-source: override the derived bundle version").option("--default-install-parent <path>", "with --from-source: override the derived default install parent").option("-o, --output <path>", "where to write the zip \u2014 a dir gets the server filename, a file path is used as-is (default: cwd)").option("--json", "machine output ({ path, filename, bytes })").action(async (slugArg, opts, cmd) => {
|
|
5013
|
+
const json = Boolean(opts.json) || Boolean(cmd.optsWithGlobals().json);
|
|
5014
|
+
if (opts.fromSource) {
|
|
5015
|
+
if (!opts.workspace) fail("--from-source requires --workspace <id> (the workspace holding the draft).");
|
|
5016
|
+
} else if (!slugArg) {
|
|
5017
|
+
fail("Provide a bundle slug to package, or use --from-source --workspace <id>.");
|
|
5018
|
+
}
|
|
5019
|
+
const client = await makeClient(resolveConfig(cmd.optsWithGlobals()));
|
|
5020
|
+
let result;
|
|
5021
|
+
if (opts.fromSource) {
|
|
5022
|
+
const query = {};
|
|
5023
|
+
if (opts.target) query.target = opts.target;
|
|
5024
|
+
if (slugArg ?? opts.slug) query.slug = slugArg ?? opts.slug;
|
|
5025
|
+
if (opts.title) query.title = opts.title;
|
|
5026
|
+
if (opts.version) query.version = opts.version;
|
|
5027
|
+
if (opts.defaultInstallParent) query.defaultInstallParent = opts.defaultInstallParent;
|
|
5028
|
+
result = await downloadZip(
|
|
5029
|
+
"packaging draft from source",
|
|
5030
|
+
() => client.GET("/workspaces/{workspaceId}/bundle-preview/package", {
|
|
5031
|
+
params: { path: { workspaceId: opts.workspace }, query },
|
|
5032
|
+
parseAs: "arrayBuffer"
|
|
5033
|
+
}),
|
|
5034
|
+
opts.output
|
|
5035
|
+
);
|
|
5036
|
+
} else {
|
|
5037
|
+
const query = {};
|
|
5038
|
+
if (opts.target) query.target = opts.target;
|
|
5039
|
+
result = await downloadZip(
|
|
5040
|
+
"packaging installed bundle",
|
|
5041
|
+
() => client.GET("/me/bundle-installs/{bundleSlug}/package", {
|
|
5042
|
+
params: { path: { bundleSlug: slugArg }, query },
|
|
5043
|
+
parseAs: "arrayBuffer"
|
|
5044
|
+
}),
|
|
5045
|
+
opts.output
|
|
5046
|
+
);
|
|
5047
|
+
}
|
|
5048
|
+
if (json) return emit(result, true);
|
|
5049
|
+
console.log(style.green("\u2713") + ` wrote ${style.bold(result.path)} ${style.dim(`(${result.bytes} bytes)`)}`);
|
|
5050
|
+
});
|
|
4925
5051
|
skills.command("set-lane").description("Alias of `sechroom lane set` (kept for back-compat) \u2014 write this checkout's lane pin").option("--code-lane <id>", "code-surface lane id (e.g. claude-code-chris)").option("--design-lane <id>", "design / substrate-authoring lane id (e.g. claude-design-chris)").option("--json", "machine output").action(
|
|
4926
5052
|
(opts, cmd) => setLane({
|
|
4927
5053
|
codeLane: opts.codeLane,
|
|
@@ -5015,23 +5141,23 @@ workers your loop skills call (e.g. find-prior-art \u2192 substrate-miner).`
|
|
|
5015
5141
|
|
|
5016
5142
|
// src/commands/reset.ts
|
|
5017
5143
|
import { homedir as homedir4 } from "os";
|
|
5018
|
-
import { join as
|
|
5019
|
-
import { existsSync as
|
|
5144
|
+
import { join as join14 } from "path";
|
|
5145
|
+
import { existsSync as existsSync12, readFileSync as readFileSync7, rmSync as rmSync3 } from "fs";
|
|
5020
5146
|
var SKILLS_LOCK2 = ".sechroom-skills.json";
|
|
5021
|
-
var localSkillsDir = () =>
|
|
5022
|
-
var globalSkillsDir = () =>
|
|
5023
|
-
var localAgentsDir = () =>
|
|
5024
|
-
var globalAgentsDir = () =>
|
|
5147
|
+
var localSkillsDir = () => join14(process.cwd(), ".claude", "skills");
|
|
5148
|
+
var globalSkillsDir = () => join14(homedir4(), ".claude", "skills");
|
|
5149
|
+
var localAgentsDir = () => join14(process.cwd(), ".claude", "agents");
|
|
5150
|
+
var globalAgentsDir = () => join14(homedir4(), ".claude", "agents");
|
|
5025
5151
|
function removeMaterialisedSkills(dir) {
|
|
5026
5152
|
const removed = [];
|
|
5027
|
-
const lockPath =
|
|
5028
|
-
if (!
|
|
5153
|
+
const lockPath = join14(dir, SKILLS_LOCK2);
|
|
5154
|
+
if (!existsSync12(lockPath)) return removed;
|
|
5029
5155
|
try {
|
|
5030
5156
|
const lock = JSON.parse(readFileSync7(lockPath, "utf8"));
|
|
5031
5157
|
for (const entry of Object.values(lock)) {
|
|
5032
5158
|
for (const name of entry.skills ?? []) {
|
|
5033
|
-
const p =
|
|
5034
|
-
if (
|
|
5159
|
+
const p = join14(dir, name);
|
|
5160
|
+
if (existsSync12(p)) {
|
|
5035
5161
|
rmSync3(p, { recursive: true, force: true });
|
|
5036
5162
|
removed.push(p);
|
|
5037
5163
|
}
|
|
@@ -5076,18 +5202,18 @@ function registerReset(program2) {
|
|
|
5076
5202
|
}
|
|
5077
5203
|
}
|
|
5078
5204
|
const removed = [];
|
|
5079
|
-
const stateDir =
|
|
5080
|
-
if (
|
|
5205
|
+
const stateDir = join14(process.cwd(), ".sechroom");
|
|
5206
|
+
if (existsSync12(stateDir)) {
|
|
5081
5207
|
rmSync3(stateDir, { recursive: true, force: true });
|
|
5082
5208
|
removed.push(stateDir);
|
|
5083
5209
|
}
|
|
5084
|
-
const legacyCfg =
|
|
5085
|
-
if (
|
|
5210
|
+
const legacyCfg = join14(process.cwd(), ".sechroom.json");
|
|
5211
|
+
if (existsSync12(legacyCfg)) {
|
|
5086
5212
|
rmSync3(legacyCfg, { force: true });
|
|
5087
5213
|
removed.push(legacyCfg);
|
|
5088
5214
|
}
|
|
5089
|
-
const legacySem =
|
|
5090
|
-
if (
|
|
5215
|
+
const legacySem = join14(process.cwd(), ".sem");
|
|
5216
|
+
if (existsSync12(legacySem)) {
|
|
5091
5217
|
rmSync3(legacySem, { force: true });
|
|
5092
5218
|
removed.push(legacySem);
|
|
5093
5219
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sechroom/cli",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.230-rc.326bc4de",
|
|
4
4
|
"description": "Sechroom CLI — a thin, generated client over the Sechroom HTTP API. An agent/human surface alongside MCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|