@sechroom/cli 2026.6.136-rc.c2c1009c → 2026.6.138-rc.a8484373

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/dist/index.js +75 -28
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2943,57 +2943,104 @@ I can pin this checkout's lane so operator skills + the continuity hook resolve
2943
2943
  writePin(code || void 0, design || void 0);
2944
2944
  }
2945
2945
 
2946
- // src/setup/skills-offer.ts
2946
+ // src/setup/skill-resolution.ts
2947
+ var SYSTEM_WORKSPACE_ID = "wsp_system";
2947
2948
  var ROLE_TAG = "sechroom:role:skill-template";
2949
+ function tagsOf(row) {
2950
+ const m = row?.item ?? row;
2951
+ return m?.tags ?? m?.Tags ?? [];
2952
+ }
2948
2953
  function tagValue(tags, prefix) {
2949
2954
  return tags.find((t) => t.startsWith(prefix))?.slice(prefix.length);
2950
2955
  }
2951
- async function maybeOfferSkills(cfg, personalWorkspaceId, opts) {
2952
- if (!personalWorkspaceId || opts.dryRun) return;
2953
- const surface = opts.surface ?? "claude-code";
2954
- let rows = [];
2956
+ function bodyOf(row) {
2957
+ const m = row?.item ?? row;
2958
+ return m?.text ?? m?.Text ?? "";
2959
+ }
2960
+ function skillsFromRows(rows, surface, source) {
2961
+ const out = /* @__PURE__ */ new Map();
2962
+ for (const row of rows ?? []) {
2963
+ const tags = tagsOf(row);
2964
+ if (!tags.includes(ROLE_TAG)) continue;
2965
+ if (tagValue(tags, "target:") !== surface) continue;
2966
+ const name = tagValue(tags, "skill:");
2967
+ if (!name) continue;
2968
+ out.set(name, { name, body: bodyOf(row), source });
2969
+ }
2970
+ return out;
2971
+ }
2972
+ function resolveSkills(systemRows, personalRows, surface) {
2973
+ const merged = skillsFromRows(systemRows, surface, "system");
2974
+ for (const [name, skill] of skillsFromRows(
2975
+ personalRows,
2976
+ surface,
2977
+ "personal"
2978
+ )) {
2979
+ merged.set(name, skill);
2980
+ }
2981
+ return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
2982
+ }
2983
+
2984
+ // src/setup/skills-offer.ts
2985
+ async function fetchFeedRows(cfg, workspaceId) {
2955
2986
  try {
2956
2987
  const client = await makeClient(cfg);
2957
2988
  const feed = await client.GET("/workspaces/{workspaceId}/memories/feed", {
2958
2989
  params: {
2959
- path: { workspaceId: personalWorkspaceId },
2990
+ path: { workspaceId },
2991
+ // cascadeWorkspaces: skills land in an "Operator Skills" SUB-workspace;
2992
+ // includeText: the feed omits bodies by default, we need them for SKILL.md.
2960
2993
  query: { limit: 200, cascadeWorkspaces: true, includeText: true }
2961
2994
  }
2962
2995
  }).then((r) => r.data).catch(() => void 0);
2963
- rows = feed?.results ?? feed?.Results ?? [];
2996
+ return feed?.results ?? feed?.Results ?? [];
2964
2997
  } catch {
2965
- return;
2998
+ return [];
2966
2999
  }
2967
- const skills = rows.map((r) => r.item ?? r).filter((m) => {
2968
- const tags = m.tags ?? m.Tags ?? [];
2969
- return tags.includes(ROLE_TAG) && tagValue(tags, "target:") === surface;
2970
- });
2971
- if (skills.length === 0) return;
2972
- const byName = /* @__PURE__ */ new Map();
2973
- for (const m of skills) {
2974
- const name = tagValue(m.tags ?? m.Tags ?? [], "skill:");
2975
- if (name) byName.set(name, m);
3000
+ }
3001
+ async function maybeOfferSkills(cfg, personalWorkspaceId, opts) {
3002
+ const surface = opts.surface ?? "claude-code";
3003
+ const [systemRows, personalRows] = await Promise.all([
3004
+ fetchFeedRows(cfg, SYSTEM_WORKSPACE_ID),
3005
+ personalWorkspaceId ? fetchFeedRows(cfg, personalWorkspaceId) : Promise.resolve([])
3006
+ ]);
3007
+ const resolved = resolveSkills(systemRows, personalRows, surface);
3008
+ if (resolved.length === 0) return;
3009
+ if (opts.dryRun) {
3010
+ process.stderr.write(
3011
+ `
3012
+ Would materialise ${style.bold(String(resolved.length))} operator skill(s) for ${surface}:
3013
+ ` + resolved.map((s) => ` ${s.name} ${style.dim(`[${s.source}]`)}`).join("\n") + "\n"
3014
+ );
3015
+ return;
2976
3016
  }
2977
- const names = [...byName.keys()].sort();
2978
- if (names.length === 0) return;
3017
+ const names = resolved.map((s) => s.name);
2979
3018
  process.stderr.write(
2980
3019
  `
2981
- Found ${style.bold(String(names.length))} operator skill(s) installed in your workspace: ${names.join(", ")}.
3020
+ Found ${style.bold(String(names.length))} operator skill(s) available to you: ${names.join(", ")}.
2982
3021
  `
2983
3022
  );
2984
3023
  const dir = join5(homedir5(), ".claude", "skills");
2985
3024
  const materialise = opts.yes ? true : canPrompt() ? await promptYesNo(`Write them to ${dir}/ so ${surface} can use them?`) : false;
2986
3025
  if (!materialise) return;
2987
3026
  const written = [];
2988
- for (const [name, m] of byName) {
2989
- const body = m.text ?? m.Text ?? "";
2990
- mkdirSync5(join5(dir, name), { recursive: true });
2991
- writeFileSync5(join5(dir, name, "SKILL.md"), body.endsWith("\n") ? body : body + "\n");
2992
- written.push(name);
3027
+ for (const s of resolved) {
3028
+ mkdirSync5(join5(dir, s.name), { recursive: true });
3029
+ writeFileSync5(
3030
+ join5(dir, s.name, "SKILL.md"),
3031
+ s.body.endsWith("\n") ? s.body : s.body + "\n"
3032
+ );
3033
+ written.push(s.name);
2993
3034
  }
2994
- process.stderr.write(`${style.green("\u2713")} wrote ${written.length} skill(s) to ${dir}
2995
- `);
2996
- await ensureLanePin(cfg, { yes: opts.yes, dryRun: opts.dryRun, clients: [surface] });
3035
+ process.stderr.write(
3036
+ `${style.green("\u2713")} wrote ${written.length} skill(s) to ${dir}
3037
+ `
3038
+ );
3039
+ await ensureLanePin(cfg, {
3040
+ yes: opts.yes,
3041
+ dryRun: opts.dryRun,
3042
+ clients: [surface]
3043
+ });
2997
3044
  }
2998
3045
 
2999
3046
  // src/commands/setup.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sechroom/cli",
3
- "version": "2026.6.136-rc.c2c1009c",
3
+ "version": "2026.6.138-rc.a8484373",
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",