@mrciphersmith/keryx 0.2.74 → 0.2.75

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/cli.js +84 -11
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -16448,7 +16448,13 @@ function writeJsonl(file, history, ts) {
16448
16448
  function redactHistory(history) {
16449
16449
  return history.map((message) => ({
16450
16450
  ...message,
16451
- content: redactSensitiveText(message.content)
16451
+ content: redactSensitiveText(message.content),
16452
+ ...message.toolCalls === undefined ? {} : {
16453
+ toolCalls: message.toolCalls.map((call) => ({
16454
+ ...call,
16455
+ arguments: redactSensitiveText(call.arguments)
16456
+ }))
16457
+ }
16452
16458
  }));
16453
16459
  }
16454
16460
  function readJsonl2(file) {
@@ -17613,6 +17619,9 @@ async function createProjectSkill(projectRoot, options) {
17613
17619
  if (!await pathExists(metaprojectRoot)) {
17614
17620
  throw new Error("Metaproject is not initialized. Run: keryx init");
17615
17621
  }
17622
+ if (!isRoutableSkillTarget(options.target)) {
17623
+ throw new Error(`keryx skills create: target ${JSON.stringify(options.target)} reads as prose, not a routing key. ` + 'A target is what `keryx skills route` matches against \u2014 a concept ("auth flow"), a symbol (`IResultDqReport`), ' + "or a path (`src/dq/DqScoreCard.tsx`). A sentence matches nothing and verifies as permanently stale.");
17624
+ }
17616
17625
  const moduleName = slugify3(options.module ?? inferModule(options.target));
17617
17626
  const skillName = slugify3(options.name ?? inferSkillName(options.target));
17618
17627
  const format = options.format ?? "auto";
@@ -18605,6 +18614,23 @@ function authorityFor(manifest, subject) {
18605
18614
  function normalizeProposalLifecycleResult(value) {
18606
18615
  return JSON.parse(JSON.stringify(value));
18607
18616
  }
18617
+ async function readProposalSecurityGate(cwd, workspaceId, proposalId) {
18618
+ const file = path86.join(cwd, ".metaproject", "workspaces", workspaceId, "proposals", `${proposalId}.json`);
18619
+ let parsed;
18620
+ try {
18621
+ parsed = JSON.parse(await readFile44(file, "utf8"));
18622
+ } catch {
18623
+ return;
18624
+ }
18625
+ if (typeof parsed !== "object" || parsed === null)
18626
+ return;
18627
+ const record = parsed;
18628
+ const gate = record.security?.gate;
18629
+ if (gate !== "pass" && gate !== "needs-approval")
18630
+ return;
18631
+ const evidenceRefs = Array.isArray(record.evidence) ? record.evidence.map((entry) => typeof entry === "object" && entry !== null ? entry.uri : undefined).filter((uri) => typeof uri === "string") : [];
18632
+ return { gate, evidenceRefs };
18633
+ }
18608
18634
  var ProposalLifecycleError;
18609
18635
  var init_proposal_lifecycle = __esm(() => {
18610
18636
  init_fs();
@@ -18788,13 +18814,16 @@ async function digestLedgerPrefix(handle, byteLength) {
18788
18814
  return digest2;
18789
18815
  const buffer = Buffer.alloc(Math.min(ledgerDigestChunkBytes, byteLength));
18790
18816
  let position = 0;
18791
- while (position < byteLength) {
18817
+ const maxChunks = Math.ceil(byteLength / buffer.length);
18818
+ for (let chunk = 0;chunk < maxChunks && position < byteLength; chunk += 1) {
18792
18819
  const result = await handle.read(buffer, 0, Math.min(buffer.length, byteLength - position), position);
18793
18820
  if (result.bytesRead === 0)
18794
18821
  return;
18795
18822
  digest2.update(buffer.subarray(0, result.bytesRead));
18796
18823
  position += result.bytesRead;
18797
18824
  }
18825
+ if (position < byteLength)
18826
+ return;
18798
18827
  return digest2;
18799
18828
  }
18800
18829
  function parseCheckpoint(value) {
@@ -33530,6 +33559,28 @@ async function validateValue(value, schema, valuePath, errors, rootSchema, schem
33530
33559
  message: `Expected array length >= ${schema.minItems}`
33531
33560
  });
33532
33561
  }
33562
+ if (Array.isArray(value) && schema.maxItems !== undefined && value.length > schema.maxItems) {
33563
+ errors.push({
33564
+ path: valuePath,
33565
+ message: `Expected array length <= ${schema.maxItems}`
33566
+ });
33567
+ }
33568
+ if (typeof value === "number" && schema.exclusiveMinimum !== undefined && value <= schema.exclusiveMinimum) {
33569
+ errors.push({
33570
+ path: valuePath,
33571
+ message: `Expected number > ${schema.exclusiveMinimum}`
33572
+ });
33573
+ }
33574
+ if (schema.not !== undefined) {
33575
+ const negated = [];
33576
+ await validateValue(value, schema.not, valuePath, negated, rootSchema, schemaCache);
33577
+ if (negated.length === 0) {
33578
+ errors.push({
33579
+ path: valuePath,
33580
+ message: "Expected value NOT to match the prohibited subschema"
33581
+ });
33582
+ }
33583
+ }
33533
33584
  if (typeof value === "string") {
33534
33585
  if (schema.minLength !== undefined && value.length < schema.minLength) {
33535
33586
  errors.push({
@@ -40350,7 +40401,7 @@ ${renderUntrackedFiles(untracked, config)}
40350
40401
  ` : ""}
40351
40402
  ## Risk Hints
40352
40403
 
40353
- ${renderRiskHints(risky, shape)}
40404
+ ${renderRiskHints(risky, shape, shape.files)}
40354
40405
 
40355
40406
  ## Hunks
40356
40407
 
@@ -40723,13 +40774,19 @@ function renderDiffCounts(file) {
40723
40774
  }
40724
40775
  return file.changed !== undefined ? `: ~${file.changed} changed` : "";
40725
40776
  }
40726
- function renderRiskHints(risky, shape) {
40777
+ function renderRiskHints(risky, shape, files) {
40727
40778
  const hints = risky.map((file) => `- ${file.path}`);
40728
40779
  if (shape.pathsAbbreviated) {
40729
40780
  hints.push("- (paths are abbreviated by `--stat`; risk matching is unreliable \u2014 re-run with `--numstat` or `--name-only` to check)");
40730
40781
  }
40731
40782
  if (hints.length === 0) {
40732
- return shape.mode === "unenumerable" ? "- unknown: no file list in this output shape" : "- none";
40783
+ if (shape.mode === "unenumerable") {
40784
+ return "- unknown: no file list in this output shape";
40785
+ }
40786
+ if (files.length === 0 && (shape.reportedFileCount ?? 0) > 0) {
40787
+ return `- unknown: ${shape.reportedFileCount} file(s) changed but this output shape lists no per-file rows \u2014 nothing was checked. Re-run with \`--numstat\` or \`--name-only\`.`;
40788
+ }
40789
+ return "- none";
40733
40790
  }
40734
40791
  return hints.join(`
40735
40792
  `);
@@ -49574,8 +49631,11 @@ init_project_skills();
49574
49631
  import { readFile as readFile72, readdir as readdir24 } from "fs/promises";
49575
49632
  import path135 from "path";
49576
49633
  var PROJECT_REVIEWER_MODULE = "review";
49634
+ function escapeRegexLiteral(literal) {
49635
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
49636
+ }
49577
49637
  function metadataLine(content, label) {
49578
- const match = content.match(new RegExp(`^${label.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")}:\\s*(.+)$`, "m"));
49638
+ const match = content.match(new RegExp(`^${escapeRegexLiteral(label)}:\\s*(.+)$`, "m"));
49579
49639
  return match?.[1]?.trim();
49580
49640
  }
49581
49641
  async function skillDirs(root) {
@@ -63322,7 +63382,7 @@ import { spawnSync as spawnSync2 } from "child_process";
63322
63382
  // package.json
63323
63383
  var package_default = {
63324
63384
  name: "@mrciphersmith/keryx",
63325
- version: "0.2.74",
63385
+ version: "0.2.75",
63326
63386
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
63327
63387
  private: false,
63328
63388
  publishConfig: {
@@ -82895,13 +82955,26 @@ async function workspaceCommand(args2) {
82895
82955
  return;
82896
82956
  }
82897
82957
  if (subcommand === "confirm-review") {
82898
- rejectUnknownOptions(args2.slice(3), new Set);
82958
+ rejectUnknownOptions(args2.slice(3), new Set(["--acknowledge-security"]));
82899
82959
  const workspaceId = args2[1];
82900
82960
  const proposalId = args2[2];
82901
82961
  if (!workspaceId || !proposalId)
82902
- throw new Error("Usage: keryx workspace confirm-review <workspace-id> <proposal-id>");
82903
- const { token, expiresAt } = await mintConfirmToken(process.cwd(), workspaceId, proposalId, { securityAcknowledged: true });
82904
- console.log(JSON.stringify({ token, expiresAt }, null, 2));
82962
+ throw new Error("Usage: keryx workspace confirm-review <workspace-id> <proposal-id> [--acknowledge-security]");
82963
+ const security = await readProposalSecurityGate(process.cwd(), workspaceId, proposalId);
82964
+ if (!security)
82965
+ throw new Error(`keryx workspace confirm-review: cannot read proposal ${proposalId} in workspace ${workspaceId} \u2014 refusing to mint a token for a proposal whose security gate is unknown`);
82966
+ const acknowledgeSecurity = booleanFlag(args2, "--acknowledge-security");
82967
+ if (security.gate === "needs-approval" && !acknowledgeSecurity) {
82968
+ throw new Error([
82969
+ `keryx workspace confirm-review: proposal ${proposalId} is gated \`needs-approval\` \u2014 its evidence tripped the security scan.`,
82970
+ ...security.evidenceRefs.length > 0 ? [`Evidence: ${security.evidenceRefs.join(", ")}`] : [],
82971
+ "Read the evidence, then re-run with --acknowledge-security to state that you have.",
82972
+ "Accepting without reading it is the thing this gate exists to prevent."
82973
+ ].join(`
82974
+ `));
82975
+ }
82976
+ const { token, expiresAt } = await mintConfirmToken(process.cwd(), workspaceId, proposalId, { securityAcknowledged: security.gate === "needs-approval" && acknowledgeSecurity });
82977
+ console.log(JSON.stringify({ token, expiresAt, securityGate: security.gate, securityAcknowledged: security.gate === "needs-approval" && acknowledgeSecurity }, null, 2));
82905
82978
  return;
82906
82979
  }
82907
82980
  if (subcommand === "review") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.74",
3
+ "version": "0.2.75",
4
4
  "description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
5
5
  "private": false,
6
6
  "publishConfig": {