@mrciphersmith/keryx 0.3.3 → 0.3.5

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 (67) hide show
  1. package/dist/cli.js +125 -51
  2. package/package.json +1 -1
  3. package/src/gdskills/bundled/install-manifest.json +231 -2
  4. package/src/gdskills/bundled/stacks/csharp-dotnet/agent-refs.json +4 -0
  5. package/src/gdskills/bundled/stacks/csharp-dotnet/governance/eval.json +1881 -0
  6. package/src/gdskills/bundled/stacks/csharp-dotnet/governance/scout.json +33 -0
  7. package/src/gdskills/bundled/stacks/csharp-dotnet/pack.json +38 -0
  8. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/coding-style.mdc +100 -0
  9. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/patterns.mdc +107 -0
  10. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/security.mdc +86 -0
  11. package/src/gdskills/bundled/stacks/csharp-dotnet/rules/testing.mdc +89 -0
  12. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/SKILL.md +143 -0
  13. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-build-fix/evals.json +77 -0
  14. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/SKILL.md +121 -0
  15. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-code-review/evals.json +77 -0
  16. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/SKILL.md +134 -0
  17. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-implementation/evals.json +76 -0
  18. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/SKILL.md +130 -0
  19. package/src/gdskills/bundled/stacks/csharp-dotnet/skills/dotnet-testing/evals.json +77 -0
  20. package/src/gdskills/bundled/stacks/flutter-dart/agent-refs.json +4 -0
  21. package/src/gdskills/bundled/stacks/flutter-dart/governance/eval.json +1849 -0
  22. package/src/gdskills/bundled/stacks/flutter-dart/governance/scout.json +33 -0
  23. package/src/gdskills/bundled/stacks/flutter-dart/pack.json +41 -0
  24. package/src/gdskills/bundled/stacks/flutter-dart/rules/coding-style.mdc +98 -0
  25. package/src/gdskills/bundled/stacks/flutter-dart/rules/patterns.mdc +88 -0
  26. package/src/gdskills/bundled/stacks/flutter-dart/rules/security.mdc +91 -0
  27. package/src/gdskills/bundled/stacks/flutter-dart/rules/testing.mdc +101 -0
  28. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/SKILL.md +134 -0
  29. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-build-fix/evals.json +79 -0
  30. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/SKILL.md +124 -0
  31. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-code-review/evals.json +74 -0
  32. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/SKILL.md +139 -0
  33. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-implementation/evals.json +77 -0
  34. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/SKILL.md +134 -0
  35. package/src/gdskills/bundled/stacks/flutter-dart/skills/flutter-testing/evals.json +74 -0
  36. package/src/gdskills/bundled/stacks/kotlin-android/agent-refs.json +4 -0
  37. package/src/gdskills/bundled/stacks/kotlin-android/governance/eval.json +1889 -0
  38. package/src/gdskills/bundled/stacks/kotlin-android/governance/scout.json +34 -0
  39. package/src/gdskills/bundled/stacks/kotlin-android/pack.json +38 -0
  40. package/src/gdskills/bundled/stacks/kotlin-android/rules/coding-style.mdc +89 -0
  41. package/src/gdskills/bundled/stacks/kotlin-android/rules/patterns.mdc +96 -0
  42. package/src/gdskills/bundled/stacks/kotlin-android/rules/security.mdc +90 -0
  43. package/src/gdskills/bundled/stacks/kotlin-android/rules/testing.mdc +89 -0
  44. package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/SKILL.md +150 -0
  45. package/src/gdskills/bundled/stacks/kotlin-android/skills/compose-implementation/evals.json +77 -0
  46. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/SKILL.md +151 -0
  47. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-build-fix/evals.json +76 -0
  48. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/SKILL.md +139 -0
  49. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-code-review/evals.json +78 -0
  50. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/SKILL.md +131 -0
  51. package/src/gdskills/bundled/stacks/kotlin-android/skills/kotlin-android-testing/evals.json +77 -0
  52. package/src/gdskills/bundled/stacks/swift-ios/agent-refs.json +4 -0
  53. package/src/gdskills/bundled/stacks/swift-ios/governance/eval.json +1803 -0
  54. package/src/gdskills/bundled/stacks/swift-ios/governance/scout.json +32 -0
  55. package/src/gdskills/bundled/stacks/swift-ios/pack.json +38 -0
  56. package/src/gdskills/bundled/stacks/swift-ios/rules/coding-style.mdc +92 -0
  57. package/src/gdskills/bundled/stacks/swift-ios/rules/patterns.mdc +112 -0
  58. package/src/gdskills/bundled/stacks/swift-ios/rules/security.mdc +78 -0
  59. package/src/gdskills/bundled/stacks/swift-ios/rules/testing.mdc +90 -0
  60. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/SKILL.md +144 -0
  61. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-build-fix/evals.json +75 -0
  62. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/SKILL.md +122 -0
  63. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-code-review/evals.json +75 -0
  64. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/SKILL.md +131 -0
  65. package/src/gdskills/bundled/stacks/swift-ios/skills/swift-testing/evals.json +75 -0
  66. package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/SKILL.md +149 -0
  67. package/src/gdskills/bundled/stacks/swift-ios/skills/swiftui-implementation/evals.json +76 -0
