@synkro-sh/cli 1.7.43 → 1.7.45

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/bootstrap.js CHANGED
@@ -4826,7 +4826,7 @@ function writeConfigEnv(opts) {
4826
4826
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
4827
4827
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
4828
4828
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
4829
- `SYNKRO_VERSION=${shellQuoteSingle("1.7.43")}`
4829
+ `SYNKRO_VERSION=${shellQuoteSingle("1.7.45")}`
4830
4830
  ];
4831
4831
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
4832
4832
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -6106,7 +6106,10 @@ async function syncSkillFiles() {
6106
6106
  console.log(` indexing ${tasks.length} skill file${tasks.length === 1 ? "" : "s"} \u2014 comparing against existing ruleset (this may take a few seconds)...`);
6107
6107
  await ingestSkillTasks(tasks, mcpPort);
6108
6108
  }
6109
- function discoverSkillFiles(repoRoot, excludeHashes) {
6109
+ function normSkillName(name) {
6110
+ return name.toLowerCase().replace(/\.mdx?$/, "");
6111
+ }
6112
+ function discoverSkillFiles(repoRoot, excludeHashes, ingestedHashes, ingestedNames) {
6110
6113
  const roots = [];
6111
6114
  const add = (p) => {
6112
6115
  try {
@@ -6137,7 +6140,8 @@ function discoverSkillFiles(repoRoot, excludeHashes) {
6137
6140
  const hash = createHash("sha256").update(content).digest("hex");
6138
6141
  if (seen.has(hash) || excludeHashes.has(hash)) return;
6139
6142
  seen.add(hash);
6140
- out.push({ source: `skill:${name}`, content, name, hash });
6143
+ const ingested = ingestedHashes.has(hash) || ingestedNames.has(normSkillName(name));
6144
+ out.push({ source: `skill:${name}`, content, name, hash, ingested });
6141
6145
  };
6142
6146
  for (const root of roots) {
6143
6147
  let entries = [];
@@ -6170,19 +6174,29 @@ function discoverySetHash(found) {
6170
6174
  }
6171
6175
  async function promptSkillDiscovery(found) {
6172
6176
  if (!process.stdin.isTTY || found.length === 0) return [];
6177
+ const selectableIdx = found.map((s, i) => s.ingested ? -1 : i).filter((i) => i >= 0);
6178
+ if (selectableIdx.length === 0) return [];
6173
6179
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
6174
6180
  const ask3 = (q) => new Promise((res) => rl.question(q, (a) => res(a.trim())));
6175
6181
  try {
6176
6182
  console.log(`
6177
6183
  Found ${found.length} skill${found.length === 1 ? "" : "s"} in your Claude Code / skills.sh folders:`);
6178
- found.forEach((s, i) => console.log(` ${i + 1}. ${s.name}`));
6179
- console.log("Ingest into Synkro as enforceable rules? Each is security-vetted first \u2014 anything");
6184
+ found.forEach((s, i) => console.log(` ${i + 1}. ${s.name}${s.ingested ? " \u2014 already ingested (not selectable)" : ""}`));
6185
+ console.log("Ingest the uningested skills as enforceable rules? Each is security-vetted first \u2014 anything");
6180
6186
  console.log("malicious (prompt injection, guardrail-weakening, secret exfil) is blocked and reported.");
6181
- const a = (await ask3("Ingest [all] / type numbers (e.g. 1,3) / [skip]: ")).toLowerCase();
6187
+ const a = (await ask3(`Ingest [all] (${selectableIdx.length} uningested) / type numbers (e.g. 1,3) / [skip]: `)).toLowerCase();
6182
6188
  if (a === "skip" || a === "n" || a === "no" || a === "s") return [];
6183
- if (a === "" || a === "all" || a === "y" || a === "yes" || a === "a") return found.map((_, i) => i);
6189
+ if (a === "" || a === "all" || a === "y" || a === "yes" || a === "a") return selectableIdx;
6184
6190
  const picks = a.split(/[\s,]+/).map((x) => parseInt(x, 10) - 1).filter((i) => Number.isInteger(i) && i >= 0 && i < found.length);
6185
- return [...new Set(picks)];
6191
+ const valid = [];
6192
+ for (const i of [...new Set(picks)]) {
6193
+ if (found[i].ingested) {
6194
+ console.log(` \xB7 ${found[i].name} is already ingested \u2014 skipped`);
6195
+ continue;
6196
+ }
6197
+ valid.push(i);
6198
+ }
6199
+ return valid;
6186
6200
  } finally {
6187
6201
  rl.close();
6188
6202
  }
@@ -6191,6 +6205,7 @@ async function discoverAndIngestSkills() {
6191
6205
  try {
6192
6206
  const sf = readFullSynkroFile();
6193
6207
  const repoRoot = sf?._repoRoot || detectGitRepo2();
6208
+ const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
6194
6209
  const excludeHashes = /* @__PURE__ */ new Set();
6195
6210
  if (sf?.skills?.length) {
6196
6211
  for (const fp of resolveSkillPaths(sf.skills, sf._repoRoot)) {
@@ -6200,9 +6215,30 @@ async function discoverAndIngestSkills() {
6200
6215
  }
6201
6216
  }
6202
6217
  }
6203
- const found = discoverSkillFiles(repoRoot, excludeHashes);
6218
+ const ingestedHashes = /* @__PURE__ */ new Set();
6219
+ const ingestedNames = /* @__PURE__ */ new Set();
6220
+ try {
6221
+ const resp = await fetch(`http://127.0.0.1:${mcpPort}/api/local/skills?status=active`, { signal: AbortSignal.timeout(5e3) });
6222
+ if (resp.ok) {
6223
+ const { skills } = await resp.json();
6224
+ for (const s of skills || []) {
6225
+ if (s.content_hash) ingestedHashes.add(s.content_hash);
6226
+ if (s.name) ingestedNames.add(normSkillName(String(s.name)));
6227
+ }
6228
+ }
6229
+ } catch {
6230
+ }
6231
+ const found = discoverSkillFiles(repoRoot, excludeHashes, ingestedHashes, ingestedNames);
6204
6232
  if (found.length === 0) return;
6205
- const setHash = discoverySetHash(found);
6233
+ const selectable = found.filter((f) => !f.ingested);
6234
+ if (selectable.length === 0) {
6235
+ const names = found.map((f) => f.name);
6236
+ const shown = names.slice(0, 5).join(", ") + (names.length > 5 ? `, +${names.length - 5} more` : "");
6237
+ console.log(`
6238
+ \u2713 ${found.length} skill${found.length === 1 ? "" : "s"} already ingested (${shown}) \u2014 nothing new to add.`);
6239
+ return;
6240
+ }
6241
+ const setHash = discoverySetHash(selectable);
6206
6242
  let prev = "";
6207
6243
  try {
6208
6244
  prev = readFileSync11(SKILLS_DISCOVERED_PATH, "utf-8").trim();
@@ -6218,7 +6254,6 @@ async function discoverAndIngestSkills() {
6218
6254
  console.log(" Skipped \u2014 you can ingest skills anytime from the Synkro dashboard or via the MCP tool.");
6219
6255
  return;
6220
6256
  }
6221
- const mcpPort = process.env.SYNKRO_MCP_PORT || "18931";
6222
6257
  console.log(` Ingesting ${picks.length} skill${picks.length === 1 ? "" : "s"} (security-vetting each first)\u2026`);
6223
6258
  const summary = await ingestSkillTasks(picks.map((i) => ({ source: found[i].source, content: found[i].content })), mcpPort);
6224
6259
  const parts = [`${summary.ingested} ingested (${summary.rules} rule${summary.rules === 1 ? "" : "s"})`];
@@ -9043,7 +9078,7 @@ var args = process.argv.slice(2);
9043
9078
  var cmd = args[0] || "";
9044
9079
  var subArgs = args.slice(1);
9045
9080
  function printVersion() {
9046
- console.log("1.7.43");
9081
+ console.log("1.7.45");
9047
9082
  }
9048
9083
  function printHelp2() {
9049
9084
  console.log(`Synkro CLI \u2014 runtime safety for AI coding agents