package/dist/cli.js CHANGED
@@ -17259,7 +17259,7 @@ var package_default;
17259
17259
  var init_package = __esm(() => {
17260
17260
  package_default = {
17261
17261
  name: "@mrciphersmith/keryx",
17262
- version: "0.3.3",
17262
+ version: "0.3.5",
17263
17263
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
17264
17264
  private: false,
17265
17265
  publishConfig: {
@@ -96156,6 +96156,28 @@ function clauseFileKindApplicability(sourceCategory, clauseText, changedFileKind
96156
96156
  }
96157
96157
  return { applicable: true, reason: "hunk is a code file; rule source is not categorised docs" };
96158
96158
  }
96159
+ var API_SCOPE_TERMS = ["api", "endpoint", "rest", "http", "openapi"];
96160
+ function isApiScopedRuleSource(source) {
96161
+ const haystack = `${(source.declaredPaths ?? []).join(" ")} ${ruleSourceTitle(source.text)} ${source.path}`.toLowerCase();
96162
+ return API_SCOPE_TERMS.some((term) => new RegExp(`\\b${term}\\b`).test(haystack));
96163
+ }
96164
+ var API_SURFACE_DIR_RE = /(^|\/)(api|routes|controllers|handlers|server)\//i;
96165
+ var HTTP_FRAMEWORK_IMPORT_RE = /\bfrom\s*["'](?:express|fastify|koa|hono|@hono\/[\w-]+|hapi|@nestjs\/(?:common|core)|next\/server|h3)["']/;
96166
+ function looksLikeApiSurfaceHunk(region) {
96167
+ return API_SURFACE_DIR_RE.test(region.path) || HTTP_FRAMEWORK_IMPORT_RE.test(region.text);
96168
+ }
96169
+ function clauseApiScopeApplicability(source, region) {
96170
+ if (!isApiScopedRuleSource(source)) {
96171
+ return { applicable: true, reason: "rule source is not API/endpoint-scoped \u2014 this gate does not apply" };
96172
+ }
96173
+ if (looksLikeApiSurfaceHunk(region)) {
96174
+ return { applicable: true, reason: `rule source is API/endpoint-scoped and ${region.path} looks like an API surface` };
96175
+ }
96176
+ return {
96177
+ applicable: false,
96178
+ reason: `rule source is API/endpoint-scoped (its declared paths/description/title name one of: ${API_SCOPE_TERMS.join(", ")}) but ${region.path} ` + "does not look like an API surface (no api/routes/controllers/handlers/server path segment, and this hunk imports no known HTTP framework)"
96179
+ };
96180
+ }
96159
96181
  var DEFAULT_MAX_JEV_RULE_CALLS = 150;
96160
96182
  function isHunkCheckableClause(clause) {
96161
96183
  return clause.state_kind === "hunk" && clause.checkable;
@@ -96199,6 +96221,11 @@ function selectRuleHunkPairs(regions, taggedSources, detectedStack, maxCalls = D
96199
96221
  notApplicable.push({ region, ruleId: source.path, reason: decision.reason });
96200
96222
  continue;
96201
96223
  }
96224
+ const apiScopeDecision = clauseApiScopeApplicability(source, region);
96225
+ if (!apiScopeDecision.applicable) {
96226
+ notApplicable.push({ region, ruleId: source.path, reason: apiScopeDecision.reason });
96227
+ continue;
96228
+ }
96202
96229
  let sawHunkCheckable = false;
96203
96230
  let anyFileKindApplicable = false;
96204
96231
  for (const clause of clauses) {
@@ -97042,6 +97069,10 @@ function touchedExportedSymbols(regionText) {
97042
97069
  return [...symbols];
97043
97070
  }
97044
97071
  var TEST_INFIX_RE = /\.(test|spec)\./;
97072
+ var TEST_DIR_RE = /(^|\/)(__tests__|tests?)\//;
97073
+ function isTestFilePath(path) {
97074
+ return TEST_INFIX_RE.test(path) || TEST_DIR_RE.test(path);
97075
+ }
97045
97076
  function stemOf(filePath) {
97046
97077
  const base = filePath.split("/").at(-1) ?? filePath;
97047
97078
  return base.replace(/\.(test|spec)\.[jt]sx?$/, "").replace(/\.[jt]sx?$/, "");
@@ -97056,7 +97087,7 @@ function testFilesTouchedNearby(regionPath, allChangedFiles) {
97056
97087
  return allChangedFiles.filter((file) => {
97057
97088
  if (file === regionPath)
97058
97089
  return false;
97059
- if (!TEST_INFIX_RE.test(file) && !/(^|\/)(__tests__|tests?)\//.test(file))
97090
+ if (!isTestFilePath(file))
97060
97091
  return false;
97061
97092
  return stemOf(file) === stem || dirOf(file) === dir;
97062
97093
  });
@@ -97163,7 +97194,10 @@ function batchRiskQuestionsForHunk(facts) {
97163
97194
  return batches;
97164
97195
  }
97165
97196
  var NON_CODE_HUNK_RE = /\.(md|txt)$/i;
97197
+ var GENERATED_METADATA_PATH_RE = /(^|\/)\.metaproject\/(flows|data|reviews)\//;
97166
97198
  function isNonCodeHunk(path) {
97199
+ if (GENERATED_METADATA_PATH_RE.test(path))
97200
+ return true;
97167
97201
  if (/(^|\/)(__fixtures__|fixtures|testdata|__snapshots__|snapshots)\//.test(path))
97168
97202
  return false;
97169
97203
  return NON_CODE_HUNK_RE.test(path);
@@ -97237,6 +97271,8 @@ function synthesizeRiskFindings(scored, threshold = DEFAULT_JEV_RISK_THRESHOLD)
97237
97271
  for (const hunk of rankHunksByRisk(scored)) {
97238
97272
  if (hunk.combinedRisk < threshold || hunk.facts.hasNearbyTest)
97239
97273
  continue;
97274
+ if (isTestFilePath(hunk.facts.region.path))
97275
+ continue;
97240
97276
  const { region } = hunk.facts;
97241
97277
  const key = `jev-risk::${region.path}:${region.startLine}-${region.endLine}`;
97242
97278
  const severity = hunk.combinedRisk >= 0.85 ? "minor" : "info";
@@ -97267,6 +97303,8 @@ function riskFindingStats(findings) {
97267
97303
  function computeRiskRoutingHints(scored, threshold = DEFAULT_JEV_RISK_THRESHOLD) {
97268
97304
  const hints = [];
97269
97305
  for (const hunk of rankHunksByRisk(scored)) {
97306
+ if (isTestFilePath(hunk.facts.region.path))
97307
+ continue;
97270
97308
  const { region } = hunk.facts;
97271
97309
  if (hunk.probabilities.security >= threshold) {
97272
97310
  hints.push({ file: region.path, startLine: region.startLine, endLine: region.endLine, dimension: "security", probability: hunk.probabilities.security, suggestedReviewer: "review-security-code" });
@@ -97420,7 +97458,7 @@ async function computeJevRiskResult(options) {
97420
97458
  topDimension: hunk.topDimension
97421
97459
  }));
97422
97460
  const status = findings.length > 0 ? "DONE_WITH_CONCERNS" : "DONE";
97423
- const summary = `Scored ${scored.length} hunk(s) (of ${selection.selected.length + selection.skipped.length} retained) across 5 risk dimensions against ${targetLabel}; ` + `${findings.length} finding(s) at/above threshold ${threshold} with no nearby test. ` + `${selection.skipped.length} hunk(s) skipped by --max-calls ${selection.maxCalls} (${selection.pairsSkipped} pair(s)); ` + `${selection.notCode.length} hunk(s) skipped as not a code hunk (docs/.md/.txt).`;
97461
+ const summary = `Scored ${scored.length} hunk(s) (of ${selection.selected.length + selection.skipped.length} retained) across 5 risk dimensions against ${targetLabel}; ` + `${findings.length} finding(s) at/above threshold ${threshold} with no nearby test. ` + `${selection.skipped.length} hunk(s) skipped by --max-calls ${selection.maxCalls} (${selection.pairsSkipped} pair(s)); ` + `${selection.notCode.length} hunk(s) skipped as not a code hunk (docs/.md/.txt, or .metaproject/{flows,data,reviews} bookkeeping).`;
97424
97462
  return {
97425
97463
  status,
97426
97464
  reviewer: "review-jev-risk",
@@ -98266,7 +98304,6 @@ function boundLinkedSections(linked, maxCalls = DEFAULT_MAX_JEV_DOCS_CALLS, maxP
98266
98304
  };
98267
98305
  }
98268
98306
  var JEV_DOCS_TOKEN_BUDGET = 64000;
98269
- var QUESTIONS_BUDGET_FRACTION4 = 0.5;
98270
98307
  var HUNK_TEXT_CHARS = 2000;
98271
98308
  var SECTION_TEXT_CHARS = 4000;
98272
98309
  function docsQuestionKey(linked) {
@@ -98300,19 +98337,25 @@ ${bounded}`;
98300
98337
  ].join(`
98301
98338
  `);
98302
98339
  }
98340
+ function buildDocsBatch(items) {
98341
+ return {
98342
+ items,
98343
+ state: items.map(renderSectionFacts).join(`
98344
+
98345
+ `),
98346
+ questions: Object.fromEntries(items.map((item) => [docsQuestionKey(item), questionFor4(item)]))
98347
+ };
98348
+ }
98349
+ var DOCS_BATCH_BUDGET_FRACTION = 0.4;
98350
+ var MAX_SECTIONS_PER_BATCH = 3;
98303
98351
  function batchDocsSections(items) {
98304
98352
  const batches = [];
98305
98353
  let current = [];
98354
+ const effectiveBudget = JEV_DOCS_TOKEN_BUDGET * DOCS_BATCH_BUDGET_FRACTION;
98306
98355
  const flush = () => {
98307
98356
  if (current.length === 0)
98308
98357
  return;
98309
- batches.push({
98310
- items: current,
98311
- state: current.map(renderSectionFacts).join(`
98312
-
98313
- `),
98314
- questions: Object.fromEntries(current.map((item) => [docsQuestionKey(item), questionFor4(item)]))
98315
- });
98358
+ batches.push(buildDocsBatch(current));
98316
98359
  current = [];
98317
98360
  };
98318
98361
  for (const item of items) {
@@ -98322,8 +98365,7 @@ function batchDocsSections(items) {
98322
98365
  `));
98323
98366
  const questionsTokens = estimateTokens2(attempt.map((linked) => `${docsQuestionKey(linked)}:noul:${questionFor4(linked).instructions}`).join(`
98324
98367
  `));
98325
- const withinQuestionsShare = questionsTokens <= JEV_DOCS_TOKEN_BUDGET * QUESTIONS_BUDGET_FRACTION4;
98326
- if (current.length > 0 && (stateTokens + questionsTokens > JEV_DOCS_TOKEN_BUDGET || !withinQuestionsShare)) {
98368
+ if (current.length > 0 && (attempt.length > MAX_SECTIONS_PER_BATCH || stateTokens + questionsTokens > effectiveBudget)) {
98327
98369
  flush();
98328
98370
  current = [item];
98329
98371
  } else {
@@ -98472,6 +98514,12 @@ function renderJevDocsMarkdown(result) {
98472
98514
  }
98473
98515
 
98474
98516
  // src/commands/review-jev-docs.ts
98517
+ function messageOf(error) {
98518
+ return error instanceof Error ? error.message : String(error);
98519
+ }
98520
+ function isMaxTokensExceededError(error) {
98521
+ return error instanceof JevRequestError && error.status === 400 && /max_tokens/i.test(error.message);
98522
+ }
98475
98523
  var JEV_DOCS_FLAGS = ["--diff", "--pr", "--max-calls", "--threshold", "--model", "--repo", "--fixtures", "--include", "--json"];
98476
98524
  function rejectUnknownJevDocsFlags(args) {
98477
98525
  const unknown = args.filter((arg) => arg.startsWith("--") && !JEV_DOCS_FLAGS.includes(arg.split("=")[0]));
@@ -98681,29 +98729,54 @@ async function computeJevDocsResult(options) {
98681
98729
  let outputTokens = 0;
98682
98730
  let costUsd = 0;
98683
98731
  let sawUsage = false;
98684
- for (const batch of batches) {
98685
- const result = await callJevSystemOne(fetchFn, { model, state: batch.state, questions: batch.questions }, { env: process.env });
98686
- jevCalls += 1;
98687
- if (result.usage.input_tokens !== undefined) {
98688
- inputTokens += result.usage.input_tokens;
98689
- sawUsage = true;
98690
- }
98691
- if (result.usage.output_tokens !== undefined) {
98692
- outputTokens += result.usage.output_tokens;
98693
- sawUsage = true;
98732
+ let jevError;
98733
+ const attemptBatch = async (batch) => {
98734
+ try {
98735
+ const result = await callJevSystemOne(fetchFn, { model, state: batch.state, questions: batch.questions }, { env: options.env ?? process.env });
98736
+ jevCalls += 1;
98737
+ if (result.usage.input_tokens !== undefined) {
98738
+ inputTokens += result.usage.input_tokens;
98739
+ sawUsage = true;
98740
+ }
98741
+ if (result.usage.output_tokens !== undefined) {
98742
+ outputTokens += result.usage.output_tokens;
98743
+ sawUsage = true;
98744
+ }
98745
+ if (result.usage.cost !== undefined) {
98746
+ costUsd += result.usage.cost;
98747
+ sawUsage = true;
98748
+ }
98749
+ for (const item of batch.items) {
98750
+ const key = `${item.section.file}::${item.section.line}`;
98751
+ const answer = result.answers[key];
98752
+ if (answer === undefined || answer.type !== "noul")
98753
+ continue;
98754
+ const finding = synthesizeDocsFinding(item, answer.noul, threshold);
98755
+ if (finding !== undefined)
98756
+ jevFindings.push(finding);
98757
+ }
98758
+ return { ok: true };
98759
+ } catch (error) {
98760
+ return { ok: false, error };
98694
98761
  }
98695
- if (result.usage.cost !== undefined) {
98696
- costUsd += result.usage.cost;
98697
- sawUsage = true;
98762
+ };
98763
+ for (const batch of batches) {
98764
+ const attempt = await attemptBatch(batch);
98765
+ if (attempt.ok)
98766
+ continue;
98767
+ if (isMaxTokensExceededError(attempt.error) && batch.items.length > 1) {
98768
+ const mid = Math.ceil(batch.items.length / 2);
98769
+ const halves = [buildDocsBatch(batch.items.slice(0, mid)), buildDocsBatch(batch.items.slice(mid))];
98770
+ for (const half of halves) {
98771
+ const halfAttempt = await attemptBatch(half);
98772
+ if (!halfAttempt.ok && jevError === undefined) {
98773
+ jevError = messageOf(halfAttempt.error);
98774
+ }
98775
+ }
98776
+ continue;
98698
98777
  }
98699
- for (const item of batch.items) {
98700
- const key = `${item.section.file}::${item.section.line}`;
98701
- const answer = result.answers[key];
98702
- if (answer === undefined || answer.type !== "noul")
98703
- continue;
98704
- const finding = synthesizeDocsFinding(item, answer.noul, threshold);
98705
- if (finding !== undefined)
98706
- jevFindings.push(finding);
98778
+ if (jevError === undefined) {
98779
+ jevError = messageOf(attempt.error);
98707
98780
  }
98708
98781
  }
98709
98782
  const findings = [...flagFindings, ...jevFindings].sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
@@ -98717,6 +98790,7 @@ async function computeJevDocsResult(options) {
98717
98790
  findings,
98718
98791
  stats,
98719
98792
  tokens: sawUsage ? { jevCalls, inputTokens, outputTokens, costUsd } : { jevCalls },
98793
+ ...jevError !== undefined ? { jevError } : {},
98720
98794
  selection: {
98721
98795
  maxCalls,
98722
98796
  maxPerFile,
@@ -98838,7 +98912,7 @@ function commentFactLines(facts) {
98838
98912
  ];
98839
98913
  }
98840
98914
  var COMMENT_TOKEN_BUDGET = 64000;
98841
- var QUESTIONS_BUDGET_FRACTION5 = 0.5;
98915
+ var QUESTIONS_BUDGET_FRACTION4 = 0.5;
98842
98916
  var REPLY_BODY_CHARS = 500;
98843
98917
  var HUNK_TEXT_CHARS2 = 1500;
98844
98918
  var COMMENT_BODY_CHARS = 1500;
@@ -98904,7 +98978,7 @@ function batchOpenComments(items) {
98904
98978
  `));
98905
98979
  const questionsTokens = estimateTokens2(attempt.map((f) => `${commentQuestionKey(f)}:choice`).join(`
98906
98980
  `));
98907
- const withinQuestionsShare = questionsTokens <= COMMENT_TOKEN_BUDGET * QUESTIONS_BUDGET_FRACTION5;
98981
+ const withinQuestionsShare = questionsTokens <= COMMENT_TOKEN_BUDGET * QUESTIONS_BUDGET_FRACTION4;
98908
98982
  if (current.length > 0 && (stateTokens + questionsTokens > COMMENT_TOKEN_BUDGET || !withinQuestionsShare)) {
98909
98983
  flush();
98910
98984
  current = [item];
@@ -99651,10 +99725,10 @@ function parseThreshold5(raw) {
99651
99725
  }
99652
99726
  return value;
99653
99727
  }
99654
- function messageOf(error) {
99728
+ function messageOf2(error) {
99655
99729
  return error instanceof Error ? error.message : String(error);
99656
99730
  }
99657
- function isMaxTokensExceededError(error) {
99731
+ function isMaxTokensExceededError2(error) {
99658
99732
  return error instanceof JevRequestError && error.status === 400 && /max_tokens/i.test(error.message);
99659
99733
  }
99660
99734
  async function fixturePrPort6(dir) {
@@ -99725,19 +99799,19 @@ async function computeJevContractResult(options) {
99725
99799
  const attempt = await attemptBatch(batch);
99726
99800
  if (attempt.ok)
99727
99801
  continue;
99728
- if (isMaxTokensExceededError(attempt.error) && batch.items.length > 1) {
99802
+ if (isMaxTokensExceededError2(attempt.error) && batch.items.length > 1) {
99729
99803
  const mid = Math.ceil(batch.items.length / 2);
99730
99804
  const halves = [buildContractClaimBatch(batch.items.slice(0, mid)), buildContractClaimBatch(batch.items.slice(mid))];
99731
99805
  for (const half of halves) {
99732
99806
  const halfAttempt = await attemptBatch(half);
99733
99807
  if (!halfAttempt.ok && jevError === undefined) {
99734
- jevError = messageOf(halfAttempt.error);
99808
+ jevError = messageOf2(halfAttempt.error);
99735
99809
  }
99736
99810
  }
99737
99811
  continue;
99738
99812
  }
99739
99813
  if (jevError === undefined) {
99740
- jevError = messageOf(attempt.error);
99814
+ jevError = messageOf2(attempt.error);
99741
99815
  }
99742
99816
  }
99743
99817
  for (const item of items) {
@@ -100149,10 +100223,10 @@ function parseThreshold6(raw) {
100149
100223
  return value;
100150
100224
  }
100151
100225
  var LIKELY_DUPLICATE_THRESHOLD = 0.5;
100152
- function messageOf2(error) {
100226
+ function messageOf3(error) {
100153
100227
  return error instanceof Error ? error.message : String(error);
100154
100228
  }
100155
- function isMaxTokensExceededError2(error) {
100229
+ function isMaxTokensExceededError3(error) {
100156
100230
  return error instanceof JevRequestError && error.status === 400 && /max_tokens/i.test(error.message);
100157
100231
  }
100158
100232
  async function loadReportRaw(reportPath) {
@@ -100226,19 +100300,19 @@ async function computeJevTriageResult(options) {
100226
100300
  const attempt = await attemptBatch(batch);
100227
100301
  if (attempt.ok)
100228
100302
  continue;
100229
- if (isMaxTokensExceededError2(attempt.error) && batch.items.length > 1) {
100303
+ if (isMaxTokensExceededError3(attempt.error) && batch.items.length > 1) {
100230
100304
  const mid = Math.ceil(batch.items.length / 2);
100231
100305
  const halves = [buildTriageBatch(batch.items.slice(0, mid)), buildTriageBatch(batch.items.slice(mid))];
100232
100306
  for (const half of halves) {
100233
100307
  const halfAttempt = await attemptBatch(half);
100234
100308
  if (!halfAttempt.ok && jevError === undefined) {
100235
- jevError = messageOf2(halfAttempt.error);
100309
+ jevError = messageOf3(halfAttempt.error);
100236
100310
  }
100237
100311
  }
100238
100312
  continue;
100239
100313
  }
100240
100314
  if (jevError === undefined) {
100241
- jevError = messageOf2(attempt.error);
100315
+ jevError = messageOf3(attempt.error);
100242
100316
  }
100243
100317
  }
100244
100318
  }
@@ -101421,7 +101495,7 @@ function applyDeterministicOverride(verdict, signals) {
101421
101495
  // src/review/conform-jev.ts
101422
101496
  init_service4();
101423
101497
  var CONFORM_TOKEN_BUDGET = 64000;
101424
- var QUESTIONS_BUDGET_FRACTION6 = 0.5;
101498
+ var QUESTIONS_BUDGET_FRACTION5 = 0.5;
101425
101499
  function questionFor6(clause) {
101426
101500
  return {
101427
101501
  type: "noul",
@@ -101461,7 +101535,7 @@ function batchConformItems(items, sharedRedactedText) {
101461
101535
  const factsTokens = estimateTokens2(renderFacts(attempt));
101462
101536
  const questionsTokens = estimateQuestionsTokens(attempt);
101463
101537
  const total = sharedTokens + factsTokens + questionsTokens;
101464
- const withinQuestionsShare = questionsTokens <= CONFORM_TOKEN_BUDGET * QUESTIONS_BUDGET_FRACTION6;
101538
+ const withinQuestionsShare = questionsTokens <= CONFORM_TOKEN_BUDGET * QUESTIONS_BUDGET_FRACTION5;
101465
101539
  if (current.length > 0 && (total > CONFORM_TOKEN_BUDGET || !withinQuestionsShare)) {
101466
101540
  flush();
101467
101541
  current = [item];
@@ -110205,7 +110279,7 @@ async function diagnose(server, options) {
110205
110279
  skipped: catalog.skipped
110206
110280
  };
110207
110281
  } catch (error) {
110208
- return { ...base, status: "failed", toolCount: 0, skipped: [], detail: messageOf3(error) };
110282
+ return { ...base, status: "failed", toolCount: 0, skipped: [], detail: messageOf4(error) };
110209
110283
  } finally {
110210
110284
  try {
110211
110285
  await connection.close();
@@ -110227,7 +110301,7 @@ function oauthErrorCodeOf(error) {
110227
110301
  return typeof code === "string" ? code : "";
110228
110302
  }
110229
110303
  function explainConnectFailure(transport, server, error) {
110230
- const message = sanitiseForDisplay(messageOf3(error));
110304
+ const message = sanitiseForDisplay(messageOf4(error));
110231
110305
  if (transport !== "http")
110232
110306
  return message;
110233
110307
  const url = displayUrl(server.raw.url);
@@ -110300,7 +110374,7 @@ function syscallCodeOf(error) {
110300
110374
  }
110301
110375
  return;
110302
110376
  }
110303
- function messageOf3(error) {
110377
+ function messageOf4(error) {
110304
110378
  return error instanceof Error ? error.message : String(error);
110305
110379
  }
110306
110380
  async function withTimeout(work, ms, name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
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": {
@@ -180,7 +180,15 @@
180
180
  "c-cpp-rules",
181
181
  "c-cpp-skills",
182
182
  "sql-db-rules",
183
- "sql-db-skills"
183
+ "sql-db-skills",
184
+ "csharp-dotnet-rules",
185
+ "csharp-dotnet-skills",
186
+ "swift-ios-rules",
187
+ "swift-ios-skills",
188
+ "kotlin-android-rules",
189
+ "kotlin-android-skills",
190
+ "flutter-dart-rules",
191
+ "flutter-dart-skills"
184
192
  ],
185
193
  "components": [
186
194
  "lang:ts-js-node",
@@ -197,7 +205,11 @@
197
205
  "framework:php-laravel",
198
206
  "framework:ruby-rails",
199
207
  "lang:c-cpp",
200
- "capability:sql-db"
208
+ "capability:sql-db",
209
+ "lang:csharp-dotnet",
210
+ "framework:swift-ios",
211
+ "framework:kotlin-android",
212
+ "framework:flutter-dart"
201
213
  ]
202
214
  },
203
215
  "php-laravel": {
@@ -243,6 +255,50 @@
243
255
  "capability:sql-db"
244
256
  ],
245
257
  "stackDetectionAware": true
258
+ },
259
+ "csharp-dotnet": {
260
+ "description": "Common rules plus the C#/.NET stack pack component (`csharp-dotnet-rules` + `csharp-dotnet-skills`, from src/gdskills/bundled/stacks/csharp-dotnet/), installed when `keryx stack detect` finds a matching tag.",
261
+ "modules": [
262
+ "core-common-rules",
263
+ "skill-lifecycle"
264
+ ],
265
+ "components": [
266
+ "lang:csharp-dotnet"
267
+ ],
268
+ "stackDetectionAware": true
269
+ },
270
+ "swift-ios": {
271
+ "description": "Common rules plus the Swift/iOS stack pack component (`swift-ios-rules` + `swift-ios-skills`, from src/gdskills/bundled/stacks/swift-ios/), installed when `keryx stack detect` finds a matching tag. Standalone pack: no `lang:swift` base exists yet (flow 336 description.md).",
272
+ "modules": [
273
+ "core-common-rules",
274
+ "skill-lifecycle"
275
+ ],
276
+ "components": [
277
+ "framework:swift-ios"
278
+ ],
279
+ "stackDetectionAware": true
280
+ },
281
+ "kotlin-android": {
282
+ "description": "Common rules plus the Kotlin/Android (Jetpack Compose) stack pack component (`kotlin-android-rules` + `kotlin-android-skills`), installed when `keryx stack detect` finds a matching tag. Standalone pack (flow 336 description.md).",
283
+ "modules": [
284
+ "core-common-rules",
285
+ "skill-lifecycle"
286
+ ],
287
+ "components": [
288
+ "framework:kotlin-android"
289
+ ],
290
+ "stackDetectionAware": true
291
+ },
292
+ "flutter-dart": {
293
+ "description": "Common rules plus the Flutter/Dart stack pack component (`flutter-dart-rules` + `flutter-dart-skills`), installed when `keryx stack detect` finds a matching tag. Standalone pack (flow 336 description.md).",
294
+ "modules": [
295
+ "core-common-rules",
296
+ "skill-lifecycle"
297
+ ],
298
+ "components": [
299
+ "framework:flutter-dart"
300
+ ],
301
+ "stackDetectionAware": true
246
302
  }
247
303
  },
248
304
  "modules": {
@@ -928,6 +984,126 @@
928
984
  "defaultInstall": true,
929
985
  "cost": "medium",
930
986
  "stability": "experimental"
987
+ },
988
+ "csharp-dotnet-rules": {
989
+ "kind": "rule",
990
+ "description": "C#/.NET stack pack rules (coding-style, patterns, security, testing; scoped to *.cs) from src/gdskills/bundled/stacks/csharp-dotnet/rules/.",
991
+ "paths": [
992
+ "src/gdskills/bundled/stacks/csharp-dotnet/rules/*.mdc"
993
+ ],
994
+ "targets": [
995
+ "claude",
996
+ "keryx-shell"
997
+ ],
998
+ "dependencies": [],
999
+ "defaultInstall": true,
1000
+ "cost": "light",
1001
+ "stability": "experimental"
1002
+ },
1003
+ "csharp-dotnet-skills": {
1004
+ "kind": "skill",
1005
+ "description": "C#/.NET stack pack skills (implementation, testing, code review, build-fix) from src/gdskills/bundled/stacks/csharp-dotnet/skills/.",
1006
+ "paths": [
1007
+ "src/gdskills/bundled/stacks/csharp-dotnet/skills/**"
1008
+ ],
1009
+ "targets": [
1010
+ "claude",
1011
+ "keryx-shell"
1012
+ ],
1013
+ "dependencies": [],
1014
+ "defaultInstall": true,
1015
+ "cost": "medium",
1016
+ "stability": "experimental"
1017
+ },
1018
+ "swift-ios-rules": {
1019
+ "kind": "rule",
1020
+ "description": "Swift/iOS stack pack rules (coding-style, patterns, security, testing; scoped to *.swift) from src/gdskills/bundled/stacks/swift-ios/rules/.",
1021
+ "paths": [
1022
+ "src/gdskills/bundled/stacks/swift-ios/rules/*.mdc"
1023
+ ],
1024
+ "targets": [
1025
+ "claude",
1026
+ "keryx-shell"
1027
+ ],
1028
+ "dependencies": [],
1029
+ "defaultInstall": true,
1030
+ "cost": "light",
1031
+ "stability": "experimental"
1032
+ },
1033
+ "swift-ios-skills": {
1034
+ "kind": "skill",
1035
+ "description": "Swift/iOS stack pack skills (implementation, testing, code review, build-fix) from src/gdskills/bundled/stacks/swift-ios/skills/.",
1036
+ "paths": [
1037
+ "src/gdskills/bundled/stacks/swift-ios/skills/**"
1038
+ ],
1039
+ "targets": [
1040
+ "claude",
1041
+ "keryx-shell"
1042
+ ],
1043
+ "dependencies": [],
1044
+ "defaultInstall": true,
1045
+ "cost": "medium",
1046
+ "stability": "experimental"
1047
+ },
1048
+ "kotlin-android-rules": {
1049
+ "kind": "rule",
1050
+ "description": "Kotlin/Android (Jetpack Compose) stack pack rules (coding-style, patterns, security, testing; scoped to *.kt/*.kts) from src/gdskills/bundled/stacks/kotlin-android/rules/.",
1051
+ "paths": [
1052
+ "src/gdskills/bundled/stacks/kotlin-android/rules/*.mdc"
1053
+ ],
1054
+ "targets": [
1055
+ "claude",
1056
+ "keryx-shell"
1057
+ ],
1058
+ "dependencies": [],
1059
+ "defaultInstall": true,
1060
+ "cost": "light",
1061
+ "stability": "experimental"
1062
+ },
1063
+ "kotlin-android-skills": {
1064
+ "kind": "skill",
1065
+ "description": "Kotlin/Android (Jetpack Compose) stack pack skills (implementation, testing, code review, build-fix) from src/gdskills/bundled/stacks/kotlin-android/skills/.",
1066
+ "paths": [
1067
+ "src/gdskills/bundled/stacks/kotlin-android/skills/**"
1068
+ ],
1069
+ "targets": [
1070
+ "claude",
1071
+ "keryx-shell"
1072
+ ],
1073
+ "dependencies": [],
1074
+ "defaultInstall": true,
1075
+ "cost": "medium",
1076
+ "stability": "experimental"
1077
+ },
1078
+ "flutter-dart-rules": {
1079
+ "kind": "rule",
1080
+ "description": "Flutter/Dart stack pack rules (coding-style, patterns, security, testing; scoped to *.dart) from src/gdskills/bundled/stacks/flutter-dart/rules/.",
1081
+ "paths": [
1082
+ "src/gdskills/bundled/stacks/flutter-dart/rules/*.mdc"
1083
+ ],
1084
+ "targets": [
1085
+ "claude",
1086
+ "keryx-shell"
1087
+ ],
1088
+ "dependencies": [],
1089
+ "defaultInstall": true,
1090
+ "cost": "light",
1091
+ "stability": "experimental"
1092
+ },
1093
+ "flutter-dart-skills": {
1094
+ "kind": "skill",
1095
+ "description": "Flutter/Dart stack pack skills (implementation, testing, code review, build-fix) from src/gdskills/bundled/stacks/flutter-dart/skills/.",
1096
+ "paths": [
1097
+ "src/gdskills/bundled/stacks/flutter-dart/skills/**"
1098
+ ],
1099
+ "targets": [
1100
+ "claude",
1101
+ "keryx-shell"
1102
+ ],
1103
+ "dependencies": [],
1104
+ "defaultInstall": true,
1105
+ "cost": "medium",
1106
+ "stability": "experimental"
931
1107
  }
932
1108
  },
933
1109
  "components": {
@@ -1139,6 +1315,59 @@
1139
1315
  "provenance": {
1140
1316
  "origin": "authored"
1141
1317
  }
1318
+ },
1319
+ "lang:csharp-dotnet": {
1320
+ "family": "language",
1321
+ "modules": [
1322
+ "csharp-dotnet-rules",
1323
+ "csharp-dotnet-skills"
1324
+ ],
1325
+ "detectionMarkers": [
1326
+ "csharp",
1327
+ "dotnet"
1328
+ ],
1329
+ "provenance": {
1330
+ "origin": "authored"
1331
+ }
1332
+ },
1333
+ "framework:swift-ios": {
1334
+ "family": "framework",
1335
+ "modules": [
1336
+ "swift-ios-rules",
1337
+ "swift-ios-skills"
1338
+ ],
1339
+ "detectionMarkers": [
1340
+ "ios"
1341
+ ],
1342
+ "provenance": {
1343
+ "origin": "authored"
1344
+ }
1345
+ },
1346
+ "framework:kotlin-android": {
1347
+ "family": "framework",
1348
+ "modules": [
1349
+ "kotlin-android-rules",
1350
+ "kotlin-android-skills"
1351
+ ],
1352
+ "detectionMarkers": [
1353
+ "android"
1354
+ ],
1355
+ "provenance": {
1356
+ "origin": "authored"
1357
+ }
1358
+ },
1359
+ "framework:flutter-dart": {
1360
+ "family": "framework",
1361
+ "modules": [
1362
+ "flutter-dart-rules",
1363
+ "flutter-dart-skills"
1364
+ ],
1365
+ "detectionMarkers": [
1366
+ "flutter"
1367
+ ],
1368
+ "provenance": {
1369
+ "origin": "authored"
1370
+ }
1142
1371
  }
1143
1372
  }
1144
1373
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "agents": [],
3
+ "note": "no pair -- honest DeepSeek deepseek-chat runner+judge gate (trials=10, strictness=high, flow 336) fails all 4 skills on trigger accuracy, never on behavior (every behavior scenario clears the 0.8 floor, mostly 1.0/1.0): dotnet-implementation TP 2/7 FP 1/8, dotnet-testing TP 4/7 FP 0/8, dotnet-code-review TP 3/7 FP 0/8, dotnet-build-fix TP 1/7 FP 0/8 (worst -- distinctive .NET vocabulary like NuGet/StyleCop/CS#### still did not route). Genuine routing weakness against an increasingly crowded catalog (12+ implement/test/review/build-fix category-mates across stacks), not an authoring defect -- stays experimental per flow 336's stack-pack-lessons.md rule against tuning descriptions to restate failing eval prompts."
4
+ }